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 // A paused movie keeps its state so it can resume later.
512 if (movieState == QMovie::Paused)
513 return;
514
515 nextFrameNumber = 0;
516 isFirstIteration = true;
517 playCounter = -1;
518
519 // Only report a graceful finish when the movie was actually running;
520 // a failed start() played nothing and must not emit finished().
521 if (movieState == QMovie::Running) {
522 enterState(QMovie::NotRunning);
523 emit q->finished();
524 }
525 }
526}
527
528/*!
529 \internal
530*/
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*/
552bool QMoviePrivate::jumpToFrame(int frameNumber)
553{
554 if (frameNumber < 0)
555 return false;
556 if (currentFrameNumber == frameNumber)
557 return true;
558 nextFrameNumber = frameNumber;
559 if (movieState == QMovie::Running)
560 nextImageTimer->stop();
563}
564
565/*!
566 \internal
567*/
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*/
585
586/*!
587 Constructs a QMovie object, passing the \a parent object to QObject's
588 constructor.
589
590 \sa setFileName(), setDevice(), setFormat()
591 */
592QMovie::QMovie(QObject *parent)
593 : QObject(*new QMoviePrivate, parent)
594{
595 Q_D(QMovie);
596 d->init(this, std::make_unique<QImageReader>());
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, parent)
609{
610 Q_D(QMovie);
611 d->init(this, std::make_unique<QImageReader>(device, format));
612 d->initialDevicePos = device->pos();
613}
614
615/*!
616 Constructs a QMovie object. QMovie will use read image data from \a
617 fileName. If \a format is not empty, QMovie will use the image format \a
618 format for decoding the image data. Otherwise, QMovie will attempt to
619 guess the format.
620
621 The \a parent object is passed to QObject's constructor.
622 */
623QMovie::QMovie(const QString &fileName, const QByteArray &format, QObject *parent)
624 : QObject(*new QMoviePrivate, parent)
625{
626 Q_D(QMovie);
627 d->init(this, std::make_unique<QImageReader>(fileName, format));
628 d->absoluteFilePath = QDir(fileName).absolutePath();
629 if (d->reader->device())
630 d->initialDevicePos = d->reader->device()->pos();
631}
632
633/*!
634 Destructs the QMovie object.
635*/
636QMovie::~QMovie()
637{
638 Q_D(QMovie);
639 d->reader.reset();
640}
641
642/*!
643 Sets the current device to \a device. QMovie will read image data from
644 this device when the movie is running.
645
646 \sa device(), setFormat()
647*/
648void QMovie::setDevice(QIODevice *device)
649{
650 Q_D(QMovie);
651 d->reader->setDevice(device);
652 d->reset();
653}
654
655/*!
656 Returns the device QMovie reads image data from. If no device has
657 currently been assigned, \nullptr is returned.
658
659 \sa setDevice(), fileName()
660*/
661QIODevice *QMovie::device() const
662{
663 Q_D(const QMovie);
664 return d->reader->device();
665}
666
667/*!
668 Sets the name of the file that QMovie reads image data from, to \a
669 fileName.
670
671 \sa fileName(), setDevice(), setFormat()
672*/
673void QMovie::setFileName(const QString &fileName)
674{
675 Q_D(QMovie);
676 d->absoluteFilePath = QDir(fileName).absolutePath();
677 d->reader->setFileName(fileName);
678 d->reset();
679}
680
681/*!
682 Returns the name of the file that QMovie reads image data from. If no file
683 name has been assigned, or if the assigned device is not a file, an empty
684 QString is returned.
685
686 \sa setFileName(), device()
687*/
688QString QMovie::fileName() const
689{
690 Q_D(const QMovie);
691 return d->reader->fileName();
692}
693
694/*!
695 Sets the format that QMovie will use when decoding image data, to \a
696 format. By default, QMovie will attempt to guess the format of the image
697 data.
698
699 You can call supportedFormats() for the full list of formats
700 QMovie supports.
701
702 \sa QImageReader::supportedImageFormats()
703*/
704void QMovie::setFormat(const QByteArray &format)
705{
706 Q_D(QMovie);
707 d->reader->setFormat(format);
708}
709
710/*!
711 Returns the format that QMovie uses when decoding image data. If no format
712 has been assigned, an empty QByteArray() is returned.
713
714 \sa setFormat()
715*/
716QByteArray QMovie::format() const
717{
718 Q_D(const QMovie);
719 return d->reader->format();
720}
721
722/*!
723 For image formats that support it, this function sets the background color
724 to \a color.
725
726 \sa backgroundColor()
727*/
728void QMovie::setBackgroundColor(const QColor &color)
729{
730 Q_D(QMovie);
731 d->reader->setBackgroundColor(color);
732}
733
734/*!
735 Returns the background color of the movie. If no background color has been
736 assigned, an invalid QColor is returned.
737
738 \sa setBackgroundColor()
739*/
740QColor QMovie::backgroundColor() const
741{
742 Q_D(const QMovie);
743 return d->reader->backgroundColor();
744}
745
746/*!
747 Returns the current state of QMovie.
748
749 \sa MovieState, stateChanged()
750*/
751QMovie::MovieState QMovie::state() const
752{
753 Q_D(const QMovie);
754 return d->movieState;
755}
756
757/*!
758 Returns the rect of the last frame. If no frame has yet been updated, an
759 invalid QRect is returned.
760
761 \sa currentImage(), currentPixmap()
762*/
763QRect QMovie::frameRect() const
764{
765 Q_D(const QMovie);
766 return d->frameRect;
767}
768
769/*!
770 Returns the current frame as a QPixmap.
771
772 \sa currentImage(), updated()
773*/
774QPixmap QMovie::currentPixmap() const
775{
776 Q_D(const QMovie);
777 return d->currentPixmap;
778}
779
780/*!
781 Returns the current frame as a QImage.
782
783 \sa currentPixmap(), updated()
784*/
785QImage QMovie::currentImage() const
786{
787 Q_D(const QMovie);
788 return d->currentPixmap.toImage();
789}
790
791/*!
792 Returns \c true if the movie is valid (e.g., the image data is readable and
793 the image format is supported); otherwise returns \c false.
794
795 For information about why the movie is not valid, see lastError().
796*/
797bool QMovie::isValid() const
798{
799 Q_D(const QMovie);
800 return d->isValid();
801}
802
803/*!
804 Returns the most recent error that occurred while attempting to read image data.
805
806 \sa lastErrorString()
807*/
808QImageReader::ImageReaderError QMovie::lastError() const
809{
810 Q_D(const QMovie);
811 return d->reader->error();
812}
813
814/*!
815 Returns a human-readable representation of the most recent error that occurred
816 while attempting to read image data.
817
818 \sa lastError()
819*/
820QString QMovie::lastErrorString() const
821{
822 Q_D(const QMovie);
823 return d->reader->errorString();
824}
825
826/*!
827 Returns the number of frames in the movie.
828
829 Certain animation formats do not support this feature, in which
830 case 0 is returned.
831*/
832int QMovie::frameCount() const
833{
834 Q_D(const QMovie);
835 return d->frameCount();
836}
837
838/*!
839 Returns the number of milliseconds QMovie will wait before updating the
840 next frame in the animation.
841*/
842int QMovie::nextFrameDelay() const
843{
844 Q_D(const QMovie);
845 return d->nextDelay;
846}
847
848/*!
849 Returns the sequence number of the current frame. The number of the first
850 frame in the movie is 0.
851*/
852int QMovie::currentFrameNumber() const
853{
854 Q_D(const QMovie);
855 return d->currentFrameNumber;
856}
857
858/*!
859 Jumps to the next frame. Returns \c true on success; otherwise returns \c false.
860*/
861bool QMovie::jumpToNextFrame()
862{
863 Q_D(QMovie);
864 return d->jumpToNextFrame();
865}
866
867/*!
868 Jumps to frame number \a frameNumber. Returns \c true on success; otherwise
869 returns \c false.
870*/
871bool QMovie::jumpToFrame(int frameNumber)
872{
873 Q_D(QMovie);
874 return d->jumpToFrame(frameNumber);
875}
876
877/*!
878 Returns the number of times the movie will loop before it finishes.
879 If the movie will only play once (no looping), loopCount returns 0.
880 If the movie loops forever, loopCount returns -1.
881
882 Note that, if the image data comes from a sequential device (e.g. a
883 socket), QMovie can only loop the movie if the cacheMode is set to
884 QMovie::CacheAll.
885*/
886int QMovie::loopCount() const
887{
888 Q_D(const QMovie);
889 return d->reader->loopCount();
890}
891
892/*!
893 If \a paused is true, QMovie will enter \l Paused state and emit
894 stateChanged(Paused); otherwise it will enter \l Running state and emit
895 stateChanged(Running).
896
897 \sa state()
898*/
899void QMovie::setPaused(bool paused)
900{
901 Q_D(QMovie);
902 if (paused) {
903 if (d->movieState == NotRunning)
904 return;
905 d->enterState(Paused);
906 d->nextImageTimer->stop();
907 } else {
908 if (d->movieState == Running)
909 return;
910 d->enterState(Running);
911 d->nextImageTimer->start(nextFrameDelay());
912 }
913}
914
915/*!
916 \property QMovie::speed
917 \brief the movie's speed
918
919 The speed is measured in percentage of the original movie speed.
920 The default speed is 100%.
921 Example:
922
923 \snippet code/src_gui_image_qmovie.cpp 1
924*/
925void QMovie::setSpeed(int percentSpeed)
926{
927 Q_D(QMovie);
928 if (!d->speed && d->movieState == Running)
929 d->nextImageTimer->start(nextFrameDelay());
930 if (percentSpeed != d->speed) {
931 d->speed = percentSpeed;
932 d->speed.notify();
933 } else {
934 d->speed.removeBindingUnlessInWrapper();
935 }
936}
937
938int QMovie::speed() const
939{
940 Q_D(const QMovie);
941 return d->speed;
942}
943
944QBindable<int> QMovie::bindableSpeed()
945{
946 Q_D(QMovie);
947 return &d->speed;
948}
949
950/*!
951 Starts the movie. QMovie will enter \l Running state, and start emitting
952 updated() and resized() as the movie progresses.
953
954 If QMovie is in the \l Paused state, this function is equivalent
955 to calling setPaused(false). If QMovie is already in the \l
956 Running state, this function does nothing.
957
958 \sa stop(), setPaused()
959*/
960void QMovie::start()
961{
962 Q_D(QMovie);
963 if (d->movieState == NotRunning) {
964 d->_q_loadNextFrame(true);
965 } else if (d->movieState == Paused) {
966 setPaused(false);
967 }
968}
969
970/*!
971 Stops the movie. QMovie enters \l NotRunning state, and stops emitting
972 updated() and resized(). If start() is called again, the movie will
973 restart from the beginning.
974
975 If QMovie is already in the \l NotRunning state, this function
976 does nothing.
977
978 \sa start(), setPaused()
979*/
980void QMovie::stop()
981{
982 Q_D(QMovie);
983 if (d->movieState == NotRunning)
984 return;
985 d->enterState(NotRunning);
986 d->nextImageTimer->stop();
987 d->nextFrameNumber = 0;
988}
989
990/*!
991 Returns the scaled size of frames.
992
993 \sa QImageReader::scaledSize()
994*/
995QSize QMovie::scaledSize()
996{
997 Q_D(QMovie);
998 return d->reader->scaledSize();
999}
1000
1001/*!
1002 Sets the scaled frame size to \a size.
1003
1004 \sa QImageReader::setScaledSize()
1005*/
1006void QMovie::setScaledSize(const QSize &size)
1007{
1008 Q_D(QMovie);
1009 d->reader->setScaledSize(size);
1010}
1011
1012/*!
1013 Returns the list of image formats supported by QMovie.
1014
1015 \sa QImageReader::supportedImageFormats()
1016*/
1017QList<QByteArray> QMovie::supportedFormats()
1018{
1019 QList<QByteArray> list = QImageReader::supportedImageFormats();
1020
1021 QBuffer buffer;
1022 buffer.open(QIODevice::ReadOnly);
1023
1024 const auto doesntSupportAnimation =
1025 [&buffer](const QByteArray &format) {
1026 return !QImageReader(&buffer, format).supportsOption(QImageIOHandler::Animation);
1027 };
1028
1029 list.removeIf(doesntSupportAnimation);
1030 return list;
1031}
1032
1033/*!
1034 \property QMovie::cacheMode
1035 \brief the movie's cache mode
1036
1037 Caching frames can be useful when the underlying animation format handler
1038 that QMovie relies on to decode the animation data does not support
1039 jumping to particular frames in the animation, or even "rewinding" the
1040 animation to the beginning (for looping). Furthermore, if the image data
1041 comes from a sequential device, it is not possible for the underlying
1042 animation handler to seek back to frames whose data has already been read
1043 (making looping altogether impossible).
1044
1045 To aid in such situations, a QMovie object can be instructed to cache the
1046 frames, at the added memory cost of keeping the frames in memory for the
1047 lifetime of the object.
1048
1049 By default, this property is set to \l CacheNone.
1050
1051 \sa QMovie::CacheMode
1052*/
1053
1054QMovie::CacheMode QMovie::cacheMode() const
1055{
1056 Q_D(const QMovie);
1057 return d->cacheMode;
1058}
1059
1060void QMovie::setCacheMode(CacheMode cacheMode)
1061{
1062 Q_D(QMovie);
1063 d->cacheMode = cacheMode;
1064}
1065
1066QBindable<QMovie::CacheMode> QMovie::bindableCacheMode()
1067{
1068 Q_D(QMovie);
1069 return &d->cacheMode;
1070}
1071
1072QT_END_NAMESPACE
1073
1074#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:531
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:552
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:581
bool next()
Definition qmovie.cpp:434
int frameCount() const
Definition qmovie.cpp:568
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