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