blob: 57a88ffd68d6424ec097d0d0f5a49b4230e54bab [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the lottie-qt module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "bmlayer_p.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <QLoggingCategory>
#include "bmshapelayer_p.h"
#include "bmfilleffect_p.h"
#include "bmbasictransform_p.h"
QT_BEGIN_NAMESPACE
BMLayer::BMLayer(const BMLayer &other)
: BMBase(other)
{
m_layerIndex = other.m_layerIndex;
m_startFrame = other.m_startFrame;
m_endFrame = other.m_endFrame;
m_startTime = other.m_startTime;
m_blendMode = other.m_blendMode;
m_3dLayer = other.m_3dLayer;
m_stretch = other.m_stretch;
m_parentLayer = other.m_parentLayer;
m_td = other.m_td;
m_clipMode = other.m_clipMode;
if (other.m_effects) {
m_effects = new BMBase;
for (BMBase *effect : other.m_effects->children())
m_effects->appendChild(effect->clone());
}
//m_transformAtFirstFrame = other.m_transformAtFirstFrame;
}
BMLayer::~BMLayer()
{
if (m_effects)
delete m_effects;
}
BMBase *BMLayer::clone() const
{
return new BMLayer(*this);
}
BMLayer *BMLayer::construct(QJsonObject definition)
{
qCDebug(lcLottieQtBodymovinParser) << "BMLayer::construct()";
BMLayer *layer = nullptr;
int type = definition.value(QLatin1String("ty")).toInt();
switch (type) {
case 4:
qCDebug(lcLottieQtBodymovinParser) << "Parse shape layer";
layer = new BMShapeLayer(definition);
break;
default:
qCWarning(lcLottieQtBodymovinParser) << "Unsupported layer type:" << type;
}
return layer;
}
bool BMLayer::active(int frame) const
{
return (!m_hidden && (frame >= m_startFrame && frame <= m_endFrame));
}
void BMLayer::parse(const QJsonObject &definition)
{
BMBase::parse(definition);
if (m_hidden)
return;
qCDebug(lcLottieQtBodymovinParser) << "BMLayer::parse():" << m_name;
m_layerIndex = definition.value(QLatin1String("ind")).toVariant().toInt();
m_startFrame = definition.value(QLatin1String("ip")).toVariant().toInt();
m_endFrame = definition.value(QLatin1String("op")).toVariant().toInt();
m_startTime = definition.value(QLatin1String("st")).toVariant().toReal();
m_blendMode = definition.value(QLatin1String("bm")).toVariant().toInt();
m_autoOrient = definition.value(QLatin1String("ao")).toBool();
m_3dLayer = definition.value(QLatin1String("ddd")).toBool();
m_stretch = definition.value(QLatin1String("sr")).toVariant().toReal();
m_parentLayer = definition.value(QLatin1String("parent")).toVariant().toInt();
m_td = definition.value(QLatin1String("td")).toInt();
int clipMode = definition.value(QLatin1String("tt")).toInt(-1);
if (clipMode > -1 && clipMode < 5)
m_clipMode = static_cast<MatteClipMode>(clipMode);
QJsonArray effects = definition.value(QLatin1String("ef")).toArray();
parseEffects(effects);
if (m_td > 1)
qCWarning(lcLottieQtBodymovinParser)
<< "BM Layer: Only alpha mask layer supported:" << m_clipMode;
if (m_blendMode > 0)
qCWarning(lcLottieQtBodymovinParser)
<< "BM Layer: Unsupported blend mode" << m_blendMode;
if (m_stretch > 1)
qCWarning(lcLottieQtBodymovinParser)
<< "BM Layer: stretch not supported" << m_stretch;
if (m_autoOrient)
qCWarning(lcLottieQtBodymovinParser)
<< "BM Layer: auto-orient not supported";
if (m_3dLayer)
qCWarning(lcLottieQtBodymovinParser)
<< "BM Layer: is a 3D layer, but not handled";
}
void BMLayer::updateProperties(int frame)
{
if (m_parentLayer)
resolveLinkedLayer();
// Update first effects, as they are not children of the layer
if (m_effects) {
for (BMBase* effect : m_effects->children())
effect->updateProperties(frame);
}
BMBase::updateProperties(frame);
}
void BMLayer::render(LottieRenderer &renderer) const
{
// Render first effects, as they affect the children
renderEffects(renderer);
BMBase::render(renderer);
}
BMBase *BMLayer::findChild(const QString &childName)
{
BMBase *child = nullptr;
if (m_effects)
child = m_effects->findChild(childName);
if (child)
return child;
else
return BMBase::findChild(childName);
}
BMLayer *BMLayer::resolveLinkedLayer()
{
if (m_linkedLayer)
return m_linkedLayer;
resolveTopRoot();
Q_ASSERT(topRoot());
for (BMBase *child : topRoot()->children()) {
BMLayer *layer = static_cast<BMLayer*>(child);
if (layer->layerId() == m_parentLayer) {
m_linkedLayer = layer;
break;
}
}
return m_linkedLayer;
}
BMLayer *BMLayer::linkedLayer() const
{
return m_linkedLayer;
}
bool BMLayer::isClippedLayer() const
{
return m_clipMode != NoClip;
}
bool BMLayer::isMaskLayer() const
{
return m_td > 0;
}
BMLayer::MatteClipMode BMLayer::clipMode() const
{
return m_clipMode;
}
int BMLayer::layerId() const
{
return m_layerIndex;
}
BMBasicTransform *BMLayer::transform() const
{
return m_layerTransform;
}
void BMLayer::renderEffects(LottieRenderer &renderer) const
{
if (!m_effects)
return;
for (BMBase* effect : m_effects->children()) {
if (effect->hidden())
continue;
effect->render(renderer);
}
}
void BMLayer::parseEffects(const QJsonArray &definition, BMBase *effectRoot)
{
QJsonArray::const_iterator it = definition.constEnd();
while (it != definition.constBegin()) {
// Create effects container if at least one effect found
if (!m_effects) {
m_effects = new BMBase;
effectRoot = m_effects;
}
it--;
QJsonObject effect = (*it).toObject();
int type = effect.value(QLatin1String("ty")).toInt();
switch (type) {
case 0:
{
BMBase *slider = new BMBase;
slider->parse(effect);
effectRoot->appendChild(slider);
break;
}
case 5:
{
if (effect.value(QLatin1String("en")).toInt()) {
BMBase *group = new BMBase;
group->parse(effect);
effectRoot->appendChild(group);
parseEffects(effect.value(QLatin1String("ef")).toArray(), group);
}
break;
}
case 21:
{
BMFillEffect *fill = new BMFillEffect;
fill->construct(effect);
effectRoot->appendChild(fill);
break;
}
default:
qCWarning(lcLottieQtBodymovinParser)
<< "BMLayer: Unsupported effect" << type;
}
}
}
QT_END_NAMESPACE