Add AnimatedSprite
authorAlan Alpert <alan.alpert@nokia.com>
Mon, 13 Feb 2012 23:33:42 +0000 (09:33 +1000)
committerQt by Nokia <qt-info@nokia.com>
Tue, 28 Feb 2012 08:19:37 +0000 (09:19 +0100)
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>
examples/qtquick/imageelements/animatedsprite.qml [moved from examples/qtquick/imageelements/simplesprite.qml with 78% similarity]
src/quick/items/items.pri
src/quick/items/qquickanimatedsprite.cpp [new file with mode: 0644]
src/quick/items/qquickanimatedsprite_p.h [new file with mode: 0644]
src/quick/items/qquickitemsmodule.cpp
src/quick/items/qquicksprite_p.h
tests/auto/qtquick2/qquickanimatedsprite/data/basic.qml [new file with mode: 0644]
tests/auto/qtquick2/qquickanimatedsprite/data/squarefacesprite.png [new file with mode: 0644]
tests/auto/qtquick2/qquickanimatedsprite/qquickanimatedsprite.pro [new file with mode: 0644]
tests/auto/qtquick2/qquickanimatedsprite/tst_qquickanimatedsprite.cpp [new file with mode: 0644]
tests/auto/qtquick2/qtquick2.pro

@@ -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);
+            }
         }
     }
 }
index f02c769..bdd1692 100644 (file)
@@ -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 (file)
index 0000000..ef79d05
--- /dev/null
@@ -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 (file)
index 0000000..062b191
--- /dev/null
@@ -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
index a4cfa26..2c74e3c 100644 (file)
@@ -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");
index 4c5e5ff..98cc90a 100644 (file)
@@ -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 (file)
index 0000000..f219e5f
--- /dev/null
@@ -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 (file)
index 0000000..f9a5d5f
Binary files /dev/null and b/tests/auto/qtquick2/qquickanimatedsprite/data/squarefacesprite.png differ
diff --git a/tests/auto/qtquick2/qquickanimatedsprite/qquickanimatedsprite.pro b/tests/auto/qtquick2/qquickanimatedsprite/qquickanimatedsprite.pro
new file mode 100644 (file)
index 0000000..aad73d5
--- /dev/null
@@ -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 (file)
index 0000000..3cc8f12
--- /dev/null
@@ -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"
index 7acd75f..60e3027 100644 (file)
@@ -31,6 +31,7 @@ QUICKTESTS =  \
     qquickaccessible \
     qquickanchors \
     qquickanimatedimage \
+    qquickanimatedsprite \
     qquickborderimage \
     qquickcanvas \
     qquickdrag \