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