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 
194 QT_BEGIN_NAMESPACE
195 
196 class QFrameInfo
197 {
198 public:
199     QPixmap pixmap;
200     int delay;
201     bool endMark;
QFrameInfo(bool endMark)202     inline QFrameInfo(bool endMark)
203         : pixmap(QPixmap()), delay(QMOVIE_INVALID_DELAY), endMark(endMark)
204     { }
205 
QFrameInfo()206     inline QFrameInfo()
207         : pixmap(QPixmap()), delay(QMOVIE_INVALID_DELAY), endMark(false)
208     { }
209 
QFrameInfo(QPixmap && pixmap,int delay)210     inline QFrameInfo(QPixmap &&pixmap, int delay)
211         : pixmap(std::move(pixmap)), delay(delay), endMark(false)
212     { }
213 
isValid()214     inline bool isValid()
215     {
216         return endMark || !(pixmap.isNull() && (delay == QMOVIE_INVALID_DELAY));
217     }
218 
isEndMarker()219     inline bool isEndMarker()
220     { return endMark; }
221 
endMarker()222     static inline QFrameInfo endMarker()
223     { return QFrameInfo(true); }
224 };
225 Q_DECLARE_TYPEINFO(QFrameInfo, Q_MOVABLE_TYPE);
226 
227 class QMoviePrivate : public QObjectPrivate
228 {
229     Q_DECLARE_PUBLIC(QMovie)
230 
231 public:
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 
enterState(QMovie::MovieState newState)243     inline void enterState(QMovie::MovieState newState) {
244         movieState = newState;
245         emit q_func()->stateChanged(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  */
QMoviePrivate(QMovie * qq)274 QMoviePrivate::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  */
reset()286 void 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  */
isDone()303 bool 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 */
speedAdjustedDelay(int delay) const316 int 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 */
infoForFrame(int frameNumber)333 QFrameInfo 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(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(reader->error());
367                     reader->device()->seek(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(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(std::move(anImage)), reader->nextImageDelay());
409                 // Cache it!
410                 frameMap.insert(i, 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(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 */
next()436 bool QMoviePrivate::next()
437 {
438     QElapsedTimer time;
439     time.start();
440     QFrameInfo info = infoForFrame(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( info.pixmap.toImage().scaled(scaledSize) );
469     else
470         currentPixmap = info.pixmap;
471 
472     if (!speed)
473         return true;
474 
475     nextDelay = speedAdjustedDelay(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  */
_q_loadNextFrame()487 void QMoviePrivate::_q_loadNextFrame()
488 {
489     _q_loadNextFrame(false);
490 }
491 
_q_loadNextFrame(bool starting)492 void QMoviePrivate::_q_loadNextFrame(bool starting)
493 {
494     Q_Q(QMovie);
495     if (next()) {
496         if (starting && movieState == QMovie::NotRunning) {
497             enterState(QMovie::Running);
498             emit q->started();
499         }
500 
501         if (frameRect.size() != currentPixmap.rect().size()) {
502             frameRect = currentPixmap.rect();
503             emit q->resized(frameRect.size());
504         }
505 
506         emit q->updated(frameRect);
507         emit q->frameChanged(currentFrameNumber);
508 
509         if (speed && movieState == QMovie::Running)
510             nextImageTimer.start(nextDelay);
511     } else {
512         // Could not read another frame
513         if (!isDone()) {
514             emit q->error(reader->error());
515         }
516 
517         // Graceful finish
518         if (movieState != QMovie::Paused) {
519             nextFrameNumber = 0;
520             isFirstIteration = true;
521             playCounter = -1;
522             enterState(QMovie::NotRunning);
523             emit q->finished();
524         }
525     }
526 }
527 
528 /*!
529     \internal
530 */
isValid() const531 bool 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(reader->error());
545     }
546     return canRead;
547 }
548 
549 /*!
550     \internal
551 */
jumpToFrame(int frameNumber)552 bool 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 */
frameCount() const568 int 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 */
jumpToNextFrame()581 bool QMoviePrivate::jumpToNextFrame()
582 {
583     return jumpToFrame(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  */
QMovie(QObject * parent)592 QMovie::QMovie(QObject *parent)
593     : QObject(*new QMoviePrivate(this), parent)
594 {
595     Q_D(QMovie);
596     d->reader = new QImageReader;
597     connect(&d->nextImageTimer, SIGNAL(timeout()), 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  */
QMovie(QIODevice * device,const QByteArray & format,QObject * parent)608 QMovie::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(&d->nextImageTimer, SIGNAL(timeout()), 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  */
QMovie(const QString & fileName,const QByteArray & format,QObject * parent)625 QMovie::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(&d->nextImageTimer, SIGNAL(timeout()), this, SLOT(_q_loadNextFrame()));
634 }
635 
636 /*!
637     Destructs the QMovie object.
638 */
~QMovie()639 QMovie::~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 */
setDevice(QIODevice * device)651 void 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 */
device() const664 QIODevice *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 */
setFileName(const QString & fileName)676 void 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 */
fileName() const691 QString 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 */
setFormat(const QByteArray & format)707 void 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 */
format() const719 QByteArray 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 */
setBackgroundColor(const QColor & color)731 void 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 */
backgroundColor() const743 QColor 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 */
state() const754 QMovie::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 */
frameRect() const766 QRect 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 */
currentPixmap() const777 QPixmap 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 */
currentImage() const788 QImage 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 */
isValid() const800 bool 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 */
lastError() const811 QImageReader::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 */
lastErrorString() const823 QString 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 */
frameCount() const835 int 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 */
nextFrameDelay() const845 int 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 */
currentFrameNumber() const855 int 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 */
jumpToNextFrame()864 bool 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 */
jumpToFrame(int frameNumber)874 bool 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 */
loopCount() const889 int 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 */
setPaused(bool paused)902 void QMovie::setPaused(bool paused)
903 {
904     Q_D(QMovie);
905     if (paused) {
906         if (d->movieState == NotRunning)
907             return;
908         d->enterState(Paused);
909         d->nextImageTimer.stop();
910     } else {
911         if (d->movieState == Running)
912             return;
913         d->enterState(Running);
914         d->nextImageTimer.start(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 */
setSpeed(int percentSpeed)928 void QMovie::setSpeed(int percentSpeed)
929 {
930     Q_D(QMovie);
931     if (!d->speed && d->movieState == Running)
932         d->nextImageTimer.start(nextFrameDelay());
933     d->speed = percentSpeed;
934 }
935 
speed() const936 int 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 */
start()952 void QMovie::start()
953 {
954     Q_D(QMovie);
955     if (d->movieState == NotRunning) {
956         d->_q_loadNextFrame(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 */
stop()972 void QMovie::stop()
973 {
974     Q_D(QMovie);
975     if (d->movieState == NotRunning)
976         return;
977     d->enterState(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 */
scaledSize()989 QSize 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 */
setScaledSize(const QSize & size)1002 void 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 */
supportedFormats()1015 QList<QByteArray> QMovie::supportedFormats()
1016 {
1017     QList<QByteArray> list = QImageReader::supportedImageFormats();
1018 
1019     QBuffer buffer;
1020     buffer.open(QIODevice::ReadOnly);
1021 
1022     const auto doesntSupportAnimation =
1023             [&buffer](const QByteArray &format) {
1024                 return !QImageReader(&buffer, format).supportsOption(QImageIOHandler::Animation);
1025             };
1026 
1027     list.erase(std::remove_if(list.begin(), list.end(), doesntSupportAnimation), 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 
cacheMode() const1052 QMovie::CacheMode QMovie::cacheMode() const
1053 {
1054     Q_D(const QMovie);
1055     return d->cacheMode;
1056 }
1057 
setCacheMode(CacheMode cacheMode)1058 void QMovie::setCacheMode(CacheMode cacheMode)
1059 {
1060     Q_D(QMovie);
1061     d->cacheMode = cacheMode;
1062 }
1063 
1064 QT_END_NAMESPACE
1065 
1066 #include "moc_qmovie.cpp"
1067