1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQuick module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
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/qquickcanvas.h>
51 #include <QtQml/qqmlinfo.h>
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"
65 "uniform highp mat4 qt_Matrix;\n"
67 "varying highp vec4 fTexS;\n"
68 "varying lowp float progress;\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"
78 " gl_Position = qt_Matrix * vec4(vPos.x, vPos.y, 0, 1);\n"
81 static const char fragmentShaderCode[] =
82 "uniform sampler2D texture;\n"
83 "uniform lowp float qt_Opacity;\n"
85 "varying highp vec4 fTexS;\n"
86 "varying lowp float progress;\n"
89 " gl_FragColor = mix(texture2D(texture, fTexS.xy), texture2D(texture, fTexS.zw), progress) * qt_Opacity;\n"
92 class QQuickSpriteSequenceMaterial : public QSGMaterial
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
101 return this - static_cast<const QQuickSpriteSequenceMaterial *>(other);
115 QQuickSpriteSequenceMaterial::QQuickSpriteSequenceMaterial()
125 setFlag(Blending, true);
128 QQuickSpriteSequenceMaterial::~QQuickSpriteSequenceMaterial()
133 class SpriteSequenceMaterialData : public QSGMaterialShader
136 SpriteSequenceMaterialData(const char * /* vertexFile */ = 0, const char * /* fragmentFile */ = 0)
141 QSGMaterialShader::deactivate();
143 for (int i=0; i<8; ++i) {
144 program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
148 virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
150 QQuickSpriteSequenceMaterial *m = static_cast<QQuickSpriteSequenceMaterial *>(newEffect);
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);
157 if (state.isMatrixDirty())
158 program()->setUniformValue(m_matrix_id, state.combinedMatrix());
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");
168 virtual const char *vertexShader() const { return vertexShaderCode; }
169 virtual const char *fragmentShader() const { return fragmentShaderCode; }
171 virtual char const *const *attributeNames() const {
172 static const char *attr[] = {
185 static float chunkOfBytes[1024];
188 float SpriteSequenceMaterialData::chunkOfBytes[1024];
190 QSGMaterialShader *QQuickSpriteSequenceMaterial::createShader() const
192 return new SpriteSequenceMaterialData;
195 struct SpriteVertex {
202 struct SpriteVertices {
210 \qmlclass SpriteSequence QQuickSpriteSequence
211 \inqmlmodule QtQuick 2
212 \ingroup qtquick-images-sprites
214 \brief Draws a sprite animation
216 SpriteSequence renders and controls a list of animations defined
217 by \l Sprite elements.
219 For full details, see the \l{Sprite Animation} overview.
222 \qmlproperty bool QtQuick2::SpriteSequence::running
224 Whether the sprite is animating or not.
229 \qmlproperty bool QtQuick2::SpriteSequence::interpolate
231 If true, interpolation will occur between sprite frames to make the
232 animation appear smoother.
237 \qmlproperty string QtQuick2::SpriteSequence::goalSprite
239 The name of the Sprite which is currently animating.
242 \qmlproperty string QtQuick2::SpriteSequence::goalSprite
244 The name of the Sprite which the animation should move to.
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.
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.
254 /*! \qmlmethod void QtQuick2::SpriteSequence::jumpTo(string sprite)
256 This function causes the sprite to jump to the specified state immediately, intermediate
257 states are not played.
260 \qmlproperty list<Sprite> QtQuick2::SpriteSequence::sprites
262 The sprite or sprites to draw. Sprites will be scaled to the size of this element.
265 //TODO: Implicitly size element to size of first sprite?
266 QQuickSpriteSequence::QQuickSpriteSequence(QQuickItem *parent) :
272 , m_pleaseReset(false)
274 , m_interpolate(true)
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()));
286 void QQuickSpriteSequence::jumpTo(const QString &sprite)
290 m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite), 0, true);
293 void QQuickSpriteSequence::setGoalSprite(const QString &sprite)
295 if (m_goalState != sprite){
296 m_goalState = sprite;
297 emit goalSpriteChanged(sprite);
298 m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite));
302 QQmlListProperty<QQuickSprite> QQuickSpriteSequence::sprites()
304 return QQmlListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
307 void QQuickSpriteSequence::createEngine()
309 //TODO: delay until component complete
311 delete m_spriteEngine;
312 if (m_sprites.count())
313 m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
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
324 static QSGGeometry::AttributeSet SpriteSequence_AttributeSet =
326 2, // Attribute Count
327 (2+2) * sizeof(float),
328 SpriteSequence_Attributes
331 void QQuickSpriteSequence::sizeVertices()
336 SpriteVertices *p = (SpriteVertices *) m_node->geometry()->vertexData();
350 QSGGeometryNode* QQuickSpriteSequence::buildNode()
352 if (!m_spriteEngine) {
353 qmlInfo(this) << "No sprite engine...";
355 } else if (m_spriteEngine->status() == QQuickPixmap::Null) {
356 m_spriteEngine->startAssemblingImage();
357 update();//Schedule another update, where we will check again
359 } else if (m_spriteEngine->status() == QQuickPixmap::Loading) {
360 update();//Schedule another update, where we will check again
364 m_material = new QQuickSpriteSequenceMaterial();
366 QImage image = m_spriteEngine->assembledImage();
369 m_sheetSize = QSizeF(image.size());
370 m_material->texture = canvas()->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);
385 QSGGeometry *g = new QSGGeometry(SpriteSequence_AttributeSet, vCount, iCount);
386 g->setDrawingMode(GL_TRIANGLES);
388 SpriteVertices *p = (SpriteVertices *) g->vertexData();
389 QRectF texRect = m_material->texture->normalizedTextureSubRect();
391 p->v1.tx = texRect.topLeft().x();
392 p->v1.ty = texRect.topLeft().y();
394 p->v2.tx = texRect.topRight().x();
395 p->v2.ty = texRect.topRight().y();
397 p->v3.tx = texRect.bottomLeft().x();
398 p->v3.ty = texRect.bottomLeft().y();
400 p->v4.tx = texRect.bottomRight().x();
401 p->v4.ty = texRect.bottomRight().y();
403 quint16 *indices = g->indexDataAsUShort();
413 m_node = new QSGGeometryNode();
414 m_node->setGeometry(g);
415 m_node->setMaterial(m_material);
416 m_node->setFlag(QSGGeometryNode::OwnsMaterial);
421 void QQuickSpriteSequence::reset()
423 m_pleaseReset = true;
426 QSGNode *QQuickSpriteSequence::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
433 m_pleaseReset = false;
441 m_node->markDirty(QSGNode::DirtyMaterial);
447 void QQuickSpriteSequence::prepareNextFrame()
450 m_node = buildNode();
451 if (m_node == 0) //error creating node
454 uint timeInt = m_timestamp.elapsed();
455 qreal time = timeInt / 1000.;
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);
467 qreal animT = m_spriteEngine->spriteStart()/1000.0;
468 qreal frameCount = m_spriteEngine->spriteFrames();
469 qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
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);
478 if (m_curFrame >= frameCount){
480 m_spriteEngine->advance();
482 frameAt = m_curFrame;
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();
493 if (frameAt < (frameCount-1))
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;