1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "bmlayer_p.h"
5
6#include <QJsonArray>
7#include <QJsonObject>
8#include <QJsonValue>
9#include <QLoggingCategory>
10
11#include "bmimagelayer_p.h"
12#include "bmshapelayer_p.h"
13#include "bmfilleffect_p.h"
14#include "bmbasictransform_p.h"
15
16QT_BEGIN_NAMESPACE
17
18BMLayer::BMLayer(const BMLayer &other)
19 : BMBase(other)
20{
21 m_layerIndex = other.m_layerIndex;
22 m_startFrame = other.m_startFrame;
23 m_endFrame = other.m_endFrame;
24 m_startTime = other.m_startTime;
25 m_blendMode = other.m_blendMode;
26 m_3dLayer = other.m_3dLayer;
27 m_stretch = other.m_stretch;
28 m_parentLayer = other.m_parentLayer;
29 m_td = other.m_td;
30 m_clipMode = other.m_clipMode;
31 if (other.m_effects) {
32 m_effects = new BMBase;
33 for (BMBase *effect : other.m_effects->children())
34 m_effects->appendChild(child: effect->clone());
35 }
36 //m_transformAtFirstFrame = other.m_transformAtFirstFrame;
37}
38
39BMLayer::~BMLayer()
40{
41 if (m_effects)
42 delete m_effects;
43}
44
45BMBase *BMLayer::clone() const
46{
47 return new BMLayer(*this);
48}
49
50BMLayer *BMLayer::construct(QJsonObject definition, const QVersionNumber &version)
51{
52 qCDebug(lcLottieQtBodymovinParser) << "BMLayer::construct()";
53
54 BMLayer *layer = nullptr;
55 int type = definition.value(key: QLatin1String("ty")).toInt();
56 switch (type) {
57 case 2:
58 qCDebug(lcLottieQtBodymovinParser) << "Parse image layer";
59 layer = new BMImageLayer(definition, version);
60 break;
61 case 4:
62 qCDebug(lcLottieQtBodymovinParser) << "Parse shape layer";
63 layer = new BMShapeLayer(definition, version);
64 break;
65 default:
66 qCWarning(lcLottieQtBodymovinParser) << "Unsupported layer type:" << type;
67 }
68 return layer;
69}
70
71bool BMLayer::active(int frame) const
72{
73 return (!m_hidden && (frame >= m_startFrame && frame <= m_endFrame));
74}
75
76void BMLayer::parse(const QJsonObject &definition)
77{
78 BMBase::parse(definition);
79 if (m_hidden)
80 return;
81
82 qCDebug(lcLottieQtBodymovinParser) << "BMLayer::parse():" << m_name;
83
84 m_layerIndex = definition.value(key: QLatin1String("ind")).toVariant().toInt();
85 m_startFrame = definition.value(key: QLatin1String("ip")).toVariant().toInt();
86 m_endFrame = definition.value(key: QLatin1String("op")).toVariant().toInt();
87 m_startTime = definition.value(key: QLatin1String("st")).toVariant().toReal();
88 m_blendMode = definition.value(key: QLatin1String("bm")).toVariant().toInt();
89 m_autoOrient = definition.value(key: QLatin1String("ao")).toBool();
90 m_3dLayer = definition.value(key: QLatin1String("ddd")).toBool();
91 m_stretch = definition.value(key: QLatin1String("sr")).toVariant().toReal();
92 m_parentLayer = definition.value(key: QLatin1String("parent")).toVariant().toInt();
93 m_td = definition.value(key: QLatin1String("td")).toInt();
94 int clipMode = definition.value(key: QLatin1String("tt")).toInt(defaultValue: -1);
95 if (clipMode > -1 && clipMode < 5)
96 m_clipMode = static_cast<MatteClipMode>(clipMode);
97
98 QJsonArray effects = definition.value(key: QLatin1String("ef")).toArray();
99 parseEffects(definition: effects);
100
101 if (m_td > 1)
102 qCWarning(lcLottieQtBodymovinParser)
103 << "BM Layer: Only alpha mask layer supported:" << m_clipMode;
104 if (m_blendMode > 0)
105 qCWarning(lcLottieQtBodymovinParser)
106 << "BM Layer: Unsupported blend mode" << m_blendMode;
107 if (m_stretch > 1)
108 qCWarning(lcLottieQtBodymovinParser)
109 << "BM Layer: stretch not supported" << m_stretch;
110 if (m_autoOrient)
111 qCWarning(lcLottieQtBodymovinParser)
112 << "BM Layer: auto-orient not supported";
113 if (m_3dLayer)
114 qCWarning(lcLottieQtBodymovinParser)
115 << "BM Layer: is a 3D layer, but not handled";
116}
117
118void BMLayer::updateProperties(int frame)
119{
120 if (m_parentLayer)
121 resolveLinkedLayer();
122
123 // Update first effects, as they are not children of the layer
124 if (m_effects) {
125 for (BMBase* effect : m_effects->children())
126 effect->updateProperties(frame);
127 }
128
129 BMBase::updateProperties(frame);
130}
131
132void BMLayer::render(LottieRenderer &renderer) const
133{
134 // Render first effects, as they affect the children
135 renderEffects(renderer);
136
137 BMBase::render(renderer);
138}
139
140BMBase *BMLayer::findChild(const QString &childName)
141{
142 BMBase *child = nullptr;
143
144 if (m_effects)
145 child = m_effects->findChild(childName);
146
147 if (child)
148 return child;
149 else
150 return BMBase::findChild(childName);
151}
152
153BMLayer *BMLayer::resolveLinkedLayer()
154{
155 if (m_linkedLayer)
156 return m_linkedLayer;
157
158 resolveTopRoot();
159
160 Q_ASSERT(topRoot());
161
162 for (BMBase *child : topRoot()->children()) {
163 BMLayer *layer = static_cast<BMLayer*>(child);
164 if (layer->layerId() == m_parentLayer) {
165 m_linkedLayer = layer;
166 break;
167 }
168 }
169 return m_linkedLayer;
170}
171
172BMLayer *BMLayer::linkedLayer() const
173{
174 return m_linkedLayer;
175}
176
177bool BMLayer::isClippedLayer() const
178{
179 return m_clipMode != NoClip;
180}
181
182bool BMLayer::isMaskLayer() const
183{
184 return m_td > 0;
185}
186
187BMLayer::MatteClipMode BMLayer::clipMode() const
188{
189 return m_clipMode;
190}
191
192int BMLayer::layerId() const
193{
194 return m_layerIndex;
195}
196
197BMBasicTransform *BMLayer::transform() const
198{
199 return m_layerTransform;
200}
201
202void BMLayer::renderEffects(LottieRenderer &renderer) const
203{
204 if (!m_effects)
205 return;
206
207 for (BMBase* effect : m_effects->children()) {
208 if (effect->hidden())
209 continue;
210 effect->render(renderer);
211 }
212}
213
214void BMLayer::parseEffects(const QJsonArray &definition, BMBase *effectRoot)
215{
216 QJsonArray::const_iterator it = definition.constEnd();
217 while (it != definition.constBegin()) {
218 // Create effects container if at least one effect found
219 if (!m_effects) {
220 m_effects = new BMBase;
221 effectRoot = m_effects;
222 }
223 it--;
224 QJsonObject effect = (*it).toObject();
225 int type = effect.value(key: QLatin1String("ty")).toInt();
226 switch (type) {
227 case 0:
228 {
229 BMBase *slider = new BMBase;
230 slider->parse(definition: effect);
231 effectRoot->appendChild(child: slider);
232 break;
233 }
234 case 5:
235 {
236 if (effect.value(key: QLatin1String("en")).toInt()) {
237 BMBase *group = new BMBase;
238 group->parse(definition: effect);
239 effectRoot->appendChild(child: group);
240 parseEffects(definition: effect.value(key: QLatin1String("ef")).toArray(), effectRoot: group);
241 }
242 break;
243 }
244 case 21:
245 {
246 BMFillEffect *fill = new BMFillEffect;
247 fill->construct(definition: effect, version: m_version);
248 effectRoot->appendChild(child: fill);
249 break;
250 }
251 default:
252 qCWarning(lcLottieQtBodymovinParser)
253 << "BMLayer: Unsupported effect" << type;
254 }
255 }
256}
257
258QT_END_NAMESPACE
259

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