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

source code of qtlottie/src/bodymovin/bmbase.cpp