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