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 "qquickanimatedsprite_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 QQuickAnimatedSpriteMaterial : public QSGMaterial
95 QQuickAnimatedSpriteMaterial();
96 ~QQuickAnimatedSpriteMaterial();
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 QQuickAnimatedSpriteMaterial *>(other);
115 QQuickAnimatedSpriteMaterial::QQuickAnimatedSpriteMaterial()
125 setFlag(Blending, true);
128 QQuickAnimatedSpriteMaterial::~QQuickAnimatedSpriteMaterial()
133 class AnimatedSpriteMaterialData : public QSGMaterialShader
136 AnimatedSpriteMaterialData(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 QQuickAnimatedSpriteMaterial *m = static_cast<QQuickAnimatedSpriteMaterial *>(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 AnimatedSpriteMaterialData::chunkOfBytes[1024];
190 QSGMaterialShader *QQuickAnimatedSpriteMaterial::createShader() const
192 return new AnimatedSpriteMaterialData;
195 struct AnimatedSpriteVertex {
202 struct AnimatedSpriteVertices {
203 AnimatedSpriteVertex v1;
204 AnimatedSpriteVertex v2;
205 AnimatedSpriteVertex v3;
206 AnimatedSpriteVertex v4;
210 \qmlclass AnimatedSprite QQuickAnimatedSprite
211 \inqmlmodule QtQuick 2
213 \brief The AnimatedSprite element draws a sprite animation
215 AnimatedSprite provides rendering and control over animations which are provided
216 as multiple frames in the same image file. You can play it at a fixed speed, at the
217 frame rate of your display, or manually advance and control the progress.
219 For details of how a sprite animation is defined see the \l{Sprite Animation} overview.
220 Note that the AnimatedSprite element does not use Sprite elements to define multiple animations,
221 but instead encapsulates a single animation itself.
225 \qmlproperty bool QtQuick2::AnimatedSprite::running
227 Whether the sprite is animating or not.
233 \qmlproperty bool QtQuick2::AnimatedSprite::interpolate
235 If true, interpolation will occur between sprite frames to make the
236 animation appear smoother.
242 \qmlproperty qreal QtQuick2::AnimatedSprite::frameRate
244 Frames per second to show in the animation. Values equal to or below 0 are invalid.
246 If frameRate is valid then it will be used to calculate the duration of the frames.
247 If not, and frameDuration is valid , then frameDuration will be used.
249 Changing this parameter will restart the animation.
253 \qmlproperty int QtQuick2::AnimatedSprite::frameDuration
255 Duration of each frame of the animation. Values equal to or below 0 are invalid.
257 If frameRate is valid then it will be used to calculate the duration of the frames.
258 If not, and frameDuration is valid, then frameDuration will be used.
260 Changing this parameter will restart the animation.
264 \qmlproperty int QtQuick2::AnimatedSprite::frameCount
266 Number of frames in this AnimatedSprite.
269 \qmlproperty int QtQuick2::AnimatedSprite::frameHeight
271 Height of a single frame in this AnimatedSprite.
273 May be omitted if it is the only sprite in the file.
276 \qmlproperty int QtQuick2::AnimatedSprite::frameWidth
278 Width of a single frame in this AnimatedSprite.
280 May be omitted if it is the only sprite in the file.
283 \qmlproperty int QtQuick2::AnimatedSprite::frameX
285 The X coordinate in the image file of the first frame of the AnimatedSprite.
287 May be omitted if the first frame starts in the upper left corner of the file.
290 \qmlproperty int QtQuick2::AnimatedSprite::frameY
292 The Y coordinate in the image file of the first frame of the AnimatedSprite.
294 May be omitted if the first frame starts in the upper left corner of the file.
297 \qmlproperty url QtQuick2::AnimatedSprite::source
299 The image source for the animation.
301 If frameHeight and frameWidth are not specified, it is assumed to be a single long row of square frames.
302 Otherwise, it can be multiple contiguous rows or rectangluar frames, when one row runs out the next will be used.
304 If frameX and frameY are specified, the row of frames will be taken with that x/y coordinate as the upper left corner.
308 \qmlproperty bool QtQuick2::AnimatedSprite::reverse
310 If true, then the animation will be played in reverse.
316 \qmlproperty bool QtQuick2::AnimatedSprite::frameSync
318 If true, then the animation will have no duration. Instead, the animation will advance
319 one frame each time a frame is rendered to the screen. This syncronizes it with the painting
320 rate as opposed to elapsed time.
322 If frameSync is set to true, it overrides both frameRate and frameDuration.
326 Changing this parameter will restart the animation.
330 \qmlproperty int QtQuick2::AnimatedSprite::loops
332 After playing the animation this many times, the animation will automatically stop. Negative values are invalid.
334 If this is set to AnimatedSprite.Infinite the animation will not stop playing on its own.
336 Default is AnimatedSprite.Infinite
340 \qmlproperty bool QtQuick2::AnimatedSprite::paused
342 When paused, the current frame can be advanced manually.
348 \qmlproperty int QtQuick2::AnimatedSprite::currentFrame
350 When paused, the current frame can be advanced manually by setting this property or calling advance().
354 //TODO: Implicitly size element to size of sprite
355 QQuickAnimatedSprite::QQuickAnimatedSprite(QQuickItem *parent) :
359 , m_sprite(new QQuickSprite)
362 , m_pleaseReset(false)
365 , m_interpolate(true)
370 setFlag(ItemHasContents);
371 connect(this, SIGNAL(runningChanged(bool)),
372 this, SLOT(update()));
373 connect(this, SIGNAL(widthChanged()),
374 this, SLOT(sizeVertices()));
375 connect(this, SIGNAL(heightChanged()),
376 this, SLOT(sizeVertices()));
379 void QQuickAnimatedSprite::reloadImage()
381 if (!isComponentComplete())
383 createEngine();//### It's not as inefficient as it sounds, but it still sucks having to recreate the engine
386 void QQuickAnimatedSprite::componentComplete()
389 QQuickItem::componentComplete();
394 void QQuickAnimatedSprite::start()
400 if (m_spriteEngine) {
401 m_spriteEngine->stop(0);
402 m_spriteEngine->updateSprites(0);
403 m_spriteEngine->start(0);
406 emit runningChanged(true);
410 void QQuickAnimatedSprite::stop()
416 emit runningChanged(false);
419 void QQuickAnimatedSprite::advance(int frames)
423 //TODO-C: May not work when running - only when paused
424 m_curFrame += frames;
425 while (m_curFrame < 0)
426 m_curFrame += m_sprite->frames();
427 m_curFrame = m_curFrame % m_sprite->frames();
428 emit currentFrameChanged(m_curFrame);
431 void QQuickAnimatedSprite::pause()
435 m_pauseOffset = m_timestamp.elapsed();
437 emit pausedChanged(true);
440 void QQuickAnimatedSprite::resume()
444 m_pauseOffset = m_pauseOffset - m_timestamp.elapsed();
446 emit pausedChanged(false);
449 void QQuickAnimatedSprite::createEngine()
452 delete m_spriteEngine;
453 QList<QQuickSprite*> spriteList;
454 spriteList << m_sprite;
455 m_spriteEngine = new QQuickSpriteEngine(QList<QQuickSprite*>(spriteList), this);
456 m_spriteEngine->startAssemblingImage();
460 static QSGGeometry::Attribute AnimatedSprite_Attributes[] = {
461 QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // pos
462 QSGGeometry::Attribute::create(1, 2, GL_FLOAT), // tex
465 static QSGGeometry::AttributeSet AnimatedSprite_AttributeSet =
467 2, // Attribute Count
468 (2+2) * sizeof(float),
469 AnimatedSprite_Attributes
472 void QQuickAnimatedSprite::sizeVertices()
477 AnimatedSpriteVertices *p = (AnimatedSpriteVertices *) m_node->geometry()->vertexData();
491 QSGGeometryNode* QQuickAnimatedSprite::buildNode()
493 if (!m_spriteEngine) {
494 qmlInfo(this) << "No sprite engine...";
496 } else if (m_spriteEngine->status() == QQuickPixmap::Null) {
497 m_spriteEngine->startAssemblingImage();
498 update();//Schedule another update, where we will check again
500 } else if (m_spriteEngine->status() == QQuickPixmap::Loading) {
501 update();//Schedule another update, where we will check again
505 m_material = new QQuickAnimatedSpriteMaterial();
507 QImage image = m_spriteEngine->assembledImage();
510 m_sheetSize = QSizeF(image.size());
511 m_material->texture = canvas()->createTextureFromImage(image);
512 m_material->texture->setFiltering(QSGTexture::Linear);
513 m_spriteEngine->start(0);
514 m_material->animT = 0;
515 m_material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width();
516 m_material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height();
517 m_material->animX2 = m_material->animX1;
518 m_material->animY2 = m_material->animY1;
519 m_material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width();
520 m_material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height();
524 QSGGeometry *g = new QSGGeometry(AnimatedSprite_AttributeSet, vCount, iCount);
525 g->setDrawingMode(GL_TRIANGLES);
527 AnimatedSpriteVertices *p = (AnimatedSpriteVertices *) g->vertexData();
542 quint16 *indices = g->indexDataAsUShort();
552 m_node = new QSGGeometryNode();
553 m_node->setGeometry(g);
554 m_node->setMaterial(m_material);
555 m_node->setFlag(QSGGeometryNode::OwnsMaterial);
560 void QQuickAnimatedSprite::reset()
562 m_pleaseReset = true;
565 QSGNode *QQuickAnimatedSprite::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
572 m_pleaseReset = false;
580 m_node->markDirty(QSGNode::DirtyMaterial);
586 void QQuickAnimatedSprite::prepareNextFrame()
589 m_node = buildNode();
590 if (m_node == 0) //error creating node
593 int timeInt = m_timestamp.elapsed() + m_pauseOffset;
594 qreal time = timeInt / 1000.;
596 double frameAt; //double just for modf
597 qreal progress = 0.0;
599 //Advance State (keeps time for psuedostates)
600 m_spriteEngine->updateSprites(timeInt);
602 //Advance AnimatedSprite
603 qreal animT = m_spriteEngine->spriteStart()/1000.0;
604 qreal frameCount = m_spriteEngine->spriteFrames();
605 qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
606 if (frameDuration > 0) {
607 qreal frame = (time - animT)/(frameDuration / 1000.0);
608 frame = qBound(qreal(0.0), frame, frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation
609 progress = modf(frame,&frameAt);
610 if (m_curFrame > frameAt) //went around
612 m_curFrame = frameAt;
615 if (m_curFrame >= frameCount){
618 m_spriteEngine->advance();
620 frameAt = m_curFrame;
623 if (m_loops > 0 && m_curLoop >= m_loops) {
628 frameAt = m_curFrame;
630 if (m_spriteEngine->sprite()->reverse())
631 frameAt = (m_spriteEngine->spriteFrames() - 1) - frameAt;
632 qreal y = m_spriteEngine->spriteY() / m_sheetSize.height();
633 qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width();
634 qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height();
635 qreal x1 = m_spriteEngine->spriteX() / m_sheetSize.width();
638 if (frameAt < (m_spriteEngine->spriteFrames()-1))
641 m_material->animX1 = x1;
642 m_material->animY1 = y;
643 m_material->animX2 = x2;
644 m_material->animY2 = y;
645 m_material->animW = w;
646 m_material->animH = h;
647 m_material->animT = m_interpolate ? progress : 0.0;