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
qsvganimatedproperty.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 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
5#include <QtCore/qpoint.h>
6#include <QtGui/qcolor.h>
7#include <QtGui/qtransform.h>
8#include <QtCore/qloggingcategory.h>
9#include <QtCore/qglobalstatic.h>
10
12
13Q_STATIC_LOGGING_CATEGORY(lcSvgAnimatedProperty, "qt.svg.animation.properties")
14
15typedef QHash<QString, QSvgAbstractAnimatedProperty::Type> AnimatableHashType;
17
18static void initHash()
19{
20 animatableProperties->insert(QStringLiteral("fill"), QSvgAbstractAnimatedProperty::Color);
21 animatableProperties->insert(QStringLiteral("fill-opacity"), QSvgAbstractAnimatedProperty::Float);
22 animatableProperties->insert(QStringLiteral("stroke-opacity"), QSvgAbstractAnimatedProperty::Float);
23 animatableProperties->insert(QStringLiteral("stroke"), QSvgAbstractAnimatedProperty::Color);
24 animatableProperties->insert(QStringLiteral("opacity"), QSvgAbstractAnimatedProperty::Float);
25 animatableProperties->insert(QStringLiteral("transform"), QSvgAbstractAnimatedProperty::Transform);
26}
27
28static qreal q_lerp(qreal a, qreal b, qreal t)
29{
30 return a + (b - a) * t;
31}
32
33static QPointF pointInterpolator(QPointF v1, QPointF v2, qreal t)
34{
35 qreal x = q_lerp(v1.x(), v2.x(), t);
36 qreal y = q_lerp(v1.y(), v2.y(), t);
37
38 return QPointF(x, y);
39}
40
41
42QSvgAbstractAnimatedProperty::QSvgAbstractAnimatedProperty(const QString &name, Type type)
43 : m_propertyName(name)
44 , m_type(type)
45{
46}
47
48QSvgAbstractAnimatedProperty::~QSvgAbstractAnimatedProperty()
49{
50}
51
52void QSvgAbstractAnimatedProperty::setKeyFrames(const QList<qreal> &keyFrames)
53{
54 m_keyFrames = keyFrames;
55}
56
57void QSvgAbstractAnimatedProperty::appendKeyFrame(qreal keyFrame)
58{
59 m_keyFrames.append(keyFrame);
60}
61
62QList<qreal> QSvgAbstractAnimatedProperty::keyFrames() const
63{
64 return m_keyFrames;
65}
66
67void QSvgAbstractAnimatedProperty::setPropertyName(const QString &name)
68{
69 m_propertyName = name;
70}
71
72QStringView QSvgAbstractAnimatedProperty::propertyName() const
73{
74 return m_propertyName;
75}
76
77QSvgAbstractAnimatedProperty::Type QSvgAbstractAnimatedProperty::type() const
78{
79 return m_type;
80}
81
82QVariant QSvgAbstractAnimatedProperty::interpolatedValue() const
83{
84 return m_interpolatedValue;
85}
86
87QSvgAbstractAnimatedProperty *QSvgAbstractAnimatedProperty::createAnimatedProperty(const QString &name)
88{
89 if (animatableProperties->isEmpty())
90 initHash();
91
92 if (!animatableProperties->contains(name)) {
93 qCDebug(lcSvgAnimatedProperty) << "Property : " << name << " is not animatable";
94 return nullptr;
95 }
96
97 QSvgAbstractAnimatedProperty::Type type = animatableProperties->value(name);
98 QSvgAbstractAnimatedProperty *prop = nullptr;
99
100 switch (type) {
101 case QSvgAbstractAnimatedProperty::Color:
102 prop = new QSvgAnimatedPropertyColor(name);
103 break;
104 case QSvgAbstractAnimatedProperty::Transform:
105 prop = new QSvgAnimatedPropertyTransform(name);
106 break;
107 case QSvgAbstractAnimatedProperty::Float:
108 prop = new QSvgAnimatedPropertyFloat(name);
109 default:
110 break;
111 }
112
113 return prop;
114}
115
116QSvgAnimatedPropertyColor::QSvgAnimatedPropertyColor(const QString &name)
117 : QSvgAbstractAnimatedProperty(name, QSvgAbstractAnimatedProperty::Color)
118{
119}
120
121void QSvgAnimatedPropertyColor::setColors(const QList<QColor> &colors)
122{
123 m_colors = colors;
124}
125
126void QSvgAnimatedPropertyColor::appendColor(const QColor &color)
127{
128 m_colors.append(color);
129}
130
131QList<QColor> QSvgAnimatedPropertyColor::colors() const
132{
133 return m_colors;
134}
135
136void QSvgAnimatedPropertyColor::interpolate(uint index, qreal t) const
137{
138 QColor c1 = m_colors.at(index - 1);
139 QColor c2 = m_colors.at(index);
140
141 int alpha = q_lerp(c1.alpha(), c2.alpha(), t);
142 int red = q_lerp(c1.red(), c2.red(), t);
143 int green = q_lerp(c1.green(), c2.green(), t);
144 int blue = q_lerp(c1.blue(), c2.blue(), t);
145
146 m_interpolatedValue = QColor(red, green, blue, alpha);
147}
148
149QSvgAnimatedPropertyFloat::QSvgAnimatedPropertyFloat(const QString &name)
150 : QSvgAbstractAnimatedProperty(name, QSvgAbstractAnimatedProperty::Float)
151{
152}
153
154void QSvgAnimatedPropertyFloat::setValues(const QList<qreal> &values)
155{
156 m_values = values;
157}
158
159void QSvgAnimatedPropertyFloat::appendValue(const qreal value)
160{
161 m_values.append(value);
162}
163
164QList<qreal> QSvgAnimatedPropertyFloat::values() const
165{
166 return m_values;
167}
168
169void QSvgAnimatedPropertyFloat::interpolate(uint index, qreal t) const
170{
171 if (index >= (uint)m_keyFrames.size()) {
172 qCWarning(lcSvgAnimatedProperty) << "Invalid index for key frames";
173 return;
174 }
175
176 qreal float1 = m_values.at(index - 1);
177 qreal float2 = m_values.at(index);
178
179 m_interpolatedValue = q_lerp(float1, float2, t);
180}
181
182QSvgAnimatedPropertyTransform::QSvgAnimatedPropertyTransform(const QString &name)
183 : QSvgAbstractAnimatedProperty(name, QSvgAbstractAnimatedProperty::Transform)
184{
185
186}
187
188void QSvgAnimatedPropertyTransform::setTransformCount(quint32 count)
189{
190 m_transformCount = count;
191}
192
193quint32 QSvgAnimatedPropertyTransform::transformCount() const
194{
195 return m_transformCount;
196}
197
198void QSvgAnimatedPropertyTransform::appendComponents(const QList<TransformComponent> &components)
199{
200 m_components.append(components);
201}
202
203QList<QSvgAnimatedPropertyTransform::TransformComponent> QSvgAnimatedPropertyTransform::components() const
204{
205 return m_components;
206}
207
208// this function iterates over all TransformComponents in two consecutive
209// key frames and interpolate between all TransformComponents. Moreover,
210// it requires all key frames to have the same number of TransformComponents.
211// This must be ensured by the parser itself, and it is handled in validateTransform
212// function in qsvgcsshandler.cpp and in createAnimateTransformNode function
213// in qsvghandler.cpp.
214void QSvgAnimatedPropertyTransform::interpolate(uint index, qreal t) const
215{
216 if (index >= (uint)m_keyFrames.size()) {
217 qCWarning(lcSvgAnimatedProperty) << "Invalid index for key frames";
218 return;
219 }
220
221 if (!m_transformCount ||
222 ((m_components.size() / qsizetype(m_transformCount)) != m_keyFrames.size())) {
223 return;
224 }
225
226 QTransform transform = QTransform();
227
228 qsizetype startIndex = (index - 1) * qsizetype(m_transformCount);
229 qsizetype endIndex = index * qsizetype(m_transformCount);
230
231 for (quint32 i = 0; i < m_transformCount; i++) {
232 TransformComponent tc1 = m_components.at(startIndex + i);
233 TransformComponent tc2 = m_components.at(endIndex + i);
234 if (tc1.type == tc2.type) {
235 if (tc1.type == TransformComponent::Translate) {
236 QPointF t1 = QPointF(tc1.values.at(0), tc1.values.at(1));
237 QPointF t2 = QPointF(tc2.values.at(0), tc2.values.at(1));
238 QPointF tr = pointInterpolator(t1, t2, t);
239 transform.translate(tr.x(), tr.y());
240 } else if (tc1.type == TransformComponent::Scale) {
241 QPointF s1 = QPointF(tc1.values.at(0), tc1.values.at(1));
242 QPointF s2 = QPointF(tc2.values.at(0), tc2.values.at(1));
243 QPointF sr = pointInterpolator(s1, s2, t);
244 transform.scale(sr.x(), sr.y());
245 } else if (tc1.type == TransformComponent::Rotate) {
246 QPointF cor1 = QPointF(tc1.values.at(1), tc1.values.at(2));
247 QPointF cor2 = QPointF(tc2.values.at(1), tc2.values.at(2));
248 QPointF corResult = pointInterpolator(cor1, cor2, t);
249 qreal angle1 = tc1.values.at(0);
250 qreal angle2 = tc2.values.at(0);
251 qreal angleResult = q_lerp(angle1, angle2, t);
252 transform.translate(corResult.x(), corResult.y());
253 transform.rotate(angleResult);
254 transform.translate(-corResult.x(), -corResult.y());
255 } else if (tc1.type == TransformComponent::Skew) {
256 QPointF skew1 = QPointF(tc1.values.at(0), tc1.values.at(1));
257 QPointF skew2 = QPointF(tc2.values.at(0), tc2.values.at(1));
258 QPointF skewResult = pointInterpolator(skew1, skew2, t);
259 transform.shear(qTan(qDegreesToRadians(skewResult.x())),
260 qTan(qDegreesToRadians(skewResult.y())));
261 }
262 }
263 }
264
265 m_interpolatedValue = transform;
266}
267
268QT_END_NAMESPACE
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex)
static void initHash()
static qreal q_lerp(qreal a, qreal b, qreal t)
static QPointF pointInterpolator(QPointF v1, QPointF v2, qreal t)