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 Declarative 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/qsgengine.h>
49 #include <QtQuick/qsgtexturematerial.h>
50 #include <QtQuick/qsgtexture.h>
51 #include <QtQuick/qquickcanvas.h>
52 #include <QtDeclarative/qdeclarativeinfo.h>
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"
66 "uniform highp mat4 qt_Matrix;\n"
68 "varying highp vec4 fTexS;\n"
69 "varying lowp float progress;\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"
79 " gl_Position = qt_Matrix * vec4(size.x * vTex.x, size.y * vTex.y, 0, 1);\n"
82 static const char fragmentShaderCode[] =
83 "uniform sampler2D texture;\n"
84 "uniform lowp float qt_Opacity;\n"
86 "varying highp vec4 fTexS;\n"
87 "varying lowp float progress;\n"
90 " gl_FragColor = mix(texture2D(texture, fTexS.xy), texture2D(texture, fTexS.zw), progress) * qt_Opacity;\n"
93 class QQuickSpriteSequenceMaterial : public QSGMaterial
96 QQuickSpriteSequenceMaterial();
97 ~QQuickSpriteSequenceMaterial();
98 virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; }
99 virtual QSGMaterialShader *createShader() const;
100 virtual int compare(const QSGMaterial *other) const
102 return this - static_cast<const QQuickSpriteSequenceMaterial *>(other);
118 QQuickSpriteSequenceMaterial::QQuickSpriteSequenceMaterial()
127 , elementHeight(1.0f)
129 setFlag(Blending, true);
132 QQuickSpriteSequenceMaterial::~QQuickSpriteSequenceMaterial()
137 class SpriteSequenceMaterialData : public QSGMaterialShader
140 SpriteSequenceMaterialData(const char * /* vertexFile */ = 0, const char * /* fragmentFile */ = 0)
145 QSGMaterialShader::deactivate();
147 for (int i=0; i<8; ++i) {
148 program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
152 virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
154 QQuickSpriteSequenceMaterial *m = static_cast<QQuickSpriteSequenceMaterial *>(newEffect);
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);
162 if (state.isMatrixDirty())
163 program()->setUniformValue(m_matrix_id, state.combinedMatrix());
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");
174 virtual const char *vertexShader() const { return vertexShaderCode; }
175 virtual const char *fragmentShader() const { return fragmentShaderCode; }
177 virtual char const *const *attributeNames() const {
178 static const char *attr[] = {
191 static float chunkOfBytes[1024];
194 float SpriteSequenceMaterialData::chunkOfBytes[1024];
196 QSGMaterialShader *QQuickSpriteSequenceMaterial::createShader() const
198 return new SpriteSequenceMaterialData;
201 struct SpriteVertex {
206 struct SpriteVertices {
214 \qmlclass SpriteSequence QQuickSpriteSequence
215 \inqmlmodule QtQuick 2
217 \brief The SpriteSequence element draws a sprite animation
221 \qmlproperty bool QtQuick2::SpriteSequence::running
223 Whether the sprite is animating or not.
228 \qmlproperty bool QtQuick2::SpriteSequence::interpolate
230 If true, interpolation will occur between sprite frames to make the
231 animation appear smoother.
236 \qmlproperty string QtQuick2::SpriteSequence::goalSprite
238 The name of the Sprite which is currently animating.
241 \qmlproperty string QtQuick2::SpriteSequence::goalSprite
243 The name of the Sprite which the animation should move to.
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.
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.
253 /*! \qmlmethod void QtQuick2::SpriteSequence::jumpTo(string sprite)
255 This function causes the sprite to jump to the specified state immediately, intermediate
256 states are not played.
259 \qmlproperty list<Sprite> QtQuick2::SpriteSequence::sprites
261 The sprite or sprites to draw. Sprites will be scaled to the size of this element.
264 //TODO: Implicitly size element to size of first sprite?
265 QQuickSpriteSequence::QQuickSpriteSequence(QQuickItem *parent) :
271 , m_pleaseReset(false)
273 , m_interpolate(true)
276 setFlag(ItemHasContents);
277 connect(this, SIGNAL(runningChanged(bool)),
278 this, SLOT(update()));
281 void QQuickSpriteSequence::jumpTo(const QString &sprite)
285 m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite), 0, true);
288 void QQuickSpriteSequence::setGoalSprite(const QString &sprite)
290 if (m_goalState != sprite){
291 m_goalState = sprite;
292 emit goalSpriteChanged(sprite);
293 m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite));
297 QDeclarativeListProperty<QQuickSprite> QQuickSpriteSequence::sprites()
299 return QDeclarativeListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
302 void QQuickSpriteSequence::createEngine()
304 //TODO: delay until component complete
306 delete m_spriteEngine;
307 if (m_sprites.count())
308 m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
314 static QSGGeometry::Attribute SpriteSequence_Attributes[] = {
315 QSGGeometry::Attribute::create(0, 2, GL_FLOAT), // tex
318 static QSGGeometry::AttributeSet SpriteSequence_AttributeSet =
320 1, // Attribute Count
322 SpriteSequence_Attributes
325 QSGGeometryNode* QQuickSpriteSequence::buildNode()
327 if (!m_spriteEngine) {
328 qmlInfo(this) << "No sprite engine...";
330 } else if (m_spriteEngine->status() == QDeclarativePixmap::Null) {
331 m_spriteEngine->startAssemblingImage();
332 update();//Schedule another update, where we will check again
334 } else if (m_spriteEngine->status() == QDeclarativePixmap::Loading) {
335 update();//Schedule another update, where we will check again
339 m_material = new QQuickSpriteSequenceMaterial();
341 QImage image = m_spriteEngine->assembledImage();
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);
362 QSGGeometry *g = new QSGGeometry(SpriteSequence_AttributeSet, vCount, iCount);
363 g->setDrawingMode(GL_TRIANGLES);
365 SpriteVertices *p = (SpriteVertices *) g->vertexData();
379 quint16 *indices = g->indexDataAsUShort();
389 m_node = new QSGGeometryNode();
390 m_node->setGeometry(g);
391 m_node->setMaterial(m_material);
392 m_node->setFlag(QSGGeometryNode::OwnsMaterial);
396 void QQuickSpriteSequence::reset()
398 m_pleaseReset = true;
401 QSGNode *QQuickSpriteSequence::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
408 m_pleaseReset = false;
416 m_node->markDirty(QSGNode::DirtyMaterial);
422 void QQuickSpriteSequence::prepareNextFrame()
425 m_node = buildNode();
426 if (m_node == 0) //error creating node
429 uint timeInt = m_timestamp.elapsed();
430 qreal time = timeInt / 1000.;
431 m_material->elementHeight = height();
432 m_material->elementWidth = width();
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);
444 qreal animT = m_spriteEngine->spriteStart()/1000.0;
445 qreal frameCount = m_spriteEngine->spriteFrames();
446 qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
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);
455 if (m_curFrame >= frameCount){
457 m_spriteEngine->advance();
459 frameAt = m_curFrame;
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();
470 if (frameAt < (frameCount-1))
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;