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