/* * anim.cpp - class for handling animations * Copyright (C) 2003-2006 Michail Pishchagin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "anim.h" #include "iconset.h" #include #include #include //#include #include #include #include /** * \class Anim * \brief Class for handling animations * * Anim is a class that can load animations. Generally, it looks like * QMovie but it loads animations in one pass and stores it in memory. * * Each frame of Anim is stored as Impix. */ static QThread *animMainThread = 0; //! \if _hide_doc_ class Anim::Private : public QObject, public QSharedData { Q_OBJECT public: QTimer *frametimer; bool empty; bool paused; int speed; int lasttimerinterval; int looping, loop; class Frame { public: Impix impix; int period; }; QList frames; int frame; public: void init() { frametimer = new QTimer(); if (animMainThread && animMainThread != QThread::currentThread()) { frametimer->moveToThread(animMainThread); moveToThread(animMainThread); } QObject::connect(frametimer, SIGNAL(timeout()), this, SLOT(refresh())); speed = 120; lasttimerinterval = -1; looping = 0; // MNG movies doesn't have loop flag? loop = 0; frame = 0; paused = true; } Private() { init(); } Private(const Private &from) : QObject(), QSharedData() { init(); speed = from.speed; lasttimerinterval = from.lasttimerinterval; looping = from.looping; loop = from.loop; frame = from.frame; paused = from.paused; frames = from.frames; if ( !paused ) unpause(); } Private(const QByteArray *ba) { init(); QBuffer buffer((QByteArray *)ba); buffer.open(QBuffer::ReadOnly); QImageReader reader(&buffer); while ( reader.canRead() ) { QImage image = reader.read(); if ( !image.isNull() ) { Frame newFrame; frames.append( newFrame ); frames.last().impix = Impix(image); frames.last().period = reader.nextImageDelay(); } else { break; } } looping = reader.loopCount(); if ( !reader.supportsAnimation() && (frames.count() == 1) ) { QImage frame = frames[0].impix.image(); // we're gonna slice the single image we've got if we're absolutely sure // that it's can be cut into multiple frames if ((frame.width() / frame.height() > 0) && !(frame.width() % frame.height())) { int h = frame.height(); QList newFrames; for (int i = 0; i < frame.width() / frame.height(); i++) { Frame newFrame; newFrames.append( newFrame ); newFrames.last().impix = Impix(frame.copy(i * h, 0, h, h)); newFrames.last().period = 120; } frames = newFrames; looping = 0; } } } ~Private() { if ( frametimer ) delete frametimer; } void pause() { paused = true; frametimer->stop(); } void unpause() { paused = false; restartTimer(); } void restart() { frame = 0; if ( !paused ) restartTimer(); } int numFrames() const { return frames.count(); } void restartTimer() { if ( !paused && speed > 0 ) { int frameperiod = frames[frame].period; int i = frameperiod >= 0 ? frameperiod * 100/speed : 0; if ( i != lasttimerinterval || !frametimer->isActive() ) { lasttimerinterval = i; frametimer->start( i ); } } else { frametimer->stop(); } } signals: void areaChanged(); public slots: void refresh() { frame++; if ( frame >= numFrames() ) { frame = 0; loop++; if ( looping > 0 && loop >= looping ) { frame = numFrames() - 1; pause(); restart(); } } emit areaChanged(); restartTimer(); } }; //! \endif /** * Creates an empty animation. */ Anim::Anim() { d = new Private(); } /** * Creates animation from QByteArray \a data. */ Anim::Anim(const QByteArray &data) { d = new Private(&data); } /** * Creates shared copy of Anim \a anim. */ Anim::Anim(const Anim &anim) { d = anim.d; } /** * Deletes animation. */ Anim::~Anim() { } /** * Returns QPixmap of current frame. */ const QPixmap &Anim::framePixmap() const { return d->frames[d->frame].impix.pixmap(); } /** * Returns QImage of current frame. */ const QImage &Anim::frameImage() const { return d->frames[d->frame].impix.image(); } /** * Returns Impix of current frame. */ const Impix &Anim::frameImpix() const { return d->frames[d->frame].impix; } /** * Returns total number of frames in animation. */ int Anim::numFrames() const { return d->numFrames(); } /** * Returns the number of current animation frame. */ int Anim::frameNumber() const { return d->frame; } /** * Returns Impix of animation frame number \a n. */ const Impix &Anim::frame(int n) const { return d->frames[n].impix; } /** * Returns \c true if numFrames() == 0 and \c false otherwise. */ bool Anim::isNull() const { return !numFrames(); } /** * Returns \c true when animation is paused. */ bool Anim::paused() const { return d->paused; } /** * Continues the animation playback. */ void Anim::unpause() { if ( !isNull() && paused() ) d->unpause(); } /** * Pauses the animation. */ void Anim::pause() { if ( !isNull() && !paused() ) d->pause(); } /** * Starts animation from the very beginning. */ void Anim::restart() { if ( !isNull() ) d->restart(); } /** * Connects internal signal with specified slot \a member of object \a receiver, which * is emitted when animation changes its frame. * * \code * class MyObject : public QObject { * Q_OBJECT * // ... * Anim *anim; * void initAnim() { * anim = new Anim( QByteArray(someData()) ); * anim->connectUpdate( this, SLOT(animUpdated()) ); * } * public slots: * void animUpdated(); * // ... * } * \endcode * * \sa disconnectUpdate() */ void Anim::connectUpdate(QObject *receiver, const char *member) { QObject::connect(d, SIGNAL(areaChanged()), receiver, member); } /** * Disconnects slot \a member, which was prevously connected with connectUpdate(). * \sa connectUpdate() */ void Anim::disconnectUpdate(QObject *receiver, const char *member) { QObject::disconnect(d, SIGNAL(areaChanged()), receiver, member); } Anim & Anim::operator= (const Anim &from) { d = from.d; return *this; } Anim Anim::copy() const { Anim anim( *this ); anim.d = new Private( *this->d.data() ); return anim; } void Anim::detach() { d.detach(); } /** * Strips the first animation frame, if there is more than one frame. */ void Anim::stripFirstFrame() { detach(); if ( numFrames() > 1 ) { d->frames.takeFirst(); if ( !paused() ) restart(); } } /** * Sets the main thread that will be used to create objects. Useful if you want * to create Anim in non-main thread. */ void Anim::setMainThread(QThread *thread) { animMainThread = thread; } /** * Get the Anim's main thread. */ QThread *Anim::mainThread() { return animMainThread; } #include "anim.moc"