📄 qmovie.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.**** This file is part of the QtGui module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************//*! \class QMovie \brief The QMovie class is a convenience class for playing movies with QImageReader. \ingroup multimedia First, create a QMovie object by passing either the name of a file or a pointer to a QIODevice containing an animated image format to QMovie's constructor. You can call isValid() to check if the image data is valid, before starting the movie. To start the movie, call start(). QMovie will enter \l Running state, and emit started() and stateChanged(). To get the current state of the movie, call state(). To display the movie in your application, you can pass your QMovie object to QLabel::setMovie(). Example: \code QLabel label; QMovie *movie = new QMovie("animations/fire.gif"); label.setMovie(movie); movie->start(); \endcode Whenever a new frame is available in the movie, QMovie will emit updated(). If the size of the frame changes, resized() is emitted. You can call currentImage() or currentPixmap() to get a copy of the current frame. When the movie is done, QMovie emits finished(). If any error occurs during playback (i.e, the image file is corrupt), QMovie will emit error(). You can control the speed of the movie playback by calling setSpeed(), which takes the percentage of the original speed as an argument. Pause the movie by calling setPaused(true). QMovie will then enter \l Paused state and emit stateChanged(). If you call setPaused(false), QMovie will reenter \l Running state and start the movie again. To stop the movie, call stop(). Certain animation formats allow you to set the background color. You can call setBackgroundColor() to set the color, or backgroundColor() to retrieve the current background color. currentFrameNumber() returns the sequence number of the current frame. The first frame in the animation has the sequence number 0. frameCount() returns the total number of frames in the animation, if the image format supports this. You can call loopCount() to get the number of times the movie should loop before finishing. nextFrameDelay() returns the number of milliseconds the current frame should be displayed. QMovie can be instructed to cache frames of an animation by calling setCacheMode(). Call supportedFormats() for a list of formats that QMovie supports. \sa QLabel, QImageReader, {Movie Example}*//*! \enum QMovie::MovieState This enum describes the different states of QMovie. \value NotRunning The movie is not running. This is QMovie's initial state, and the state it enters after stop() has been called or the movie is finished. \value Paused The movie is paused, and QMovie stops emitting updated() or resized(). This state is entered after calling pause() or setPaused(true). The current frame number it kept, and the movie will continue with the next frame when unpause() or setPaused(false) is called. \value Running The movie is running.*//*! \enum QMovie::CacheMode This enum describes the different cache modes of QMovie. \value CacheNone No frames are cached (the default). \value CacheAll All frames are cached.*//*! \fn void QMovie::started() This signal is emitted after QMovie::start() has been called, and QMovie has entered QMovie::Running state.*//*! \fn void QMovie::resized(const QSize &size) This signal is emitted when the current frame has been resized to \a size. This effect is sometimes used in animations as an alternative to replacing the frame. You can call currentImage() or currentPixmap() to get a copy of the updated frame.*//*! \fn void QMovie::updated(const QRect &rect) This signal is emitted when the rect \a rect in the current frame has been updated. You can call currentImage() or currentPixmap() to get a copy of the updated frame.*//*! \fn void QMovie::frameChanged(int frameNumber) \since 4.1 This signal is emitted when the frame number has changed to \a frameNumber. You can call currentImage() or currentPixmap() to get a copy of the frame.*//*! \fn void QMovie::stateChanged(QMovie::MovieState state) This signal is emitted every time the state of the movie changes. The new state is specified by \a state. \sa QMovie::state()*//*! \fn void QMovie::error(QImageReader::ImageReaderError error) This signal is emitted by QMovie when the error \a error occurred during playback. QMovie will stop the movie, and enter QMovie::NotRunning state.*//*! \fn void QMovie::finished() This signal is emitted when the movie has finished. \sa QMovie::stop()*/#include "qglobal.h"#ifndef QT_NO_MOVIE#include "qmovie.h"#include "qimage.h"#include "qimagereader.h"#include "qpixmap.h"#include "qrect.h"#include "qdatetime.h"#include "qtimer.h"#include "qpair.h"#include "qmap.h"#include "qlist.h"#include "qbuffer.h"#include "private/qobject_p.h"#define QMOVIE_INVALID_DELAY -1class QFrameInfo{public: QPixmap pixmap; int delay; bool endMark; inline QFrameInfo(bool endMark) : pixmap(QPixmap()), delay(QMOVIE_INVALID_DELAY), endMark(endMark) { } inline QFrameInfo() : pixmap(QPixmap()), delay(QMOVIE_INVALID_DELAY), endMark(false) { } inline QFrameInfo(const QPixmap &pixmap, int delay) : pixmap(pixmap), delay(delay), endMark(false) { } inline bool isValid() { return endMark || !(pixmap.isNull() && (delay == QMOVIE_INVALID_DELAY)); } inline bool isEndMarker() { return endMark; } static inline QFrameInfo endMarker() { return QFrameInfo(true); }};class QMoviePrivate : public QObjectPrivate{ Q_DECLARE_PUBLIC(QMovie)public: QMoviePrivate(QMovie *qq); bool isDone(); bool next(); int speedAdjustedDelay(int delay) const; bool isValid() const; bool jumpToFrame(int frameNumber); int frameCount() const; bool jumpToNextFrame(); QFrameInfo infoForFrame(int frameNumber); void reset(); inline void enterState(QMovie::MovieState newState) { movieState = newState; emit q_func()->stateChanged(newState); } // private slots void _q_loadNextFrame(); void _q_loadNextFrame(bool starting); QImageReader *reader; int speed; QMovie::MovieState movieState; QRect frameRect; QPixmap currentPixmap; int currentFrameNumber; int nextFrameNumber; int greatestFrameNumber; int nextDelay; int playCounter; qint64 initialDevicePos; QMovie::CacheMode cacheMode; bool haveReadAll; bool isFirstIteration; QMap<int, QFrameInfo> frameMap; QTimer nextImageTimer;};/*! \internal */QMoviePrivate::QMoviePrivate(QMovie *qq) : reader(0), speed(100), movieState(QMovie::NotRunning), currentFrameNumber(-1), nextFrameNumber(0), greatestFrameNumber(-1), nextDelay(0), playCounter(-1), cacheMode(QMovie::CacheNone), haveReadAll(false), isFirstIteration(true){ q_ptr = qq; nextImageTimer.setSingleShot(true);}/*! \internal */void QMoviePrivate::reset(){ nextImageTimer.stop(); if (reader->device()) initialDevicePos = reader->device()->pos(); currentFrameNumber = -1; nextFrameNumber = 0; greatestFrameNumber = -1; nextDelay = 0; playCounter = -1; haveReadAll = false; isFirstIteration = true; frameMap.clear();}/*! \internal */bool QMoviePrivate::isDone(){ return (playCounter == 0);}/*! \internal Given the original \a delay, this function returns the actual number of milliseconds to delay according to the current speed. E.g. if the speed is 200%, the result will be half of the original delay.*/int QMoviePrivate::speedAdjustedDelay(int delay) const{ return int( (qint64(delay) * qint64(100) ) / qint64(speed) );}/*! \internal Returns the QFrameInfo for the given \a frameNumber. If the frame number is invalid, an invalid QFrameInfo is returned. If the end of the animation has been reached, a special end marker QFrameInfo is returned.*/QFrameInfo QMoviePrivate::infoForFrame(int frameNumber){ if (frameNumber < 0) return QFrameInfo(); // Invalid if (haveReadAll && (frameNumber > greatestFrameNumber)) { if (frameNumber == greatestFrameNumber+1) return QFrameInfo::endMarker(); return QFrameInfo(); // Invalid } if (cacheMode == QMovie::CacheNone) { if (frameNumber != currentFrameNumber+1) { // Non-sequential frame access if (!reader->jumpToImage(frameNumber)) { if (frameNumber == 0) { // Special case: Attempt to "rewind" so we can loop // ### This could be implemented as QImageReader::rewind() if (reader->device()->isSequential()) return QFrameInfo(); // Invalid QString fileName = reader->fileName(); QByteArray format = reader->format(); QIODevice *device = reader->device(); QColor bgColor = reader->backgroundColor(); QSize scaledSize = reader->scaledSize(); delete reader; if (fileName.isEmpty()) reader = new QImageReader(device, format); else reader = new QImageReader(fileName, format); reader->canRead(); // Provoke a device->open() call reader->device()->seek(initialDevicePos); reader->setBackgroundColor(bgColor); reader->setScaledSize(scaledSize); } else { return QFrameInfo(); // Invalid } } } if (reader->canRead()) { // reader says we can read. Attempt to actually read image QImage anImage = reader->read(); if (anImage.isNull()) { // Reading image failed. return QFrameInfo(); // Invalid } if (frameNumber > greatestFrameNumber) greatestFrameNumber = frameNumber; QPixmap aPixmap = QPixmap::fromImage(anImage); int aDelay = reader->nextImageDelay(); return QFrameInfo(aPixmap, aDelay); } else { // We've read all frames now. Return an end marker haveReadAll = true; return QFrameInfo::endMarker(); } } // CacheMode == CacheAll if (frameNumber > greatestFrameNumber) { // Frame hasn't been read from file yet. Try to do it for (int i = greatestFrameNumber + 1; i <= frameNumber; ++i) { if (reader->canRead()) { // reader says we can read. Attempt to actually read image QImage anImage = reader->read(); if (anImage.isNull()) { // Reading image failed. return QFrameInfo(); // Invalid } greatestFrameNumber = i; QPixmap aPixmap = QPixmap::fromImage(anImage); int aDelay = reader->nextImageDelay(); QFrameInfo info(aPixmap, aDelay); // Cache it! frameMap.insert(i, info); if (i == frameNumber) { return info; } } else { // We've read all frames now. Return an end marker haveReadAll = true; return QFrameInfo::endMarker(); } } } // Return info for requested (cached) frame return frameMap.value(frameNumber);}/*! \internal Attempts to advance the animation to the next frame. If successful, currentFrameNumber, currentPixmap and nextDelay are updated accordingly, and true is returned. Otherwise, false is returned. When false is returned, isDone() can be called to determine whether the animation ended gracefully or an error occurred when reading the frame.*/bool QMoviePrivate::next(){ QTime time; time.start(); QFrameInfo info = infoForFrame(nextFrameNumber); if (!info.isValid()) return false; if (info.isEndMarker()) { // We reached the end of the animation. if (isFirstIteration) { if (nextFrameNumber == 0) { // No frames could be read at all (error). return false; } // End of first iteration. Initialize play counter playCounter = reader->loopCount(); isFirstIteration = false; } // Loop as appropriate if (playCounter != 0) { if (playCounter != -1) // Infinite? playCounter--; // Nope nextFrameNumber = 0; return next(); } // Loop no more. Done return false; } // Image and delay OK, update internal state currentFrameNumber = nextFrameNumber++; QSize scaledSize = reader->scaledSize(); if (scaledSize.isValid() && (scaledSize != info.pixmap.size())) currentPixmap = QPixmap::fromImage( info.pixmap.toImage().scaled(scaledSize) ); else currentPixmap = info.pixmap; nextDelay = speedAdjustedDelay(info.delay); // Adjust delay according to the time it took to read the frame int processingTime = time.elapsed(); if (processingTime > nextDelay) nextDelay = 0; else nextDelay = nextDelay - processingTime; return true;}/*! \internal */void QMoviePrivate::_q_loadNextFrame(){ _q_loadNextFrame(false);}void QMoviePrivate::_q_loadNextFrame(bool starting){ Q_Q(QMovie); if (next()) { if (starting && movieState == QMovie::NotRunning) { enterState(QMovie::Running); emit q->started(); } if (frameRect.size() != currentPixmap.rect().size()) { frameRect = currentPixmap.rect(); emit q->resized(frameRect.size()); } emit q->updated(frameRect); emit q->frameChanged(currentFrameNumber); if (movieState == QMovie::Running) nextImageTimer.start(nextDelay); } else { // Could not read another frame if (!isDone()) { emit q->error(reader->error()); } // Graceful finish if (movieState != QMovie::Paused) { nextFrameNumber = 0; isFirstIteration = true; playCounter = -1; enterState(QMovie::NotRunning); emit q->finished(); } }}/*! \internal*/bool QMoviePrivate::isValid() const{ return (greatestFrameNumber >= 0) // have we seen valid data || reader->canRead(); // or does the reader see valid data}/*! \internal*/bool QMoviePrivate::jumpToFrame(int frameNumber){ if (frameNumber < 0) return false; if (currentFrameNumber == frameNumber) return true; nextFrameNumber = frameNumber; if (movieState == QMovie::Running) nextImageTimer.stop(); _q_loadNextFrame(); return (nextFrameNumber == currentFrameNumber+1);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -