aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickspriteimage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquickspriteimage.cpp')
-rw-r--r--src/quick/items/qquickspriteimage.cpp455
1 files changed, 455 insertions, 0 deletions
diff --git a/src/quick/items/qquickspriteimage.cpp b/src/quick/items/qquickspriteimage.cpp
new file mode 100644
index 0000000000..1da46f2ae8
--- /dev/null
+++ b/src/quick/items/qquickspriteimage.cpp
@@ -0,0 +1,455 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** 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 "qquickspriteimage_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 <QFile>
+#include <cmath>
+#include <qmath.h>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+static const char vertexShaderCode[] =
+ "attribute highp vec2 vTex;\n"
+ "uniform highp vec4 animData;// interpolate(bool), duration, frameCount (this anim), timestamp (this anim)\n"
+ "uniform highp vec4 animPos;//sheet x,y, width/height of this anim\n"
+ "uniform highp vec4 animSheetSize; //width/height of whole sheet, width/height of element\n"
+ "\n"
+ "uniform highp mat4 qt_Matrix;\n"
+ "uniform highp float timestamp;\n"
+ "\n"
+ "varying highp vec4 fTexS;\n"
+ "varying lowp float progress;\n"
+ "\n"
+ "\n"
+ "void main() {\n"
+ " //Calculate frame location in texture\n"
+ " highp float frameIndex = mod((((timestamp - animData.w)*1000.)/animData.y),animData.z);\n"
+ " progress = mod((timestamp - animData.w)*1000., animData.y) / animData.y;\n"
+ "\n"
+ " frameIndex = floor(frameIndex);\n"
+ " fTexS.xy = vec2(((frameIndex + vTex.x) * animPos.z / animSheetSize.x), ((animPos.y + vTex.y * animPos.w) / animSheetSize.y));\n"
+ "\n"
+ " //Next frame is also passed, for interpolation\n"
+ " //### Should the next anim be precalculated to allow for interpolation there?\n"
+ " if (animData.x == 1.0 && frameIndex != animData.z - 1.)//Can't do it for the last frame though, this anim may not loop\n"
+ " frameIndex = mod(frameIndex+1., animData.z);\n"
+ " fTexS.zw = vec2(((frameIndex + vTex.x) * animPos.z / animSheetSize.x), ((animPos.y + vTex.y * animPos.w) / animSheetSize.y));\n"
+ "\n"
+ " gl_Position = qt_Matrix * vec4(animSheetSize.z * vTex.x, animSheetSize.w * 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 QQuickSpriteMaterial : public QSGMaterial
+{
+public:
+ QQuickSpriteMaterial();
+ virtual ~QQuickSpriteMaterial();
+ 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 QQuickSpriteMaterial *>(other);
+ }
+
+ QSGTexture *texture;
+
+ qreal timestamp;
+ float interpolate;
+ float frameDuration;
+ float frameCount;
+ float animT;
+ float animX;
+ float animY;
+ float animWidth;
+ float animHeight;
+ float sheetWidth;
+ float sheetHeight;
+ float elementWidth;
+ float elementHeight;
+};
+
+QQuickSpriteMaterial::QQuickSpriteMaterial()
+ : timestamp(0)
+ , interpolate(1.0f)
+ , frameDuration(1.0f)
+ , frameCount(1.0f)
+ , animT(0.0f)
+ , animX(0.0f)
+ , animY(0.0f)
+ , animWidth(1.0f)
+ , animHeight(1.0f)
+ , sheetWidth(1.0f)
+ , sheetHeight(1.0f)
+ , elementWidth(1.0f)
+ , elementHeight(1.0f)
+{
+ setFlag(Blending, true);
+}
+
+QQuickSpriteMaterial::~QQuickSpriteMaterial()
+{
+ delete texture;
+}
+
+class SpriteMaterialData : public QSGMaterialShader
+{
+public:
+ SpriteMaterialData(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 *)
+ {
+ QQuickSpriteMaterial *m = static_cast<QQuickSpriteMaterial *>(newEffect);
+ m->texture->bind();
+
+ program()->setUniformValue(m_opacity_id, state.opacity());
+ program()->setUniformValue(m_timestamp_id, (float) m->timestamp);
+ program()->setUniformValue(m_animData_id, m->interpolate, m->frameDuration, m->frameCount, m->animT);
+ program()->setUniformValue(m_animPos_id, m->animX, m->animY, m->animWidth, m->animHeight);
+ program()->setUniformValue(m_animSheetSize_id, m->sheetWidth, m->sheetHeight, 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_timestamp_id = program()->uniformLocation("timestamp");
+ m_animData_id = program()->uniformLocation("animData");
+ m_animPos_id = program()->uniformLocation("animPos");
+ m_animSheetSize_id = program()->uniformLocation("animSheetSize");
+ }
+
+ 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_timestamp_id;
+ int m_animData_id;
+ int m_animPos_id;
+ int m_animSheetSize_id;
+
+ static float chunkOfBytes[1024];
+};
+
+float SpriteMaterialData::chunkOfBytes[1024];
+
+QSGMaterialShader *QQuickSpriteMaterial::createShader() const
+{
+ return new SpriteMaterialData;
+}
+
+struct SpriteVertex {
+ float tx;
+ float ty;
+};
+
+struct SpriteVertices {
+ SpriteVertex v1;
+ SpriteVertex v2;
+ SpriteVertex v3;
+ SpriteVertex v4;
+};
+
+/*!
+ \qmlclass SpriteImage QQuickSpriteImage
+ \inqmlmodule QtQuick 2
+ \inherits Item
+ \brief The SpriteImage element draws a sprite animation
+
+*/
+/*!
+ \qmlproperty bool QtQuick2::SpriteImage::running
+
+ Whether the sprite is animating or not.
+
+ Default is true
+*/
+/*!
+ \qmlproperty bool QtQuick2::SpriteImage::interpolate
+
+ If true, interpolation will occur between sprite frames to make the
+ animation appear smoother.
+
+ Default is true.
+*/
+/*!
+ \qmlproperty string QtQuick2::SpriteImage::goalState
+
+ The name of the Sprite which the animation should move to.
+
+ Sprite states have defined durations and transitions between them, setting goalState
+ will cause it to disregard any path weightings (including 0) and head down the path
+ which will reach the goalState quickest (fewest animations). It will pass through
+ intermediate states on that path, and animate them for their duration.
+
+ If it is possible to return to the goalState from the starting point of the goalState
+ it will continue to do so until goalState is set to "" or an unreachable state.
+*/
+/*! \qmlmethod void QtQuick2::SpriteImage::jumpTo(string sprite)
+
+ This function causes the sprite to jump to the specified state immediately, intermediate
+ states are not played.
+*/
+/*!
+ \qmlproperty list<Sprite> QtQuick2::SpriteImage::sprites
+
+ The sprite or sprites to draw. Sprites will be scaled to the size of this element.
+*/
+
+//TODO: Implicitly size element to size of first sprite?
+QQuickSpriteImage::QQuickSpriteImage(QQuickItem *parent) :
+ QQuickItem(parent)
+ , m_node(0)
+ , m_material(0)
+ , m_spriteEngine(0)
+ , m_pleaseReset(false)
+ , m_running(true)
+ , m_interpolate(true)
+{
+ setFlag(ItemHasContents);
+ connect(this, SIGNAL(runningChanged(bool)),
+ this, SLOT(update()));
+}
+
+void QQuickSpriteImage::jumpTo(const QString &sprite)
+{
+ if (!m_spriteEngine)
+ return;
+ m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite), 0, true);
+}
+
+void QQuickSpriteImage::setGoalState(const QString &sprite)
+{
+ if (m_goalState != sprite){
+ m_goalState = sprite;
+ emit goalStateChanged(sprite);
+ m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite));
+ }
+}
+
+QDeclarativeListProperty<QQuickSprite> QQuickSpriteImage::sprites()
+{
+ return QDeclarativeListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
+}
+
+void QQuickSpriteImage::createEngine()
+{
+ //TODO: delay until component complete
+ if (m_spriteEngine)
+ delete m_spriteEngine;
+ if (m_sprites.count())
+ m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
+ else
+ m_spriteEngine = 0;
+ reset();
+}
+
+static QSGGeometry::Attribute SpriteImage_Attributes[] = {
+ QSGGeometry::Attribute::create(0, 2, GL_FLOAT), // tex
+};
+
+static QSGGeometry::AttributeSet SpriteImage_AttributeSet =
+{
+ 1, // Attribute Count
+ 2 * sizeof(float),
+ SpriteImage_Attributes
+};
+
+QSGGeometryNode* QQuickSpriteImage::buildNode()
+{
+ if (!m_spriteEngine) {
+ qWarning() << "SpriteImage: No sprite engine...";
+ return 0;
+ }
+
+ m_material = new QQuickSpriteMaterial();
+
+ QImage image = m_spriteEngine->assembledImage();
+ if (image.isNull())
+ return 0;
+ m_material->texture = canvas()->createTextureFromImage(image);
+ m_material->texture->setFiltering(QSGTexture::Linear);
+ m_spriteEngine->start(0);
+ m_material->interpolate = m_interpolate ? 1.0 : 0.0;
+ m_material->frameCount = m_spriteEngine->spriteFrames();
+ m_material->frameDuration = m_spriteEngine->spriteDuration();
+ m_material->animT = 0;
+ m_material->animX = m_spriteEngine->spriteX();
+ m_material->animY = m_spriteEngine->spriteY();
+ m_material->animWidth = m_spriteEngine->spriteWidth();
+ m_material->animHeight = m_spriteEngine->spriteHeight();
+ m_material->sheetWidth = image.width();
+ m_material->sheetHeight = image.height();
+ m_material->elementWidth = width();
+ m_material->elementHeight = height();
+
+ int vCount = 4;
+ int iCount = 6;
+ QSGGeometry *g = new QSGGeometry(SpriteImage_AttributeSet, vCount, iCount);
+ g->setDrawingMode(GL_TRIANGLES);
+
+ SpriteVertices *p = (SpriteVertices *) 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 QQuickSpriteImage::reset()
+{
+ m_pleaseReset = true;
+}
+
+QSGNode *QQuickSpriteImage::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
+{
+ if (m_pleaseReset) {
+ delete m_node;
+ delete m_material;
+
+ 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 QQuickSpriteImage::prepareNextFrame()
+{
+ if (m_node == 0)
+ m_node = buildNode();
+ if (m_node == 0) //error creating node
+ return;
+
+ uint timeInt = m_timestamp.elapsed();
+ qreal time = timeInt / 1000.;
+ m_material->timestamp = time;
+ m_material->elementHeight = height();
+ m_material->elementWidth = width();
+ m_material->interpolate = m_interpolate;
+
+ //Advance State
+ m_spriteEngine->updateSprites(timeInt);
+ int curY = m_spriteEngine->spriteY();
+ if (curY != m_material->animY){
+ m_material->animT = m_spriteEngine->spriteStart()/1000.0;
+ m_material->frameCount = m_spriteEngine->spriteFrames();
+ m_material->frameDuration = m_spriteEngine->spriteDuration();
+ m_material->animX = m_spriteEngine->spriteX();
+ m_material->animY = m_spriteEngine->spriteY();
+ m_material->animWidth = m_spriteEngine->spriteWidth();
+ m_material->animHeight = m_spriteEngine->spriteHeight();
+ }
+}
+
+QT_END_NAMESPACE