aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Alpert <alan.alpert@nokia.com>2012-02-14 09:33:42 +1000
committerQt by Nokia <qt-info@nokia.com>2012-02-28 09:19:37 +0100
commitf7dae3960b2ab6f5db3a79e3ea701f2531b909d7 (patch)
treea1ae29bf52e83c4f7e917125a5606ff457ed6765
parente38096931ba81bafe6d8737d6fc9737b77ab8723 (diff)
Add AnimatedSprite
A simpler sprite image element for the simple usecase. Because sometimes an engine with stochastic capabilities is overkill. Change-Id: I2b76c5d417719e92a548f6266bffd563dc016983 Reviewed-by: Alan Alpert <alan.alpert@nokia.com>
-rw-r--r--examples/qtquick/imageelements/animatedsprite.qml (renamed from examples/qtquick/imageelements/simplesprite.qml)29
-rw-r--r--src/quick/items/items.pri2
-rw-r--r--src/quick/items/qquickanimatedsprite.cpp613
-rw-r--r--src/quick/items/qquickanimatedsprite_p.h373
-rw-r--r--src/quick/items/qquickitemsmodule.cpp2
-rw-r--r--src/quick/items/qquicksprite_p.h1
-rw-r--r--tests/auto/qtquick2/qquickanimatedsprite/data/basic.qml58
-rw-r--r--tests/auto/qtquick2/qquickanimatedsprite/data/squarefacesprite.pngbin0 -> 496 bytes
-rw-r--r--tests/auto/qtquick2/qquickanimatedsprite/qquickanimatedsprite.pro15
-rw-r--r--tests/auto/qtquick2/qquickanimatedsprite/tst_qquickanimatedsprite.cpp83
-rw-r--r--tests/auto/qtquick2/qtquick2.pro1
11 files changed, 1170 insertions, 7 deletions
diff --git a/examples/qtquick/imageelements/simplesprite.qml b/examples/qtquick/imageelements/animatedsprite.qml
index f619913bfc..3a597bba71 100644
--- a/examples/qtquick/imageelements/simplesprite.qml
+++ b/examples/qtquick/imageelements/animatedsprite.qml
@@ -46,14 +46,29 @@ Item {
anchors.fill: parent
color: "white"
}
- SpriteImage {
+ AnimatedSprite {
+ id: sprite
anchors.fill: parent
- Sprite{
- source: "content/speaker.png"
- frames: 60
- frameSync: true
- frameWidth: 170
- frameHeight: 170
+ source: "content/speaker.png"
+ frameCount: 60
+ frameSync: true
+ frameWidth: 170
+ frameHeight: 170
+ loops: 3
+ }
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onClicked: {
+ if (!sprite.running)
+ sprite.start();
+ if (!sprite.paused)
+ sprite.pause();
+ if ( mouse.button == Qt.LeftButton ) {
+ sprite.advance(1);
+ } else {
+ sprite.advance(-1);
+ }
}
}
}
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index f02c769c3a..bdd1692e85 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -66,6 +66,7 @@ HEADERS += \
$$PWD/qquickspriteengine_p.h \
$$PWD/qquicksprite_p.h \
$$PWD/qquickspriteimage_p.h \
+ $$PWD/qquickanimatedsprite_p.h \
$$PWD/qquickdrag_p.h \
$$PWD/qquickdroparea_p.h \
$$PWD/qquickmultipointtoucharea_p.h \
@@ -117,6 +118,7 @@ SOURCES += \
$$PWD/qquickspriteengine.cpp \
$$PWD/qquicksprite.cpp \
$$PWD/qquickspriteimage.cpp \
+ $$PWD/qquickanimatedsprite.cpp \
$$PWD/qquickaccessibleattached.cpp \
$$PWD/qquickdrag.cpp \
$$PWD/qquickdroparea.cpp \
diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp
new file mode 100644
index 0000000000..ef79d052a8
--- /dev/null
+++ b/src/quick/items/qquickanimatedsprite.cpp
@@ -0,0 +1,613 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickanimatedsprite_p.h"
+#include "qquicksprite_p.h"
+#include "qquickspriteengine_p.h"
+#include <QtQuick/private/qsgcontext_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <QtQuick/qsgnode.h>
+#include <QtQuick/qsgengine.h>
+#include <QtQuick/qsgtexturematerial.h>
+#include <QtQuick/qsgtexture.h>
+#include <QtQuick/qquickcanvas.h>
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QFile>
+#include <cmath>
+#include <qmath.h>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+static const char vertexShaderCode[] =
+ "attribute highp vec2 vTex;\n"
+ "uniform highp vec3 animData;// w,h(premultiplied of anim), interpolation progress\n"
+ "uniform highp vec4 animPos;//x,y, x,y (two frames for interpolation)\n"
+ "uniform highp vec2 size;//w,h of element\n"
+ "\n"
+ "uniform highp mat4 qt_Matrix;\n"
+ "\n"
+ "varying highp vec4 fTexS;\n"
+ "varying lowp float progress;\n"
+ "\n"
+ "\n"
+ "void main() {\n"
+ " progress = animData.z;\n"
+ " //Calculate frame location in texture\n"
+ " fTexS.xy = animPos.xy + vTex.xy * animData.xy;\n"
+ " //Next frame is also passed, for interpolation\n"
+ " fTexS.zw = animPos.zw + vTex.xy * animData.xy;\n"
+ "\n"
+ " gl_Position = qt_Matrix * vec4(size.x * vTex.x, size.y * vTex.y, 0, 1);\n"
+ "}\n";
+
+static const char fragmentShaderCode[] =
+ "uniform sampler2D texture;\n"
+ "uniform lowp float qt_Opacity;\n"
+ "\n"
+ "varying highp vec4 fTexS;\n"
+ "varying lowp float progress;\n"
+ "\n"
+ "void main() {\n"
+ " gl_FragColor = mix(texture2D(texture, fTexS.xy), texture2D(texture, fTexS.zw), progress) * qt_Opacity;\n"
+ "}\n";
+
+class QQuickAnimatedSpriteMaterial : public QSGMaterial
+{
+public:
+ QQuickAnimatedSpriteMaterial();
+ ~QQuickAnimatedSpriteMaterial();
+ virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; }
+ virtual QSGMaterialShader *createShader() const;
+ virtual int compare(const QSGMaterial *other) const
+ {
+ return this - static_cast<const QQuickAnimatedSpriteMaterial *>(other);
+ }
+
+ QSGTexture *texture;
+
+ float animT;
+ float animX1;
+ float animY1;
+ float animX2;
+ float animY2;
+ float animW;
+ float animH;
+ float elementWidth;
+ float elementHeight;
+};
+
+QQuickAnimatedSpriteMaterial::QQuickAnimatedSpriteMaterial()
+ : texture(0)
+ , animT(0.0f)
+ , animX1(0.0f)
+ , animY1(0.0f)
+ , animX2(0.0f)
+ , animY2(0.0f)
+ , animW(1.0f)
+ , animH(1.0f)
+ , elementWidth(1.0f)
+ , elementHeight(1.0f)
+{
+ setFlag(Blending, true);
+}
+
+QQuickAnimatedSpriteMaterial::~QQuickAnimatedSpriteMaterial()
+{
+ delete texture;
+}
+
+class AnimatedSpriteMaterialData : public QSGMaterialShader
+{
+public:
+ AnimatedSpriteMaterialData(const char * /* vertexFile */ = 0, const char * /* fragmentFile */ = 0)
+ {
+ }
+
+ void deactivate() {
+ QSGMaterialShader::deactivate();
+
+ for (int i=0; i<8; ++i) {
+ program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
+ }
+ }
+
+ virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
+ {
+ QQuickAnimatedSpriteMaterial *m = static_cast<QQuickAnimatedSpriteMaterial *>(newEffect);
+ m->texture->bind();
+
+ program()->setUniformValue(m_opacity_id, state.opacity());
+ program()->setUniformValue(m_animData_id, m->animW, m->animH, m->animT);
+ program()->setUniformValue(m_animPos_id, m->animX1, m->animY1, m->animX2, m->animY2);
+ program()->setUniformValue(m_size_id, m->elementWidth, m->elementHeight);
+
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_matrix_id, state.combinedMatrix());
+ }
+
+ virtual void initialize() {
+ m_matrix_id = program()->uniformLocation("qt_Matrix");
+ m_opacity_id = program()->uniformLocation("qt_Opacity");
+ m_animData_id = program()->uniformLocation("animData");
+ m_animPos_id = program()->uniformLocation("animPos");
+ m_size_id = program()->uniformLocation("size");
+ }
+
+ virtual const char *vertexShader() const { return vertexShaderCode; }
+ virtual const char *fragmentShader() const { return fragmentShaderCode; }
+
+ virtual char const *const *attributeNames() const {
+ static const char *attr[] = {
+ "vTex",
+ 0
+ };
+ return attr;
+ }
+
+ int m_matrix_id;
+ int m_opacity_id;
+ int m_animData_id;
+ int m_animPos_id;
+ int m_size_id;
+
+ static float chunkOfBytes[1024];
+};
+
+float AnimatedSpriteMaterialData::chunkOfBytes[1024];
+
+QSGMaterialShader *QQuickAnimatedSpriteMaterial::createShader() const
+{
+ return new AnimatedSpriteMaterialData;
+}
+
+struct AnimatedSpriteVertex {
+ float tx;
+ float ty;
+};
+
+struct AnimatedSpriteVertices {
+ AnimatedSpriteVertex v1;
+ AnimatedSpriteVertex v2;
+ AnimatedSpriteVertex v3;
+ AnimatedSpriteVertex v4;
+};
+
+/*!
+ \qmlclass AnimatedSprite QQuickAnimatedSprite
+ \inqmlmodule QtQuick 2
+ \inherits Item
+ \brief The AnimatedSprite element draws a sprite animation
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::AnimatedSprite::running
+
+ Whether the sprite is animating or not.
+
+ Default is true
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::AnimatedSprite::interpolate
+
+ If true, interpolation will occur between sprite frames to make the
+ animation appear smoother.
+
+ Default is true.
+*/
+
+/*!
+ \qmlproperty qreal QtQuick2::AnimatedSprite::frameRate
+
+ Frames per second to show in the animation. Values below 0 are invalid.
+
+ If frameRate is valid then it will be used to calculate the duration of the frames.
+ If not, and frameDuration is valid , then frameDuration will be used.
+*/
+
+/*!
+ \qmlproperty int QtQuick2::AnimatedSprite::frameDuration
+
+ Duration of each frame of the animation. Values below 0 are invalid.
+
+ If frameRate is valid then it will be used to calculate the duration of the frames.
+ If not, and frameDuration is valid, then frameDuration will be used.
+*/
+
+/*!
+ \qmlproperty int QtQuick2::AnimatedSprite::frameCount
+
+ Number of frames in this AnimatedSprite.
+*/
+/*!
+ \qmlproperty int QtQuick2::AnimatedSprite::frameHeight
+
+ Height of a single frame in this AnimatedSprite.
+
+ May be omitted if it is the only sprite in the file.
+*/
+/*!
+ \qmlproperty int QtQuick2::AnimatedSprite::frameWidth
+
+ Width of a single frame in this AnimatedSprite.
+
+ May be omitted if it is the only sprite in the file.
+*/
+/*!
+ \qmlproperty int QtQuick2::AnimatedSprite::frameX
+
+ The X coordinate in the image file of the first frame of the AnimatedSprite.
+
+ May be omitted if the first frame starts in the upper left corner of the file.
+*/
+/*!
+ \qmlproperty int QtQuick2::AnimatedSprite::frameY
+
+ The Y coordinate in the image file of the first frame of the AnimatedSprite.
+
+ May be omitted if the first frame starts in the upper left corner of the file.
+*/
+/*!
+ \qmlproperty url QtQuick2::AnimatedSprite::source
+
+ The image source for the animation.
+
+ If frameHeight and frameWidth are not specified, it is assumed to be a single long row of square frames.
+ Otherwise, it can be multiple contiguous rows or rectangluar frames, when one row runs out the next will be used.
+
+ If frameX and frameY are specified, the row of frames will be taken with that x/y coordinate as the upper left corner.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::AnimatedSprite::reverse
+
+ If true, then the animation will be played in reverse.
+
+ Default is false.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::AnimatedSprite::frameSync
+
+ If true, then the animation will have no duration. Instead, the animation will advance
+ one frame each time a frame is rendered to the screen. This syncronizes it with the painting
+ rate as opposed to elapsed time.
+
+ If frameSync is set to true, it overrides both frameRate and frameDuration.
+
+ Default is false.
+*/
+
+/*!
+ \qmlproperty int QtQuick2::AnimatedSprite::loops
+
+ After playing the animation this many times, the animation will automatically stop. Negative values are invalid.
+
+ If this is set to AnimatedSprite.Infinite the animation will not stop playing on its own.
+
+ Default is AnimatedSprite.Infinite
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::AnimatedSprite::paused
+
+ When paused, the current frame can be advanced manually.
+
+ Default is false.
+*/
+
+/*!
+ \qmlproperty int QtQuick2::AnimatedSprite::currentFrame
+
+ When paused, the current frame can be advanced manually by setting this property or calling advance().
+
+*/
+
+//TODO: Implicitly size element to size of sprite
+QQuickAnimatedSprite::QQuickAnimatedSprite(QQuickItem *parent) :
+ QQuickItem(parent)
+ , m_node(0)
+ , m_material(0)
+ , m_sprite(new QQuickSprite)
+ , m_spriteEngine(0)
+ , m_curFrame(0)
+ , m_pleaseReset(false)
+ , m_running(true)
+ , m_paused(false)
+ , m_interpolate(true)
+ , m_loops(-1)
+ , m_curLoop(0)
+ , m_pauseOffset(0)
+{
+ setFlag(ItemHasContents);
+ connect(this, SIGNAL(runningChanged(bool)),
+ this, SLOT(update()));
+}
+
+void QQuickAnimatedSprite::reloadImage()
+{
+ if (!isComponentComplete())
+ return;
+ createEngine();//### It's not as inefficient as it sounds, but it still sucks having to recreate the engine
+}
+
+void QQuickAnimatedSprite::componentComplete()
+{
+ createEngine();
+ QQuickItem::componentComplete();
+ if (m_running)
+ start();
+}
+
+void QQuickAnimatedSprite::start()
+{
+ if (m_running)
+ return;
+ m_curLoop = 0;
+ m_timestamp.start();
+ m_running = true;
+ emit runningChanged(true);
+ update();
+}
+
+void QQuickAnimatedSprite::stop()
+{
+ if (!m_running)
+ return;
+ m_running = false;
+ emit runningChanged(false);
+}
+
+void QQuickAnimatedSprite::advance(int frames)
+{
+ if (!frames)
+ return;
+ //TODO-C: May not work when running - only when paused
+ m_curFrame += frames;
+ while (m_curFrame < 0)
+ m_curFrame += m_sprite->frames();
+ m_curFrame = m_curFrame % m_sprite->frames();
+ emit currentFrameChanged(m_curFrame);
+}
+
+void QQuickAnimatedSprite::pause()
+{
+ if (m_paused)
+ return;
+ m_pauseOffset = m_timestamp.elapsed();
+ m_paused = true;
+ emit pausedChanged(true);
+}
+
+void QQuickAnimatedSprite::resume()
+{
+ if (!m_paused)
+ return;
+ m_pauseOffset = m_pauseOffset - m_timestamp.elapsed();
+ m_paused = false;
+ emit pausedChanged(false);
+}
+
+void QQuickAnimatedSprite::createEngine()
+{
+ if (m_spriteEngine)
+ delete m_spriteEngine;
+ QList<QQuickSprite*> spriteList;
+ spriteList << m_sprite;
+ m_spriteEngine = new QQuickSpriteEngine(QList<QQuickSprite*>(spriteList), this);
+ m_spriteEngine->startAssemblingImage();
+ reset();
+}
+
+static QSGGeometry::Attribute AnimatedSprite_Attributes[] = {
+ QSGGeometry::Attribute::create(0, 2, GL_FLOAT), // tex
+};
+
+static QSGGeometry::AttributeSet AnimatedSprite_AttributeSet =
+{
+ 1, // Attribute Count
+ 2 * sizeof(float),
+ AnimatedSprite_Attributes
+};
+
+QSGGeometryNode* QQuickAnimatedSprite::buildNode()
+{
+ if (!m_spriteEngine) {
+ qmlInfo(this) << "No sprite engine...";
+ return 0;
+ } else if (m_spriteEngine->status() == QDeclarativePixmap::Null) {
+ m_spriteEngine->startAssemblingImage();
+ update();//Schedule another update, where we will check again
+ return 0;
+ } else if (m_spriteEngine->status() == QDeclarativePixmap::Loading) {
+ update();//Schedule another update, where we will check again
+ return 0;
+ }
+
+ m_material = new QQuickAnimatedSpriteMaterial();
+
+ QImage image = m_spriteEngine->assembledImage();
+ if (image.isNull())
+ return 0;
+ m_sheetSize = QSizeF(image.size());
+ m_material->texture = canvas()->createTextureFromImage(image);
+ m_material->texture->setFiltering(QSGTexture::Linear);
+ m_spriteEngine->start(0);
+ m_material->animT = 0;
+ m_material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width();
+ m_material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height();
+ m_material->animX2 = m_material->animX1;
+ m_material->animY2 = m_material->animY1;
+ m_material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width();
+ m_material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height();
+ m_material->elementWidth = width();
+ m_material->elementHeight = height();
+
+ int vCount = 4;
+ int iCount = 6;
+ QSGGeometry *g = new QSGGeometry(AnimatedSprite_AttributeSet, vCount, iCount);
+ g->setDrawingMode(GL_TRIANGLES);
+
+ AnimatedSpriteVertices *p = (AnimatedSpriteVertices *) g->vertexData();
+
+ p->v1.tx = 0;
+ p->v1.ty = 0;
+
+ p->v2.tx = 1.0;
+ p->v2.ty = 0;
+
+ p->v3.tx = 0;
+ p->v3.ty = 1.0;
+
+ p->v4.tx = 1.0;
+ p->v4.ty = 1.0;
+
+ quint16 *indices = g->indexDataAsUShort();
+ indices[0] = 0;
+ indices[1] = 1;
+ indices[2] = 2;
+ indices[3] = 1;
+ indices[4] = 3;
+ indices[5] = 2;
+
+
+ m_timestamp.start();
+ m_node = new QSGGeometryNode();
+ m_node->setGeometry(g);
+ m_node->setMaterial(m_material);
+ m_node->setFlag(QSGGeometryNode::OwnsMaterial);
+ return m_node;
+}
+
+void QQuickAnimatedSprite::reset()
+{
+ m_pleaseReset = true;
+}
+
+QSGNode *QQuickAnimatedSprite::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
+{
+ if (m_pleaseReset) {
+ delete m_node;
+
+ m_node = 0;
+ m_material = 0;
+ m_pleaseReset = false;
+ }
+
+ prepareNextFrame();
+
+ if (m_running) {
+ update();
+ if (m_node)
+ m_node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ return m_node;
+}
+
+void QQuickAnimatedSprite::prepareNextFrame()
+{
+ if (m_node == 0)
+ m_node = buildNode();
+ if (m_node == 0) //error creating node
+ return;
+
+ uint timeInt = m_timestamp.elapsed() + m_pauseOffset;
+ qreal time = timeInt / 1000.;
+ m_material->elementHeight = height();
+ m_material->elementWidth = width();
+
+ double frameAt; //double just for modf
+ qreal progress;
+ if (!m_paused) {
+ //Advance State (keeps time for psuedostates)
+ m_spriteEngine->updateSprites(timeInt);
+
+ //Advance AnimatedSprite
+ qreal animT = m_spriteEngine->spriteStart()/1000.0;
+ qreal frameCount = m_spriteEngine->spriteFrames();
+ qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
+ if (frameDuration > 0) {
+ qreal frame = (time - animT)/(frameDuration / 1000.0);
+ frame = qBound(qreal(0.0), frame, frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation
+ progress = modf(frame,&frameAt);
+ if (m_curFrame > frameAt) //went around
+ m_curLoop++;
+ m_curFrame = frameAt;
+ } else {
+ m_curFrame++;
+ if (m_curFrame >= frameCount){
+ m_curFrame = 0;
+ m_curLoop++;
+ m_spriteEngine->advance();
+ }
+ frameAt = m_curFrame;
+ progress = 0;
+ }
+ if (m_loops > 0 && m_curLoop >= m_loops) {
+ frameAt = 0;
+ m_running = false;
+ }
+ } else {
+ frameAt = m_curFrame;
+ }
+ if (m_spriteEngine->sprite()->reverse())
+ frameAt = (m_spriteEngine->spriteFrames() - 1) - frameAt;
+ qreal y = m_spriteEngine->spriteY() / m_sheetSize.height();
+ qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width();
+ qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height();
+ qreal x1 = m_spriteEngine->spriteX() / m_sheetSize.width();
+ x1 += frameAt * w;
+ qreal x2 = x1;
+ if (frameAt < (m_spriteEngine->spriteFrames()-1))
+ x2 += w;
+
+ m_material->animX1 = x1;
+ m_material->animY1 = y;
+ m_material->animX2 = x2;
+ m_material->animY2 = y;
+ m_material->animW = w;
+ m_material->animH = h;
+ m_material->animT = m_interpolate ? progress : 0.0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h
new file mode 100644
index 0000000000..062b191621
--- /dev/null
+++ b/src/quick/items/qquickanimatedsprite_p.h
@@ -0,0 +1,373 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKANIMATEDSPRITE_P_H
+#define QQUICKANIMATEDSPRITE_P_H
+
+#include <QtQuick/QQuickItem>
+#include <private/qquicksprite_p.h>
+#include <QTime>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QSGContext;
+class QQuickSprite;
+class QQuickSpriteEngine;
+class QSGGeometryNode;
+class QQuickAnimatedSpriteMaterial;
+class Q_AUTOTEST_EXPORT QQuickAnimatedSprite : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged)
+ Q_PROPERTY(bool interpolate READ interpolate WRITE setInterpolate NOTIFY interpolateChanged)
+ //###try to share similar spriteEngines for less overhead?
+ //These properties come out of QQuickSprite, since a SimpleSpriteImage is a renderer for a single sprite
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(bool reverse READ reverse WRITE setReverse NOTIFY reverseChanged)
+ Q_PROPERTY(bool frameSync READ frameSync WRITE setFrameSync NOTIFY frameSyncChanged)
+ Q_PROPERTY(int frameCount READ frameCount WRITE setFrameCount NOTIFY frameCountChanged)
+ //If frame height or width is not specified, it is assumed to be a single long row of square frames.
+ //Otherwise, it can be multiple contiguous rows, when one row runs out the next will be used.
+ Q_PROPERTY(int frameHeight READ frameHeight WRITE setFrameHeight NOTIFY frameHeightChanged)
+ Q_PROPERTY(int frameWidth READ frameWidth WRITE setFrameWidth NOTIFY frameWidthChanged)
+ Q_PROPERTY(int frameX READ frameX WRITE setFrameX NOTIFY frameXChanged)
+ Q_PROPERTY(int frameY READ frameY WRITE setFrameY NOTIFY frameYChanged)
+ //Precedence order: frameRate, frameDuration
+ Q_PROPERTY(qreal frameRate READ frameRate WRITE setFrameRate NOTIFY frameRateChanged RESET resetFrameRate)
+ Q_PROPERTY(int frameDuration READ frameDuration WRITE setFrameDuration NOTIFY frameDurationChanged RESET resetFrameDuration)
+ //Extra Simple Sprite Stuff
+ Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopsChanged)
+ Q_PROPERTY(bool paused READ paused WRITE setPaused NOTIFY pausedChanged)
+ Q_PROPERTY(int currentFrame READ currentFrame WRITE setCurrentFrame NOTIFY currentFrameChanged)
+
+ Q_ENUMS(LoopParameters)
+public:
+ explicit QQuickAnimatedSprite(QQuickItem *parent = 0);
+ enum LoopParameters {
+ Infinite = -1
+ };
+
+ bool running() const
+ {
+ return m_running;
+ }
+
+ bool interpolate() const
+ {
+ return m_interpolate;
+ }
+
+ QUrl source() const
+ {
+ return m_sprite->source();
+ }
+
+ bool reverse() const
+ {
+ return m_sprite->reverse();
+ }
+
+ bool frameSync() const
+ {
+ return m_sprite->frameSync();
+ }
+
+ int frameCount() const
+ {
+ return m_sprite->frames();
+ }
+
+ int frameHeight() const
+ {
+ return m_sprite->frameHeight();
+ }
+
+ int frameWidth() const
+ {
+ return m_sprite->frameWidth();
+ }
+
+ int frameX() const
+ {
+ return m_sprite->frameX();
+ }
+
+ int frameY() const
+ {
+ return m_sprite->frameY();
+ }
+
+ qreal frameRate() const
+ {
+ return m_sprite->frameRate();
+ }
+
+ int frameDuration() const
+ {
+ return m_sprite->frameDuration();
+ }
+
+ int loops() const
+ {
+ return m_loops;
+ }
+
+ bool paused() const
+ {
+ return m_paused;
+ }
+
+ int currentFrame() const
+ {
+ return m_curFrame;
+ }
+
+signals:
+
+ void pausedChanged(bool arg);
+ void runningChanged(bool arg);
+ void interpolateChanged(bool arg);
+
+ void sourceChanged(QUrl arg);
+
+ void reverseChanged(bool arg);
+
+ void frameSyncChanged(bool arg);
+
+ void frameCountChanged(int arg);
+
+ void frameHeightChanged(int arg);
+
+ void frameWidthChanged(int arg);
+
+ void frameXChanged(int arg);
+
+ void frameYChanged(int arg);
+
+ void frameRateChanged(qreal arg);
+
+ void frameDurationChanged(int arg);
+
+ void loopsChanged(int arg);
+
+ void currentFrameChanged(int arg);
+
+public slots:
+ void start();
+ void stop();
+ void restart() {stop(); start();}
+ void advance(int frames=1);
+ void pause();
+ void resume();
+
+ void setRunning(bool arg)
+ {
+ if (m_running != arg) {
+ if (m_running)
+ stop();
+ else
+ start();
+ }
+ }
+
+ void setPaused(bool arg)
+ {
+ if (m_paused != arg) {
+ if (m_paused)
+ resume();
+ else
+ pause();
+ }
+ }
+
+ void setInterpolate(bool arg)
+ {
+ if (m_interpolate != arg) {
+ m_interpolate = arg;
+ emit interpolateChanged(arg);
+ }
+ }
+
+ void setSource(QUrl arg)
+ {
+ if (m_sprite->m_source != arg) {
+ m_sprite->setSource(arg);
+ emit sourceChanged(arg);
+ }
+ }
+
+ void setReverse(bool arg)
+ {
+ if (m_sprite->m_reverse != arg) {
+ m_sprite->setReverse(arg);
+ emit reverseChanged(arg);
+ }
+ }
+
+ void setFrameSync(bool arg)
+ {
+ if (m_sprite->m_frameSync != arg) {
+ m_sprite->setFrameSync(arg);
+ emit frameSyncChanged(arg);
+ }
+ }
+
+ void setFrameCount(int arg)
+ {
+ if (m_sprite->m_frames != arg) {
+ m_sprite->setFrameCount(arg);
+ emit frameCountChanged(arg);
+ reloadImage();
+ }
+ }
+
+ void setFrameHeight(int arg)
+ {
+ if (m_sprite->m_frameHeight != arg) {
+ m_sprite->setFrameHeight(arg);
+ emit frameHeightChanged(arg);
+ reloadImage();
+ }
+ }
+
+ void setFrameWidth(int arg)
+ {
+ if (m_sprite->m_frameWidth != arg) {
+ m_sprite->setFrameWidth(arg);
+ emit frameWidthChanged(arg);
+ reloadImage();
+ }
+ }
+
+ void setFrameX(int arg)
+ {
+ if (m_sprite->m_frameX != arg) {
+ m_sprite->setFrameX(arg);
+ emit frameXChanged(arg);
+ reloadImage();
+ }
+ }
+
+ void setFrameY(int arg)
+ {
+ if (m_sprite->m_frameY != arg) {
+ m_sprite->setFrameY(arg);
+ emit frameYChanged(arg);
+ reloadImage();
+ }
+ }
+
+ void setFrameRate(qreal arg)
+ {
+ if (m_sprite->m_frameRate != arg) {
+ m_sprite->setFrameRate(arg);
+ emit frameRateChanged(arg);
+ }
+ }
+
+ void setFrameDuration(int arg)
+ {
+ if (m_sprite->m_frameDuration != arg) {
+ m_sprite->setFrameDuration(arg);
+ emit frameDurationChanged(arg);
+ }
+ }
+
+ void resetFrameRate()
+ {
+ setFrameRate(-1.0);
+ }
+
+ void resetFrameDuration()
+ {
+ setFrameDuration(-1);
+ }
+
+ void setLoops(int arg)
+ {
+ if (m_loops != arg) {
+ m_loops = arg;
+ emit loopsChanged(arg);
+ }
+ }
+
+ void setCurrentFrame(int arg) //TODO-C: Probably only works when paused
+ {
+ if (m_curFrame != arg) {
+ m_curFrame = arg;
+ emit currentFrameChanged(arg); //TODO-C Only emitted on manual advance!
+ }
+ }
+
+
+private slots:
+ void createEngine();
+protected:
+ void reset();
+ void componentComplete();
+ QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+private:
+ void prepareNextFrame();
+ void reloadImage();
+ QSGGeometryNode* buildNode();
+ QSGGeometryNode *m_node;
+ QQuickAnimatedSpriteMaterial *m_material;
+ QQuickSprite* m_sprite;
+ QQuickSpriteEngine* m_spriteEngine;
+ QTime m_timestamp;
+ int m_curFrame;
+ bool m_pleaseReset;
+ bool m_running;
+ bool m_paused;
+ bool m_interpolate;
+ QSizeF m_sheetSize;
+ int m_loops;
+ int m_curLoop;
+ int m_pauseOffset;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QQUICKANIMATEDSPRITE_P_H
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index a4cfa26205..2c74e3c91c 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -78,6 +78,7 @@
#include <QtQuick/private/qquickcontext2d_p.h>
#include "qquicksprite_p.h"
#include "qquickspriteimage_p.h"
+#include "qquickanimatedsprite_p.h"
#include "qquickdrag_p.h"
#include "qquickdroparea_p.h"
#include "qquickmultipointtoucharea_p.h"
@@ -200,6 +201,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickCanvasItem>("QtQuick", 2, 0, "Canvas");
qmlRegisterType<QQuickSprite>("QtQuick", 2, 0, "Sprite");
+ qmlRegisterType<QQuickAnimatedSprite>("QtQuick", 2, 0, "AnimatedSprite");
qmlRegisterType<QQuickSpriteImage>("QtQuick", 2, 0, "SpriteImage");
qmlRegisterType<QQuickParentChange>(uri, major, minor,"ParentChange");
diff --git a/src/quick/items/qquicksprite_p.h b/src/quick/items/qquicksprite_p.h
index 4c5e5ff58e..98cc90ad8c 100644
--- a/src/quick/items/qquicksprite_p.h
+++ b/src/quick/items/qquicksprite_p.h
@@ -279,6 +279,7 @@ private slots:
private:
friend class QQuickImageParticle;
friend class QQuickSpriteImage;
+ friend class QQuickAnimatedSprite;
friend class QQuickSpriteEngine;
friend class QQuickStochasticEngine;
int m_generatedCount;
diff --git a/tests/auto/qtquick2/qquickanimatedsprite/data/basic.qml b/tests/auto/qtquick2/qquickanimatedsprite/data/basic.qml
new file mode 100644
index 0000000000..f219e5fb81
--- /dev/null
+++ b/tests/auto/qtquick2/qquickanimatedsprite/data/basic.qml
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Rectangle {
+ color: "black"
+ width: 320
+ height: 320
+
+ AnimatedSprite {
+ objectName: "sprite"
+ loops: 3
+ source: "squarefacesprite.png"
+ frames: 6
+ frameDuration: 120
+ width: 160
+ height: 160
+ }
+}
diff --git a/tests/auto/qtquick2/qquickanimatedsprite/data/squarefacesprite.png b/tests/auto/qtquick2/qquickanimatedsprite/data/squarefacesprite.png
new file mode 100644
index 0000000000..f9a5d5fcce
--- /dev/null
+++ b/tests/auto/qtquick2/qquickanimatedsprite/data/squarefacesprite.png
Binary files differ
diff --git a/tests/auto/qtquick2/qquickanimatedsprite/qquickanimatedsprite.pro b/tests/auto/qtquick2/qquickanimatedsprite/qquickanimatedsprite.pro
new file mode 100644
index 0000000000..aad73d538d
--- /dev/null
+++ b/tests/auto/qtquick2/qquickanimatedsprite/qquickanimatedsprite.pro
@@ -0,0 +1,15 @@
+CONFIG += testcase
+TARGET = tst_qquickanimatedsprite
+SOURCES += tst_qquickanimatedsprite.cpp
+
+include (../../shared/util.pri)
+
+macx:CONFIG -= app_bundle
+
+testDataFiles.files = data
+testDataFiles.path = .
+DEPLOYMENT += testDataFiles
+
+CONFIG += parallel_test
+
+QT += core-private gui-private declarative-private quick-private network testlib
diff --git a/tests/auto/qtquick2/qquickanimatedsprite/tst_qquickanimatedsprite.cpp b/tests/auto/qtquick2/qquickanimatedsprite/tst_qquickanimatedsprite.cpp
new file mode 100644
index 0000000000..3cc8f129fd
--- /dev/null
+++ b/tests/auto/qtquick2/qquickanimatedsprite/tst_qquickanimatedsprite.cpp
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include "../../shared/util.h"
+#include <QtQuick/qquickview.h>
+#include <private/qquickanimatedsprite_p.h>
+
+class tst_qquickanimatedsprite : public QDeclarativeDataTest
+{
+ Q_OBJECT
+public:
+ tst_qquickanimatedsprite(){}
+
+private slots:
+ void test_properties();
+};
+
+void tst_qquickanimatedsprite::test_properties()
+{
+ QQuickView *canvas = new QQuickView(0);
+
+ canvas->setSource(testFileUrl("basic.qml"));
+ canvas->show();
+ QTest::qWaitForWindowShown(canvas);
+
+ QVERIFY(canvas->rootObject());
+ QQuickAnimatedSprite* sprite = canvas->rootObject()->findChild<QQuickAnimatedSprite*>("sprite");
+ QVERIFY(sprite);
+
+ QVERIFY(sprite->running());
+ QVERIFY(!sprite->paused());
+ QVERIFY(sprite->interpolate());
+ QCOMPARE(sprite->loops(), 3);
+
+ sprite->setRunning(false);
+ QVERIFY(!sprite->running());
+ sprite->setInterpolate(false);
+ QVERIFY(!sprite->interpolate());
+
+ delete canvas;
+}
+
+QTEST_MAIN(tst_qquickanimatedsprite)
+
+#include "tst_qquickanimatedsprite.moc"
diff --git a/tests/auto/qtquick2/qtquick2.pro b/tests/auto/qtquick2/qtquick2.pro
index 7acd75f69e..60e302759e 100644
--- a/tests/auto/qtquick2/qtquick2.pro
+++ b/tests/auto/qtquick2/qtquick2.pro
@@ -31,6 +31,7 @@ QUICKTESTS = \
qquickaccessible \
qquickanchors \
qquickanimatedimage \
+ qquickanimatedsprite \
qquickborderimage \
qquickcanvas \
qquickdrag \