fca6fbc19e1c9be53b053122ebb2468c576fca8b
[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/qsgtexturematerial.h>
49 #include <QtQuick/qsgtexture.h>
50 #include <QtQuick/qquickcanvas.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 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 QQuickSpriteImageMaterial : public QSGMaterial
93 {
94 public:
95     QQuickSpriteImageMaterial();
96     ~QQuickSpriteImageMaterial();
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 QQuickSpriteImageMaterial *>(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 QQuickSpriteImageMaterial::QQuickSpriteImageMaterial()
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 QQuickSpriteImageMaterial::~QQuickSpriteImageMaterial()
132 {
133     delete texture;
134 }
135
136 class SpriteImageMaterialData : public QSGMaterialShader
137 {
138 public:
139     SpriteImageMaterialData(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         QQuickSpriteImageMaterial *m = static_cast<QQuickSpriteImageMaterial *>(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 SpriteImageMaterialData::chunkOfBytes[1024];
194
195 QSGMaterialShader *QQuickSpriteImageMaterial::createShader() const
196 {
197     return new SpriteImageMaterialData;
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? or currentSprite?
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 QQmlListProperty<QQuickSprite> QQuickSpriteImage::sprites()
297 {
298     return QQmlListProperty<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         qmlInfo(this) << "No sprite engine...";
328         return 0;
329     } else if (m_spriteEngine->status() == QQuickPixmap::Null) {
330         m_spriteEngine->startAssemblingImage();
331         update();//Schedule another update, where we will check again
332         return 0;
333     } else if (m_spriteEngine->status() == QQuickPixmap::Loading) {
334         update();//Schedule another update, where we will check again
335         return 0;
336     }
337
338     m_material = new QQuickSpriteImageMaterial();
339
340     QImage image = m_spriteEngine->assembledImage();
341     if (image.isNull())
342         return 0;
343     m_sheetSize = QSizeF(image.size());
344     m_material->texture = canvas()->createTextureFromImage(image);
345     m_material->texture->setFiltering(QSGTexture::Linear);
346     m_spriteEngine->start(0);
347     m_material->animT = 0;
348     m_material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width();
349     m_material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height();
350     m_material->animX2 = m_material->animX1;
351     m_material->animY2 = m_material->animY1;
352     m_material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width();
353     m_material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height();
354     m_material->elementWidth = width();
355     m_material->elementHeight = height();
356     m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
357     emit currentSpriteChanged(m_curState);
358
359     int vCount = 4;
360     int iCount = 6;
361     QSGGeometry *g = new QSGGeometry(SpriteImage_AttributeSet, vCount, iCount);
362     g->setDrawingMode(GL_TRIANGLES);
363
364     SpriteVertices *p = (SpriteVertices *) g->vertexData();
365
366     p->v1.tx = 0;
367     p->v1.ty = 0;
368
369     p->v2.tx = 1.0;
370     p->v2.ty = 0;
371
372     p->v3.tx = 0;
373     p->v3.ty = 1.0;
374
375     p->v4.tx = 1.0;
376     p->v4.ty = 1.0;
377
378     quint16 *indices = g->indexDataAsUShort();
379     indices[0] = 0;
380     indices[1] = 1;
381     indices[2] = 2;
382     indices[3] = 1;
383     indices[4] = 3;
384     indices[5] = 2;
385
386
387     m_timestamp.start();
388     m_node = new QSGGeometryNode();
389     m_node->setGeometry(g);
390     m_node->setMaterial(m_material);
391     m_node->setFlag(QSGGeometryNode::OwnsMaterial);
392     return m_node;
393 }
394
395 void QQuickSpriteImage::reset()
396 {
397     m_pleaseReset = true;
398 }
399
400 QSGNode *QQuickSpriteImage::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
401 {
402     if (m_pleaseReset) {
403         delete m_node;
404
405         m_node = 0;
406         m_material = 0;
407         m_pleaseReset = false;
408     }
409
410     prepareNextFrame();
411
412     if (m_running) {
413         update();
414         if (m_node)
415             m_node->markDirty(QSGNode::DirtyMaterial);
416     }
417
418     return m_node;
419 }
420
421 void QQuickSpriteImage::prepareNextFrame()
422 {
423     if (m_node == 0)
424         m_node = buildNode();
425     if (m_node == 0) //error creating node
426         return;
427
428     uint timeInt = m_timestamp.elapsed();
429     qreal time =  timeInt / 1000.;
430     m_material->elementHeight = height();
431     m_material->elementWidth = width();
432
433     //Advance State
434     m_spriteEngine->updateSprites(timeInt);
435     if (m_curStateIdx != m_spriteEngine->curState()) {
436         m_curStateIdx = m_spriteEngine->curState();
437         m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
438         emit currentSpriteChanged(m_curState);
439         m_curFrame= -1;
440     }
441
442     //Advance Sprite
443     qreal animT = m_spriteEngine->spriteStart()/1000.0;
444     qreal frameCount = m_spriteEngine->spriteFrames();
445     qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
446     double frameAt;
447     qreal progress;
448     if (frameDuration > 0) {
449         qreal frame = (time - animT)/(frameDuration / 1000.0);
450         frame = qBound(qreal(0.0), frame, frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation
451         progress = modf(frame,&frameAt);
452     } else {
453         m_curFrame++;
454         if (m_curFrame >= frameCount){
455             m_curFrame = 0;
456             m_spriteEngine->advance();
457         }
458         frameAt = m_curFrame;
459         progress = 0;
460     }
461     if (m_spriteEngine->sprite()->reverse())
462         frameAt = (m_spriteEngine->spriteFrames() - 1) - frameAt;
463     qreal y = m_spriteEngine->spriteY() / m_sheetSize.height();
464     qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width();
465     qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height();
466     qreal x1 = m_spriteEngine->spriteX() / m_sheetSize.width();
467     x1 += frameAt * w;
468     qreal x2 = x1;
469     if (frameAt < (frameCount-1))
470         x2 += w;
471
472     m_material->animX1 = x1;
473     m_material->animY1 = y;
474     m_material->animX2 = x2;
475     m_material->animY2 = y;
476     m_material->animW = w;
477     m_material->animH = h;
478     m_material->animT = m_interpolate ? progress : 0.0;
479 }
480
481 QT_END_NAMESPACE