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::setPropertyName(const QString &name)
71{
72 m_propertyName = name;
73}
74
75QStringView QSvgAbstractAnimatedProperty::propertyName() const
76{
77 return m_propertyName;
78}
79
80QSvgAbstractAnimatedProperty::Type QSvgAbstractAnimatedProperty::type() const
81{
82 return m_type;
83}
84
85QVariant QSvgAbstractAnimatedProperty::interpolatedValue() const
86{
87 return m_interpolatedValue;
88}
89
90QSvgAbstractAnimatedProperty *QSvgAbstractAnimatedProperty::createAnimatedProperty(const QString &name)
91{
92 if (animatableProperties->isEmpty())
93 initHash();
94
95 if (!animatableProperties->contains(name)) {
96 qCDebug(lcSvgAnimatedProperty) << "Property : " << name << " is not animatable";
97 return nullptr;
98 }
99
100 QSvgAbstractAnimatedProperty::Type type = animatableProperties->value(name);
101 QSvgAbstractAnimatedProperty *prop = nullptr;
102
103 switch (type) {
104 case QSvgAbstractAnimatedProperty::Color:
105 prop = new QSvgAnimatedPropertyColor(name);
106 break;
107 case QSvgAbstractAnimatedProperty::Transform:
108 prop = new QSvgAnimatedPropertyTransform(name);
109 break;
110 case QSvgAbstractAnimatedProperty::Float:
111 prop = new QSvgAnimatedPropertyFloat(name);
112 default:
113 break;
114 }
115
116 return prop;
117}
118
119QSvgAnimatedPropertyColor::QSvgAnimatedPropertyColor(const QString &name)
120 : QSvgAbstractAnimatedProperty(name, QSvgAbstractAnimatedProperty::Color)
121{
122}
123
124void QSvgAnimatedPropertyColor::setColors(const QList<QColor> &colors)
125{
126 m_colors = colors;
127}
128
129void QSvgAnimatedPropertyColor::appendColor(const QColor &color)
130{
131 m_colors.append(color);
132}
133
134QList<QColor> QSvgAnimatedPropertyColor::colors() const
135{
136 return m_colors;
137}
138
139void QSvgAnimatedPropertyColor::interpolate(uint index, qreal t) const
140{
141 QColor c1 = m_colors.at(index - 1);
142 QColor c2 = m_colors.at(index);
143
144 int alpha = q_lerp(c1.alpha(), c2.alpha(), t);
145 int red = q_lerp(c1.red(), c2.red(), t);
146 int green = q_lerp(c1.green(), c2.green(), t);
147 int blue = q_lerp(c1.blue(), c2.blue(), t);
148
149 m_interpolatedValue = QColor(red, green, blue, alpha);
150}
151
152QSvgAnimatedPropertyFloat::QSvgAnimatedPropertyFloat(const QString &name)
153 : QSvgAbstractAnimatedProperty(name, QSvgAbstractAnimatedProperty::Float)
154{
155}
156
157void QSvgAnimatedPropertyFloat::setValues(const QList<qreal> &values)
158{
159 m_values = values;
160}
161
162void QSvgAnimatedPropertyFloat::appendValue(const qreal value)
163{
164 m_values.append(value);
165}
166
167QList<qreal> QSvgAnimatedPropertyFloat::values() const
168{
169 return m_values;
170}
171
172void QSvgAnimatedPropertyFloat::interpolate(uint index, qreal t) const
173{
174 if (index >= (uint)m_keyFrames.size()) {
175 qCWarning(lcSvgAnimatedProperty) << "Invalid index for key frames";
176 return;
177 }
178
179 qreal float1 = m_values.at(index - 1);
180 qreal float2 = m_values.at(index);
181
182 m_interpolatedValue = q_lerp(float1, float2, t);
183}
184
185QSvgAnimatedPropertyTransform::QSvgAnimatedPropertyTransform(const QString &name)
186 : QSvgAbstractAnimatedProperty(name, QSvgAbstractAnimatedProperty::Transform)
187{
188
189}
190
191void QSvgAnimatedPropertyTransform::setTransformCount(quint32 count)
192{
193 m_transformCount = count;
194}
195
196quint32 QSvgAnimatedPropertyTransform::transformCount() const
197{
198 return m_transformCount;
199}
200
201void QSvgAnimatedPropertyTransform::appendComponents(const QList<TransformComponent> &components)
202{
203 m_components.append(components);
204}
205
206QList<QSvgAnimatedPropertyTransform::TransformComponent> QSvgAnimatedPropertyTransform::components() const
207{
208 return m_components;
209}
210
211// this function iterates over all TransformComponents in two consecutive
212// key frames and interpolate between all TransformComponents. Moreover,
213// it requires all key frames to have the same number of TransformComponents.
214// This must be ensured by the parser itself, and it is handled in validateTransform
215// function in qsvgcsshandler.cpp and in createAnimateTransformNode function
216// in qsvghandler.cpp.
217void QSvgAnimatedPropertyTransform::interpolate(uint index, qreal t) const
218{
219 if (index >= (uint)m_keyFrames.size()) {
220 qCWarning(lcSvgAnimatedProperty) << "Invalid index for key frames";
221 return;
222 }
223
224 if (!m_transformCount ||
225 ((m_components.size() / qsizetype(m_transformCount)) != m_keyFrames.size())) {
226 return;
227 }
228
229 QTransform transform = QTransform();
230
231 qsizetype startIndex = (index - 1) * qsizetype(m_transformCount);
232 qsizetype endIndex = index * qsizetype(m_transformCount);
233
234 for (quint32 i = 0; i < m_transformCount; i++) {
235 TransformComponent tc1 = m_components.at(startIndex + i);
236 TransformComponent tc2 = m_components.at(endIndex + i);
237 if (tc1.type == tc2.type) {
238 if (tc1.type == TransformComponent::Translate) {
239 QPointF t1 = QPointF(tc1.values.at(0), tc1.values.at(1));
240 QPointF t2 = QPointF(tc2.values.at(0), tc2.values.at(1));
241 QPointF tr = pointInterpolator(t1, t2, t);
242 transform.translate(tr.x(), tr.y());
243 } else if (tc1.type == TransformComponent::Scale) {
244 QPointF s1 = QPointF(tc1.values.at(0), tc1.values.at(1));
245 QPointF s2 = QPointF(tc2.values.at(0), tc2.values.at(1));
246 QPointF sr = pointInterpolator(s1, s2, t);
247 transform.scale(sr.x(), sr.y());
248 } else if (tc1.type == TransformComponent::Rotate) {
249 QPointF cor1 = QPointF(tc1.values.at(1), tc1.values.at(2));
250 QPointF cor2 = QPointF(tc2.values.at(1), tc2.values.at(2));
251 QPointF corResult = pointInterpolator(cor1, cor2, t);
252 qreal angle1 = tc1.values.at(0);
253 qreal angle2 = tc2.values.at(0);
254 qreal angleResult = q_lerp(angle1, angle2, t);
255 transform.translate(corResult.x(), corResult.y());
256 transform.rotate(angleResult);
257 transform.translate(-corResult.x(), -corResult.y());
258 } else if (tc1.type == TransformComponent::Skew) {
259 QPointF skew1 = QPointF(tc1.values.at(0), tc1.values.at(1));
260 QPointF skew2 = QPointF(tc2.values.at(0), tc2.values.at(1));
261 QPointF skewResult = pointInterpolator(skew1, skew2, t);
262 transform.shear(qTan(qDegreesToRadians(skewResult.x())),
263 qTan(qDegreesToRadians(skewResult.y())));
264 }
265 }
266 }
267
268 m_interpolatedValue = transform;
269}
270
271QT_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)