1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickspritesequence_p.h"
41#include "qquickspritesequence_p_p.h"
42#include "qquicksprite_p.h"
43#include "qquickspriteengine_p.h"
44#include <QtQuick/private/qsgcontext_p.h>
45#include <private/qsgadaptationlayer_p.h>
46#include <QtQuick/qsgnode.h>
47#include <QtQuick/qsgtexturematerial.h>
48#include <QtQuick/qsgtexture.h>
49#include <QtQuick/qquickwindow.h>
50#include <QtQml/qqmlinfo.h>
51#include <QFile>
52#include <cmath>
53#include <qmath.h>
54#include <QDebug>
55
56QT_BEGIN_NAMESPACE
57
58/*!
59 \qmltype SpriteSequence
60 \instantiates QQuickSpriteSequence
61 \inqmlmodule QtQuick
62 \ingroup qtquick-visual-utility
63 \inherits Item
64 \brief Draws a sprite animation.
65
66 SpriteSequence renders and controls a list of animations defined
67 by \l Sprite types.
68
69 For full details, see the \l{Sprite Animations} overview.
70 \sa {Sprite animations with SpriteSequence}
71*/
72/*!
73 \qmlproperty bool QtQuick::SpriteSequence::running
74
75 Whether the sprite is animating or not.
76
77 Default is \c true.
78*/
79/*!
80 \qmlproperty bool QtQuick::SpriteSequence::interpolate
81
82 If \c true, interpolation will occur between sprite frames to make the
83 animation appear smoother.
84
85 Default is \c true.
86*/
87/*!
88 \qmlproperty string QtQuick::SpriteSequence::currentSprite
89
90 The name of the \l Sprite that is currently animating.
91*/
92/*!
93 \qmlproperty string QtQuick::SpriteSequence::goalSprite
94
95 The name of the \l Sprite that the animation should move to.
96
97 Sprite states have defined durations and transitions between them; setting \c goalSprite
98 will cause it to disregard any path weightings (including \c 0) and head down the path
99 that will reach the \c goalSprite quickest (fewest animations). It will pass through
100 intermediate states on that path, and animate them for their duration.
101
102 If it is possible to return to the \c goalSprite from the starting point of the \c goalSprite,
103 it will continue to do so until \c goalSprite is set to \c "" or an unreachable state.
104*/
105/*! \qmlmethod QtQuick::SpriteSequence::jumpTo(string sprite)
106
107 This function causes the SpriteSequence to jump to the specified \a sprite immediately;
108 intermediate sprites are not played.
109*/
110/*!
111 \qmlproperty list<Sprite> QtQuick::SpriteSequence::sprites
112
113 The sprite or sprites to draw. Sprites will be scaled to the size of this item.
114*/
115
116//TODO: Implicitly size element to size of first sprite?
117QQuickSpriteSequence::QQuickSpriteSequence(QQuickItem *parent) :
118 QQuickItem(*(new QQuickSpriteSequencePrivate), parent)
119{
120 setFlag(flag: ItemHasContents);
121 connect(sender: this, SIGNAL(runningChanged(bool)),
122 receiver: this, SLOT(update()));
123}
124
125void QQuickSpriteSequence::jumpTo(const QString &sprite)
126{
127 Q_D(QQuickSpriteSequence);
128 if (!d->m_spriteEngine)
129 return;
130 d->m_spriteEngine->setGoal(state: d->m_spriteEngine->stateIndex(s: sprite), sprite: 0, jump: true);
131}
132
133void QQuickSpriteSequence::setGoalSprite(const QString &sprite)
134{
135 Q_D(QQuickSpriteSequence);
136 if (d->m_goalState != sprite){
137 d->m_goalState = sprite;
138 emit goalSpriteChanged(arg: sprite);
139 if (d->m_spriteEngine)
140 d->m_spriteEngine->setGoal(state: d->m_spriteEngine->stateIndex(s: sprite));
141 }
142}
143
144void QQuickSpriteSequence::setRunning(bool arg)
145{
146 Q_D(QQuickSpriteSequence);
147 if (d->m_running != arg) {
148 d->m_running = arg;
149 Q_EMIT runningChanged(arg);
150 }
151}
152
153void QQuickSpriteSequence::setInterpolate(bool arg)
154{
155 Q_D(QQuickSpriteSequence);
156 if (d->m_interpolate != arg) {
157 d->m_interpolate = arg;
158 Q_EMIT interpolateChanged(arg);
159 }
160}
161
162QQmlListProperty<QQuickSprite> QQuickSpriteSequence::sprites()
163{
164 Q_D(QQuickSpriteSequence);
165 return QQmlListProperty<QQuickSprite>(this, &d->m_sprites,
166 spriteAppend, spriteCount, spriteAt,
167 spriteClear, spriteReplace, spriteRemoveLast);
168}
169
170bool QQuickSpriteSequence::running() const
171{
172 Q_D(const QQuickSpriteSequence);
173 return d->m_running;
174}
175
176bool QQuickSpriteSequence::interpolate() const
177{
178 Q_D(const QQuickSpriteSequence);
179 return d->m_interpolate;
180}
181
182QString QQuickSpriteSequence::goalSprite() const
183{
184 Q_D(const QQuickSpriteSequence);
185 return d->m_goalState;
186}
187
188QString QQuickSpriteSequence::currentSprite() const
189{
190 Q_D(const QQuickSpriteSequence);
191 return d->m_curState;
192}
193
194void QQuickSpriteSequence::createEngine()
195{
196 Q_D(QQuickSpriteSequence);
197 //TODO: delay until component complete
198 if (d->m_spriteEngine)
199 delete d->m_spriteEngine;
200 if (d->m_sprites.count()) {
201 d->m_spriteEngine = new QQuickSpriteEngine(d->m_sprites, this);
202 if (!d->m_goalState.isEmpty())
203 d->m_spriteEngine->setGoal(state: d->m_spriteEngine->stateIndex(s: d->m_goalState));
204 } else {
205 d->m_spriteEngine = nullptr;
206 }
207 reset();
208}
209
210QSGSpriteNode *QQuickSpriteSequence::initNode()
211{
212 Q_D(QQuickSpriteSequence);
213
214 if (!d->m_spriteEngine) {
215 qmlWarning(me: this) << "No sprite engine...";
216 return nullptr;
217 } else if (d->m_spriteEngine->status() == QQuickPixmap::Null) {
218 d->m_spriteEngine->startAssemblingImage();
219 update();//Schedule another update, where we will check again
220 return nullptr;
221 } else if (d->m_spriteEngine->status() == QQuickPixmap::Loading) {
222 update();//Schedule another update, where we will check again
223 return nullptr;
224 }
225
226 QImage image = d->m_spriteEngine->assembledImage(maxSize: d->sceneGraphRenderContext()->maxTextureSize());
227 if (image.isNull())
228 return nullptr;
229
230 QSGSpriteNode *node = d->sceneGraphContext()->createSpriteNode();
231
232 d->m_sheetSize = QSize(image.size() / image.devicePixelRatioF());
233 node->setTexture(window()->createTextureFromImage(image));
234 d->m_spriteEngine->start(index: 0);
235 node->setTime(0.0f);
236 node->setSourceA(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY()));
237 node->setSourceB(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY()));
238 node->setSpriteSize(QSize(d->m_spriteEngine->spriteWidth(), d->m_spriteEngine->spriteHeight()));
239 node->setSheetSize(d->m_sheetSize);
240 node->setSize(QSizeF(width(), height()));
241
242 d->m_curState = d->m_spriteEngine->state(idx: d->m_spriteEngine->curState())->name();
243 emit currentSpriteChanged(arg: d->m_curState);
244 d->m_timestamp.start();
245 return node;
246}
247
248void QQuickSpriteSequence::reset()
249{
250 Q_D(QQuickSpriteSequence);
251 d->m_pleaseReset = true;
252}
253
254QSGNode *QQuickSpriteSequence::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
255{
256 Q_D(QQuickSpriteSequence);
257
258 if (d->m_pleaseReset) {
259 delete oldNode;
260
261 oldNode = nullptr;
262 d->m_pleaseReset = false;
263 }
264
265 QSGSpriteNode *node = static_cast<QSGSpriteNode *>(oldNode);
266 if (!node)
267 node = initNode();
268
269 if (node)
270 prepareNextFrame(node);
271
272 if (d->m_running) {
273 update();
274 }
275
276 return node;
277}
278
279void QQuickSpriteSequence::prepareNextFrame(QSGSpriteNode *node)
280{
281 Q_D(QQuickSpriteSequence);
282
283 uint timeInt = d->m_timestamp.elapsed();
284 qreal time = timeInt / 1000.;
285
286 //Advance State
287 d->m_spriteEngine->updateSprites(time: timeInt);
288 if (d->m_curStateIdx != d->m_spriteEngine->curState()) {
289 d->m_curStateIdx = d->m_spriteEngine->curState();
290 d->m_curState = d->m_spriteEngine->state(idx: d->m_spriteEngine->curState())->name();
291 emit currentSpriteChanged(arg: d->m_curState);
292 d->m_curFrame= -1;
293 }
294
295 //Advance Sprite
296 qreal animT = d->m_spriteEngine->spriteStart()/1000.0;
297 qreal frameCount = d->m_spriteEngine->spriteFrames();
298 qreal frameDuration = d->m_spriteEngine->spriteDuration()/frameCount;
299 double frameAt;
300 qreal progress;
301 if (frameDuration > 0) {
302 qreal frame = (time - animT)/(frameDuration / 1000.0);
303 frame = qBound(min: qreal(0.0), val: frame, max: frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation
304 progress = std::modf(x: frame,iptr: &frameAt);
305 } else {
306 d->m_curFrame++;
307 if (d->m_curFrame >= frameCount){
308 d->m_curFrame = 0;
309 d->m_spriteEngine->advance();
310 }
311 frameAt = d->m_curFrame;
312 progress = 0;
313 }
314 if (d->m_spriteEngine->sprite()->reverse())
315 frameAt = (d->m_spriteEngine->spriteFrames() - 1) - frameAt;
316 int y = d->m_spriteEngine->spriteY();
317 int w = d->m_spriteEngine->spriteWidth();
318 int h = d->m_spriteEngine->spriteHeight();
319 int x1 = d->m_spriteEngine->spriteX();
320 x1 += frameAt * w;
321 int x2 = x1;
322 if (frameAt < (frameCount-1))
323 x2 += w;
324
325 node->setSourceA(QPoint(x1, y));
326 node->setSourceB(QPoint(x2, y));
327 node->setSpriteSize(QSize(w, h));
328 node->setTime(d->m_interpolate ? progress : 0.0);
329 node->setSize(QSizeF(width(), height()));
330 node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
331 node->update();
332}
333
334QT_END_NAMESPACE
335
336#include "moc_qquickspritesequence_p.cpp"
337

source code of qtdeclarative/src/quick/items/qquickspritesequence.cpp