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