Doc: Changed \qmlclass to \qmltype and added \instantiates.
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickspritesequence.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 "qquickspritesequence_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/qquickwindow.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 QQuickSpriteSequenceMaterial : public QSGMaterial
93 {
94 public:
95     QQuickSpriteSequenceMaterial();
96     ~QQuickSpriteSequenceMaterial();
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 QQuickSpriteSequenceMaterial *>(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 QQuickSpriteSequenceMaterial::QQuickSpriteSequenceMaterial()
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 QQuickSpriteSequenceMaterial::~QQuickSpriteSequenceMaterial()
129 {
130     delete texture;
131 }
132
133 class SpriteSequenceMaterialData : public QSGMaterialShader
134 {
135 public:
136     SpriteSequenceMaterialData(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         QQuickSpriteSequenceMaterial *m = static_cast<QQuickSpriteSequenceMaterial *>(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 SpriteSequenceMaterialData::chunkOfBytes[1024];
189
190 QSGMaterialShader *QQuickSpriteSequenceMaterial::createShader() const
191 {
192     return new SpriteSequenceMaterialData;
193 }
194
195 struct SpriteVertex {
196     float x;
197     float y;
198     float tx;
199     float ty;
200 };
201
202 struct SpriteVertices {
203     SpriteVertex v1;
204     SpriteVertex v2;
205     SpriteVertex v3;
206     SpriteVertex v4;
207 };
208
209 /*!
210     \qmltype SpriteSequence
211     \instantiates QQuickSpriteSequence
212     \inqmlmodule QtQuick 2
213     \ingroup qtquick-visual-utility
214     \inherits Item
215     \brief Draws a sprite animation
216
217     SpriteSequence renders and controls a list of animations defined
218     by \l Sprite types.
219
220     For full details, see the \l{Sprite Animation} overview.
221 */
222 /*!
223     \qmlproperty bool QtQuick2::SpriteSequence::running
224
225     Whether the sprite is animating or not.
226
227     Default is true
228 */
229 /*!
230     \qmlproperty bool QtQuick2::SpriteSequence::interpolate
231
232     If true, interpolation will occur between sprite frames to make the
233     animation appear smoother.
234
235     Default is true.
236 */
237 /*!
238     \qmlproperty string QtQuick2::SpriteSequence::goalSprite
239
240     The name of the Sprite which is currently animating.
241 */
242 /*!
243     \qmlproperty string QtQuick2::SpriteSequence::goalSprite
244
245     The name of the Sprite which the animation should move to.
246
247     Sprite states have defined durations and transitions between them, setting goalState
248     will cause it to disregard any path weightings (including 0) and head down the path
249     which will reach the goalState quickest (fewest animations). It will pass through
250     intermediate states on that path, and animate them for their duration.
251
252     If it is possible to return to the goalState from the starting point of the goalState
253     it will continue to do so until goalState is set to "" or an unreachable state.
254 */
255 /*! \qmlmethod void QtQuick2::SpriteSequence::jumpTo(string sprite)
256
257     This function causes the SpriteSequence to jump to the specified sprite immediately, intermediate
258     sprites are not played. The \a sprite argument is the name of the sprite you wish to jump to.
259 */
260 /*!
261     \qmlproperty list<Sprite> QtQuick2::SpriteSequence::sprites
262
263     The sprite or sprites to draw. Sprites will be scaled to the size of this item.
264 */
265
266 //TODO: Implicitly size element to size of first sprite?
267 QQuickSpriteSequence::QQuickSpriteSequence(QQuickItem *parent) :
268     QQuickItem(parent)
269     , m_node(0)
270     , m_material(0)
271     , m_spriteEngine(0)
272     , m_curFrame(0)
273     , m_pleaseReset(false)
274     , m_running(true)
275     , m_interpolate(true)
276     , m_curStateIdx(0)
277 {
278     setFlag(ItemHasContents);
279     connect(this, SIGNAL(runningChanged(bool)),
280             this, SLOT(update()));
281     connect(this, SIGNAL(widthChanged()),
282             this, SLOT(sizeVertices()));
283     connect(this, SIGNAL(heightChanged()),
284             this, SLOT(sizeVertices()));
285 }
286
287 void QQuickSpriteSequence::jumpTo(const QString &sprite)
288 {
289     if (!m_spriteEngine)
290         return;
291     m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite), 0, true);
292 }
293
294 void QQuickSpriteSequence::setGoalSprite(const QString &sprite)
295 {
296     if (m_goalState != sprite){
297         m_goalState = sprite;
298         emit goalSpriteChanged(sprite);
299         m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite));
300     }
301 }
302
303 QQmlListProperty<QQuickSprite> QQuickSpriteSequence::sprites()
304 {
305     return QQmlListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
306 }
307
308 void QQuickSpriteSequence::createEngine()
309 {
310     //TODO: delay until component complete
311     if (m_spriteEngine)
312         delete m_spriteEngine;
313     if (m_sprites.count())
314         m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
315     else
316         m_spriteEngine = 0;
317     reset();
318 }
319
320 static QSGGeometry::Attribute SpriteSequence_Attributes[] = {
321     QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),   // pos
322     QSGGeometry::Attribute::create(1, 2, GL_FLOAT),         // tex
323 };
324
325 static QSGGeometry::AttributeSet SpriteSequence_AttributeSet =
326 {
327     2, // Attribute Count
328     (2+2) * sizeof(float),
329     SpriteSequence_Attributes
330 };
331
332 void QQuickSpriteSequence::sizeVertices()
333 {
334     if (!m_node)
335         return;
336
337     SpriteVertices *p = (SpriteVertices *) m_node->geometry()->vertexData();
338     p->v1.x = 0;
339     p->v1.y = 0;
340
341     p->v2.x = width();
342     p->v2.y = 0;
343
344     p->v3.x = 0;
345     p->v3.y = height();
346
347     p->v4.x = width();
348     p->v4.y = height();
349 }
350
351 QSGGeometryNode* QQuickSpriteSequence::buildNode()
352 {
353     if (!m_spriteEngine) {
354         qmlInfo(this) << "No sprite engine...";
355         return 0;
356     } else if (m_spriteEngine->status() == QQuickPixmap::Null) {
357         m_spriteEngine->startAssemblingImage();
358         update();//Schedule another update, where we will check again
359         return 0;
360     } else if (m_spriteEngine->status() == QQuickPixmap::Loading) {
361         update();//Schedule another update, where we will check again
362         return 0;
363     }
364
365     m_material = new QQuickSpriteSequenceMaterial();
366
367     QImage image = m_spriteEngine->assembledImage();
368     if (image.isNull())
369         return 0;
370     m_sheetSize = QSizeF(image.size());
371     m_material->texture = window()->createTextureFromImage(image);
372     m_material->texture->setFiltering(QSGTexture::Linear);
373     m_spriteEngine->start(0);
374     m_material->animT = 0;
375     m_material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width();
376     m_material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height();
377     m_material->animX2 = m_material->animX1;
378     m_material->animY2 = m_material->animY1;
379     m_material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width();
380     m_material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height();
381     m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
382     emit currentSpriteChanged(m_curState);
383
384     int vCount = 4;
385     int iCount = 6;
386     QSGGeometry *g = new QSGGeometry(SpriteSequence_AttributeSet, vCount, iCount);
387     g->setDrawingMode(GL_TRIANGLES);
388
389     SpriteVertices *p = (SpriteVertices *) g->vertexData();
390     QRectF texRect = m_material->texture->normalizedTextureSubRect();
391
392     p->v1.tx = texRect.topLeft().x();
393     p->v1.ty = texRect.topLeft().y();
394
395     p->v2.tx = texRect.topRight().x();
396     p->v2.ty = texRect.topRight().y();
397
398     p->v3.tx = texRect.bottomLeft().x();
399     p->v3.ty = texRect.bottomLeft().y();
400
401     p->v4.tx = texRect.bottomRight().x();
402     p->v4.ty = texRect.bottomRight().y();
403
404     quint16 *indices = g->indexDataAsUShort();
405     indices[0] = 0;
406     indices[1] = 1;
407     indices[2] = 2;
408     indices[3] = 1;
409     indices[4] = 3;
410     indices[5] = 2;
411
412
413     m_timestamp.start();
414     m_node = new QSGGeometryNode();
415     m_node->setGeometry(g);
416     m_node->setMaterial(m_material);
417     m_node->setFlag(QSGGeometryNode::OwnsMaterial);
418     sizeVertices();
419     return m_node;
420 }
421
422 void QQuickSpriteSequence::reset()
423 {
424     m_pleaseReset = true;
425 }
426
427 QSGNode *QQuickSpriteSequence::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
428 {
429     if (m_pleaseReset) {
430         delete m_node;
431
432         m_node = 0;
433         m_material = 0;
434         m_pleaseReset = false;
435     }
436
437     prepareNextFrame();
438
439     if (m_running) {
440         update();
441         if (m_node)
442             m_node->markDirty(QSGNode::DirtyMaterial);
443     }
444
445     return m_node;
446 }
447
448 void QQuickSpriteSequence::prepareNextFrame()
449 {
450     if (m_node == 0)
451         m_node = buildNode();
452     if (m_node == 0) //error creating node
453         return;
454
455     uint timeInt = m_timestamp.elapsed();
456     qreal time =  timeInt / 1000.;
457
458     //Advance State
459     m_spriteEngine->updateSprites(timeInt);
460     if (m_curStateIdx != m_spriteEngine->curState()) {
461         m_curStateIdx = m_spriteEngine->curState();
462         m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
463         emit currentSpriteChanged(m_curState);
464         m_curFrame= -1;
465     }
466
467     //Advance Sprite
468     qreal animT = m_spriteEngine->spriteStart()/1000.0;
469     qreal frameCount = m_spriteEngine->spriteFrames();
470     qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
471     double frameAt;
472     qreal progress;
473     if (frameDuration > 0) {
474         qreal frame = (time - animT)/(frameDuration / 1000.0);
475         frame = qBound(qreal(0.0), frame, frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation
476         progress = modf(frame,&frameAt);
477     } else {
478         m_curFrame++;
479         if (m_curFrame >= frameCount){
480             m_curFrame = 0;
481             m_spriteEngine->advance();
482         }
483         frameAt = m_curFrame;
484         progress = 0;
485     }
486     if (m_spriteEngine->sprite()->reverse())
487         frameAt = (m_spriteEngine->spriteFrames() - 1) - frameAt;
488     qreal y = m_spriteEngine->spriteY() / m_sheetSize.height();
489     qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width();
490     qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height();
491     qreal x1 = m_spriteEngine->spriteX() / m_sheetSize.width();
492     x1 += frameAt * w;
493     qreal x2 = x1;
494     if (frameAt < (frameCount-1))
495         x2 += w;
496
497     m_material->animX1 = x1;
498     m_material->animY1 = y;
499     m_material->animX2 = x2;
500     m_material->animY2 = y;
501     m_material->animW = w;
502     m_material->animH = h;
503     m_material->animT = m_interpolate ? progress : 0.0;
504 }
505
506 QT_END_NAMESPACE