1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the Declarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
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>
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"
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(size.x * vTex.x, size.y * vTex.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 QQuickSpriteMaterial : public QSGMaterial
95 QQuickSpriteMaterial();
96 virtual ~QQuickSpriteMaterial();
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 QQuickSpriteMaterial *>(other);
117 QQuickSpriteMaterial::QQuickSpriteMaterial()
126 , elementHeight(1.0f)
128 setFlag(Blending, true);
131 QQuickSpriteMaterial::~QQuickSpriteMaterial()
136 class SpriteMaterialData : public QSGMaterialShader
139 SpriteMaterialData(const char * /* vertexFile */ = 0, const char * /* fragmentFile */ = 0)
144 QSGMaterialShader::deactivate();
146 for (int i=0; i<8; ++i) {
147 program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
151 virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
153 QQuickSpriteMaterial *m = static_cast<QQuickSpriteMaterial *>(newEffect);
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);
161 if (state.isMatrixDirty())
162 program()->setUniformValue(m_matrix_id, state.combinedMatrix());
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");
173 virtual const char *vertexShader() const { return vertexShaderCode; }
174 virtual const char *fragmentShader() const { return fragmentShaderCode; }
176 virtual char const *const *attributeNames() const {
177 static const char *attr[] = {
190 static float chunkOfBytes[1024];
193 float SpriteMaterialData::chunkOfBytes[1024];
195 QSGMaterialShader *QQuickSpriteMaterial::createShader() const
197 return new SpriteMaterialData;
200 struct SpriteVertex {
205 struct SpriteVertices {
213 \qmlclass SpriteImage QQuickSpriteImage
214 \inqmlmodule QtQuick 2
216 \brief The SpriteImage element draws a sprite animation
220 \qmlproperty bool QtQuick2::SpriteImage::running
222 Whether the sprite is animating or not.
227 \qmlproperty bool QtQuick2::SpriteImage::interpolate
229 If true, interpolation will occur between sprite frames to make the
230 animation appear smoother.
235 \qmlproperty string QtQuick2::SpriteImage::goalSprite
237 The name of the Sprite which is currently animating.
240 \qmlproperty string QtQuick2::SpriteImage::goalSprite
242 The name of the Sprite which the animation should move to.
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.
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.
252 /*! \qmlmethod void QtQuick2::SpriteImage::jumpTo(string sprite)
254 This function causes the sprite to jump to the specified state immediately, intermediate
255 states are not played.
258 \qmlproperty list<Sprite> QtQuick2::SpriteImage::sprites
260 The sprite or sprites to draw. Sprites will be scaled to the size of this element.
263 //TODO: Implicitly size element to size of first sprite?
264 QQuickSpriteImage::QQuickSpriteImage(QQuickItem *parent) :
270 , m_pleaseReset(false)
272 , m_interpolate(true)
275 setFlag(ItemHasContents);
276 connect(this, SIGNAL(runningChanged(bool)),
277 this, SLOT(update()));
280 void QQuickSpriteImage::jumpTo(const QString &sprite)
284 m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite), 0, true);
287 void QQuickSpriteImage::setGoalSprite(const QString &sprite)
289 if (m_goalState != sprite){
290 m_goalState = sprite;
291 emit goalSpriteChanged(sprite);
292 m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite));
296 QDeclarativeListProperty<QQuickSprite> QQuickSpriteImage::sprites()
298 return QDeclarativeListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
301 void QQuickSpriteImage::createEngine()
303 //TODO: delay until component complete
305 delete m_spriteEngine;
306 if (m_sprites.count())
307 m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
313 static QSGGeometry::Attribute SpriteImage_Attributes[] = {
314 QSGGeometry::Attribute::create(0, 2, GL_FLOAT), // tex
317 static QSGGeometry::AttributeSet SpriteImage_AttributeSet =
319 1, // Attribute Count
321 SpriteImage_Attributes
324 QSGGeometryNode* QQuickSpriteImage::buildNode()
326 if (!m_spriteEngine) {
327 qWarning() << "SpriteImage: No sprite engine...";
331 m_material = new QQuickSpriteMaterial();
333 QImage image = m_spriteEngine->assembledImage();
336 m_sheetSize = QSizeF(image.size());
337 m_material->texture = canvas()->createTextureFromImage(image);
338 m_material->texture->setFiltering(QSGTexture::Linear);
339 m_spriteEngine->start(0);
340 m_material->animT = 0;
341 m_material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width();
342 m_material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height();
343 m_material->animX2 = m_material->animX1;
344 m_material->animY2 = m_material->animY1;
345 m_material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width();
346 m_material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height();
347 m_material->elementWidth = width();
348 m_material->elementHeight = height();
349 m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
350 emit currentSpriteChanged(m_curState);
354 QSGGeometry *g = new QSGGeometry(SpriteImage_AttributeSet, vCount, iCount);
355 g->setDrawingMode(GL_TRIANGLES);
357 SpriteVertices *p = (SpriteVertices *) g->vertexData();
371 quint16 *indices = g->indexDataAsUShort();
381 m_node = new QSGGeometryNode();
382 m_node->setGeometry(g);
383 m_node->setMaterial(m_material);
384 m_node->setFlag(QSGGeometryNode::OwnsMaterial);
388 void QQuickSpriteImage::reset()
390 m_pleaseReset = true;
393 QSGNode *QQuickSpriteImage::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
401 m_pleaseReset = false;
409 m_node->markDirty(QSGNode::DirtyMaterial);
415 void QQuickSpriteImage::prepareNextFrame()
418 m_node = buildNode();
419 if (m_node == 0) //error creating node
422 uint timeInt = m_timestamp.elapsed();
423 qreal time = timeInt / 1000.;
424 m_material->elementHeight = height();
425 m_material->elementWidth = width();
428 m_spriteEngine->updateSprites(timeInt);
429 if (m_curStateIdx != m_spriteEngine->curState()) {
430 m_curStateIdx = m_spriteEngine->curState();
431 m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
432 emit currentSpriteChanged(m_curState);
437 qreal animT = m_spriteEngine->spriteStart()/1000.0;
438 qreal frameCount = m_spriteEngine->spriteFrames();
439 qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
442 if (frameDuration > 0) {
443 qreal frame = (time - animT)/(frameDuration / 1000.0);
444 frame = qBound(qreal(0.0), frame, frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation
445 progress = modf(frame,&frameAt);
448 if (m_curFrame >= frameCount){
450 m_spriteEngine->advance();
452 frameAt = m_curFrame;
455 if (m_spriteEngine->sprite()->reverse())
456 frameAt = (m_spriteEngine->spriteFrames() - 1) - frameAt;
457 qreal y = m_spriteEngine->spriteY() / m_sheetSize.height();
458 qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width();
459 qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height();
460 qreal x1 = m_spriteEngine->spriteX() / m_sheetSize.width();
463 if (frameAt < (frameCount-1))
466 m_material->animX1 = x1;
467 m_material->animY1 = y;
468 m_material->animX2 = x2;
469 m_material->animY2 = y;
470 m_material->animW = w;
471 m_material->animH = h;
472 m_material->animT = m_interpolate ? progress : 0.0;