1 | /****************************************************************************
|
---|
2 | ** $Id: qmovie.cpp 2 2005-11-16 15:49:26Z dmik $
|
---|
3 | **
|
---|
4 | ** Implementation of movie classes
|
---|
5 | **
|
---|
6 | ** Created : 970617
|
---|
7 | **
|
---|
8 | ** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
|
---|
9 | **
|
---|
10 | ** This file is part of the kernel module of the Qt GUI Toolkit.
|
---|
11 | **
|
---|
12 | ** This file may be distributed under the terms of the Q Public License
|
---|
13 | ** as defined by Trolltech AS of Norway and appearing in the file
|
---|
14 | ** LICENSE.QPL included in the packaging of this file.
|
---|
15 | **
|
---|
16 | ** This file may be distributed and/or modified under the terms of the
|
---|
17 | ** GNU General Public License version 2 as published by the Free Software
|
---|
18 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
19 | ** packaging of this file.
|
---|
20 | **
|
---|
21 | ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
|
---|
22 | ** licenses may use this file in accordance with the Qt Commercial License
|
---|
23 | ** Agreement provided with the Software.
|
---|
24 | **
|
---|
25 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
---|
26 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
---|
27 | **
|
---|
28 | ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
|
---|
29 | ** information about Qt Commercial License Agreements.
|
---|
30 | ** See http://www.trolltech.com/qpl/ for QPL licensing information.
|
---|
31 | ** See http://www.trolltech.com/gpl/ for GPL licensing information.
|
---|
32 | **
|
---|
33 | ** Contact info@trolltech.com if any conditions of this licensing are
|
---|
34 | ** not clear to you.
|
---|
35 | **
|
---|
36 | **********************************************************************/
|
---|
37 |
|
---|
38 | // #define QT_SAVE_MOVIE_HACK
|
---|
39 |
|
---|
40 | #include "qtimer.h"
|
---|
41 | #include "qpainter.h"
|
---|
42 | #include "qptrlist.h"
|
---|
43 | #include "qbitmap.h"
|
---|
44 | #include "qmovie.h"
|
---|
45 | #include "qfile.h"
|
---|
46 | #include "qbuffer.h"
|
---|
47 | #include "qobject.h"
|
---|
48 | #include "qpixmapcache.h"
|
---|
49 |
|
---|
50 | #ifndef QT_NO_MOVIE
|
---|
51 |
|
---|
52 | #ifdef Q_WS_QWS
|
---|
53 | #include "qgfx_qws.h"
|
---|
54 | #endif
|
---|
55 |
|
---|
56 | #include "qasyncio.h"
|
---|
57 | #include "qasyncimageio.h"
|
---|
58 |
|
---|
59 | #include <stdlib.h>
|
---|
60 |
|
---|
61 | /*!
|
---|
62 | \class QMovie qmovie.h
|
---|
63 | \brief The QMovie class provides incremental loading of animations or images, signalling as it progresses.
|
---|
64 |
|
---|
65 | \ingroup images
|
---|
66 | \ingroup graphics
|
---|
67 | \ingroup multimedia
|
---|
68 | \mainclass
|
---|
69 |
|
---|
70 | The simplest way to display a QMovie is to use a QLabel and
|
---|
71 | QLabel::setMovie().
|
---|
72 |
|
---|
73 | A QMovie provides a QPixmap as the framePixmap(); connections can
|
---|
74 | be made via connectResize() and connectUpdate() to receive
|
---|
75 | notification of size and pixmap changes. All decoding is driven
|
---|
76 | by the normal event-processing mechanisms.
|
---|
77 |
|
---|
78 | The movie begins playing as soon as the QMovie is created
|
---|
79 | (actually, once control returns to the event loop). When the last
|
---|
80 | frame in the movie has been played, it may loop back to the start
|
---|
81 | if such looping is defined in the input source.
|
---|
82 |
|
---|
83 | QMovie objects are explicitly shared. This means that a QMovie
|
---|
84 | copied from another QMovie will be displaying the same frame at
|
---|
85 | all times. If one shared movie pauses, all pause. To make \e
|
---|
86 | independent movies, they must be constructed separately.
|
---|
87 |
|
---|
88 | The set of data formats supported by QMovie is determined by the
|
---|
89 | decoder factories that have been installed; the format of the
|
---|
90 | input is determined as the input is decoded.
|
---|
91 |
|
---|
92 | The supported formats are MNG (if Qt is configured with MNG
|
---|
93 | support enabled) and GIF (if Qt is configured with GIF support
|
---|
94 | enabled, see qgif.h).
|
---|
95 |
|
---|
96 | If Qt is configured to support GIF reading, we are required to
|
---|
97 | state that "The Graphics Interchange Format(c) is the Copyright
|
---|
98 | property of CompuServe Incorporated. GIF(sm) is a Service Mark
|
---|
99 | property of CompuServe Incorporated.
|
---|
100 |
|
---|
101 | \warning If you are in a country that recognizes software patents
|
---|
102 | and in which Unisys holds a patent on LZW compression and/or
|
---|
103 | decompression and you want to use GIF, Unisys may require you to
|
---|
104 | license that technology. Such countries include Canada, Japan,
|
---|
105 | the USA, France, Germany, Italy and the UK.
|
---|
106 |
|
---|
107 | GIF support may be removed completely in a future version of Qt.
|
---|
108 | We recommend using the MNG or PNG format.
|
---|
109 |
|
---|
110 | \img qmovie.png QMovie
|
---|
111 |
|
---|
112 | \sa QLabel::setMovie()
|
---|
113 | */
|
---|
114 |
|
---|
115 | /*!
|
---|
116 | \enum QMovie::Status
|
---|
117 |
|
---|
118 | \value SourceEmpty
|
---|
119 | \value UnrecognizedFormat
|
---|
120 | \value Paused
|
---|
121 | \value EndOfFrame
|
---|
122 | \value EndOfLoop
|
---|
123 | \value EndOfMovie
|
---|
124 | \value SpeedChanged
|
---|
125 | */
|
---|
126 |
|
---|
127 | class QMoviePrivate : public QObject, public QShared,
|
---|
128 | private QDataSink, private QImageConsumer
|
---|
129 | {
|
---|
130 | Q_OBJECT
|
---|
131 |
|
---|
132 | public: // for QMovie
|
---|
133 |
|
---|
134 | // Creates a null Private
|
---|
135 | QMoviePrivate();
|
---|
136 |
|
---|
137 | // NOTE: The ownership of the QDataSource is transferred to the Private
|
---|
138 | QMoviePrivate(QDataSource* src, QMovie* movie, int bufsize);
|
---|
139 |
|
---|
140 | virtual ~QMoviePrivate();
|
---|
141 |
|
---|
142 | bool isNull() const;
|
---|
143 |
|
---|
144 | // Initialize, possibly to the null state
|
---|
145 | void init(bool fully);
|
---|
146 | void flushBuffer();
|
---|
147 | void updatePixmapFromImage();
|
---|
148 | void updatePixmapFromImage(const QPoint& off, const QRect& area);
|
---|
149 | void showChanges();
|
---|
150 |
|
---|
151 | // This as QImageConsumer
|
---|
152 | void changed(const QRect& rect);
|
---|
153 | void end();
|
---|
154 | void preFrameDone(); //util func
|
---|
155 | void frameDone();
|
---|
156 | void frameDone(const QPoint&, const QRect& rect);
|
---|
157 | void restartTimer();
|
---|
158 | void setLooping(int l);
|
---|
159 | void setFramePeriod(int milliseconds);
|
---|
160 | void setSize(int w, int h);
|
---|
161 |
|
---|
162 | // This as QDataSink
|
---|
163 | int readyToReceive();
|
---|
164 | void receive(const uchar* b, int bytecount);
|
---|
165 | void eof();
|
---|
166 | void pause();
|
---|
167 |
|
---|
168 | signals:
|
---|
169 | void sizeChanged(const QSize&);
|
---|
170 | void areaChanged(const QRect&);
|
---|
171 | void dataStatus(int);
|
---|
172 |
|
---|
173 | public slots:
|
---|
174 | void refresh();
|
---|
175 |
|
---|
176 | public:
|
---|
177 | QMovie *that;
|
---|
178 | QWidget * display_widget;
|
---|
179 |
|
---|
180 | QImageDecoder *decoder;
|
---|
181 |
|
---|
182 | // Cyclic buffer
|
---|
183 | int buf_size;
|
---|
184 | uchar *buffer;
|
---|
185 | int buf_r, buf_w, buf_usage;
|
---|
186 |
|
---|
187 | int framenumber;
|
---|
188 | int frameperiod;
|
---|
189 | int speed;
|
---|
190 | QTimer *frametimer;
|
---|
191 | int lasttimerinterval;
|
---|
192 | int loop;
|
---|
193 | bool movie_ended;
|
---|
194 | bool dirty_cache;
|
---|
195 | bool waitingForFrameTick;
|
---|
196 | int stepping;
|
---|
197 | QRect changed_area;
|
---|
198 | QRect valid_area;
|
---|
199 | QDataPump *pump;
|
---|
200 | QDataSource *source;
|
---|
201 | QPixmap mypixmap;
|
---|
202 | QBitmap mymask;
|
---|
203 | QColor bg;
|
---|
204 |
|
---|
205 | int error;
|
---|
206 | bool empty;
|
---|
207 |
|
---|
208 | #ifdef QT_SAVE_MOVIE_HACK
|
---|
209 | bool save_image;
|
---|
210 | int image_number;
|
---|
211 | #endif
|
---|
212 | };
|
---|
213 |
|
---|
214 |
|
---|
215 | QMoviePrivate::QMoviePrivate()
|
---|
216 | {
|
---|
217 | dirty_cache = FALSE;
|
---|
218 | buffer = 0;
|
---|
219 | pump = 0;
|
---|
220 | source = 0;
|
---|
221 | decoder = 0;
|
---|
222 | display_widget=0;
|
---|
223 | buf_size = 0;
|
---|
224 | init(FALSE);
|
---|
225 | }
|
---|
226 |
|
---|
227 | // NOTE: The ownership of the QDataSource is transferred to the Private
|
---|
228 | QMoviePrivate::QMoviePrivate(QDataSource* src, QMovie* movie, int bufsize) :
|
---|
229 | that(movie),
|
---|
230 | buf_size(bufsize)
|
---|
231 | {
|
---|
232 | frametimer = new QTimer(this);
|
---|
233 | pump = src ? new QDataPump(src, this) : 0;
|
---|
234 | QObject::connect(frametimer, SIGNAL(timeout()), this, SLOT(refresh()));
|
---|
235 | dirty_cache = FALSE;
|
---|
236 | source = src;
|
---|
237 | buffer = 0;
|
---|
238 | decoder = 0;
|
---|
239 | speed = 100;
|
---|
240 | display_widget=0;
|
---|
241 | init(TRUE);
|
---|
242 | }
|
---|
243 |
|
---|
244 | QMoviePrivate::~QMoviePrivate()
|
---|
245 | {
|
---|
246 | if ( buffer ) // Avoid purify complaint
|
---|
247 | delete [] buffer;
|
---|
248 | delete pump;
|
---|
249 | delete decoder;
|
---|
250 | delete source;
|
---|
251 |
|
---|
252 | // Too bad.. but better be safe than sorry
|
---|
253 | if ( dirty_cache )
|
---|
254 | QPixmapCache::clear();
|
---|
255 | }
|
---|
256 |
|
---|
257 | bool QMoviePrivate::isNull() const
|
---|
258 | {
|
---|
259 | return !buf_size;
|
---|
260 | }
|
---|
261 |
|
---|
262 | // Initialize. Only actually allocate any space if \a fully is TRUE,
|
---|
263 | // otherwise, just enough to be a valid null Private.
|
---|
264 | void QMoviePrivate::init(bool fully)
|
---|
265 | {
|
---|
266 | #ifdef QT_SAVE_MOVIE_HACK
|
---|
267 | save_image = TRUE;
|
---|
268 | image_number = 0;
|
---|
269 | #endif
|
---|
270 |
|
---|
271 | buf_usage = buf_r = buf_w = 0;
|
---|
272 | if ( buffer ) // Avoid purify complaint
|
---|
273 | delete [] buffer;
|
---|
274 | buffer = fully ? new uchar[buf_size] : 0;
|
---|
275 | if ( buffer )
|
---|
276 | memset( buffer, 0, buf_size );
|
---|
277 |
|
---|
278 | delete decoder;
|
---|
279 | decoder = fully ? new QImageDecoder(this) : 0;
|
---|
280 |
|
---|
281 | #ifdef AVOID_OPEN_FDS
|
---|
282 | if ( source && !source->isOpen() )
|
---|
283 | source->open(IO_ReadOnly);
|
---|
284 | #endif
|
---|
285 |
|
---|
286 | waitingForFrameTick = FALSE;
|
---|
287 | stepping = -1;
|
---|
288 | framenumber = 0;
|
---|
289 | frameperiod = -1;
|
---|
290 | if (fully) frametimer->stop();
|
---|
291 | lasttimerinterval = -1;
|
---|
292 | changed_area.setRect(0,0,-1,-1);
|
---|
293 | valid_area = changed_area;
|
---|
294 | loop = -1;
|
---|
295 | movie_ended = FALSE;
|
---|
296 | error = 0;
|
---|
297 | empty = TRUE;
|
---|
298 | }
|
---|
299 |
|
---|
300 | void QMoviePrivate::flushBuffer()
|
---|
301 | {
|
---|
302 | int used;
|
---|
303 | while (buf_usage && !waitingForFrameTick && stepping != 0 && !error) {
|
---|
304 | used = decoder->decode(buffer + buf_r, QMIN(buf_usage, buf_size - buf_r));
|
---|
305 | if (used <= 0) {
|
---|
306 | if ( used < 0 ) {
|
---|
307 | error = 1;
|
---|
308 | emit dataStatus(QMovie::UnrecognizedFormat);
|
---|
309 | }
|
---|
310 | break;
|
---|
311 | }
|
---|
312 | buf_r = (buf_r + used) % buf_size;
|
---|
313 | buf_usage -= used;
|
---|
314 | }
|
---|
315 |
|
---|
316 | // Some formats, like MNG, can make stuff happen without any extra data.
|
---|
317 | // Only do this if the movie hasn't ended, however or we'll never get the end of loop signal.
|
---|
318 | if (!movie_ended) {
|
---|
319 | used = decoder->decode(buffer + buf_r, 0);
|
---|
320 | if (used <= 0) {
|
---|
321 | if ( used < 0 ) {
|
---|
322 | error = 1;
|
---|
323 | emit dataStatus(QMovie::UnrecognizedFormat);
|
---|
324 | }
|
---|
325 | }
|
---|
326 | }
|
---|
327 |
|
---|
328 | if (error)
|
---|
329 | frametimer->stop();
|
---|
330 | maybeReady();
|
---|
331 | }
|
---|
332 |
|
---|
333 | void QMoviePrivate::updatePixmapFromImage()
|
---|
334 | {
|
---|
335 | if (changed_area.isEmpty()) return;
|
---|
336 | updatePixmapFromImage(QPoint(0,0),changed_area);
|
---|
337 | }
|
---|
338 |
|
---|
339 | void QMoviePrivate::updatePixmapFromImage(const QPoint& off,
|
---|
340 | const QRect& area)
|
---|
341 | {
|
---|
342 | // Create temporary QImage to hold the part we want
|
---|
343 | const QImage& gimg = decoder->image();
|
---|
344 | QImage img = gimg.copy(area);
|
---|
345 |
|
---|
346 | #ifdef QT_SAVE_MOVIE_HACK
|
---|
347 | if ( save_image ) {
|
---|
348 | QString name;
|
---|
349 | name.sprintf("movie%i.ppm",image_number++);
|
---|
350 | gimg.save( name, "PPM" );
|
---|
351 | }
|
---|
352 | #endif
|
---|
353 |
|
---|
354 | // Resize to size of image
|
---|
355 | if (mypixmap.width() != gimg.width() || mypixmap.height() != gimg.height())
|
---|
356 | mypixmap.resize(gimg.width(), gimg.height());
|
---|
357 |
|
---|
358 | // Convert to pixmap and paste that onto myself
|
---|
359 | QPixmap lines;
|
---|
360 |
|
---|
361 | #ifndef QT_NO_SPRINTF
|
---|
362 | if (!(frameperiod < 0 && loop == -1)) {
|
---|
363 | // its an animation, lets see if we converted
|
---|
364 | // this frame already.
|
---|
365 | QString key;
|
---|
366 | key.sprintf( "%08lx:%04d", ( long )this, framenumber );
|
---|
367 | if ( !QPixmapCache::find( key, lines ) ) {
|
---|
368 | lines.convertFromImage(img, Qt::ColorOnly);
|
---|
369 | QPixmapCache::insert( key, lines );
|
---|
370 | dirty_cache = TRUE;
|
---|
371 | }
|
---|
372 | } else
|
---|
373 | #endif
|
---|
374 | {
|
---|
375 | lines.convertFromImage(img, Qt::ColorOnly);
|
---|
376 | }
|
---|
377 |
|
---|
378 | if (bg.isValid()) {
|
---|
379 | QPainter p;
|
---|
380 | p.begin(&mypixmap);
|
---|
381 | p.fillRect(area, bg);
|
---|
382 | p.drawPixmap(area, lines);
|
---|
383 | p.end();
|
---|
384 | } else {
|
---|
385 | if (gimg.hasAlphaBuffer()) {
|
---|
386 | // Resize to size of image
|
---|
387 | if (mymask.isNull()) {
|
---|
388 | mymask.resize(gimg.width(), gimg.height());
|
---|
389 | mymask.fill( Qt::color1 );
|
---|
390 | }
|
---|
391 | }
|
---|
392 | mypixmap.setMask(QBitmap()); // Remove reference to my mask
|
---|
393 | copyBlt( &mypixmap, area.left(), area.top(),
|
---|
394 | &lines, off.x(), off.y(), area.width(), area.height() );
|
---|
395 | }
|
---|
396 |
|
---|
397 | #ifdef Q_WS_QWS
|
---|
398 | if(display_widget) {
|
---|
399 | QGfx * mygfx=display_widget->graphicsContext();
|
---|
400 | if(mygfx) {
|
---|
401 | double xscale,yscale;
|
---|
402 | xscale=display_widget->width();
|
---|
403 | yscale=display_widget->height();
|
---|
404 | xscale=xscale/((double)mypixmap.width());
|
---|
405 | yscale=yscale/((double)mypixmap.height());
|
---|
406 | double xh,yh;
|
---|
407 | xh=xscale*((double)area.left());
|
---|
408 | yh=yscale*((double)area.top());
|
---|
409 | mygfx->setSource(&mypixmap);
|
---|
410 | mygfx->setAlphaType(QGfx::IgnoreAlpha);
|
---|
411 | mygfx->stretchBlt(0,0,display_widget->width(),
|
---|
412 | display_widget->height(),mypixmap.width(),
|
---|
413 | mypixmap.height());
|
---|
414 | delete mygfx;
|
---|
415 | }
|
---|
416 | }
|
---|
417 | #endif
|
---|
418 | }
|
---|
419 |
|
---|
420 | void QMoviePrivate::showChanges()
|
---|
421 | {
|
---|
422 | if (changed_area.isValid()) {
|
---|
423 | updatePixmapFromImage();
|
---|
424 |
|
---|
425 | valid_area = valid_area.unite(changed_area);
|
---|
426 | emit areaChanged(changed_area);
|
---|
427 |
|
---|
428 | changed_area.setWidth(-1); // make empty
|
---|
429 | }
|
---|
430 | }
|
---|
431 |
|
---|
432 | // Private as QImageConsumer
|
---|
433 | void QMoviePrivate::changed(const QRect& rect)
|
---|
434 | {
|
---|
435 | if (!frametimer->isActive())
|
---|
436 | frametimer->start(0);
|
---|
437 | changed_area = changed_area.unite(rect);
|
---|
438 | }
|
---|
439 |
|
---|
440 | void QMoviePrivate::end()
|
---|
441 | {
|
---|
442 | movie_ended = TRUE;
|
---|
443 | }
|
---|
444 |
|
---|
445 | void QMoviePrivate::preFrameDone()
|
---|
446 | {
|
---|
447 | if (stepping > 0) {
|
---|
448 | stepping--;
|
---|
449 | if (!stepping) {
|
---|
450 | frametimer->stop();
|
---|
451 | emit dataStatus( QMovie::Paused );
|
---|
452 | }
|
---|
453 | } else {
|
---|
454 | waitingForFrameTick = TRUE;
|
---|
455 | restartTimer();
|
---|
456 | }
|
---|
457 | }
|
---|
458 | void QMoviePrivate::frameDone()
|
---|
459 | {
|
---|
460 | preFrameDone();
|
---|
461 | showChanges();
|
---|
462 | emit dataStatus(QMovie::EndOfFrame);
|
---|
463 | framenumber++;
|
---|
464 | }
|
---|
465 | void QMoviePrivate::frameDone(const QPoint& p,
|
---|
466 | const QRect& rect)
|
---|
467 | {
|
---|
468 | preFrameDone();
|
---|
469 | const QImage& gimg = decoder->image();
|
---|
470 | if (framenumber==0)
|
---|
471 | emit sizeChanged(gimg.size());
|
---|
472 | valid_area = valid_area.unite(QRect(p,rect.size()));
|
---|
473 | updatePixmapFromImage(p,rect);
|
---|
474 | emit areaChanged(QRect(p,rect.size()));
|
---|
475 | emit dataStatus(QMovie::EndOfFrame);
|
---|
476 | framenumber++;
|
---|
477 | }
|
---|
478 |
|
---|
479 | void QMoviePrivate::restartTimer()
|
---|
480 | {
|
---|
481 | if (speed > 0) {
|
---|
482 | int i = frameperiod >= 0 ? frameperiod * 100/speed : 0;
|
---|
483 | if ( i != lasttimerinterval || !frametimer->isActive() ) {
|
---|
484 | lasttimerinterval = i;
|
---|
485 | frametimer->start( i );
|
---|
486 | }
|
---|
487 | } else {
|
---|
488 | frametimer->stop();
|
---|
489 | }
|
---|
490 | }
|
---|
491 |
|
---|
492 | void QMoviePrivate::setLooping(int nloops)
|
---|
493 | {
|
---|
494 | if (loop == -1) { // Only if we don't already know how many loops!
|
---|
495 | if (source && source->rewindable()) {
|
---|
496 | source->enableRewind(TRUE);
|
---|
497 | loop = nloops;
|
---|
498 | } else {
|
---|
499 | // Cannot loop from this source
|
---|
500 | loop = -2;
|
---|
501 | }
|
---|
502 | }
|
---|
503 | }
|
---|
504 |
|
---|
505 | void QMoviePrivate::setFramePeriod(int milliseconds)
|
---|
506 | {
|
---|
507 | // Animation: only show complete frame
|
---|
508 | frameperiod = milliseconds;
|
---|
509 | if (stepping<0 && frameperiod >= 0) restartTimer();
|
---|
510 | }
|
---|
511 |
|
---|
512 | void QMoviePrivate::setSize(int w, int h)
|
---|
513 | {
|
---|
514 | if (mypixmap.width() != w || mypixmap.height() != h) {
|
---|
515 | mypixmap.resize(w, h);
|
---|
516 | emit sizeChanged(QSize(w, h));
|
---|
517 | }
|
---|
518 | }
|
---|
519 |
|
---|
520 |
|
---|
521 | // Private as QDataSink
|
---|
522 |
|
---|
523 | int QMoviePrivate::readyToReceive()
|
---|
524 | {
|
---|
525 | // Could pre-fill buffer, but more efficient to just leave the
|
---|
526 | // data back at the source.
|
---|
527 | return (waitingForFrameTick || !stepping || buf_usage || error)
|
---|
528 | ? 0 : buf_size;
|
---|
529 | }
|
---|
530 |
|
---|
531 | void QMoviePrivate::receive(const uchar* b, int bytecount)
|
---|
532 | {
|
---|
533 | if ( bytecount ) empty = FALSE;
|
---|
534 |
|
---|
535 | while (bytecount && !waitingForFrameTick && stepping != 0) {
|
---|
536 | int used = decoder->decode(b, bytecount);
|
---|
537 | if (used<=0) {
|
---|
538 | if ( used < 0 ) {
|
---|
539 | error = 1;
|
---|
540 | emit dataStatus(QMovie::UnrecognizedFormat);
|
---|
541 | }
|
---|
542 | break;
|
---|
543 | }
|
---|
544 | b+=used;
|
---|
545 | bytecount-=used;
|
---|
546 | }
|
---|
547 |
|
---|
548 | // Append unused to buffer
|
---|
549 | while (bytecount--) {
|
---|
550 | buffer[buf_w] = *b++;
|
---|
551 | buf_w = (buf_w+1)%buf_size;
|
---|
552 | buf_usage++;
|
---|
553 | }
|
---|
554 | }
|
---|
555 |
|
---|
556 | void QMoviePrivate::eof()
|
---|
557 | {
|
---|
558 | if ( !movie_ended )
|
---|
559 | return;
|
---|
560 |
|
---|
561 | if ( empty )
|
---|
562 | emit dataStatus(QMovie::SourceEmpty);
|
---|
563 |
|
---|
564 | #ifdef QT_SAVE_MOVIE_HACK
|
---|
565 | save_image = FALSE;
|
---|
566 | #endif
|
---|
567 |
|
---|
568 | emit dataStatus(QMovie::EndOfLoop);
|
---|
569 |
|
---|
570 | if (loop >= 0) {
|
---|
571 | if (loop) {
|
---|
572 | loop--;
|
---|
573 | if (!loop) return;
|
---|
574 | }
|
---|
575 | delete decoder;
|
---|
576 | decoder = new QImageDecoder(this);
|
---|
577 | source->rewind();
|
---|
578 | framenumber = 0;
|
---|
579 | movie_ended = FALSE;
|
---|
580 | } else {
|
---|
581 | delete decoder;
|
---|
582 | decoder = 0;
|
---|
583 | if ( buffer ) // Avoid purify complaint
|
---|
584 | delete [] buffer;
|
---|
585 | buffer = 0;
|
---|
586 | emit dataStatus(QMovie::EndOfMovie);
|
---|
587 | #ifdef AVOID_OPEN_FDS
|
---|
588 | if ( source )
|
---|
589 | source->close();
|
---|
590 | #endif
|
---|
591 | }
|
---|
592 | }
|
---|
593 |
|
---|
594 | void QMoviePrivate::pause()
|
---|
595 | {
|
---|
596 | if ( stepping ) {
|
---|
597 | stepping = 0;
|
---|
598 | frametimer->stop();
|
---|
599 | emit dataStatus( QMovie::Paused );
|
---|
600 | }
|
---|
601 | }
|
---|
602 |
|
---|
603 | void QMoviePrivate::refresh()
|
---|
604 | {
|
---|
605 | if (!decoder) {
|
---|
606 | frametimer->stop();
|
---|
607 | return;
|
---|
608 | }
|
---|
609 |
|
---|
610 | if (frameperiod < 0 && loop == -1) {
|
---|
611 | // Only show changes if probably not an animation
|
---|
612 | showChanges();
|
---|
613 | }
|
---|
614 |
|
---|
615 | if (!buf_usage) {
|
---|
616 | frametimer->stop();
|
---|
617 | }
|
---|
618 |
|
---|
619 | waitingForFrameTick = FALSE;
|
---|
620 | flushBuffer();
|
---|
621 | }
|
---|
622 |
|
---|
623 | ///////////////// End of Private /////////////////
|
---|
624 |
|
---|
625 |
|
---|
626 |
|
---|
627 |
|
---|
628 |
|
---|
629 | /*!
|
---|
630 | Constructs a null QMovie. The only interesting thing to do with
|
---|
631 | such a movie is to assign another movie to it.
|
---|
632 |
|
---|
633 | \sa isNull()
|
---|
634 | */
|
---|
635 | QMovie::QMovie()
|
---|
636 | {
|
---|
637 | d = new QMoviePrivate();
|
---|
638 | }
|
---|
639 |
|
---|
640 | /*!
|
---|
641 | Constructs a QMovie with an external data source. You should later
|
---|
642 | call pushData() to send incoming animation data to the movie.
|
---|
643 |
|
---|
644 | The \a bufsize argument sets the maximum amount of data the movie
|
---|
645 | will transfer from the data source per event loop. The lower this
|
---|
646 | value, the better interleaved the movie playback will be with
|
---|
647 | other event processing, but the slower the overall processing will
|
---|
648 | be.
|
---|
649 |
|
---|
650 | \sa pushData()
|
---|
651 | */
|
---|
652 | QMovie::QMovie(int bufsize)
|
---|
653 | {
|
---|
654 | d = new QMoviePrivate(0, this, bufsize);
|
---|
655 | }
|
---|
656 |
|
---|
657 | /*!
|
---|
658 | Returns the maximum amount of data that can currently be pushed
|
---|
659 | into the movie by a call to pushData(). This is affected by the
|
---|
660 | initial buffer size, but varies as the movie plays and data is
|
---|
661 | consumed.
|
---|
662 | */
|
---|
663 | int QMovie::pushSpace() const
|
---|
664 | {
|
---|
665 | return d->readyToReceive();
|
---|
666 | }
|
---|
667 |
|
---|
668 | /*!
|
---|
669 | Pushes \a length bytes from \a data into the movie. \a length must
|
---|
670 | be no more than the amount returned by pushSpace() since the
|
---|
671 | previous call to pushData().
|
---|
672 | */
|
---|
673 | void QMovie::pushData(const uchar* data, int length)
|
---|
674 | {
|
---|
675 | d->receive(data,length);
|
---|
676 | }
|
---|
677 |
|
---|
678 | #ifdef Q_WS_QWS // ##### Temporary performance experiment
|
---|
679 | /*!
|
---|
680 | \internal
|
---|
681 | */
|
---|
682 | void QMovie::setDisplayWidget(QWidget * w)
|
---|
683 | {
|
---|
684 | d->display_widget=w;
|
---|
685 | }
|
---|
686 | #endif
|
---|
687 |
|
---|
688 | /*!
|
---|
689 | Constructs a QMovie that reads an image sequence from the given
|
---|
690 | data source, \a src. The source must be allocated dynamically,
|
---|
691 | because QMovie will take ownership of it and will destroy it when
|
---|
692 | the movie is destroyed. The movie starts playing as soon as event
|
---|
693 | processing continues.
|
---|
694 |
|
---|
695 | The \a bufsize argument sets the maximum amount of data the movie
|
---|
696 | will transfer from the data source per event loop. The lower this
|
---|
697 | value, the better interleaved the movie playback will be with
|
---|
698 | other event processing, but the slower the overall processing will
|
---|
699 | be.
|
---|
700 | */
|
---|
701 | QMovie::QMovie(QDataSource* src, int bufsize)
|
---|
702 | {
|
---|
703 | d = new QMoviePrivate(src, this, bufsize);
|
---|
704 | }
|
---|
705 |
|
---|
706 | /*!
|
---|
707 | Constructs a QMovie that reads an image sequence from the file, \a
|
---|
708 | fileName.
|
---|
709 |
|
---|
710 | The \a bufsize argument sets the maximum amount of data the movie
|
---|
711 | will transfer from the data source per event loop. The lower this
|
---|
712 | value, the better interleaved the movie playback will be with
|
---|
713 | other event processing, but the slower the overall processing will
|
---|
714 | be.
|
---|
715 | */
|
---|
716 | QMovie::QMovie(const QString &fileName, int bufsize)
|
---|
717 | {
|
---|
718 | QFile* file = new QFile(fileName);
|
---|
719 | if ( !fileName.isEmpty() )
|
---|
720 | file->open(IO_ReadOnly);
|
---|
721 | d = new QMoviePrivate(new QIODeviceSource(file, bufsize), this, bufsize);
|
---|
722 | }
|
---|
723 |
|
---|
724 | /*!
|
---|
725 | Constructs a QMovie that reads an image sequence from the byte
|
---|
726 | array, \a data.
|
---|
727 |
|
---|
728 | The \a bufsize argument sets the maximum amount of data the movie
|
---|
729 | will transfer from the data source per event loop. The lower this
|
---|
730 | value, the better interleaved the movie playback will be with
|
---|
731 | other event processing, but the slower the overall processing will
|
---|
732 | be.
|
---|
733 | */
|
---|
734 | QMovie::QMovie(QByteArray data, int bufsize)
|
---|
735 | {
|
---|
736 | QBuffer* buffer = new QBuffer(data);
|
---|
737 | buffer->open(IO_ReadOnly);
|
---|
738 | d = new QMoviePrivate(new QIODeviceSource(buffer, bufsize), this, bufsize);
|
---|
739 | }
|
---|
740 |
|
---|
741 | /*!
|
---|
742 | Constructs a movie that uses the same data as movie \a movie.
|
---|
743 | QMovies use explicit sharing, so operations on the copy will
|
---|
744 | affect both.
|
---|
745 | */
|
---|
746 | QMovie::QMovie(const QMovie& movie)
|
---|
747 | {
|
---|
748 | d = movie.d;
|
---|
749 | d->ref();
|
---|
750 | }
|
---|
751 |
|
---|
752 | /*!
|
---|
753 | Destroys the QMovie. If this is the last reference to the data of
|
---|
754 | the movie, the data is deallocated.
|
---|
755 | */
|
---|
756 | QMovie::~QMovie()
|
---|
757 | {
|
---|
758 | if (d->deref()) delete d;
|
---|
759 | }
|
---|
760 |
|
---|
761 | /*!
|
---|
762 | Returns TRUE if the movie is null; otherwise returns FALSE.
|
---|
763 | */
|
---|
764 | bool QMovie::isNull() const
|
---|
765 | {
|
---|
766 | return d->isNull();
|
---|
767 | }
|
---|
768 |
|
---|
769 | /*!
|
---|
770 | Makes this movie use the same data as movie \a movie. QMovies use
|
---|
771 | explicit sharing.
|
---|
772 | */
|
---|
773 | QMovie& QMovie::operator=(const QMovie& movie)
|
---|
774 | {
|
---|
775 | movie.d->ref();
|
---|
776 | if (d->deref()) delete d;
|
---|
777 | d = movie.d;
|
---|
778 | return *this;
|
---|
779 | }
|
---|
780 |
|
---|
781 |
|
---|
782 | /*!
|
---|
783 | Sets the background color of the pixmap to \a c. If the background
|
---|
784 | color isValid(), the pixmap will never have a mask because the
|
---|
785 | background color will be used in transparent regions of the image.
|
---|
786 |
|
---|
787 | \sa backgroundColor()
|
---|
788 | */
|
---|
789 | void QMovie::setBackgroundColor(const QColor& c)
|
---|
790 | {
|
---|
791 | d->bg = c;
|
---|
792 | }
|
---|
793 |
|
---|
794 | /*!
|
---|
795 | Returns the background color of the movie set by
|
---|
796 | setBackgroundColor().
|
---|
797 | */
|
---|
798 | const QColor& QMovie::backgroundColor() const
|
---|
799 | {
|
---|
800 | return d->bg;
|
---|
801 | }
|
---|
802 |
|
---|
803 | /*!
|
---|
804 | Returns the area of the pixmap for which pixels have been
|
---|
805 | generated.
|
---|
806 | */
|
---|
807 | const QRect& QMovie::getValidRect() const
|
---|
808 | {
|
---|
809 | return d->valid_area;
|
---|
810 | }
|
---|
811 |
|
---|
812 | /*!
|
---|
813 | Returns the current frame of the movie, as a QPixmap. It is not
|
---|
814 | generally useful to keep a copy of this pixmap. It is better to
|
---|
815 | keep a copy of the QMovie and get the framePixmap() only when
|
---|
816 | needed for drawing.
|
---|
817 |
|
---|
818 | \sa frameImage()
|
---|
819 | */
|
---|
820 | const QPixmap& QMovie::framePixmap() const
|
---|
821 | {
|
---|
822 | return d->mypixmap;
|
---|
823 | }
|
---|
824 |
|
---|
825 | /*!
|
---|
826 | Returns the current frame of the movie, as a QImage. It is not
|
---|
827 | generally useful to keep a copy of this image. Also note that you
|
---|
828 | must not call this function if the movie is finished(), since by
|
---|
829 | then the image will not be available.
|
---|
830 |
|
---|
831 | \sa framePixmap()
|
---|
832 | */
|
---|
833 | const QImage& QMovie::frameImage() const
|
---|
834 | {
|
---|
835 | return d->decoder->image();
|
---|
836 | }
|
---|
837 |
|
---|
838 | /*!
|
---|
839 | Returns the number of steps remaining after a call to step(). If
|
---|
840 | the movie is paused, steps() returns 0. If it's running normally
|
---|
841 | or is finished, steps() returns a negative number.
|
---|
842 | */
|
---|
843 | int QMovie::steps() const
|
---|
844 | {
|
---|
845 | return d->stepping;
|
---|
846 | }
|
---|
847 |
|
---|
848 | /*!
|
---|
849 | Returns the number of times EndOfFrame has been emitted since the
|
---|
850 | start of the current loop of the movie. Thus, before any
|
---|
851 | EndOfFrame has been emitted the value will be 0; within slots
|
---|
852 | processing the first signal, frameNumber() will be 1, and so on.
|
---|
853 | */
|
---|
854 | int QMovie::frameNumber() const { return d->framenumber; }
|
---|
855 |
|
---|
856 | /*!
|
---|
857 | Returns TRUE if the image is paused; otherwise returns FALSE.
|
---|
858 | */
|
---|
859 | bool QMovie::paused() const
|
---|
860 | {
|
---|
861 | return d->stepping == 0;
|
---|
862 | }
|
---|
863 |
|
---|
864 | /*!
|
---|
865 | Returns TRUE if the image is no longer playing: this happens when
|
---|
866 | all loops of all frames are complete; otherwise returns FALSE.
|
---|
867 | */
|
---|
868 | bool QMovie::finished() const
|
---|
869 | {
|
---|
870 | return !d->decoder;
|
---|
871 | }
|
---|
872 |
|
---|
873 | /*!
|
---|
874 | Returns TRUE if the image is not single-stepping, not paused, and
|
---|
875 | not finished; otherwise returns FALSE.
|
---|
876 | */
|
---|
877 | bool QMovie::running() const
|
---|
878 | {
|
---|
879 | return d->stepping<0 && d->decoder;
|
---|
880 | }
|
---|
881 |
|
---|
882 | /*!
|
---|
883 | Pauses the progress of the animation.
|
---|
884 |
|
---|
885 | \sa unpause()
|
---|
886 | */
|
---|
887 | void QMovie::pause()
|
---|
888 | {
|
---|
889 | d->pause();
|
---|
890 | }
|
---|
891 |
|
---|
892 | /*!
|
---|
893 | Unpauses the progress of the animation.
|
---|
894 |
|
---|
895 | \sa pause()
|
---|
896 | */
|
---|
897 | void QMovie::unpause()
|
---|
898 | {
|
---|
899 | if ( d->stepping >= 0 ) {
|
---|
900 | if (d->isNull())
|
---|
901 | return;
|
---|
902 | d->stepping = -1;
|
---|
903 | d->restartTimer();
|
---|
904 | }
|
---|
905 | }
|
---|
906 |
|
---|
907 | /*!
|
---|
908 | \overload
|
---|
909 |
|
---|
910 | Steps forward, showing \a steps frames, and then pauses.
|
---|
911 | */
|
---|
912 | void QMovie::step(int steps)
|
---|
913 | {
|
---|
914 | if (d->isNull())
|
---|
915 | return;
|
---|
916 | d->stepping = steps;
|
---|
917 | d->frametimer->start(0);
|
---|
918 | d->waitingForFrameTick = FALSE; // Full speed ahead!
|
---|
919 | }
|
---|
920 |
|
---|
921 | /*!
|
---|
922 | Steps forward 1 frame and then pauses.
|
---|
923 | */
|
---|
924 | void QMovie::step()
|
---|
925 | {
|
---|
926 | step(1);
|
---|
927 | }
|
---|
928 |
|
---|
929 | /*!
|
---|
930 | Rewinds the movie to the beginning. If the movie has not been
|
---|
931 | paused, it begins playing again.
|
---|
932 | */
|
---|
933 | void QMovie::restart()
|
---|
934 | {
|
---|
935 | if (d->isNull())
|
---|
936 | return;
|
---|
937 | if (d->source->rewindable()) {
|
---|
938 | d->source->enableRewind(TRUE);
|
---|
939 | d->source->rewind();
|
---|
940 | int s = d->stepping;
|
---|
941 | d->init(TRUE);
|
---|
942 | if ( s>0 )
|
---|
943 | step(s);
|
---|
944 | else if ( s==0 )
|
---|
945 | pause();
|
---|
946 | }
|
---|
947 | }
|
---|
948 |
|
---|
949 | /*!
|
---|
950 | Returns the movie's play speed as a percentage. The default is 100
|
---|
951 | percent.
|
---|
952 |
|
---|
953 | \sa setSpeed()
|
---|
954 | */
|
---|
955 | int QMovie::speed() const
|
---|
956 | {
|
---|
957 | return d->speed;
|
---|
958 | }
|
---|
959 |
|
---|
960 | /*!
|
---|
961 | Sets the movie's play speed as a percentage, to \a percent. This
|
---|
962 | is a percentage of the speed dictated by the input data format.
|
---|
963 | The default is 100 percent.
|
---|
964 | */
|
---|
965 | void QMovie::setSpeed(int percent)
|
---|
966 | {
|
---|
967 | int oldspeed = d->speed;
|
---|
968 | if ( oldspeed != percent && percent >= 0 ) {
|
---|
969 | d->speed = percent;
|
---|
970 | // Restart timer only if really needed
|
---|
971 | if (d->stepping < 0) {
|
---|
972 | if ( !percent || !oldspeed // To or from zero
|
---|
973 | || oldspeed*4 / percent > 4 // More than 20% slower
|
---|
974 | || percent*4 / oldspeed > 4 // More than 20% faster
|
---|
975 | )
|
---|
976 | d->restartTimer();
|
---|
977 | }
|
---|
978 | }
|
---|
979 | }
|
---|
980 |
|
---|
981 | /*!
|
---|
982 | Connects the \a{receiver}'s \a member of type \c{void member(const
|
---|
983 | QSize&)} so that it is signalled when the movie changes size.
|
---|
984 |
|
---|
985 | Note that due to the explicit sharing of QMovie objects, these
|
---|
986 | connections persist until they are explicitly disconnected with
|
---|
987 | disconnectResize() or until \e every shared copy of the movie is
|
---|
988 | deleted.
|
---|
989 | */
|
---|
990 | void QMovie::connectResize(QObject* receiver, const char *member)
|
---|
991 | {
|
---|
992 | QObject::connect(d, SIGNAL(sizeChanged(const QSize&)), receiver, member);
|
---|
993 | }
|
---|
994 |
|
---|
995 | /*!
|
---|
996 | Disconnects the \a{receiver}'s \a member (or all members if \a
|
---|
997 | member is zero) that were previously connected by connectResize().
|
---|
998 | */
|
---|
999 | void QMovie::disconnectResize(QObject* receiver, const char *member)
|
---|
1000 | {
|
---|
1001 | QObject::disconnect(d, SIGNAL(sizeChanged(const QSize&)), receiver, member);
|
---|
1002 | }
|
---|
1003 |
|
---|
1004 | /*!
|
---|
1005 | Connects the \a{receiver}'s \a member of type \c{void member(const
|
---|
1006 | QRect&)} so that it is signalled when an area of the framePixmap()
|
---|
1007 | has changed since the previous frame.
|
---|
1008 |
|
---|
1009 | Note that due to the explicit sharing of QMovie objects, these
|
---|
1010 | connections persist until they are explicitly disconnected with
|
---|
1011 | disconnectUpdate() or until \e every shared copy of the movie is
|
---|
1012 | deleted.
|
---|
1013 | */
|
---|
1014 | void QMovie::connectUpdate(QObject* receiver, const char *member)
|
---|
1015 | {
|
---|
1016 | QObject::connect(d, SIGNAL(areaChanged(const QRect&)), receiver, member);
|
---|
1017 | }
|
---|
1018 |
|
---|
1019 | /*!
|
---|
1020 | Disconnects the \a{receiver}'s \a member (or all members if \q
|
---|
1021 | member is zero) that were previously connected by connectUpdate().
|
---|
1022 | */
|
---|
1023 | void QMovie::disconnectUpdate(QObject* receiver, const char *member)
|
---|
1024 | {
|
---|
1025 | QObject::disconnect(d, SIGNAL(areaChanged(const QRect&)), receiver, member);
|
---|
1026 | }
|
---|
1027 |
|
---|
1028 | /*!
|
---|
1029 | Connects the \a{receiver}'s \a member, of type \c{void
|
---|
1030 | member(int)} so that it is signalled when the movie changes
|
---|
1031 | status. The status codes are negative for errors and positive for
|
---|
1032 | information.
|
---|
1033 |
|
---|
1034 | \table
|
---|
1035 | \header \i Status Code \i Meaning
|
---|
1036 | \row \i QMovie::SourceEmpty
|
---|
1037 | \i signalled if the input cannot be read.
|
---|
1038 | \row \i QMovie::UnrecognizedFormat
|
---|
1039 | \i signalled if the input data is unrecognized.
|
---|
1040 | \row \i QMovie::Paused
|
---|
1041 | \i signalled when the movie is paused by a call to paused()
|
---|
1042 | or by after \link step() stepping \endlink pauses.
|
---|
1043 | \row \i QMovie::EndOfFrame
|
---|
1044 | \i signalled at end-of-frame after any update and Paused signals.
|
---|
1045 | \row \i QMovie::EndOfLoop
|
---|
1046 | \i signalled at end-of-loop, after any update signals,
|
---|
1047 | EndOfFrame - but before EndOfMovie.
|
---|
1048 | \row \i QMovie::EndOfMovie
|
---|
1049 | \i signalled when the movie completes and is not about to loop.
|
---|
1050 | \endtable
|
---|
1051 |
|
---|
1052 | More status messages may be added in the future, so a general test
|
---|
1053 | for errors would test for negative.
|
---|
1054 |
|
---|
1055 | Note that due to the explicit sharing of QMovie objects, these
|
---|
1056 | connections persist until they are explicitly disconnected with
|
---|
1057 | disconnectStatus() or until \e every shared copy of the movie is
|
---|
1058 | deleted.
|
---|
1059 | */
|
---|
1060 | void QMovie::connectStatus(QObject* receiver, const char *member)
|
---|
1061 | {
|
---|
1062 | QObject::connect(d, SIGNAL(dataStatus(int)), receiver, member);
|
---|
1063 | }
|
---|
1064 |
|
---|
1065 | /*!
|
---|
1066 | Disconnects the \a{receiver}'s \a member (or all members if \a
|
---|
1067 | member is zero) that were previously connected by connectStatus().
|
---|
1068 | */
|
---|
1069 | void QMovie::disconnectStatus(QObject* receiver, const char *member)
|
---|
1070 | {
|
---|
1071 | QObject::disconnect(d, SIGNAL(dataStatus(int)), receiver, member);
|
---|
1072 | }
|
---|
1073 |
|
---|
1074 |
|
---|
1075 | #include "qmovie.moc"
|
---|
1076 |
|
---|
1077 | #endif // QT_NO_MOVIE
|
---|