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