1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qlottiegfill_p.h"
5
6#include <QLinearGradient>
7#include <QRadialGradient>
8#include <QtMath>
9#include <QColor>
10
11QT_BEGIN_NAMESPACE
12
13QLottieGFill::QLottieGFill(const QLottieGFill &other)
14 : QLottieShape(other)
15{
16 if (m_hidden)
17 return;
18
19 m_opacity = other.m_opacity;
20 m_startPoint = other.m_startPoint;
21 m_endPoint = other.m_endPoint;
22 m_highlightLength = other.m_highlightLength;
23 m_highlightAngle = other.m_highlightAngle;
24 m_colors = other.m_colors;
25 if (other.gradientType() == QGradient::LinearGradient)
26 m_gradient = new QLinearGradient;
27 else if (other.gradientType() == QGradient::RadialGradient)
28 m_gradient = new QRadialGradient;
29 else {
30 Q_UNREACHABLE();
31 }
32}
33
34QLottieGFill::~QLottieGFill()
35{
36 if (m_gradient)
37 delete m_gradient;
38}
39
40QLottieBase *QLottieGFill::clone() const
41{
42 return new QLottieGFill(*this);
43}
44
45QLottieGFill::QLottieGFill(const QJsonObject &definition, QLottieBase *parent)
46{
47 setParent(parent);
48
49 QLottieBase::parse(definition);
50 if (m_hidden)
51 return;
52
53 qCDebug(lcLottieQtLottieParser) << "QLottieGFill::construct():" << m_name;
54
55 int type = definition.value(key: QLatin1String("t")).toVariant().toInt();
56 switch (type) {
57 case 1:
58 m_gradient = new QLinearGradient;
59 break;
60 case 2:
61 m_gradient = new QRadialGradient;
62 break;
63 default:
64 qCWarning(lcLottieQtLottieParser) << "Unknown gradient fill type";
65 }
66
67 QJsonObject color = definition.value(key: QLatin1String("g")).toObject();
68 int elementCount = color.value(key: QLatin1String("p")).toInt();
69 bool isAnimated = color.value(key: QLatin1String("k")).toObject().value(key: QLatin1String("a")).toVariant().toBool();
70 if (!isAnimated) {
71 QJsonArray colorArr = color.value(key: QLatin1String("k")).toObject().value(key: QLatin1String("k")).toArray();
72 for (int i = 0; i < (elementCount * 4); i += 4) {
73 // p denotes the color stop percentage
74 QVector4D colorVec;
75 colorVec[0] = colorArr.at(i: i + 1).toVariant().toFloat();
76 colorVec[1] = colorArr.at(i: i + 2).toVariant().toFloat();
77 colorVec[2] = colorArr.at(i: i + 3).toVariant().toFloat();
78 // Set gradient stop position into w of the vector
79 colorVec[3] = 1.0;
80 QLottieProperty4D<QVector4D> colorPos;
81 colorPos.setValue(colorVec);
82 qreal pos = colorArr.at(i: i + 0).toVariant().toFloat();
83 m_colors[qRound(d: pos * 100)] = colorPos;
84 }
85 for (int i = (elementCount * 4); i < colorArr.size(); i+= 2) {
86 qreal pos = colorArr.at(i).toVariant().toFloat();
87 qreal opacity = colorArr.at(i: i + 1).toVariant().toFloat();
88 QLottieProperty4D<QVector4D> colorVec = m_colors[qRound(d: pos * 100)];
89 QVector4D color = colorVec.value();
90 color[3] = opacity;
91 colorVec.setValue(color);
92 m_colors[qRound(d: pos * 100)] = colorVec;
93 }
94 } else {
95 qCInfo(lcLottieQtLottieParser) << "Animated gradient is not supported";
96 }
97
98 QJsonObject opacity = definition.value(key: QLatin1String("o")).toObject();
99 opacity = resolveExpression(definition: opacity);
100 m_opacity.construct(definition: opacity);
101
102 QJsonObject startPoint = definition.value(key: QLatin1String("s")).toObject();
103 startPoint = resolveExpression(definition: startPoint);
104 m_startPoint.construct(definition: startPoint);
105
106 QJsonObject endPoint = definition.value(key: QLatin1String("e")).toObject();
107 endPoint = resolveExpression(definition: endPoint);
108 m_endPoint.construct(definition: endPoint);
109
110 QJsonObject highlight = definition.value(key: QLatin1String("h")).toObject();
111 m_highlightLength.construct(definition: highlight);
112
113 QJsonObject angle = definition.value(key: QLatin1String("a")).toObject();
114 angle = resolveExpression(definition: angle);
115 m_highlightAngle.construct(definition: angle);
116
117 m_highlightAngle.setValue(0.0);
118}
119
120void QLottieGFill::updateProperties(int frame)
121{
122 QGradient::Type type = gradientType();
123 if (type != QGradient::LinearGradient &&
124 type != QGradient::RadialGradient)
125 return;
126
127 m_startPoint.update(frame);
128 m_endPoint.update(frame);
129 m_highlightLength.update(frame);
130 m_highlightAngle.update(frame);
131 m_opacity.update(frame);
132 QHash<int, QLottieProperty4D<QVector4D>>::iterator colorIt = m_colors.begin();
133 while (colorIt != m_colors.end()) {
134 (*colorIt).update(frame);
135 ++colorIt;
136 }
137
138 setGradient();
139}
140
141void QLottieGFill::render(QLottieRenderer &renderer) const
142{
143 renderer.render(fill: *this);
144}
145
146QGradient *QLottieGFill::value() const
147{
148 return m_gradient;
149}
150
151QGradient::Type QLottieGFill::gradientType() const
152{
153 if (m_gradient)
154 return m_gradient->type();
155 else
156 return QGradient::NoGradient;
157}
158
159QPointF QLottieGFill::startPoint() const
160{
161 return m_startPoint.value();
162}
163
164QPointF QLottieGFill::endPoint() const
165{
166 return m_endPoint.value();
167}
168
169qreal QLottieGFill::highlightLength() const
170{
171 return m_highlightLength.value();
172}
173
174qreal QLottieGFill::highlightAngle() const
175{
176 return m_highlightAngle.value();
177}
178
179qreal QLottieGFill::opacity() const
180{
181 return m_opacity.value();
182}
183
184void QLottieGFill::setGradient()
185{
186 QHash<int, QLottieProperty4D<QVector4D>>::iterator colorIt = m_colors.begin();
187 while (colorIt != m_colors.end()) {
188 QVector4D colorPos = (*colorIt).value();
189 int pos = colorIt.key();
190 qreal opacity = m_opacity.value() / 100.0;
191 opacity *= static_cast<qreal>(colorPos[3]);
192 QColor color;
193 color.setRedF(static_cast<qreal>(colorPos[0]));
194 color.setGreenF(static_cast<qreal>(colorPos[1]));
195 color.setBlueF(static_cast<qreal>(colorPos[2]));
196 color.setAlphaF(opacity);
197 m_gradient->setColorAt(pos: pos / 100.0, color);
198 ++colorIt;
199 }
200
201 switch (gradientType()) {
202 case QGradient::LinearGradient:
203 {
204 QLinearGradient *g = static_cast<QLinearGradient*>(m_gradient);
205 g->setStart(m_startPoint.value());
206 g->setFinalStop(m_endPoint.value());
207 break;
208 }
209 case QGradient::RadialGradient:
210 {
211 QRadialGradient *g = static_cast<QRadialGradient*>(m_gradient);
212 QLineF radLine(m_startPoint.value(), m_endPoint.value());
213 g->setCenter(radLine.p1());
214 g->setRadius(radLine.length());
215 radLine.setAngle(radLine.angle() - m_highlightAngle.value());
216 // QRadialGradient needs focalPoint to be inside (and not on) radius circle
217 qreal radFraction = qMin(a: (m_highlightLength.value() / 100.0), b: 0.999);
218 g->setFocalPoint(radLine.pointAt(t: radFraction));
219 break;
220 }
221 default:
222 break;
223 }
224}
225
226QT_END_NAMESPACE
227

source code of qtlottie/src/lottie/qlottiegfill.cpp