e34944b97095d77264143f7dac24123d8a46c7bb
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickspriteimage.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 Declarative 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 "qquickspriteimage_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/qsgengine.h>
49 #include <QtQuick/qsgtexturematerial.h>
50 #include <QtQuick/qsgtexture.h>
51 #include <QtQuick/qquickcanvas.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 vTex;\n"
61     "uniform highp vec3 animData;// w,h(premultiplied of anim), interpolation progress\n"
62     "uniform highp vec4 animPos;//x,y, x,y (two frames for interpolation)\n"
63     "uniform highp vec2 size;//w,h of element\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(size.x * vTex.x, size.y * vTex.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 QQuickSpriteMaterial : public QSGMaterial
93 {
94 public:
95     QQuickSpriteMaterial();
96     virtual ~QQuickSpriteMaterial();
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 QQuickSpriteMaterial *>(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     float elementWidth;
114     float elementHeight;
115 };
116
117 QQuickSpriteMaterial::QQuickSpriteMaterial()
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     , elementWidth(1.0f)
126     , elementHeight(1.0f)
127 {
128     setFlag(Blending, true);
129 }
130
131 QQuickSpriteMaterial::~QQuickSpriteMaterial()
132 {
133     delete texture;
134 }
135
136 class SpriteMaterialData : public QSGMaterialShader
137 {
138 public:
139     SpriteMaterialData(const char * /* vertexFile */ = 0, const char * /* fragmentFile */ = 0)
140     {
141     }
142
143     void deactivate() {
144         QSGMaterialShader::deactivate();
145
146         for (int i=0; i<8; ++i) {
147             program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
148         }
149     }
150
151     virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
152     {
153         QQuickSpriteMaterial *m = static_cast<QQuickSpriteMaterial *>(newEffect);
154         m->texture->bind();
155
156         program()->setUniformValue(m_opacity_id, state.opacity());
157         program()->setUniformValue(m_animData_id, m->animW, m->animH, m->animT);
158         program()->setUniformValue(m_animPos_id, m->animX1, m->animY1, m->animX2, m->animY2);
159         program()->setUniformValue(m_size_id, m->elementWidth, m->elementHeight);
160
161         if (state.isMatrixDirty())
162             program()->setUniformValue(m_matrix_id, state.combinedMatrix());
163     }
164
165     virtual void initialize() {
166         m_matrix_id = program()->uniformLocation("qt_Matrix");
167         m_opacity_id = program()->uniformLocation("qt_Opacity");
168         m_animData_id = program()->uniformLocation("animData");
169         m_animPos_id = program()->uniformLocation("animPos");
170         m_size_id = program()->uniformLocation("size");
171     }
172
173     virtual const char *vertexShader() const { return vertexShaderCode; }
174     virtual const char *fragmentShader() const { return fragmentShaderCode; }
175
176     virtual char const *const *attributeNames() const {
177         static const char *attr[] = {
178            "vTex",
179             0
180         };
181         return attr;
182     }
183
184     int m_matrix_id;
185     int m_opacity_id;
186     int m_animData_id;
187     int m_animPos_id;
188     int m_size_id;
189
190     static float chunkOfBytes[1024];
191 };
192
193 float SpriteMaterialData::chunkOfBytes[1024];
194
195 QSGMaterialShader *QQuickSpriteMaterial::createShader() const
196 {
197     return new SpriteMaterialData;
198 }
199
200 struct SpriteVertex {
201     float tx;
202     float ty;
203 };
204
205 struct SpriteVertices {
206     SpriteVertex v1;
207     SpriteVertex v2;
208     SpriteVertex v3;
209     SpriteVertex v4;
210 };
211
212 /*!
213     \qmlclass SpriteImage QQuickSpriteImage
214     \inqmlmodule QtQuick 2
215     \inherits Item
216     \brief The SpriteImage element draws a sprite animation
217
218 */
219 /*!
220     \qmlproperty bool QtQuick2::SpriteImage::running
221
222     Whether the sprite is animating or not.
223
224     Default is true
225 */
226 /*!
227     \qmlproperty bool QtQuick2::SpriteImage::interpolate
228
229     If true, interpolation will occur between sprite frames to make the
230     animation appear smoother.
231
232     Default is true.
233 */
234 /*!
235     \qmlproperty string QtQuick2::SpriteImage::goalSprite
236
237     The name of the Sprite which is currently animating.
238 */
239 /*!
240     \qmlproperty string QtQuick2::SpriteImage::goalSprite
241
242     The name of the Sprite which the animation should move to.
243
244     Sprite states have defined durations and transitions between them, setting goalState
245     will cause it to disregard any path weightings (including 0) and head down the path
246     which will reach the goalState quickest (fewest animations). It will pass through
247     intermediate states on that path, and animate them for their duration.
248
249     If it is possible to return to the goalState from the starting point of the goalState
250     it will continue to do so until goalState is set to "" or an unreachable state.
251 */
252 /*! \qmlmethod void QtQuick2::SpriteImage::jumpTo(string sprite)
253
254     This function causes the sprite to jump to the specified state immediately, intermediate
255     states are not played.
256 */
257 /*!
258     \qmlproperty list<Sprite> QtQuick2::SpriteImage::sprites
259
260     The sprite or sprites to draw. Sprites will be scaled to the size of this element.
261 */
262
263 //TODO: Implicitly size element to size of first sprite?
264 QQuickSpriteImage::QQuickSpriteImage(QQuickItem *parent) :
265     QQuickItem(parent)
266     , m_node(0)
267     , m_material(0)
268     , m_spriteEngine(0)
269     , m_curFrame(0)
270     , m_pleaseReset(false)
271     , m_running(true)
272     , m_interpolate(true)
273     , m_curStateIdx(0)
274 {
275     setFlag(ItemHasContents);
276     connect(this, SIGNAL(runningChanged(bool)),
277             this, SLOT(update()));
278 }
279
280 void QQuickSpriteImage::jumpTo(const QString &sprite)
281 {
282     if (!m_spriteEngine)
283         return;
284     m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite), 0, true);
285 }
286
287 void QQuickSpriteImage::setGoalSprite(const QString &sprite)
288 {
289     if (m_goalState != sprite){
290         m_goalState = sprite;
291         emit goalSpriteChanged(sprite);
292         m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite));
293     }
294 }
295
296 QDeclarativeListProperty<QQuickSprite> QQuickSpriteImage::sprites()
297 {
298     return QDeclarativeListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
299 }
300
301 void QQuickSpriteImage::createEngine()
302 {
303     //TODO: delay until component complete
304     if (m_spriteEngine)
305         delete m_spriteEngine;
306     if (m_sprites.count())
307         m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
308     else
309         m_spriteEngine = 0;
310     reset();
311 }
312
313 static QSGGeometry::Attribute SpriteImage_Attributes[] = {
314     QSGGeometry::Attribute::create(0, 2, GL_FLOAT),         // tex
315 };
316
317 static QSGGeometry::AttributeSet SpriteImage_AttributeSet =
318 {
319     1, // Attribute Count
320     2 * sizeof(float),
321     SpriteImage_Attributes
322 };
323
324 QSGGeometryNode* QQuickSpriteImage::buildNode()
325 {
326     if (!m_spriteEngine) {
327         qWarning() << "SpriteImage: No sprite engine...";
328         return 0;
329     }
330
331     m_material = new QQuickSpriteMaterial();
332
333     QImage image = m_spriteEngine->assembledImage();
334     if (image.isNull())
335         return 0;
336     m_sheetSize = QSizeF(image.size());
337     m_material->texture = canvas()->createTextureFromImage(image);
338     m_material->texture->setFiltering(QSGTexture::Linear);
339     m_spriteEngine->start(0);
340     m_material->animT = 0;
341     m_material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width();
342     m_material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height();
343     m_material->animX2 = m_material->animX1;
344     m_material->animY2 = m_material->animY1;
345     m_material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width();
346     m_material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height();
347     m_material->elementWidth = width();
348     m_material->elementHeight = height();
349     m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
350     emit currentSpriteChanged(m_curState);
351
352     int vCount = 4;
353     int iCount = 6;
354     QSGGeometry *g = new QSGGeometry(SpriteImage_AttributeSet, vCount, iCount);
355     g->setDrawingMode(GL_TRIANGLES);
356
357     SpriteVertices *p = (SpriteVertices *) g->vertexData();
358
359     p->v1.tx = 0;
360     p->v1.ty = 0;
361
362     p->v2.tx = 1.0;
363     p->v2.ty = 0;
364
365     p->v3.tx = 0;
366     p->v3.ty = 1.0;
367
368     p->v4.tx = 1.0;
369     p->v4.ty = 1.0;
370
371     quint16 *indices = g->indexDataAsUShort();
372     indices[0] = 0;
373     indices[1] = 1;
374     indices[2] = 2;
375     indices[3] = 1;
376     indices[4] = 3;
377     indices[5] = 2;
378
379
380     m_timestamp.start();
381     m_node = new QSGGeometryNode();
382     m_node->setGeometry(g);
383     m_node->setMaterial(m_material);
384     m_node->setFlag(QSGGeometryNode::OwnsMaterial);
385     return m_node;
386 }
387
388 void QQuickSpriteImage::reset()
389 {
390     m_pleaseReset = true;
391 }
392
393 QSGNode *QQuickSpriteImage::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
394 {
395     if (m_pleaseReset) {
396         delete m_node;
397         delete m_material;
398
399         m_node = 0;
400         m_material = 0;
401         m_pleaseReset = false;
402     }
403
404     prepareNextFrame();
405
406     if (m_running) {
407         update();
408         if (m_node)
409             m_node->markDirty(QSGNode::DirtyMaterial);
410     }
411
412     return m_node;
413 }
414
415 void QQuickSpriteImage::prepareNextFrame()
416 {
417     if (m_node == 0)
418         m_node = buildNode();
419     if (m_node == 0) //error creating node
420         return;
421
422     uint timeInt = m_timestamp.elapsed();
423     qreal time =  timeInt / 1000.;
424     m_material->elementHeight = height();
425     m_material->elementWidth = width();
426
427     //Advance State
428     m_spriteEngine->updateSprites(timeInt);
429     if (m_curStateIdx != m_spriteEngine->curState()) {
430         m_curStateIdx = m_spriteEngine->curState();
431         m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
432         emit currentSpriteChanged(m_curState);
433         m_curFrame= -1;
434     }
435
436     //Advance Sprite
437     qreal animT = m_spriteEngine->spriteStart()/1000.0;
438     qreal frameCount = m_spriteEngine->spriteFrames();
439     qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
440     double frameAt;
441     qreal progress;
442     if (frameDuration > 0) {
443         qreal frame = (time - animT)/(frameDuration / 1000.0);
444         frame = qBound(qreal(0.0), frame, frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation
445         progress = modf(frame,&frameAt);
446     } else {
447         m_curFrame++;
448         if (m_curFrame >= frameCount){
449             m_curFrame = 0;
450             m_spriteEngine->advance();
451         }
452         frameAt = m_curFrame;
453         progress = 0;
454     }
455     if (m_spriteEngine->sprite()->reverse())
456         frameAt = (m_spriteEngine->spriteFrames() - 1) - frameAt;
457     qreal y = m_spriteEngine->spriteY() / m_sheetSize.height();
458     qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width();
459     qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height();
460     qreal x1 = m_spriteEngine->spriteX() / m_sheetSize.width();
461     x1 += frameAt * w;
462     qreal x2 = x1;
463     if (frameAt < (frameCount-1))
464         x2 += w;
465
466     m_material->animX1 = x1;
467     m_material->animY1 = y;
468     m_material->animX2 = x2;
469     m_material->animY2 = y;
470     m_material->animW = w;
471     m_material->animH = h;
472     m_material->animT = m_interpolate ? progress : 0.0;
473 }
474
475 QT_END_NAMESPACE