1 /*
2 * anim.cpp - class for handling animations
3 * Copyright (C) 2003-2006 Michail Pishchagin
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21 #include "anim.h"
22 #include "iconset.h"
23
24 #include <QObject>
25 #include <QImageReader>
26 #include <QTimer>
27 //#include <QApplication>
28 #include <QBuffer>
29 #include <QImage>
30 #include <QThread>
31
32 /**
33 * \class Anim
34 * \brief Class for handling animations
35 *
36 * Anim is a class that can load animations. Generally, it looks like
37 * QMovie but it loads animations in one pass and stores it in memory.
38 *
39 * Each frame of Anim is stored as Impix.
40 */
41
42 static QThread *animMainThread = 0;
43
44 //! \if _hide_doc_
45 class Anim::Private : public QObject, public QSharedData
46 {
47 Q_OBJECT
48 public:
49 QTimer *frametimer;
50
51 bool empty;
52 bool paused;
53
54 int speed;
55 int lasttimerinterval;
56
57 int looping, loop;
58
59 class Frame {
60 public:
61 Impix impix;
62 int period;
63 };
64
65 QList<Frame> frames;
66 int frame;
67
68 public:
init()69 void init()
70 {
71 frametimer = new QTimer();
72 if (animMainThread && animMainThread != QThread::currentThread()) {
73 frametimer->moveToThread(animMainThread);
74 moveToThread(animMainThread);
75 }
76 QObject::connect(frametimer, SIGNAL(timeout()), this, SLOT(refresh()));
77
78 speed = 120;
79 lasttimerinterval = -1;
80
81 looping = 0; // MNG movies doesn't have loop flag?
82 loop = 0;
83 frame = 0;
84 paused = true;
85 }
86
Private()87 Private()
88 {
89 init();
90 }
91
Private(const Private & from)92 Private(const Private &from)
93 : QObject(), QSharedData()
94 {
95 init();
96
97 speed = from.speed;
98 lasttimerinterval = from.lasttimerinterval;
99 looping = from.looping;
100 loop = from.loop;
101 frame = from.frame;
102 paused = from.paused;
103 frames = from.frames;
104
105 if ( !paused )
106 unpause();
107 }
108
Private(const QByteArray * ba)109 Private(const QByteArray *ba)
110 {
111 init();
112
113 QBuffer buffer((QByteArray *)ba);
114 buffer.open(QBuffer::ReadOnly);
115 QImageReader reader(&buffer);
116
117 while ( reader.canRead() ) {
118 QImage image = reader.read();
119 if ( !image.isNull() ) {
120 Frame newFrame;
121 frames.append( newFrame );
122 frames.last().impix = Impix(image);
123 frames.last().period = reader.nextImageDelay();
124 }
125 else {
126 break;
127 }
128 }
129
130 looping = reader.loopCount();
131
132 if ( !reader.supportsAnimation() && (frames.count() == 1) ) {
133 QImage frame = frames[0].impix.image();
134
135 // we're gonna slice the single image we've got if we're absolutely sure
136 // that it's can be cut into multiple frames
137 if ((frame.width() / frame.height() > 0) && !(frame.width() % frame.height())) {
138 int h = frame.height();
139 QList<Frame> newFrames;
140
141 for (int i = 0; i < frame.width() / frame.height(); i++) {
142 Frame newFrame;
143 newFrames.append( newFrame );
144 newFrames.last().impix = Impix(frame.copy(i * h, 0, h, h));
145 newFrames.last().period = 120;
146 }
147
148 frames = newFrames;
149 looping = 0;
150 }
151 }
152 }
153
~Private()154 ~Private()
155 {
156 if ( frametimer )
157 delete frametimer;
158 }
159
pause()160 void pause()
161 {
162 paused = true;
163 frametimer->stop();
164 }
165
unpause()166 void unpause()
167 {
168 paused = false;
169 restartTimer();
170 }
171
restart()172 void restart()
173 {
174 frame = 0;
175 if ( !paused )
176 restartTimer();
177 }
178
numFrames() const179 int numFrames() const
180 {
181 return frames.count();
182 }
183
restartTimer()184 void restartTimer()
185 {
186 if ( !paused && speed > 0 ) {
187 int frameperiod = frames[frame].period;
188 int i = frameperiod >= 0 ? frameperiod * 100/speed : 0;
189 if ( i != lasttimerinterval || !frametimer->isActive() ) {
190 lasttimerinterval = i;
191 frametimer->start( i );
192 }
193 } else {
194 frametimer->stop();
195 }
196 }
197
198 signals:
199 void areaChanged();
200
201 public slots:
refresh()202 void refresh()
203 {
204 frame++;
205 if ( frame >= numFrames() ) {
206 frame = 0;
207
208 loop++;
209 if ( looping > 0 && loop >= looping ) {
210 frame = numFrames() - 1;
211 pause();
212 restart();
213 }
214 }
215
216 emit areaChanged();
217 restartTimer();
218 }
219 };
220 //! \endif
221
222 /**
223 * Creates an empty animation.
224 */
Anim()225 Anim::Anim()
226 {
227 d = new Private();
228 }
229
230 /**
231 * Creates animation from QByteArray \a data.
232 */
Anim(const QByteArray & data)233 Anim::Anim(const QByteArray &data)
234 {
235 d = new Private(&data);
236 }
237
238 /**
239 * Creates shared copy of Anim \a anim.
240 */
Anim(const Anim & anim)241 Anim::Anim(const Anim &anim)
242 {
243 d = anim.d;
244 }
245
246 /**
247 * Deletes animation.
248 */
~Anim()249 Anim::~Anim()
250 {
251 }
252
253 /**
254 * Returns QPixmap of current frame.
255 */
framePixmap() const256 const QPixmap &Anim::framePixmap() const
257 {
258 return d->frames[d->frame].impix.pixmap();
259 }
260
261 /**
262 * Returns QImage of current frame.
263 */
frameImage() const264 const QImage &Anim::frameImage() const
265 {
266 return d->frames[d->frame].impix.image();
267 }
268
269 /**
270 * Returns Impix of current frame.
271 */
frameImpix() const272 const Impix &Anim::frameImpix() const
273 {
274 return d->frames[d->frame].impix;
275 }
276
277 /**
278 * Returns total number of frames in animation.
279 */
numFrames() const280 int Anim::numFrames() const
281 {
282 return d->numFrames();
283 }
284
285 /**
286 * Returns the number of current animation frame.
287 */
frameNumber() const288 int Anim::frameNumber() const
289 {
290 return d->frame;
291 }
292
293 /**
294 * Returns Impix of animation frame number \a n.
295 */
frame(int n) const296 const Impix &Anim::frame(int n) const
297 {
298 return d->frames[n].impix;
299 }
300
301 /**
302 * Returns \c true if numFrames() == 0 and \c false otherwise.
303 */
isNull() const304 bool Anim::isNull() const
305 {
306 return !numFrames();
307 }
308
309 /**
310 * Returns \c true when animation is paused.
311 */
paused() const312 bool Anim::paused() const
313 {
314 return d->paused;
315 }
316
317 /**
318 * Continues the animation playback.
319 */
unpause()320 void Anim::unpause()
321 {
322 if ( !isNull() && paused() )
323 d->unpause();
324 }
325
326 /**
327 * Pauses the animation.
328 */
pause()329 void Anim::pause()
330 {
331 if ( !isNull() && !paused() )
332 d->pause();
333 }
334
335 /**
336 * Starts animation from the very beginning.
337 */
restart()338 void Anim::restart()
339 {
340 if ( !isNull() )
341 d->restart();
342 }
343
344 /**
345 * Connects internal signal with specified slot \a member of object \a receiver, which
346 * is emitted when animation changes its frame.
347 *
348 * \code
349 * class MyObject : public QObject {
350 * Q_OBJECT
351 * // ...
352 * Anim *anim;
353 * void initAnim() {
354 * anim = new Anim( QByteArray(someData()) );
355 * anim->connectUpdate( this, SLOT(animUpdated()) );
356 * }
357 * public slots:
358 * void animUpdated();
359 * // ...
360 * }
361 * \endcode
362 *
363 * \sa disconnectUpdate()
364 */
connectUpdate(QObject * receiver,const char * member)365 void Anim::connectUpdate(QObject *receiver, const char *member)
366 {
367 QObject::connect(d, SIGNAL(areaChanged()), receiver, member);
368 }
369
370 /**
371 * Disconnects slot \a member, which was prevously connected with connectUpdate().
372 * \sa connectUpdate()
373 */
disconnectUpdate(QObject * receiver,const char * member)374 void Anim::disconnectUpdate(QObject *receiver, const char *member)
375 {
376 QObject::disconnect(d, SIGNAL(areaChanged()), receiver, member);
377 }
378
operator =(const Anim & from)379 Anim & Anim::operator= (const Anim &from)
380 {
381 d = from.d;
382
383 return *this;
384 }
385
copy() const386 Anim Anim::copy() const
387 {
388 Anim anim( *this );
389 anim.d = new Private( *this->d.data() );
390
391 return anim;
392 }
393
detach()394 void Anim::detach()
395 {
396 d.detach();
397 }
398
399 /**
400 * Strips the first animation frame, if there is more than one frame.
401 */
stripFirstFrame()402 void Anim::stripFirstFrame()
403 {
404 detach();
405 if ( numFrames() > 1 ) {
406 d->frames.takeFirst();
407
408 if ( !paused() )
409 restart();
410 }
411 }
412
413 /**
414 * Sets the main thread that will be used to create objects. Useful if you want
415 * to create Anim in non-main thread.
416 */
setMainThread(QThread * thread)417 void Anim::setMainThread(QThread *thread)
418 {
419 animMainThread = thread;
420 }
421
422 /**
423 * Get the Anim's main thread.
424 */
mainThread()425 QThread *Anim::mainThread()
426 {
427 return animMainThread;
428 }
429
430 #include "anim.moc"
431