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 "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>
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 QQuickSpriteImageMaterial : public QSGMaterial
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
101 return this - static_cast<const QQuickSpriteImageMaterial *>(other);
117 QQuickSpriteImageMaterial::QQuickSpriteImageMaterial()
126 , elementHeight(1.0f)
128 setFlag(Blending, true);
131 QQuickSpriteImageMaterial::~QQuickSpriteImageMaterial()
136 class SpriteImageMaterialData : public QSGMaterialShader
139 SpriteImageMaterialData(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 QQuickSpriteImageMaterial *m = static_cast<QQuickSpriteImageMaterial *>(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 SpriteImageMaterialData::chunkOfBytes[1024];
195 QSGMaterialShader *QQuickSpriteImageMaterial::createShader() const
197 return new SpriteImageMaterialData;
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? or currentSprite?
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 QQmlListProperty<QQuickSprite> QQuickSpriteImage::sprites()
298 return QQmlListProperty<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 qmlInfo(this) << "No sprite engine...";
329 } else if (m_spriteEngine->status() == QQuickPixmap::Null) {
330 m_spriteEngine->startAssemblingImage();
331 update();//Schedule another update, where we will check again
333 } else if (m_spriteEngine->status() == QQuickPixmap::Loading) {
334 update();//Schedule another update, where we will check again
338 m_material = new QQuickSpriteImageMaterial();
340 QImage image = m_spriteEngine->assembledImage();
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);
361 QSGGeometry *g = new QSGGeometry(SpriteImage_AttributeSet, vCount, iCount);
362 g->setDrawingMode(GL_TRIANGLES);
364 SpriteVertices *p = (SpriteVertices *) g->vertexData();
378 quint16 *indices = g->indexDataAsUShort();
388 m_node = new QSGGeometryNode();
389 m_node->setGeometry(g);
390 m_node->setMaterial(m_material);
391 m_node->setFlag(QSGGeometryNode::OwnsMaterial);
395 void QQuickSpriteImage::reset()
397 m_pleaseReset = true;
400 QSGNode *QQuickSpriteImage::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
407 m_pleaseReset = false;
415 m_node->markDirty(QSGNode::DirtyMaterial);
421 void QQuickSpriteImage::prepareNextFrame()
424 m_node = buildNode();
425 if (m_node == 0) //error creating node
428 uint timeInt = m_timestamp.elapsed();
429 qreal time = timeInt / 1000.;
430 m_material->elementHeight = height();
431 m_material->elementWidth = width();
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);
443 qreal animT = m_spriteEngine->spriteStart()/1000.0;
444 qreal frameCount = m_spriteEngine->spriteFrames();
445 qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
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);
454 if (m_curFrame >= frameCount){
456 m_spriteEngine->advance();
458 frameAt = m_curFrame;
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();
469 if (frameAt < (frameCount-1))
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;