1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qlottiebase_p.h"
5
6#include <QLoggingCategory>
7#include <QRegularExpression>
8#include <QRegularExpressionMatch>
9
10QT_BEGIN_NAMESPACE
11
12Q_LOGGING_CATEGORY(lcLottieQtLottieParser, "qt.lottieqt.lottie.parser", QtWarningMsg);
13Q_LOGGING_CATEGORY(lcLottieQtLottieUpdate, "qt.lottieqt.lottie.update");
14Q_LOGGING_CATEGORY(lcLottieQtLottieRender, "qt.lottieqt.lottie.render");
15
16QLottieBase::QLottieBase(const QLottieBase &other)
17{
18 m_definition = other.m_definition;
19 m_type = other.m_type;
20 m_hidden = other.m_hidden;
21 m_name = other.m_name;
22 m_autoOrient = other.m_autoOrient;
23 for (QLottieBase *child : other.m_children) {
24 QLottieBase *clone = child->clone();
25 clone->setParent(this);
26 appendChild(child: clone);
27 }
28}
29
30QLottieBase::~QLottieBase()
31{
32 qDeleteAll(c: m_children);
33}
34
35QLottieBase *QLottieBase::clone() const
36{
37 return new QLottieBase(*this);
38}
39
40QString QLottieBase::name() const
41{
42 return m_name;
43}
44
45void QLottieBase::setName(const QString &name)
46{
47 m_name = name;
48}
49
50bool QLottieBase::setProperty(QLottieLiteral::PropertyType propertyName, QVariant value)
51{
52 for (QLottieBase *child : std::as_const(t&: m_children)) {
53 bool changed = child->setProperty(propertyName, value);
54 if (changed)
55 return true;
56 }
57 return false;
58}
59
60int QLottieBase::type() const
61{
62 return m_type;
63}
64
65void QLottieBase::setType(int type)
66{
67 m_type = type;
68}
69
70void QLottieBase::prependChild(QLottieBase *child)
71{
72 m_children.push_front(t: child);
73}
74
75void QLottieBase::insertChildBeforeLast(QLottieBase *child)
76{
77 m_children.insert(i: qMax(a: m_children.size() - 1, b: 0), t: child);
78}
79
80void QLottieBase::appendChild(QLottieBase *child)
81{
82 m_children.push_back(t: child);
83}
84
85QLottieBase *QLottieBase::findChild(const QString &childName)
86{
87 if (name() == childName)
88 return this;
89
90 QLottieBase *found = nullptr;
91 for (QLottieBase *child : std::as_const(t&: m_children)) {
92 found = child->findChild(childName);
93 if (found)
94 break;
95 }
96 return found;
97}
98
99void QLottieBase::updateProperties(int frame)
100{
101 if (m_hidden)
102 return;
103
104 for (QLottieBase *child : std::as_const(t&: m_children))
105 child->updateProperties(frame);
106}
107
108void QLottieBase::render(QLottieRenderer &renderer) const
109{
110 if (m_hidden)
111 return;
112
113 renderer.saveState();
114 for (QLottieBase *child : std::as_const(t: m_children)) {
115 if (child->m_hidden)
116 continue;
117 child->render(renderer);
118 }
119 renderer.restoreState();
120}
121
122bool QLottieBase::isStructureDumping() const
123{
124 if (m_structureDumping < 0) {
125 QLottieBase *p = parent();
126 m_structureDumping = p ? p->isStructureDumping() : 0;
127 }
128 return bool(m_structureDumping);
129}
130
131void QLottieBase::resolveTopRoot()
132{
133 if (!m_topRoot) {
134 QLottieBase *p = this;
135 while (p) {
136 m_topRoot = p;
137 p = p->parent();
138 }
139 }
140 Q_ASSERT(m_topRoot);
141}
142
143QLottieBase *QLottieBase::topRoot() const
144{
145 return m_topRoot;
146}
147
148void QLottieBase::parse(const QJsonObject &definition)
149{
150 qCDebug(lcLottieQtLottieParser) << "QLottieBase::parse()";
151
152 m_definition = definition;
153
154 m_hidden = definition.value(key: QLatin1String("hd")).toBool(defaultValue: false);
155 m_name = definition.value(key: QLatin1String("nm")).toString();
156 m_matchName = definition.value(key: QLatin1String("mn")).toString();
157 m_autoOrient = definition.value(key: QLatin1String("ao")).toBool();
158
159 if (m_autoOrient)
160 qCInfo(lcLottieQtLottieParser, "Element has auto-orientation set, but it is not supported");
161}
162
163const QJsonObject &QLottieBase::definition() const
164{
165 return m_definition;
166}
167
168bool QLottieBase::active(int frame) const
169{
170 Q_UNUSED(frame);
171 return !m_hidden;
172}
173
174bool QLottieBase::hidden() const
175{
176 return m_hidden;
177}
178
179void QLottieBase::setParent(QLottieBase *parent)
180{
181 m_parent = parent;
182}
183
184
185const QJsonObject QLottieBase::resolveExpression(const QJsonObject &definition)
186{
187 QString expr = definition.value(key: QLatin1String("x")).toString();
188
189 // If there is no expression, return the original object definition
190 if (expr.isEmpty())
191 return definition;
192
193 // Find out layer handle
194 resolveTopRoot();
195
196 QRegularExpression re(QStringLiteral("effect\\(\\'(.*?)\\'\\)\\(\\'(.*?)\\'\\)"));
197 QRegularExpressionMatch match = re.match(subject: expr);
198 if (!match.hasMatch())
199 return definition;
200
201 QString effect = match.captured(nth: 1);
202 QString elementName = match.captured(nth: 2);
203
204 QJsonObject retVal = definition;
205
206 if (QLottieBase *source = m_topRoot->findChild(childName: effect)) {
207 if (source->children().size())
208 retVal = source->children().at(i: 0)->definition().value(key: QLatin1String("v")).toObject();
209 else
210 retVal = source->definition().value(key: QLatin1String("v")).toObject();
211 if (source->children().size() > 1)
212 qCWarning(lcLottieQtLottieParser) << "Effect source points"
213 "to a group that has"
214 "many children. The"
215 "first is be picked";
216 } else {
217 qCWarning(lcLottieQtLottieParser) << "Failed to find specified effect" << effect;
218 }
219
220 // Let users of the json know that it is originated from expression,
221 // so they can adjust their behavior accordingly
222 retVal.insert(key: QLatin1String("fromExpression"), value: true);
223
224 return retVal;
225}
226
227QT_END_NAMESPACE
228

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