753dd01acfe07e05d3dbdcb73bb988374629c537
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickanimatedimage.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickanimatedimage_p.h"
43 #include "qquickanimatedimage_p_p.h"
44
45 #ifndef QT_NO_MOVIE
46
47 #include <QtDeclarative/qdeclarativeinfo.h>
48 #include <QtGui/qmovie.h>
49 #include <QtNetwork/qnetworkrequest.h>
50 #include <QtNetwork/qnetworkreply.h>
51
52 #include <private/qdeclarativeengine_p.h>
53
54 QT_BEGIN_NAMESPACE
55 /*!
56     \qmlclass AnimatedImage QQuickAnimatedImage
57     \inqmlmodule QtQuick 2
58     \inherits Image
59     \ingroup basic-visual-elements
60
61     The AnimatedImage element extends the features of the \l Image element, providing
62     a way to play animations stored as images containing a series of frames,
63     such as those stored in GIF files.
64
65     Information about the current frame and total length of the animation can be
66     obtained using the \l currentFrame and \l frameCount properties. You can
67     start, pause and stop the animation by changing the values of the \l playing
68     and \l paused properties.
69
70     The full list of supported formats can be determined with QMovie::supportedFormats().
71
72     \section1 Example Usage
73
74     \beginfloatleft
75     \image animatedimageitem.gif
76     \endfloat
77
78     The following QML shows how to display an animated image and obtain information
79     about its state, such as the current frame and total number of frames.
80     The result is an animated image with a simple progress indicator underneath it.
81
82     \bold Note: Unlike images, animated images are not cached or shared internally.
83
84     \clearfloat
85     \snippet doc/src/snippets/declarative/animatedimage.qml document
86
87     \sa BorderImage, Image
88 */
89
90 /*!
91     \qmlproperty url QtQuick2::AnimatedImage::source
92
93     This property holds the URL that refers to the source image.
94
95     AnimatedImage can handle any image format supported by Qt, loaded from any
96     URL scheme supported by Qt.
97
98     \sa QDeclarativeImageProvider
99 */
100
101 /*!
102     \qmlproperty bool QtQuick2::AnimatedImage::asynchronous
103
104     Specifies that images on the local filesystem should be loaded
105     asynchronously in a separate thread.  The default value is
106     false, causing the user interface thread to block while the
107     image is loaded.  Setting \a asynchronous to true is useful where
108     maintaining a responsive user interface is more desirable
109     than having images immediately visible.
110
111     Note that this property is only valid for images read from the
112     local filesystem.  Images loaded via a network resource (e.g. HTTP)
113     are always loaded asynchronously.
114 */
115
116 /*!
117     \qmlproperty bool QtQuick2::AnimatedImage::mirror
118
119     This property holds whether the image should be horizontally inverted
120     (effectively displaying a mirrored image).
121
122     The default value is false.
123 */
124
125 QQuickAnimatedImage::QQuickAnimatedImage(QQuickItem *parent)
126     : QQuickImage(*(new QQuickAnimatedImagePrivate), parent)
127 {
128 }
129
130 QQuickAnimatedImage::~QQuickAnimatedImage()
131 {
132     Q_D(QQuickAnimatedImage);
133     delete d->_movie;
134 }
135
136 /*!
137   \qmlproperty bool QtQuick2::AnimatedImage::paused
138   This property holds whether the animated image is paused.
139
140   By default, this property is false. Set it to true when you want to pause
141   the animation.
142 */
143
144 bool QQuickAnimatedImage::isPaused() const
145 {
146     Q_D(const QQuickAnimatedImage);
147     if (!d->_movie)
148         return false;
149     return d->_movie->state()==QMovie::Paused;
150 }
151
152 void QQuickAnimatedImage::setPaused(bool pause)
153 {
154     Q_D(QQuickAnimatedImage);
155     if (pause == d->paused)
156         return;
157     d->paused = pause;
158     if (!d->_movie)
159         return;
160     d->_movie->setPaused(pause);
161 }
162
163 /*!
164   \qmlproperty bool QtQuick2::AnimatedImage::playing
165   This property holds whether the animated image is playing.
166
167   By default, this property is true, meaning that the animation
168   will start playing immediately.
169 */
170
171 bool QQuickAnimatedImage::isPlaying() const
172 {
173     Q_D(const QQuickAnimatedImage);
174     if (!d->_movie)
175         return false;
176     return d->_movie->state()!=QMovie::NotRunning;
177 }
178
179 void QQuickAnimatedImage::setPlaying(bool play)
180 {
181     Q_D(QQuickAnimatedImage);
182     if (play == d->playing)
183         return;
184     d->playing = play;
185     if (!d->_movie)
186         return;
187     if (play)
188         d->_movie->start();
189     else
190         d->_movie->stop();
191 }
192
193 /*!
194   \qmlproperty int QtQuick2::AnimatedImage::currentFrame
195   \qmlproperty int QtQuick2::AnimatedImage::frameCount
196
197   currentFrame is the frame that is currently visible. By monitoring this property
198   for changes, you can animate other items at the same time as the image.
199
200   frameCount is the number of frames in the animation. For some animation formats,
201   frameCount is unknown and has a value of zero.
202 */
203 int QQuickAnimatedImage::currentFrame() const
204 {
205     Q_D(const QQuickAnimatedImage);
206     if (!d->_movie)
207         return d->preset_currentframe;
208     return d->_movie->currentFrameNumber();
209 }
210
211 void QQuickAnimatedImage::setCurrentFrame(int frame)
212 {
213     Q_D(QQuickAnimatedImage);
214     if (!d->_movie) {
215         d->preset_currentframe = frame;
216         return;
217     }
218     d->_movie->jumpToFrame(frame);
219 }
220
221 int QQuickAnimatedImage::frameCount() const
222 {
223     Q_D(const QQuickAnimatedImage);
224     if (!d->_movie)
225         return 0;
226     return d->_movie->frameCount();
227 }
228
229 void QQuickAnimatedImage::setSource(const QUrl &url)
230 {
231     Q_D(QQuickAnimatedImage);
232     if (url == d->url)
233         return;
234
235     delete d->_movie;
236     d->_movie = 0;
237
238     if (d->reply) {
239         d->reply->deleteLater();
240         d->reply = 0;
241     }
242
243     d->url = url;
244     emit sourceChanged(d->url);
245
246     if (isComponentComplete())
247         load();
248 }
249
250 void QQuickAnimatedImage::load()
251 {
252     Q_D(QQuickAnimatedImage);
253
254     QQuickImageBase::Status oldStatus = d->status;
255     qreal oldProgress = d->progress;
256
257     if (d->url.isEmpty()) {
258         delete d->_movie;
259         d->setImage(QImage());
260         d->progress = 0;
261         d->status = Null;
262         if (d->status != oldStatus)
263             emit statusChanged(d->status);
264         if (d->progress != oldProgress)
265             emit progressChanged(d->progress);
266     } else {
267         QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url);
268         if (!lf.isEmpty()) {
269             //### should be unified with movieRequestFinished
270             d->_movie = new QMovie(lf);
271             if (!d->_movie->isValid()){
272                 qmlInfo(this) << "Error Reading Animated Image File " << d->url.toString();
273                 delete d->_movie;
274                 d->_movie = 0;
275                 d->status = Error;
276                 if (d->status != oldStatus)
277                     emit statusChanged(d->status);
278                 return;
279             }
280             connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)),
281                     this, SLOT(playingStatusChanged()));
282             connect(d->_movie, SIGNAL(frameChanged(int)),
283                     this, SLOT(movieUpdate()));
284             d->_movie->setCacheMode(QMovie::CacheAll);
285             if (d->playing)
286                 d->_movie->start();
287             else
288                 d->_movie->jumpToFrame(0);
289             if (d->paused)
290                 d->_movie->setPaused(true);
291             d->setImage(d->_movie->currentPixmap().toImage());
292             d->status = Ready;
293             d->progress = 1.0;
294             if (d->status != oldStatus)
295                 emit statusChanged(d->status);
296             if (d->progress != oldProgress)
297                 emit progressChanged(d->progress);
298             return;
299         }
300
301         d->status = Loading;
302         d->progress = 0;
303         emit statusChanged(d->status);
304         emit progressChanged(d->progress);
305         QNetworkRequest req(d->url);
306         req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
307         d->reply = qmlEngine(this)->networkAccessManager()->get(req);
308         QObject::connect(d->reply, SIGNAL(finished()),
309                          this, SLOT(movieRequestFinished()));
310         QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
311                          this, SLOT(requestProgress(qint64,qint64)));
312     }
313 }
314
315 #define ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION 16
316
317 void QQuickAnimatedImage::movieRequestFinished()
318 {
319     Q_D(QQuickAnimatedImage);
320
321     d->redirectCount++;
322     if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) {
323         QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
324         if (redirect.isValid()) {
325             QUrl url = d->reply->url().resolved(redirect.toUrl());
326             d->reply->deleteLater();
327             d->reply = 0;
328             setSource(url);
329             return;
330         }
331     }
332     d->redirectCount=0;
333
334     d->_movie = new QMovie(d->reply);
335     if (!d->_movie->isValid()){
336 #ifndef QT_NO_DEBUG_STREAM
337         qmlInfo(this) << "Error Reading Animated Image File " << d->url;
338 #endif
339         delete d->_movie;
340         d->_movie = 0;
341         d->status = Error;
342         emit statusChanged(d->status);
343         return;
344     }
345     connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)),
346             this, SLOT(playingStatusChanged()));
347     connect(d->_movie, SIGNAL(frameChanged(int)),
348             this, SLOT(movieUpdate()));
349     d->_movie->setCacheMode(QMovie::CacheAll);
350     if (d->playing)
351         d->_movie->start();
352     if (d->paused || !d->playing) {
353         d->_movie->jumpToFrame(d->preset_currentframe);
354         d->preset_currentframe = 0;
355     }
356     if (d->paused)
357         d->_movie->setPaused(true);
358     d->setImage(d->_movie->currentPixmap().toImage());
359     d->status = Ready;
360     emit statusChanged(d->status);
361 }
362
363 void QQuickAnimatedImage::movieUpdate()
364 {
365     Q_D(QQuickAnimatedImage);
366     d->setImage(d->_movie->currentPixmap().toImage());
367     emit frameChanged();
368 }
369
370 void QQuickAnimatedImage::playingStatusChanged()
371 {
372     Q_D(QQuickAnimatedImage);
373     if ((d->_movie->state() != QMovie::NotRunning) != d->playing) {
374         d->playing = (d->_movie->state() != QMovie::NotRunning);
375         emit playingChanged();
376     }
377     if ((d->_movie->state() == QMovie::Paused) != d->paused) {
378         d->playing = (d->_movie->state() == QMovie::Paused);
379         emit pausedChanged();
380     }
381 }
382
383 void QQuickAnimatedImage::componentComplete()
384 {
385     Q_D(QQuickAnimatedImage);
386     QQuickItem::componentComplete(); // NOT QQuickImage
387     if (d->url.isValid())
388         load();
389     if (!d->reply) {
390         setCurrentFrame(d->preset_currentframe);
391         d->preset_currentframe = 0;
392     }
393 }
394
395 QT_END_NAMESPACE
396
397 #endif // QT_NO_MOVIE