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