Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickspritesequence.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
9#include <QtQuick/private/qsgcontext_p.h>
10#include <private/qsgadaptationlayer_p.h>
11#include <QtQuick/qsgnode.h>
12#include <QtQuick/qsgtexturematerial.h>
13#include <QtQuick/qsgtexture.h>
14#include <QtQuick/qquickwindow.h>
15#include <QtQml/qqmlinfo.h>
16#include <QFile>
17#include <cmath>
18#include <qmath.h>
19#include <QDebug>
20
22
23/*!
24 \qmltype SpriteSequence
25 \nativetype QQuickSpriteSequence
26 \inqmlmodule QtQuick
27 \ingroup qtquick-visual-utility
28 \inherits Item
29 \brief Draws a sprite animation.
30
31 SpriteSequence renders and controls a list of animations defined
32 by \l Sprite types.
33
34 For full details, see the \l{Sprite Animations} overview.
35 \sa {Sprite animations with SpriteSequence}
36*/
37/*!
38 \qmlproperty bool QtQuick::SpriteSequence::running
39
40 Whether the sprite is animating or not.
41
42 Default is \c true.
43*/
44/*!
45 \qmlproperty bool QtQuick::SpriteSequence::interpolate
46
47 If \c true, interpolation will occur between sprite frames to make the
48 animation appear smoother.
49
50 Default is \c true.
51*/
52/*!
53 \qmlproperty string QtQuick::SpriteSequence::currentSprite
54
55 The name of the \l Sprite that is currently animating.
56*/
57/*!
58 \qmlproperty string QtQuick::SpriteSequence::goalSprite
59
60 The name of the \l Sprite that the animation should move to.
61
62 Sprite states have defined durations and transitions between them; setting \c goalSprite
63 will cause it to disregard any path weightings (including \c 0) and head down the path
64 that will reach the \c goalSprite quickest (fewest animations). It will pass through
65 intermediate states on that path, and animate them for their duration.
66
67 If it is possible to return to the \c goalSprite from the starting point of the \c goalSprite,
68 it will continue to do so until \c goalSprite is set to \c "" or an unreachable state.
69*/
70/*! \qmlmethod QtQuick::SpriteSequence::jumpTo(string sprite)
71
72 This function causes the SpriteSequence to jump to the specified \a sprite immediately;
73 intermediate sprites are not played.
74*/
75/*!
76 \qmlproperty list<Sprite> QtQuick::SpriteSequence::sprites
77
78 The sprite or sprites to draw. Sprites will be scaled to the size of this item.
79*/
80
81//TODO: Implicitly size element to size of first sprite?
82QQuickSpriteSequence::QQuickSpriteSequence(QQuickItem *parent) :
83 QQuickItem(*(new QQuickSpriteSequencePrivate), parent)
84{
85 setFlag(ItemHasContents);
86 connect(this, SIGNAL(runningChanged(bool)),
87 this, SLOT(update()));
88}
89
90void QQuickSpriteSequence::jumpTo(const QString &sprite)
91{
92 Q_D(QQuickSpriteSequence);
93 if (!d->m_spriteEngine)
94 return;
95 d->m_spriteEngine->setGoal(d->m_spriteEngine->stateIndex(sprite), 0, true);
96}
97
98void QQuickSpriteSequence::setGoalSprite(const QString &sprite)
99{
100 Q_D(QQuickSpriteSequence);
101 if (d->m_goalState != sprite){
102 d->m_goalState = sprite;
103 emit goalSpriteChanged(sprite);
104 if (d->m_spriteEngine)
105 d->m_spriteEngine->setGoal(d->m_spriteEngine->stateIndex(sprite));
106 }
107}
108
109void QQuickSpriteSequence::setRunning(bool arg)
110{
111 Q_D(QQuickSpriteSequence);
112 if (d->m_running != arg) {
113 d->m_running = arg;
114 Q_EMIT runningChanged(arg);
115 }
116}
117
118void QQuickSpriteSequence::setInterpolate(bool arg)
119{
120 Q_D(QQuickSpriteSequence);
121 if (d->m_interpolate != arg) {
122 d->m_interpolate = arg;
123 Q_EMIT interpolateChanged(arg);
124 }
125}
126
127QQmlListProperty<QQuickSprite> QQuickSpriteSequence::sprites()
128{
129 Q_D(QQuickSpriteSequence);
130 return QQmlListProperty<QQuickSprite>(this, &d->m_sprites,
131 spriteAppend, spriteCount, spriteAt,
132 spriteClear, spriteReplace, spriteRemoveLast);
133}
134
135bool QQuickSpriteSequence::running() const
136{
137 Q_D(const QQuickSpriteSequence);
138 return d->m_running;
139}
140
141bool QQuickSpriteSequence::interpolate() const
142{
143 Q_D(const QQuickSpriteSequence);
144 return d->m_interpolate;
145}
146
147QString QQuickSpriteSequence::goalSprite() const
148{
149 Q_D(const QQuickSpriteSequence);
150 return d->m_goalState;
151}
152
153QString QQuickSpriteSequence::currentSprite() const
154{
155 Q_D(const QQuickSpriteSequence);
156 return d->m_curState;
157}
158
159void QQuickSpriteSequence::createEngine()
160{
161 Q_D(QQuickSpriteSequence);
162 //TODO: delay until component complete
163 if (d->m_spriteEngine)
164 delete d->m_spriteEngine;
165 if (d->m_sprites.size()) {
166 d->m_spriteEngine = new QQuickSpriteEngine(d->m_sprites, this);
167 if (!d->m_goalState.isEmpty())
168 d->m_spriteEngine->setGoal(d->m_spriteEngine->stateIndex(d->m_goalState));
169 } else {
170 d->m_spriteEngine = nullptr;
171 }
172 reset();
173}
174
175QSGSpriteNode *QQuickSpriteSequence::initNode()
176{
177 Q_D(QQuickSpriteSequence);
178
179 if (!d->m_spriteEngine) {
180 qmlWarning(this) << "No sprite engine...";
181 return nullptr;
182 } else if (d->m_spriteEngine->status() == QQuickPixmap::Null) {
183 d->m_spriteEngine->startAssemblingImage();
184 update();//Schedule another update, where we will check again
185 return nullptr;
186 } else if (d->m_spriteEngine->status() == QQuickPixmap::Loading) {
187 update();//Schedule another update, where we will check again
188 return nullptr;
189 }
190
191 QImage image = d->m_spriteEngine->assembledImage(d->sceneGraphRenderContext()->maxTextureSize());
192 if (image.isNull())
193 return nullptr;
194
195 QSGSpriteNode *node = d->sceneGraphContext()->createSpriteNode();
196
197 d->m_sheetSize = QSize(image.size() / image.devicePixelRatio());
198 node->setTexture(window()->createTextureFromImage(image));
199 d->m_spriteEngine->start(0);
200 node->setTime(0.0f);
201 node->setSourceA(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY()));
202 node->setSourceB(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY()));
203 node->setSpriteSize(QSize(d->m_spriteEngine->spriteWidth(), d->m_spriteEngine->spriteHeight()));
204 node->setSheetSize(d->m_sheetSize);
205 node->setSize(QSizeF(width(), height()));
206
207 d->m_curState = d->m_spriteEngine->state(d->m_spriteEngine->curState())->name();
208 emit currentSpriteChanged(d->m_curState);
209 d->m_timestamp.start();
210 return node;
211}
212
213void QQuickSpriteSequence::reset()
214{
215 Q_D(QQuickSpriteSequence);
216 d->m_pleaseReset = true;
217}
218
219QSGNode *QQuickSpriteSequence::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
220{
221 Q_D(QQuickSpriteSequence);
222
223 if (d->m_pleaseReset) {
224 delete oldNode;
225
226 oldNode = nullptr;
227 d->m_pleaseReset = false;
228 }
229
230 QSGSpriteNode *node = static_cast<QSGSpriteNode *>(oldNode);
231 if (!node)
232 node = initNode();
233
234 if (node)
235 prepareNextFrame(node);
236
237 if (d->m_running) {
238 update();
239 }
240
241 return node;
242}
243
244void QQuickSpriteSequence::prepareNextFrame(QSGSpriteNode *node)
245{
246 Q_D(QQuickSpriteSequence);
247
248 uint timeInt = d->m_timestamp.elapsed();
249 qreal time = timeInt / 1000.;
250
251 //Advance State
252 d->m_spriteEngine->updateSprites(timeInt);
253 if (d->m_curStateIdx != d->m_spriteEngine->curState()) {
254 d->m_curStateIdx = d->m_spriteEngine->curState();
255 d->m_curState = d->m_spriteEngine->state(d->m_spriteEngine->curState())->name();
256 emit currentSpriteChanged(d->m_curState);
257 d->m_curFrame= -1;
258 }
259
260 //Advance Sprite
261 qreal animT = d->m_spriteEngine->spriteStart()/1000.0;
262 qreal frameCount = d->m_spriteEngine->spriteFrames();
263 qreal frameDuration = d->m_spriteEngine->spriteDuration()/frameCount;
264 double frameAt;
265 qreal progress;
266 if (frameDuration > 0) {
267 qreal frame = (time - animT)/(frameDuration / 1000.0);
268 frame = qBound(qreal(0.0), frame, frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation
269 progress = std::modf(frame,&frameAt);
270 } else {
271 d->m_curFrame++;
272 if (d->m_curFrame >= frameCount){
273 d->m_curFrame = 0;
274 d->m_spriteEngine->advance();
275 }
276 frameAt = d->m_curFrame;
277 progress = 0;
278 }
279 if (d->m_spriteEngine->sprite()->reverse())
280 frameAt = (d->m_spriteEngine->spriteFrames() - 1) - frameAt;
281 int y = d->m_spriteEngine->spriteY();
282 int w = d->m_spriteEngine->spriteWidth();
283 int h = d->m_spriteEngine->spriteHeight();
284 int x1 = d->m_spriteEngine->spriteX();
285 x1 += frameAt * w;
286 int x2 = x1;
287 if (frameAt < (frameCount-1))
288 x2 += w;
289
290 node->setSourceA(QPoint(x1, y));
291 node->setSourceB(QPoint(x2, y));
292 node->setSpriteSize(QSize(w, h));
293 node->setTime(d->m_interpolate ? progress : 0.0);
294 node->setSize(QSizeF(width(), height()));
295 node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
296 node->update();
297}
298
299QT_END_NAMESPACE
300
301#include "moc_qquickspritesequence_p.cpp"