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

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