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