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