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
4#include "qquicksprite_p.h"
5#include "qquickimagebase_p.h"
6#include <qqml.h>
7#include <QQmlContext>
8#include <QDebug>
9#include <QRandomGenerator>
10
11QT_BEGIN_NAMESPACE
12
13/*!
14 \qmltype Sprite
15 \instantiates QQuickSprite
16 \inqmlmodule QtQuick
17 \ingroup qtquick-visual-utility
18 \brief Specifies sprite animations.
19
20 Sprite defines a series of one or more frames to be animated and rendered by SpriteSequence.
21 The sprites can be in the middle of an image file, or split along multiple rows, as long as they form
22 a contiguous line wrapping to the next row of the file from the left edge of the file.
23
24 For full details, see the \l{Sprite Animations} overview.
25*/
26/*!
27 \qmlproperty int QtQuick::Sprite::duration
28
29 Duration of the animation. Values below 0 are invalid.
30
31 If frameRate is valid then it will be used to calculate the duration of the frames.
32 If not, and frameDuration is valid, then frameDuration will be used. Otherwise duration is used.
33*/
34/*!
35 \qmlproperty int QtQuick::Sprite::durationVariation
36
37 The duration of the animation can vary by up to this amount. Variation will never decrease the
38 length of the animation to less than 0.
39
40 durationVariation will only take effect if duration is
41 used to calculate the duration of frames.
42
43 Default is 0.
44*/
45
46/*!
47 \qmlproperty qreal QtQuick::Sprite::frameRate
48
49 Frames per second to show in the animation. Values below 0 are invalid.
50
51 If frameRate is valid then it will be used to calculate the duration of the frames.
52 If not, and frameDuration is valid , then frameDuration will be used. Otherwise duration is used.
53*/
54/*!
55 \qmlproperty qreal QtQuick::Sprite::frameRateVariation
56
57 The frame rate between animations can vary by up to this amount. Variation will never decrease the
58 length of the animation to less than 0.
59
60 frameRateVariation will only take effect if frameRate is
61 used to calculate the duration of frames.
62
63 Default is 0.
64*/
65
66/*!
67 \qmlproperty int QtQuick::Sprite::frameDuration
68
69 Duration of each frame of the animation in milliseconds. Values below 0 are invalid.
70
71 If frameRate is valid then it will be used to calculate the duration of the frames.
72 If not, and frameDuration is valid, then frameDuration will be used. Otherwise duration is used.
73*/
74/*!
75 \qmlproperty int QtQuick::Sprite::frameDurationVariation
76
77 The duration of a frame in the animation can vary by up to this amount. Variation will never decrease the
78 length of the animation to less than 0.
79
80 frameDurationVariation will only take effect if frameDuration is
81 used to calculate the duration of frames.
82
83 Default is 0.
84*/
85
86/*!
87 \qmlproperty string QtQuick::Sprite::name
88
89 The name of this sprite, for use in the to property of other sprites.
90*/
91/*!
92 \qmlproperty QVariantMap QtQuick::Sprite::to
93
94 A list of other sprites and weighted transitions to them,
95 for example {"a":1, "b":2, "c":0} would specify that one-third should
96 transition to sprite "a" when this sprite is done, and two-thirds should
97 transition to sprite "b" when this sprite is done. As the transitions are
98 chosen randomly, these proportions will not be exact. With "c":0 in the list,
99 no sprites will randomly transition to "c", but it wll be a valid path if a sprite
100 goal is set.
101
102 If no list is specified, or the sum of weights in the list is zero, then the sprite
103 will repeat itself after completing.
104*/
105/*!
106 \qmlproperty int QtQuick::Sprite::frameCount
107
108 Number of frames in this sprite.
109*/
110/*!
111 \qmlproperty int QtQuick::Sprite::frameHeight
112
113 Height of a single frame in this sprite.
114*/
115/*!
116 \qmlproperty int QtQuick::Sprite::frameWidth
117
118 Width of a single frame in this sprite.
119*/
120/*!
121 \qmlproperty int QtQuick::Sprite::frameX
122
123 The X coordinate in the image file of the first frame of the sprite.
124*/
125/*!
126 \qmlproperty int QtQuick::Sprite::frameY
127
128 The Y coordinate in the image file of the first frame of the sprite.
129*/
130/*!
131 \qmlproperty url QtQuick::Sprite::source
132
133 The image source for the animation.
134
135 If frameHeight and frameWidth are not specified, it is assumed to be a single long row of square frames.
136 Otherwise, it can be multiple contiguous rows or rectangluar frames, when one row runs out the next will be used.
137
138 If frameX and frameY are specified, the row of frames will be taken with that x/y coordinate as the upper left corner.
139*/
140
141/*!
142 \qmlproperty bool QtQuick::Sprite::reverse
143
144 If true, then the animation will be played in reverse.
145
146 Default is false.
147*/
148
149/*!
150 \qmlproperty bool QtQuick::Sprite::randomStart
151
152 If true, then the animation will start its first animation with a random amount of its duration skipped.
153 This allows them to not look like they all just started when the animation begins.
154
155 This only affects the very first animation played. Transitioning to another animation, or the same
156 animation again, will not trigger this.
157
158 Default is false.
159*/
160
161/*!
162 \qmlproperty bool QtQuick::Sprite::frameSync
163
164 If true, then the animation will have no duration. Instead, the animation will advance
165 one frame each time a frame is rendered to the screen. This synchronizes it with the painting
166 rate as opposed to elapsed time.
167
168 If frameSync is set to true, it overrides all of duration, frameRate and frameDuration.
169
170 Default is false.
171*/
172
173static const int unsetDuration = -2;//-1 means perframe for duration
174QQuickSprite::QQuickSprite(QObject *parent)
175 : QQuickStochasticState(parent)
176 , m_generatedCount(0)
177 , m_framesPerRow(0)
178 , m_rowY(0)
179 , m_rowStartX(0)
180 , m_reverse(false)
181 , m_frameHeight(0)
182 , m_frameWidth(0)
183 , m_frames(1)
184 , m_frameX(0)
185 , m_frameY(0)
186 , m_frameRate(unsetDuration)
187 , m_frameRateVariation(0)
188 , m_frameDuration(unsetDuration)
189 , m_frameDurationVariation(0)
190 , m_frameSync(false)
191 , m_devicePixelRatio(1.0)
192{
193}
194
195/*! \internal */
196QQuickSprite::~QQuickSprite()
197{
198}
199
200int QQuickSprite::variedDuration() const //Deals with precedence when multiple durations are set
201{
202 if (m_frameSync)
203 return 0;
204
205 if (m_frameRate != unsetDuration) {
206 qreal fpms = (m_frameRate
207 + (m_frameRateVariation * QRandomGenerator::global()->generateDouble() * 2)
208 - m_frameRateVariation) / 1000.0;
209 return qMax(a: qreal(0.0) , b: m_frames / fpms);
210 } else if (m_frameDuration != unsetDuration) {
211 int mspf = m_frameDuration
212 + (m_frameDurationVariation * QRandomGenerator::global()->generateDouble() * 2)
213 - m_frameDurationVariation;
214 return qMax(a: 0, b: m_frames * mspf);
215 } else if (duration() >= 0) {
216 qWarning() << "Sprite::duration is changing meaning to the full animation duration.";
217 qWarning() << "Use Sprite::frameDuration for the old meaning, of per frame duration.";
218 qWarning() << "As an interim measure, duration/durationVariation means the same as frameDuration/frameDurationVariation, and you'll get this warning spewed out everywhere to motivate you.";
219 //Note that the spammyness is due to this being the best location to detect, but also called once each animation loop
220 return QQuickStochasticState::variedDuration() * m_frames;
221 }
222 return 1000; //When nothing set
223}
224
225void QQuickSprite::startImageLoading()
226{
227 m_pix.clear(this);
228 if (!m_source.isEmpty()) {
229 const QQmlContext *context = qmlContext(this);
230 QQmlEngine *e = context ? context->engine() : nullptr;
231 if (!e) { //If not created in QML, you must set the QObject parent to the QML element so this can work
232 context = qmlContext(parent());
233 e = context ? context->engine() : nullptr;
234 if (!e)
235 qWarning() << "QQuickSprite: Cannot find QQmlEngine - this class is only for use in QML and may not work";
236 }
237 const QUrl resolvedUrl = context ? context->resolvedUrl(m_source) : m_source;
238 QUrl loadUrl = resolvedUrl;
239 QQuickImageBase::resolve2xLocalFile(url: resolvedUrl, targetDevicePixelRatio: m_devicePixelRatio, sourceUrl: &loadUrl,
240 sourceDevicePixelRatio: &m_devicePixelRatio);
241
242 m_pix.load(e, loadUrl);
243 }
244}
245
246QT_END_NAMESPACE
247
248#include "moc_qquicksprite_p.cpp"
249

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