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 | |
11 | QT_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 | |
173 | static const int unsetDuration = -2;//-1 means perframe for duration |
174 | QQuickSprite::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 */ |
196 | QQuickSprite::~QQuickSprite() |
197 | { |
198 | } |
199 | |
200 | int 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 | |
225 | void 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 | |
246 | QT_END_NAMESPACE |
247 | |
248 | #include "moc_qquicksprite_p.cpp" |
249 | |