| /**************************************************************************** |
| ** |
| ** 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 |