1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40/*!
41 \class QMovie
42
43 \inmodule QtGui
44
45 \brief The QMovie class is a convenience class for playing movies
46 with QImageReader.
47
48 This class is used to show simple animations without sound. If you want
49 to display video and media content, use the \l{Qt Multimedia}
50 multimedia framework instead.
51
52 First, create a QMovie object by passing either the name of a file or a
53 pointer to a QIODevice containing an animated image format to QMovie's
54 constructor. You can call isValid() to check if the image data is valid,
55 before starting the movie. To start the movie, call start(). QMovie will
56 enter \l Running state, and emit started() and stateChanged(). To get the
57 current state of the movie, call state().
58
59 To display the movie in your application, you can pass your QMovie object
60 to QLabel::setMovie(). Example:
61
62 \snippet code/src_gui_image_qmovie.cpp 0
63
64 Whenever a new frame is available in the movie, QMovie will emit
65 updated(). If the size of the frame changes, resized() is emitted. You can
66 call currentImage() or currentPixmap() to get a copy of the current
67 frame. When the movie is done, QMovie emits finished(). If any error
68 occurs during playback (i.e, the image file is corrupt), QMovie will emit
69 error().
70
71 You can control the speed of the movie playback by calling setSpeed(),
72 which takes the percentage of the original speed as an argument. Pause the
73 movie by calling setPaused(true). QMovie will then enter \l Paused state
74 and emit stateChanged(). If you call setPaused(false), QMovie will reenter
75 \l Running state and start the movie again. To stop the movie, call
76 stop().
77
78 Certain animation formats allow you to set the background color. You can
79 call setBackgroundColor() to set the color, or backgroundColor() to
80 retrieve the current background color.
81
82 currentFrameNumber() returns the sequence number of the current frame. The
83 first frame in the animation has the sequence number 0. frameCount()
84 returns the total number of frames in the animation, if the image format
85 supports this. You can call loopCount() to get the number of times the
86 movie should loop before finishing. nextFrameDelay() returns the number of
87 milliseconds the current frame should be displayed.
88
89 QMovie can be instructed to cache frames of an animation by calling
90 setCacheMode().
91
92 Call supportedFormats() for a list of formats that QMovie supports.
93
94 \sa QLabel, QImageReader, {Movie Example}
95*/
96
97/*! \enum QMovie::MovieState
98
99 This enum describes the different states of QMovie.
100
101 \value NotRunning The movie is not running. This is QMovie's initial
102 state, and the state it enters after stop() has been called or the movie
103 is finished.
104
105 \value Paused The movie is paused, and QMovie stops emitting updated() or
106 resized(). This state is entered after calling pause() or
107 setPaused(true). The current frame number it kept, and the movie will
108 continue with the next frame when unpause() or setPaused(false) is called.
109
110 \value Running The movie is running.
111*/
112
113/*! \enum QMovie::CacheMode
114
115 This enum describes the different cache modes of QMovie.
116
117 \value CacheNone No frames are cached (the default).
118
119 \value CacheAll All frames are cached.
120*/
121
122/*! \fn void QMovie::started()
123
124 This signal is emitted after QMovie::start() has been called, and QMovie
125 has entered QMovie::Running state.
126*/
127
128/*! \fn void QMovie::resized(const QSize &size)
129
130 This signal is emitted when the current frame has been resized to \a
131 size. This effect is sometimes used in animations as an alternative to
132 replacing the frame. You can call currentImage() or currentPixmap() to get a
133 copy of the updated frame.
134*/
135
136/*! \fn void QMovie::updated(const QRect &rect)
137
138 This signal is emitted when the rect \a rect in the current frame has been
139 updated. You can call currentImage() or currentPixmap() to get a copy of the
140 updated frame.
141*/
142
143/*! \fn void QMovie::frameChanged(int frameNumber)
144 \since 4.1
145
146 This signal is emitted when the frame number has changed to
147 \a frameNumber. You can call currentImage() or currentPixmap() to get a
148 copy of the frame.
149*/
150
151/*!
152 \fn void QMovie::stateChanged(QMovie::MovieState state)
153
154 This signal is emitted every time the state of the movie changes. The new
155 state is specified by \a state.
156
157 \sa QMovie::state()
158*/
159
160/*! \fn void QMovie::error(QImageReader::ImageReaderError error)
161
162 This signal is emitted by QMovie when the error \a error occurred during
163 playback. QMovie will stop the movie, and enter QMovie::NotRunning state.
164
165 \sa lastError(), lastErrorString()
166*/
167
168/*! \fn void QMovie::finished()
169
170 This signal is emitted when the movie has finished.
171
172 \sa QMovie::stop()
173*/
174
175#include "qmovie.h"
176
177#include "qglobal.h"
178#include "qelapsedtimer.h"
179#include "qimage.h"
180#include "qimagereader.h"
181#include "qpixmap.h"
182#include "qrect.h"
183#include "qelapsedtimer.h"
184#include "qtimer.h"
185#include "qpair.h"
186#include "qmap.h"
187#include "qlist.h"
188#include "qbuffer.h"
189#include "qdir.h"
190#include "private/qobject_p.h"
191
192#define QMOVIE_INVALID_DELAY -1
193
194QT_BEGIN_NAMESPACE
195
196class QFrameInfo
197{
198public:
199 QPixmap pixmap;
200 int delay;
201 bool endMark;
202 inline QFrameInfo(bool endMark)
203 : pixmap(QPixmap()), delay(QMOVIE_INVALID_DELAY), endMark(endMark)
204 { }
205
206 inline QFrameInfo()
207 : pixmap(QPixmap()), delay(QMOVIE_INVALID_DELAY), endMark(false)
208 { }
209
210 inline QFrameInfo(QPixmap &&pixmap, int delay)
211 : pixmap(std::move(pixmap)), delay(delay), endMark(false)
212 { }
213
214 inline bool isValid()
215 {
216 return endMark || !(pixmap.isNull() && (delay == QMOVIE_INVALID_DELAY));
217 }
218
219 inline bool isEndMarker()
220 { return endMark; }
221
222 static inline QFrameInfo endMarker()
223 { return QFrameInfo(true); }
224};
225Q_DECLARE_TYPEINFO(QFrameInfo, Q_MOVABLE_TYPE);
226
227class QMoviePrivate : public QObjectPrivate
228{
229 Q_DECLARE_PUBLIC(QMovie)
230
231public:
232 QMoviePrivate(QMovie *qq);
233 bool isDone();
234 bool next();
235 int speedAdjustedDelay(int delay) const;
236 bool isValid() const;
237 bool jumpToFrame(int frameNumber);
238 int frameCount() const;
239 bool jumpToNextFrame();
240 QFrameInfo infoForFrame(int frameNumber);
241 void reset();
242
243 inline void enterState(QMovie::MovieState newState) {
244 movieState = newState;
245 emit q_func()->stateChanged(state: newState);
246 }
247
248 // private slots
249 void _q_loadNextFrame();
250 void _q_loadNextFrame(bool starting);
251
252 QImageReader *reader;
253 int speed;
254 QMovie::MovieState movieState;
255 QRect frameRect;
256 QPixmap currentPixmap;
257 int currentFrameNumber;
258 int nextFrameNumber;
259 int greatestFrameNumber;
260 int nextDelay;
261 int playCounter;
262 qint64 initialDevicePos;
263 QMovie::CacheMode cacheMode;
264 bool haveReadAll;
265 bool isFirstIteration;
266 QMap<int, QFrameInfo> frameMap;
267 QString absoluteFilePath;
268
269 QTimer nextImageTimer;
270};
271
272/*! \internal
273 */
274QMoviePrivate::QMoviePrivate(QMovie *qq)
275 : reader(nullptr), speed(100), movieState(QMovie::NotRunning),
276 currentFrameNumber(-1), nextFrameNumber(0), greatestFrameNumber(-1),
277 nextDelay(0), playCounter(-1),
278 cacheMode(QMovie::CacheNone), haveReadAll(false), isFirstIteration(true)
279{
280 q_ptr = qq;
281 nextImageTimer.setSingleShot(true);
282}
283
284/*! \internal
285 */
286void QMoviePrivate::reset()
287{
288 nextImageTimer.stop();
289 if (reader->device())
290 initialDevicePos = reader->device()->pos();
291 currentFrameNumber = -1;
292 nextFrameNumber = 0;
293 greatestFrameNumber = -1;
294 nextDelay = 0;
295 playCounter = -1;
296 haveReadAll = false;
297 isFirstIteration = true;
298 frameMap.clear();
299}
300
301/*! \internal
302 */
303bool QMoviePrivate::isDone()
304{
305 return (playCounter == 0);
306}
307
308/*!
309 \internal
310
311 Given the original \a delay, this function returns the
312 actual number of milliseconds to delay according to
313 the current speed. E.g. if the speed is 200%, the
314 result will be half of the original delay.
315*/
316int QMoviePrivate::speedAdjustedDelay(int delay) const
317{
318 return int( (qint64(delay) * qint64(100) ) / qint64(speed) );
319}
320
321/*!
322 \internal
323
324 Returns the QFrameInfo for the given \a frameNumber.
325
326 If the frame number is invalid, an invalid QFrameInfo is
327 returned.
328
329 If the end of the animation has been reached, a
330 special end marker QFrameInfo is returned.
331
332*/
333QFrameInfo QMoviePrivate::infoForFrame(int frameNumber)
334{
335 Q_Q(QMovie);
336
337 if (frameNumber < 0)
338 return QFrameInfo(); // Invalid
339
340 if (haveReadAll && (frameNumber > greatestFrameNumber)) {
341 if (frameNumber == greatestFrameNumber+1)
342 return QFrameInfo::endMarker();
343 return QFrameInfo(); // Invalid
344 }
345
346 if (cacheMode == QMovie::CacheNone) {
347 if (frameNumber != currentFrameNumber+1) {
348 // Non-sequential frame access
349 if (!reader->jumpToImage(imageNumber: frameNumber)) {
350 if (frameNumber == 0) {
351 // Special case: Attempt to "rewind" so we can loop
352 // ### This could be implemented as QImageReader::rewind()
353 if (reader->device()->isSequential())
354 return QFrameInfo(); // Invalid
355 QString fileName = reader->fileName();
356 QByteArray format = reader->format();
357 QIODevice *device = reader->device();
358 QColor bgColor = reader->backgroundColor();
359 QSize scaledSize = reader->scaledSize();
360 delete reader;
361 if (fileName.isEmpty())
362 reader = new QImageReader(device, format);
363 else
364 reader = new QImageReader(absoluteFilePath, format);
365 if (!reader->canRead()) // Provoke a device->open() call
366 emit q->error(error: reader->error());
367 reader->device()->seek(pos: initialDevicePos);
368 reader->setBackgroundColor(bgColor);
369 reader->setScaledSize(scaledSize);
370 } else {
371 return QFrameInfo(); // Invalid
372 }
373 }
374 }
375 if (reader->canRead()) {
376 // reader says we can read. Attempt to actually read image
377 QImage anImage = reader->read();
378 if (anImage.isNull()) {
379 // Reading image failed.
380 return QFrameInfo(); // Invalid
381 }
382 if (frameNumber > greatestFrameNumber)
383 greatestFrameNumber = frameNumber;
384 return QFrameInfo(QPixmap::fromImage(image: std::move(anImage)), reader->nextImageDelay());
385 } else if (frameNumber != 0) {
386 // We've read all frames now. Return an end marker
387 haveReadAll = true;
388 return QFrameInfo::endMarker();
389 } else {
390 // No readable frames
391 haveReadAll = true;
392 return QFrameInfo();
393 }
394 }
395
396 // CacheMode == CacheAll
397 if (frameNumber > greatestFrameNumber) {
398 // Frame hasn't been read from file yet. Try to do it
399 for (int i = greatestFrameNumber + 1; i <= frameNumber; ++i) {
400 if (reader->canRead()) {
401 // reader says we can read. Attempt to actually read image
402 QImage anImage = reader->read();
403 if (anImage.isNull()) {
404 // Reading image failed.
405 return QFrameInfo(); // Invalid
406 }
407 greatestFrameNumber = i;
408 QFrameInfo info(QPixmap::fromImage(image: std::move(anImage)), reader->nextImageDelay());
409 // Cache it!
410 frameMap.insert(key: i, value: info);
411 if (i == frameNumber) {
412 return info;
413 }
414 } else {
415 // We've read all frames now. Return an end marker
416 haveReadAll = true;
417 return frameNumber == greatestFrameNumber + 1 ? QFrameInfo::endMarker() : QFrameInfo();
418 }
419 }
420 }
421 // Return info for requested (cached) frame
422 return frameMap.value(akey: frameNumber);
423}
424
425/*!
426 \internal
427
428 Attempts to advance the animation to the next frame.
429 If successful, currentFrameNumber, currentPixmap and
430 nextDelay are updated accordingly, and true is returned.
431 Otherwise, false is returned.
432 When false is returned, isDone() can be called to
433 determine whether the animation ended gracefully or
434 an error occurred when reading the frame.
435*/
436bool QMoviePrivate::next()
437{
438 QElapsedTimer time;
439 time.start();
440 QFrameInfo info = infoForFrame(frameNumber: nextFrameNumber);
441 if (!info.isValid())
442 return false;
443 if (info.isEndMarker()) {
444 // We reached the end of the animation.
445 if (isFirstIteration) {
446 if (nextFrameNumber == 0) {
447 // No frames could be read at all (error).
448 return false;
449 }
450 // End of first iteration. Initialize play counter
451 playCounter = reader->loopCount();
452 isFirstIteration = false;
453 }
454 // Loop as appropriate
455 if (playCounter != 0) {
456 if (playCounter != -1) // Infinite?
457 playCounter--; // Nope
458 nextFrameNumber = 0;
459 return next();
460 }
461 // Loop no more. Done
462 return false;
463 }
464 // Image and delay OK, update internal state
465 currentFrameNumber = nextFrameNumber++;
466 QSize scaledSize = reader->scaledSize();
467 if (scaledSize.isValid() && (scaledSize != info.pixmap.size()))
468 currentPixmap = QPixmap::fromImage( image: info.pixmap.toImage().scaled(s: scaledSize) );
469 else
470 currentPixmap = info.pixmap;
471
472 if (!speed)
473 return true;
474
475 nextDelay = speedAdjustedDelay(delay: info.delay);
476 // Adjust delay according to the time it took to read the frame
477 int processingTime = time.elapsed();
478 if (processingTime > nextDelay)
479 nextDelay = 0;
480 else
481 nextDelay = nextDelay - processingTime;
482 return true;
483}
484
485/*! \internal
486 */
487void QMoviePrivate::_q_loadNextFrame()
488{
489 _q_loadNextFrame(starting: false);
490}
491
492void QMoviePrivate::_q_loadNextFrame(bool starting)
493{
494 Q_Q(QMovie);
495 if (next()) {
496 if (starting && movieState == QMovie::NotRunning) {
497 enterState(newState: QMovie::Running);
498 emit q->started();
499 }
500
501 if (frameRect.size() != currentPixmap.rect().size()) {
502 frameRect = currentPixmap.rect();
503 emit q->resized(size: frameRect.size());
504 }
505
506 emit q->updated(rect: frameRect);
507 emit q->frameChanged(frameNumber: currentFrameNumber);
508
509 if (speed && movieState == QMovie::Running)
510 nextImageTimer.start(msec: nextDelay);
511 } else {
512 // Could not read another frame
513 if (!isDone()) {
514 emit q->error(error: reader->error());
515 }
516
517 // Graceful finish
518 if (movieState != QMovie::Paused) {
519 nextFrameNumber = 0;
520 isFirstIteration = true;
521 playCounter = -1;
522 enterState(newState: QMovie::NotRunning);
523 emit q->finished();
524 }
525 }
526}
527
528/*!
529 \internal
530*/
531bool QMoviePrivate::isValid() const
532{
533 Q_Q(const QMovie);
534
535 if (greatestFrameNumber >= 0)
536 return true; // have we seen valid data
537 bool canRead = reader->canRead();
538 if (!canRead) {
539 // let the consumer know it's broken
540 //
541 // ### the const_cast here is ugly, but 'const' of this method is
542 // technically wrong right now, since it may cause the underlying device
543 // to open.
544 emit const_cast<QMovie*>(q)->error(error: reader->error());
545 }
546 return canRead;
547}
548
549/*!
550 \internal
551*/
552bool QMoviePrivate::jumpToFrame(int frameNumber)
553{
554 if (frameNumber < 0)
555 return false;
556 if (currentFrameNumber == frameNumber)
557 return true;
558 nextFrameNumber = frameNumber;
559 if (movieState == QMovie::Running)
560 nextImageTimer.stop();
561 _q_loadNextFrame();
562 return (nextFrameNumber == currentFrameNumber+1);
563}
564
565/*!
566 \internal
567*/
568int QMoviePrivate::frameCount() const
569{
570 int result;
571 if ((result = reader->imageCount()) != 0)
572 return result;
573 if (haveReadAll)
574 return greatestFrameNumber+1;
575 return 0; // Don't know
576}
577
578/*!
579 \internal
580*/
581bool QMoviePrivate::jumpToNextFrame()
582{
583 return jumpToFrame(frameNumber: currentFrameNumber+1);
584}
585
586/*!
587 Constructs a QMovie object, passing the \a parent object to QObject's
588 constructor.
589
590 \sa setFileName(), setDevice(), setFormat()
591 */
592QMovie::QMovie(QObject *parent)
593 : QObject(*new QMoviePrivate(this), parent)
594{
595 Q_D(QMovie);
596 d->reader = new QImageReader;
597 connect(sender: &d->nextImageTimer, SIGNAL(timeout()), receiver: this, SLOT(_q_loadNextFrame()));
598}
599
600/*!
601 Constructs a QMovie object. QMovie will use read image data from \a
602 device, which it assumes is open and readable. If \a format is not empty,
603 QMovie will use the image format \a format for decoding the image
604 data. Otherwise, QMovie will attempt to guess the format.
605
606 The \a parent object is passed to QObject's constructor.
607 */
608QMovie::QMovie(QIODevice *device, const QByteArray &format, QObject *parent)
609 : QObject(*new QMoviePrivate(this), parent)
610{
611 Q_D(QMovie);
612 d->reader = new QImageReader(device, format);
613 d->initialDevicePos = device->pos();
614 connect(sender: &d->nextImageTimer, SIGNAL(timeout()), receiver: this, SLOT(_q_loadNextFrame()));
615}
616
617/*!
618 Constructs a QMovie object. QMovie will use read image data from \a
619 fileName. If \a format is not empty, QMovie will use the image format \a
620 format for decoding the image data. Otherwise, QMovie will attempt to
621 guess the format.
622
623 The \a parent object is passed to QObject's constructor.
624 */
625QMovie::QMovie(const QString &fileName, const QByteArray &format, QObject *parent)
626 : QObject(*new QMoviePrivate(this), parent)
627{
628 Q_D(QMovie);
629 d->absoluteFilePath = QDir(fileName).absolutePath();
630 d->reader = new QImageReader(fileName, format);
631 if (d->reader->device())
632 d->initialDevicePos = d->reader->device()->pos();
633 connect(sender: &d->nextImageTimer, SIGNAL(timeout()), receiver: this, SLOT(_q_loadNextFrame()));
634}
635
636/*!
637 Destructs the QMovie object.
638*/
639QMovie::~QMovie()
640{
641 Q_D(QMovie);
642 delete d->reader;
643}
644
645/*!
646 Sets the current device to \a device. QMovie will read image data from
647 this device when the movie is running.
648
649 \sa device(), setFormat()
650*/
651void QMovie::setDevice(QIODevice *device)
652{
653 Q_D(QMovie);
654 d->reader->setDevice(device);
655 d->reset();
656}
657
658/*!
659 Returns the device QMovie reads image data from. If no device has
660 currently been assigned, \nullptr is returned.
661
662 \sa setDevice(), fileName()
663*/
664QIODevice *QMovie::device() const
665{
666 Q_D(const QMovie);
667 return d->reader->device();
668}
669
670/*!
671 Sets the name of the file that QMovie reads image data from, to \a
672 fileName.
673
674 \sa fileName(), setDevice(), setFormat()
675*/
676void QMovie::setFileName(const QString &fileName)
677{
678 Q_D(QMovie);
679 d->absoluteFilePath = QDir(fileName).absolutePath();
680 d->reader->setFileName(fileName);
681 d->reset();
682}
683
684/*!
685 Returns the name of the file that QMovie reads image data from. If no file
686 name has been assigned, or if the assigned device is not a file, an empty
687 QString is returned.
688
689 \sa setFileName(), device()
690*/
691QString QMovie::fileName() const
692{
693 Q_D(const QMovie);
694 return d->reader->fileName();
695}
696
697/*!
698 Sets the format that QMovie will use when decoding image data, to \a
699 format. By default, QMovie will attempt to guess the format of the image
700 data.
701
702 You can call supportedFormats() for the full list of formats
703 QMovie supports.
704
705 \sa QImageReader::supportedImageFormats()
706*/
707void QMovie::setFormat(const QByteArray &format)
708{
709 Q_D(QMovie);
710 d->reader->setFormat(format);
711}
712
713/*!
714 Returns the format that QMovie uses when decoding image data. If no format
715 has been assigned, an empty QByteArray() is returned.
716
717 \sa setFormat()
718*/
719QByteArray QMovie::format() const
720{
721 Q_D(const QMovie);
722 return d->reader->format();
723}
724
725/*!
726 For image formats that support it, this function sets the background color
727 to \a color.
728
729 \sa backgroundColor()
730*/
731void QMovie::setBackgroundColor(const QColor &color)
732{
733 Q_D(QMovie);
734 d->reader->setBackgroundColor(color);
735}
736
737/*!
738 Returns the background color of the movie. If no background color has been
739 assigned, an invalid QColor is returned.
740
741 \sa setBackgroundColor()
742*/
743QColor QMovie::backgroundColor() const
744{
745 Q_D(const QMovie);
746 return d->reader->backgroundColor();
747}
748
749/*!
750 Returns the current state of QMovie.
751
752 \sa MovieState, stateChanged()
753*/
754QMovie::MovieState QMovie::state() const
755{
756 Q_D(const QMovie);
757 return d->movieState;
758}
759
760/*!
761 Returns the rect of the last frame. If no frame has yet been updated, an
762 invalid QRect is returned.
763
764 \sa currentImage(), currentPixmap()
765*/
766QRect QMovie::frameRect() const
767{
768 Q_D(const QMovie);
769 return d->frameRect;
770}
771
772/*!
773 Returns the current frame as a QPixmap.
774
775 \sa currentImage(), updated()
776*/
777QPixmap QMovie::currentPixmap() const
778{
779 Q_D(const QMovie);
780 return d->currentPixmap;
781}
782
783/*!
784 Returns the current frame as a QImage.
785
786 \sa currentPixmap(), updated()
787*/
788QImage QMovie::currentImage() const
789{
790 Q_D(const QMovie);
791 return d->currentPixmap.toImage();
792}
793
794/*!
795 Returns \c true if the movie is valid (e.g., the image data is readable and
796 the image format is supported); otherwise returns \c false.
797
798 For information about why the movie is not valid, see lastError().
799*/
800bool QMovie::isValid() const
801{
802 Q_D(const QMovie);
803 return d->isValid();
804}
805
806/*!
807 Returns the most recent error that occurred while attempting to read image data.
808
809 \sa lastErrorString()
810*/
811QImageReader::ImageReaderError QMovie::lastError() const
812{
813 Q_D(const QMovie);
814 return d->reader->error();
815}
816
817/*!
818 Returns a human-readable representation of the most recent error that occurred
819 while attempting to read image data.
820
821 \sa lastError()
822*/
823QString QMovie::lastErrorString() const
824{
825 Q_D(const QMovie);
826 return d->reader->errorString();
827}
828
829/*!
830 Returns the number of frames in the movie.
831
832 Certain animation formats do not support this feature, in which
833 case 0 is returned.
834*/
835int QMovie::frameCount() const
836{
837 Q_D(const QMovie);
838 return d->frameCount();
839}
840
841/*!
842 Returns the number of milliseconds QMovie will wait before updating the
843 next frame in the animation.
844*/
845int QMovie::nextFrameDelay() const
846{
847 Q_D(const QMovie);
848 return d->nextDelay;
849}
850
851/*!
852 Returns the sequence number of the current frame. The number of the first
853 frame in the movie is 0.
854*/
855int QMovie::currentFrameNumber() const
856{
857 Q_D(const QMovie);
858 return d->currentFrameNumber;
859}
860
861/*!
862 Jumps to the next frame. Returns \c true on success; otherwise returns \c false.
863*/
864bool QMovie::jumpToNextFrame()
865{
866 Q_D(QMovie);
867 return d->jumpToNextFrame();
868}
869
870/*!
871 Jumps to frame number \a frameNumber. Returns \c true on success; otherwise
872 returns \c false.
873*/
874bool QMovie::jumpToFrame(int frameNumber)
875{
876 Q_D(QMovie);
877 return d->jumpToFrame(frameNumber);
878}
879
880/*!
881 Returns the number of times the movie will loop before it finishes.
882 If the movie will only play once (no looping), loopCount returns 0.
883 If the movie loops forever, loopCount returns -1.
884
885 Note that, if the image data comes from a sequential device (e.g. a
886 socket), QMovie can only loop the movie if the cacheMode is set to
887 QMovie::CacheAll.
888*/
889int QMovie::loopCount() const
890{
891 Q_D(const QMovie);
892 return d->reader->loopCount();
893}
894
895/*!
896 If \a paused is true, QMovie will enter \l Paused state and emit
897 stateChanged(Paused); otherwise it will enter \l Running state and emit
898 stateChanged(Running).
899
900 \sa state()
901*/
902void QMovie::setPaused(bool paused)
903{
904 Q_D(QMovie);
905 if (paused) {
906 if (d->movieState == NotRunning)
907 return;
908 d->enterState(newState: Paused);
909 d->nextImageTimer.stop();
910 } else {
911 if (d->movieState == Running)
912 return;
913 d->enterState(newState: Running);
914 d->nextImageTimer.start(msec: nextFrameDelay());
915 }
916}
917
918/*!
919 \property QMovie::speed
920 \brief the movie's speed
921
922 The speed is measured in percentage of the original movie speed.
923 The default speed is 100%.
924 Example:
925
926 \snippet code/src_gui_image_qmovie.cpp 1
927*/
928void QMovie::setSpeed(int percentSpeed)
929{
930 Q_D(QMovie);
931 if (!d->speed && d->movieState == Running)
932 d->nextImageTimer.start(msec: nextFrameDelay());
933 d->speed = percentSpeed;
934}
935
936int QMovie::speed() const
937{
938 Q_D(const QMovie);
939 return d->speed;
940}
941
942/*!
943 Starts the movie. QMovie will enter \l Running state, and start emitting
944 updated() and resized() as the movie progresses.
945
946 If QMovie is in the \l Paused state, this function is equivalent
947 to calling setPaused(false). If QMovie is already in the \l
948 Running state, this function does nothing.
949
950 \sa stop(), setPaused()
951*/
952void QMovie::start()
953{
954 Q_D(QMovie);
955 if (d->movieState == NotRunning) {
956 d->_q_loadNextFrame(starting: true);
957 } else if (d->movieState == Paused) {
958 setPaused(false);
959 }
960}
961
962/*!
963 Stops the movie. QMovie enters \l NotRunning state, and stops emitting
964 updated() and resized(). If start() is called again, the movie will
965 restart from the beginning.
966
967 If QMovie is already in the \l NotRunning state, this function
968 does nothing.
969
970 \sa start(), setPaused()
971*/
972void QMovie::stop()
973{
974 Q_D(QMovie);
975 if (d->movieState == NotRunning)
976 return;
977 d->enterState(newState: NotRunning);
978 d->nextImageTimer.stop();
979 d->nextFrameNumber = 0;
980}
981
982/*!
983 \since 4.1
984
985 Returns the scaled size of frames.
986
987 \sa QImageReader::scaledSize()
988*/
989QSize QMovie::scaledSize()
990{
991 Q_D(QMovie);
992 return d->reader->scaledSize();
993}
994
995/*!
996 \since 4.1
997
998 Sets the scaled frame size to \a size.
999
1000 \sa QImageReader::setScaledSize()
1001*/
1002void QMovie::setScaledSize(const QSize &size)
1003{
1004 Q_D(QMovie);
1005 d->reader->setScaledSize(size);
1006}
1007
1008/*!
1009 \since 4.1
1010
1011 Returns the list of image formats supported by QMovie.
1012
1013 \sa QImageReader::supportedImageFormats()
1014*/
1015QList<QByteArray> QMovie::supportedFormats()
1016{
1017 QList<QByteArray> list = QImageReader::supportedImageFormats();
1018
1019 QBuffer buffer;
1020 buffer.open(openMode: QIODevice::ReadOnly);
1021
1022 const auto doesntSupportAnimation =
1023 [&buffer](const QByteArray &format) {
1024 return !QImageReader(&buffer, format).supportsOption(option: QImageIOHandler::Animation);
1025 };
1026
1027 list.erase(first: std::remove_if(first: list.begin(), last: list.end(), pred: doesntSupportAnimation), last: list.end());
1028 return list;
1029}
1030
1031/*!
1032 \property QMovie::cacheMode
1033 \brief the movie's cache mode
1034
1035 Caching frames can be useful when the underlying animation format handler
1036 that QMovie relies on to decode the animation data does not support
1037 jumping to particular frames in the animation, or even "rewinding" the
1038 animation to the beginning (for looping). Furthermore, if the image data
1039 comes from a sequential device, it is not possible for the underlying
1040 animation handler to seek back to frames whose data has already been read
1041 (making looping altogether impossible).
1042
1043 To aid in such situations, a QMovie object can be instructed to cache the
1044 frames, at the added memory cost of keeping the frames in memory for the
1045 lifetime of the object.
1046
1047 By default, this property is set to \l CacheNone.
1048
1049 \sa QMovie::CacheMode
1050*/
1051
1052QMovie::CacheMode QMovie::cacheMode() const
1053{
1054 Q_D(const QMovie);
1055 return d->cacheMode;
1056}
1057
1058void QMovie::setCacheMode(CacheMode cacheMode)
1059{
1060 Q_D(QMovie);
1061 d->cacheMode = cacheMode;
1062}
1063
1064QT_END_NAMESPACE
1065
1066#include "moc_qmovie.cpp"
1067

source code of qtbase/src/gui/image/qmovie.cpp