QtDeclarative: Fix warnings about uninitialized variables.
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickanimatedsprite.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQuick module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickanimatedsprite_p.h"
43 #include "qquicksprite_p.h"
44 #include "qquickspriteengine_p.h"
45 #include <QtQuick/private/qsgcontext_p.h>
46 #include <private/qsgadaptationlayer_p.h>
47 #include <QtQuick/qsgnode.h>
48 #include <QtQuick/qsgtexturematerial.h>
49 #include <QtQuick/qsgtexture.h>
50 #include <QtQuick/qquickcanvas.h>
51 #include <QtQml/qqmlinfo.h>
52 #include <QFile>
53 #include <cmath>
54 #include <qmath.h>
55 #include <QDebug>
56
57 QT_BEGIN_NAMESPACE
58
59 static const char vertexShaderCode[] =
60     "attribute highp vec2 vPos;\n"
61     "attribute highp vec2 vTex;\n"
62     "uniform highp vec3 animData;// w,h(premultiplied of anim), interpolation progress\n"
63     "uniform highp vec4 animPos;//x,y, x,y (two frames for interpolation)\n"
64     "\n"
65     "uniform highp mat4 qt_Matrix;\n"
66     "\n"
67     "varying highp vec4 fTexS;\n"
68     "varying lowp float progress;\n"
69     "\n"
70     "\n"
71     "void main() {\n"
72     "    progress = animData.z;\n"
73     "    //Calculate frame location in texture\n"
74     "    fTexS.xy = animPos.xy + vTex.xy * animData.xy;\n"
75     "    //Next frame is also passed, for interpolation\n"
76     "    fTexS.zw = animPos.zw + vTex.xy * animData.xy;\n"
77     "\n"
78     "    gl_Position = qt_Matrix * vec4(vPos.x, vPos.y, 0, 1);\n"
79     "}\n";
80
81 static const char fragmentShaderCode[] =
82     "uniform sampler2D texture;\n"
83     "uniform lowp float qt_Opacity;\n"
84     "\n"
85     "varying highp vec4 fTexS;\n"
86     "varying lowp float progress;\n"
87     "\n"
88     "void main() {\n"
89     "    gl_FragColor = mix(texture2D(texture, fTexS.xy), texture2D(texture, fTexS.zw), progress) * qt_Opacity;\n"
90     "}\n";
91
92 class QQuickAnimatedSpriteMaterial : public QSGMaterial
93 {
94 public:
95     QQuickAnimatedSpriteMaterial();
96     ~QQuickAnimatedSpriteMaterial();
97     virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; }
98     virtual QSGMaterialShader *createShader() const;
99     virtual int compare(const QSGMaterial *other) const
100     {
101         return this - static_cast<const QQuickAnimatedSpriteMaterial *>(other);
102     }
103
104     QSGTexture *texture;
105
106     float animT;
107     float animX1;
108     float animY1;
109     float animX2;
110     float animY2;
111     float animW;
112     float animH;
113 };
114
115 QQuickAnimatedSpriteMaterial::QQuickAnimatedSpriteMaterial()
116     : texture(0)
117     , animT(0.0f)
118     , animX1(0.0f)
119     , animY1(0.0f)
120     , animX2(0.0f)
121     , animY2(0.0f)
122     , animW(1.0f)
123     , animH(1.0f)
124 {
125     setFlag(Blending, true);
126 }
127
128 QQuickAnimatedSpriteMaterial::~QQuickAnimatedSpriteMaterial()
129 {
130     delete texture;
131 }
132
133 class AnimatedSpriteMaterialData : public QSGMaterialShader
134 {
135 public:
136     AnimatedSpriteMaterialData(const char * /* vertexFile */ = 0, const char * /* fragmentFile */ = 0)
137     {
138     }
139
140     void deactivate() {
141         QSGMaterialShader::deactivate();
142
143         for (int i=0; i<8; ++i) {
144             program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
145         }
146     }
147
148     virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
149     {
150         QQuickAnimatedSpriteMaterial *m = static_cast<QQuickAnimatedSpriteMaterial *>(newEffect);
151         m->texture->bind();
152
153         program()->setUniformValue(m_opacity_id, state.opacity());
154         program()->setUniformValue(m_animData_id, m->animW, m->animH, m->animT);
155         program()->setUniformValue(m_animPos_id, m->animX1, m->animY1, m->animX2, m->animY2);
156
157         if (state.isMatrixDirty())
158             program()->setUniformValue(m_matrix_id, state.combinedMatrix());
159     }
160
161     virtual void initialize() {
162         m_matrix_id = program()->uniformLocation("qt_Matrix");
163         m_opacity_id = program()->uniformLocation("qt_Opacity");
164         m_animData_id = program()->uniformLocation("animData");
165         m_animPos_id = program()->uniformLocation("animPos");
166     }
167
168     virtual const char *vertexShader() const { return vertexShaderCode; }
169     virtual const char *fragmentShader() const { return fragmentShaderCode; }
170
171     virtual char const *const *attributeNames() const {
172         static const char *attr[] = {
173            "vPos",
174            "vTex",
175             0
176         };
177         return attr;
178     }
179
180     int m_matrix_id;
181     int m_opacity_id;
182     int m_animData_id;
183     int m_animPos_id;
184
185     static float chunkOfBytes[1024];
186 };
187
188 float AnimatedSpriteMaterialData::chunkOfBytes[1024];
189
190 QSGMaterialShader *QQuickAnimatedSpriteMaterial::createShader() const
191 {
192     return new AnimatedSpriteMaterialData;
193 }
194
195 struct AnimatedSpriteVertex {
196     float x;
197     float y;
198     float tx;
199     float ty;
200 };
201
202 struct AnimatedSpriteVertices {
203     AnimatedSpriteVertex v1;
204     AnimatedSpriteVertex v2;
205     AnimatedSpriteVertex v3;
206     AnimatedSpriteVertex v4;
207 };
208
209 /*!
210     \qmlclass AnimatedSprite QQuickAnimatedSprite
211     \inqmlmodule QtQuick 2
212     \inherits Item
213     \brief The AnimatedSprite element draws a sprite animation
214
215     AnimatedSprite provides rendering and control over animations which are provided
216     as multiple frames in the same image file. You can play it at a fixed speed, at the
217     frame rate of your display, or manually advance and control the progress.
218
219     For details of how a sprite animation is defined see the \l{Sprite Animation} overview.
220     Note that the AnimatedSprite element does not use Sprite elements to define multiple animations,
221     but instead encapsulates a single animation itself.
222 */
223
224 /*!
225     \qmlproperty bool QtQuick2::AnimatedSprite::running
226
227     Whether the sprite is animating or not.
228
229     Default is true
230 */
231
232 /*!
233     \qmlproperty bool QtQuick2::AnimatedSprite::interpolate
234
235     If true, interpolation will occur between sprite frames to make the
236     animation appear smoother.
237
238     Default is true.
239 */
240
241 /*!
242     \qmlproperty qreal QtQuick2::AnimatedSprite::frameRate
243
244     Frames per second to show in the animation. Values equal to or below 0 are invalid.
245
246     If frameRate is valid  then it will be used to calculate the duration of the frames.
247     If not, and frameDuration is valid , then frameDuration will be used.
248
249     Changing this parameter will restart the animation.
250 */
251
252 /*!
253     \qmlproperty int QtQuick2::AnimatedSprite::frameDuration
254
255     Duration of each frame of the animation. Values equal to or below 0 are invalid.
256
257     If frameRate is valid then it will be used to calculate the duration of the frames.
258     If not, and frameDuration is valid, then frameDuration will be used.
259
260     Changing this parameter will restart the animation.
261 */
262
263 /*!
264     \qmlproperty int QtQuick2::AnimatedSprite::frameCount
265
266     Number of frames in this AnimatedSprite.
267 */
268 /*!
269     \qmlproperty int QtQuick2::AnimatedSprite::frameHeight
270
271     Height of a single frame in this AnimatedSprite.
272
273     May be omitted if it is the only sprite in the file.
274 */
275 /*!
276     \qmlproperty int QtQuick2::AnimatedSprite::frameWidth
277
278     Width of a single frame in this AnimatedSprite.
279
280     May be omitted if it is the only sprite in the file.
281 */
282 /*!
283     \qmlproperty int QtQuick2::AnimatedSprite::frameX
284
285     The X coordinate in the image file of the first frame of the AnimatedSprite.
286
287     May be omitted if the first frame starts in the upper left corner of the file.
288 */
289 /*!
290     \qmlproperty int QtQuick2::AnimatedSprite::frameY
291
292     The Y coordinate in the image file of the first frame of the AnimatedSprite.
293
294     May be omitted if the first frame starts in the upper left corner of the file.
295 */
296 /*!
297     \qmlproperty url QtQuick2::AnimatedSprite::source
298
299     The image source for the animation.
300
301     If frameHeight and frameWidth are not specified, it is assumed to be a single long row of square frames.
302     Otherwise, it can be multiple contiguous rows or rectangluar frames, when one row runs out the next will be used.
303
304     If frameX and frameY are specified, the row of frames will be taken with that x/y coordinate as the upper left corner.
305 */
306
307 /*!
308     \qmlproperty bool QtQuick2::AnimatedSprite::reverse
309
310     If true, then the animation will be played in reverse.
311
312     Default is false.
313 */
314
315 /*!
316     \qmlproperty bool QtQuick2::AnimatedSprite::frameSync
317
318     If true, then the animation will have no duration. Instead, the animation will advance
319     one frame each time a frame is rendered to the screen. This syncronizes it with the painting
320     rate as opposed to elapsed time.
321
322     If frameSync is set to true, it overrides both frameRate and frameDuration.
323
324     Default is false.
325
326     Changing this parameter will restart the animation.
327 */
328
329 /*!
330     \qmlproperty int QtQuick2::AnimatedSprite::loops
331
332     After playing the animation this many times, the animation will automatically stop. Negative values are invalid.
333
334     If this is set to AnimatedSprite.Infinite the animation will not stop playing on its own.
335
336     Default is AnimatedSprite.Infinite
337 */
338
339 /*!
340     \qmlproperty bool QtQuick2::AnimatedSprite::paused
341
342     When paused, the current frame can be advanced manually.
343
344     Default is false.
345 */
346
347 /*!
348     \qmlproperty int QtQuick2::AnimatedSprite::currentFrame
349
350     When paused, the current frame can be advanced manually by setting this property or calling advance().
351
352 */
353
354 //TODO: Implicitly size element to size of sprite
355 QQuickAnimatedSprite::QQuickAnimatedSprite(QQuickItem *parent) :
356     QQuickItem(parent)
357     , m_node(0)
358     , m_material(0)
359     , m_sprite(new QQuickSprite)
360     , m_spriteEngine(0)
361     , m_curFrame(0)
362     , m_pleaseReset(false)
363     , m_running(true)
364     , m_paused(false)
365     , m_interpolate(true)
366     , m_loops(-1)
367     , m_curLoop(0)
368     , m_pauseOffset(0)
369 {
370     setFlag(ItemHasContents);
371     connect(this, SIGNAL(runningChanged(bool)),
372             this, SLOT(update()));
373     connect(this, SIGNAL(widthChanged()),
374             this, SLOT(sizeVertices()));
375     connect(this, SIGNAL(heightChanged()),
376             this, SLOT(sizeVertices()));
377 }
378
379 void QQuickAnimatedSprite::reloadImage()
380 {
381     if (!isComponentComplete())
382         return;
383     createEngine();//### It's not as inefficient as it sounds, but it still sucks having to recreate the engine
384 }
385
386 void QQuickAnimatedSprite::componentComplete()
387 {
388     createEngine();
389     QQuickItem::componentComplete();
390     if (m_running)
391         start();
392 }
393
394 void QQuickAnimatedSprite::start()
395 {
396     if (m_running)
397         return;
398     m_curLoop = 0;
399     m_timestamp.start();
400     if (m_spriteEngine) {
401         m_spriteEngine->stop(0);
402         m_spriteEngine->updateSprites(0);
403         m_spriteEngine->start(0);
404     }
405     m_running = true;
406     emit runningChanged(true);
407     update();
408 }
409
410 void QQuickAnimatedSprite::stop()
411 {
412     if (!m_running)
413         return;
414     m_running = false;
415     m_pauseOffset = 0;
416     emit runningChanged(false);
417 }
418
419 void QQuickAnimatedSprite::advance(int frames)
420 {
421     if (!frames)
422         return;
423     //TODO-C: May not work when running - only when paused
424     m_curFrame += frames;
425     while (m_curFrame < 0)
426         m_curFrame += m_sprite->frames();
427     m_curFrame = m_curFrame % m_sprite->frames();
428     emit currentFrameChanged(m_curFrame);
429 }
430
431 void QQuickAnimatedSprite::pause()
432 {
433     if (m_paused)
434         return;
435     m_pauseOffset = m_timestamp.elapsed();
436     m_paused = true;
437     emit pausedChanged(true);
438 }
439
440 void QQuickAnimatedSprite::resume()
441 {
442     if (!m_paused)
443         return;
444     m_pauseOffset = m_pauseOffset - m_timestamp.elapsed();
445     m_paused = false;
446     emit pausedChanged(false);
447 }
448
449 void QQuickAnimatedSprite::createEngine()
450 {
451     if (m_spriteEngine)
452         delete m_spriteEngine;
453     QList<QQuickSprite*> spriteList;
454     spriteList << m_sprite;
455     m_spriteEngine = new QQuickSpriteEngine(QList<QQuickSprite*>(spriteList), this);
456     m_spriteEngine->startAssemblingImage();
457     reset();
458 }
459
460 static QSGGeometry::Attribute AnimatedSprite_Attributes[] = {
461     QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),   // pos
462     QSGGeometry::Attribute::create(1, 2, GL_FLOAT),         // tex
463 };
464
465 static QSGGeometry::AttributeSet AnimatedSprite_AttributeSet =
466 {
467     2, // Attribute Count
468     (2+2) * sizeof(float),
469     AnimatedSprite_Attributes
470 };
471
472 void QQuickAnimatedSprite::sizeVertices()
473 {
474     if (!m_node)
475         return;
476
477     AnimatedSpriteVertices *p = (AnimatedSpriteVertices *) m_node->geometry()->vertexData();
478     p->v1.x = 0;
479     p->v1.y = 0;
480
481     p->v2.x = width();
482     p->v2.y = 0;
483
484     p->v3.x = 0;
485     p->v3.y = height();
486
487     p->v4.x = width();
488     p->v4.y = height();
489 }
490
491 QSGGeometryNode* QQuickAnimatedSprite::buildNode()
492 {
493     if (!m_spriteEngine) {
494         qmlInfo(this) << "No sprite engine...";
495         return 0;
496     } else if (m_spriteEngine->status() == QQuickPixmap::Null) {
497         m_spriteEngine->startAssemblingImage();
498         update();//Schedule another update, where we will check again
499         return 0;
500     } else if (m_spriteEngine->status() == QQuickPixmap::Loading) {
501         update();//Schedule another update, where we will check again
502         return 0;
503     }
504
505     m_material = new QQuickAnimatedSpriteMaterial();
506
507     QImage image = m_spriteEngine->assembledImage();
508     if (image.isNull())
509         return 0;
510     m_sheetSize = QSizeF(image.size());
511     m_material->texture = canvas()->createTextureFromImage(image);
512     m_material->texture->setFiltering(QSGTexture::Linear);
513     m_spriteEngine->start(0);
514     m_material->animT = 0;
515     m_material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width();
516     m_material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height();
517     m_material->animX2 = m_material->animX1;
518     m_material->animY2 = m_material->animY1;
519     m_material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width();
520     m_material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height();
521
522     int vCount = 4;
523     int iCount = 6;
524     QSGGeometry *g = new QSGGeometry(AnimatedSprite_AttributeSet, vCount, iCount);
525     g->setDrawingMode(GL_TRIANGLES);
526
527     AnimatedSpriteVertices *p = (AnimatedSpriteVertices *) g->vertexData();
528
529     p->v1.tx = 0;
530     p->v1.ty = 0;
531
532     p->v2.tx = 1.0;
533     p->v2.ty = 0;
534
535     p->v3.tx = 0;
536     p->v3.ty = 1.0;
537
538     p->v4.tx = 1.0;
539     p->v4.ty = 1.0;
540
541
542     quint16 *indices = g->indexDataAsUShort();
543     indices[0] = 0;
544     indices[1] = 1;
545     indices[2] = 2;
546     indices[3] = 1;
547     indices[4] = 3;
548     indices[5] = 2;
549
550
551     m_timestamp.start();
552     m_node = new QSGGeometryNode();
553     m_node->setGeometry(g);
554     m_node->setMaterial(m_material);
555     m_node->setFlag(QSGGeometryNode::OwnsMaterial);
556     sizeVertices();
557     return m_node;
558 }
559
560 void QQuickAnimatedSprite::reset()
561 {
562     m_pleaseReset = true;
563 }
564
565 QSGNode *QQuickAnimatedSprite::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
566 {
567     if (m_pleaseReset) {
568         delete m_node;
569
570         m_node = 0;
571         m_material = 0;
572         m_pleaseReset = false;
573     }
574
575     prepareNextFrame();
576
577     if (m_running) {
578         update();
579         if (m_node)
580             m_node->markDirty(QSGNode::DirtyMaterial);
581     }
582
583     return m_node;
584 }
585
586 void QQuickAnimatedSprite::prepareNextFrame()
587 {
588     if (m_node == 0)
589         m_node = buildNode();
590     if (m_node == 0) //error creating node
591         return;
592
593     int timeInt = m_timestamp.elapsed() + m_pauseOffset;
594     qreal time =  timeInt / 1000.;
595
596     double frameAt; //double just for modf
597     qreal progress = 0.0;
598     if (!m_paused) {
599         //Advance State (keeps time for psuedostates)
600         m_spriteEngine->updateSprites(timeInt);
601
602         //Advance AnimatedSprite
603         qreal animT = m_spriteEngine->spriteStart()/1000.0;
604         qreal frameCount = m_spriteEngine->spriteFrames();
605         qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
606         if (frameDuration > 0) {
607             qreal frame = (time - animT)/(frameDuration / 1000.0);
608             frame = qBound(qreal(0.0), frame, frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation
609             progress = modf(frame,&frameAt);
610             if (m_curFrame > frameAt) //went around
611                 m_curLoop++;
612             m_curFrame = frameAt;
613         } else {
614             m_curFrame++;
615             if (m_curFrame >= frameCount){
616                 m_curFrame = 0;
617                 m_curLoop++;
618                 m_spriteEngine->advance();
619             }
620             frameAt = m_curFrame;
621             progress = 0;
622         }
623         if (m_loops > 0 && m_curLoop >= m_loops) {
624             frameAt = 0;
625             m_running = false;
626         }
627     } else {
628         frameAt = m_curFrame;
629     }
630     if (m_spriteEngine->sprite()->reverse())
631         frameAt = (m_spriteEngine->spriteFrames() - 1) - frameAt;
632     qreal y = m_spriteEngine->spriteY() / m_sheetSize.height();
633     qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width();
634     qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height();
635     qreal x1 = m_spriteEngine->spriteX() / m_sheetSize.width();
636     x1 += frameAt * w;
637     qreal x2 = x1;
638     if (frameAt < (m_spriteEngine->spriteFrames()-1))
639         x2 += w;
640
641     m_material->animX1 = x1;
642     m_material->animY1 = y;
643     m_material->animX2 = x2;
644     m_material->animY2 = y;
645     m_material->animW = w;
646     m_material->animH = h;
647     m_material->animT = m_interpolate ? progress : 0.0;
648 }
649
650 QT_END_NAMESPACE