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/qquickwindow.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 \qmltype SpriteSequence
211 \instantiates QQuickSpriteSequence
212 \inqmlmodule QtQuick 2
213 \ingroup qtquick-visual-utility
215 \brief Draws a sprite animation
217 SpriteSequence renders and controls a list of animations defined
220 For full details, see the \l{Sprite Animation} overview.
223 \qmlproperty bool QtQuick2::SpriteSequence::running
225 Whether the sprite is animating or not.
230 \qmlproperty bool QtQuick2::SpriteSequence::interpolate
232 If true, interpolation will occur between sprite frames to make the
233 animation appear smoother.
238 \qmlproperty string QtQuick2::SpriteSequence::goalSprite
240 The name of the Sprite which is currently animating.
243 \qmlproperty string QtQuick2::SpriteSequence::goalSprite
245 The name of the Sprite which the animation should move to.
247 Sprite states have defined durations and transitions between them, setting goalState
248 will cause it to disregard any path weightings (including 0) and head down the path
249 which will reach the goalState quickest (fewest animations). It will pass through
250 intermediate states on that path, and animate them for their duration.
252 If it is possible to return to the goalState from the starting point of the goalState
253 it will continue to do so until goalState is set to "" or an unreachable state.
255 /*! \qmlmethod void QtQuick2::SpriteSequence::jumpTo(string sprite)
257 This function causes the SpriteSequence to jump to the specified sprite immediately, intermediate
258 sprites are not played. The \a sprite argument is the name of the sprite you wish to jump to.
261 \qmlproperty list<Sprite> QtQuick2::SpriteSequence::sprites
263 The sprite or sprites to draw. Sprites will be scaled to the size of this item.
266 //TODO: Implicitly size element to size of first sprite?
267 QQuickSpriteSequence::QQuickSpriteSequence(QQuickItem *parent) :
273 , m_pleaseReset(false)
275 , m_interpolate(true)
278 setFlag(ItemHasContents);
279 connect(this, SIGNAL(runningChanged(bool)),
280 this, SLOT(update()));
281 connect(this, SIGNAL(widthChanged()),
282 this, SLOT(sizeVertices()));
283 connect(this, SIGNAL(heightChanged()),
284 this, SLOT(sizeVertices()));
287 void QQuickSpriteSequence::jumpTo(const QString &sprite)
291 m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite), 0, true);
294 void QQuickSpriteSequence::setGoalSprite(const QString &sprite)
296 if (m_goalState != sprite){
297 m_goalState = sprite;
298 emit goalSpriteChanged(sprite);
299 m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite));
303 QQmlListProperty<QQuickSprite> QQuickSpriteSequence::sprites()
305 return QQmlListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
308 void QQuickSpriteSequence::createEngine()
310 //TODO: delay until component complete
312 delete m_spriteEngine;
313 if (m_sprites.count())
314 m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
320 static QSGGeometry::Attribute SpriteSequence_Attributes[] = {
321 QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // pos
322 QSGGeometry::Attribute::create(1, 2, GL_FLOAT), // tex
325 static QSGGeometry::AttributeSet SpriteSequence_AttributeSet =
327 2, // Attribute Count
328 (2+2) * sizeof(float),
329 SpriteSequence_Attributes
332 void QQuickSpriteSequence::sizeVertices()
337 SpriteVertices *p = (SpriteVertices *) m_node->geometry()->vertexData();
351 QSGGeometryNode* QQuickSpriteSequence::buildNode()
353 if (!m_spriteEngine) {
354 qmlInfo(this) << "No sprite engine...";
356 } else if (m_spriteEngine->status() == QQuickPixmap::Null) {
357 m_spriteEngine->startAssemblingImage();
358 update();//Schedule another update, where we will check again
360 } else if (m_spriteEngine->status() == QQuickPixmap::Loading) {
361 update();//Schedule another update, where we will check again
365 m_material = new QQuickSpriteSequenceMaterial();
367 QImage image = m_spriteEngine->assembledImage();
370 m_sheetSize = QSizeF(image.size());
371 m_material->texture = window()->createTextureFromImage(image);
372 m_material->texture->setFiltering(QSGTexture::Linear);
373 m_spriteEngine->start(0);
374 m_material->animT = 0;
375 m_material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width();
376 m_material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height();
377 m_material->animX2 = m_material->animX1;
378 m_material->animY2 = m_material->animY1;
379 m_material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width();
380 m_material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height();
381 m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
382 emit currentSpriteChanged(m_curState);
386 QSGGeometry *g = new QSGGeometry(SpriteSequence_AttributeSet, vCount, iCount);
387 g->setDrawingMode(GL_TRIANGLES);
389 SpriteVertices *p = (SpriteVertices *) g->vertexData();
390 QRectF texRect = m_material->texture->normalizedTextureSubRect();
392 p->v1.tx = texRect.topLeft().x();
393 p->v1.ty = texRect.topLeft().y();
395 p->v2.tx = texRect.topRight().x();
396 p->v2.ty = texRect.topRight().y();
398 p->v3.tx = texRect.bottomLeft().x();
399 p->v3.ty = texRect.bottomLeft().y();
401 p->v4.tx = texRect.bottomRight().x();
402 p->v4.ty = texRect.bottomRight().y();
404 quint16 *indices = g->indexDataAsUShort();
414 m_node = new QSGGeometryNode();
415 m_node->setGeometry(g);
416 m_node->setMaterial(m_material);
417 m_node->setFlag(QSGGeometryNode::OwnsMaterial);
422 void QQuickSpriteSequence::reset()
424 m_pleaseReset = true;
427 QSGNode *QQuickSpriteSequence::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
434 m_pleaseReset = false;
442 m_node->markDirty(QSGNode::DirtyMaterial);
448 void QQuickSpriteSequence::prepareNextFrame()
451 m_node = buildNode();
452 if (m_node == 0) //error creating node
455 uint timeInt = m_timestamp.elapsed();
456 qreal time = timeInt / 1000.;
459 m_spriteEngine->updateSprites(timeInt);
460 if (m_curStateIdx != m_spriteEngine->curState()) {
461 m_curStateIdx = m_spriteEngine->curState();
462 m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
463 emit currentSpriteChanged(m_curState);
468 qreal animT = m_spriteEngine->spriteStart()/1000.0;
469 qreal frameCount = m_spriteEngine->spriteFrames();
470 qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
473 if (frameDuration > 0) {
474 qreal frame = (time - animT)/(frameDuration / 1000.0);
475 frame = qBound(qreal(0.0), frame, frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation
476 progress = modf(frame,&frameAt);
479 if (m_curFrame >= frameCount){
481 m_spriteEngine->advance();
483 frameAt = m_curFrame;
486 if (m_spriteEngine->sprite()->reverse())
487 frameAt = (m_spriteEngine->spriteFrames() - 1) - frameAt;
488 qreal y = m_spriteEngine->spriteY() / m_sheetSize.height();
489 qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width();
490 qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height();
491 qreal x1 = m_spriteEngine->spriteX() / m_sheetSize.width();
494 if (frameAt < (frameCount-1))
497 m_material->animX1 = x1;
498 m_material->animY1 = y;
499 m_material->animX2 = x2;
500 m_material->animY2 = y;
501 m_material->animW = w;
502 m_material->animH = h;
503 m_material->animT = m_interpolate ? progress : 0.0;