diff options
author | Rebecca Worledge <rebecca.worledge@qt.io> | 2020-02-14 20:13:34 -0800 |
---|---|---|
committer | Rebecca Worledge <rebecca.worledge@qt.io> | 2020-02-14 20:29:30 -0800 |
commit | 03f115744229f841d42c37b1a699e9b8da7873fc (patch) | |
tree | 08af6c0c1638e663d070b7ab7e7c562a6904faa7 /src | |
parent | 5074794ebd6c1b062500e0cb05464860906625f6 (diff) |
Add Image support to Qt Lottie
Adds image parsing, loading, and rendering to Qt Lottie as per the
Bodymovin spec.
Change-Id: I71950227155b9fbe030fabcfc9c4ae5b8fc1fb6a
Reviewed-by: Rebecca Worledge <rebecca.worledge@theqtcompany.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/bodymovin/bmimage.cpp | 120 | ||||
-rw-r--r-- | src/bodymovin/bmimage_p.h | 84 | ||||
-rw-r--r-- | src/bodymovin/bmimagelayer.cpp | 158 | ||||
-rw-r--r-- | src/bodymovin/bmimagelayer_p.h | 77 | ||||
-rw-r--r-- | src/bodymovin/bmlayer.cpp | 5 | ||||
-rw-r--r-- | src/bodymovin/bodymovin.pro | 4 | ||||
-rw-r--r-- | src/bodymovin/lottierenderer_p.h | 2 | ||||
-rw-r--r-- | src/imports/lottieanimation.cpp | 3 | ||||
-rw-r--r-- | src/imports/rasterrenderer/batchrenderer.cpp | 22 | ||||
-rw-r--r-- | src/imports/rasterrenderer/lottierasterrenderer.cpp | 17 | ||||
-rw-r--r-- | src/imports/rasterrenderer/lottierasterrenderer.h | 1 |
11 files changed, 490 insertions, 3 deletions
diff --git a/src/bodymovin/bmimage.cpp b/src/bodymovin/bmimage.cpp new file mode 100644 index 0000000..8035edf --- /dev/null +++ b/src/bodymovin/bmimage.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** 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 "bmimage_p.h" + +#include <QDir> +#include <QFileInfo> +#include <QJsonObject> + +#include "bmtrimpath_p.h" + +QT_BEGIN_NAMESPACE + +BMImage::BMImage(const BMImage &other) + : BMBase(other) +{ + m_position = other.m_position; + m_radius = other.m_radius; + m_image = other.m_image; +} + +BMImage::BMImage(const QJsonObject &definition, BMBase *parent) +{ + setParent(parent); + construct(definition); +} + +BMBase *BMImage::clone() const +{ + return new BMImage(*this); +} + +void BMImage::construct(const QJsonObject &definition) +{ + BMBase::parse(definition); + if (m_hidden) + return; + + qCDebug(lcLottieQtBodymovinParser) << "BMImage::construct():" << m_name; + + QJsonObject asset = definition.value(QLatin1String("asset")).toObject(); + QString assetString = asset.value(QLatin1String("p")).toString(); + + if (assetString.startsWith(QLatin1String("data:image"))) { + QStringList assetsDataStringList = assetString.split(QLatin1String(",")); + if (assetsDataStringList.length() > 1) { + QByteArray assetData = QByteArray::fromBase64(assetsDataStringList[1].toLatin1()); + m_image.loadFromData(assetData); + } + } + else { + QFileInfo info(asset.value(QLatin1String("fileSource")).toString()); + QString url = info.path() + QDir::separator() + asset.value(QLatin1String("u")).toString() + assetString; + QString path = QUrl(url).toLocalFile(); + m_image.load(path); + if (m_image.isNull()) { + qWarning() << "Unable to load file " << path; + } + } + + QJsonObject position = definition.value(QLatin1String("p")).toObject(); + position = resolveExpression(position); + m_position.construct(position); + + QJsonObject radius = definition.value(QLatin1String("r")).toObject(); + radius = resolveExpression(radius); + m_radius.construct(radius); +} + +void BMImage::updateProperties(int frame) +{ + m_position.update(frame); + m_radius.update(frame); + + m_center = QPointF(m_position.value().x() - m_radius.value() / 2, + m_position.value().y() - m_radius.value() / 2); +} + +void BMImage::render(LottieRenderer &renderer) const +{ + renderer.render(*this); +} + +QPointF BMImage::position() const +{ + return m_position.value(); +} + +qreal BMImage::radius() const +{ + return m_radius.value(); +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmimage_p.h b/src/bodymovin/bmimage_p.h new file mode 100644 index 0000000..2b988df --- /dev/null +++ b/src/bodymovin/bmimage_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef BMIMAGE_P_H +#define BMIMAGE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QImage> +#include <QPointF> + +#include <QtBodymovin/private/bmproperty_p.h> +#include <QtBodymovin/private/bmspatialproperty_p.h> + +QT_BEGIN_NAMESPACE + +class QJsonObject; + +class BODYMOVIN_EXPORT BMImage : public BMBase +{ +public: + BMImage() = default; + explicit BMImage(const BMImage &other); + BMImage(const QJsonObject &definition, BMBase *parent = nullptr); + + BMBase *clone() const override; + + void construct(const QJsonObject &definition); + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + + QPointF position() const; + qreal radius() const; + + QPointF getCenter() const { return m_center; } + QImage getImage() const { return m_image; } + +protected: + BMSpatialProperty m_position; + BMProperty<qreal> m_radius; + + QImage m_image; + QPointF m_center; +}; + +QT_END_NAMESPACE + +#endif // BMIMAGE_P_H diff --git a/src/bodymovin/bmimagelayer.cpp b/src/bodymovin/bmimagelayer.cpp new file mode 100644 index 0000000..f228d1c --- /dev/null +++ b/src/bodymovin/bmimagelayer.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** 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 "bmimagelayer_p.h" + +#include <QJsonObject> +#include <QJsonArray> + + +#include "bmbase_p.h" +#include "bmimage_p.h" +#include "bmshape_p.h" +#include "bmtrimpath_p.h" +#include "bmbasictransform_p.h" +#include "lottierenderer_p.h" + +QT_BEGIN_NAMESPACE + +BMImageLayer::BMImageLayer(const BMImageLayer &other) + : BMLayer(other) +{ + m_maskProperties = other.m_maskProperties; + m_layerTransform = new BMBasicTransform(*other.m_layerTransform); + m_appliedTrim = other.m_appliedTrim; +} + +BMImageLayer::BMImageLayer(const QJsonObject &definition) +{ + m_type = BM_LAYER_IMAGE_IX; + + BMLayer::parse(definition); + + BMImage *image = new BMImage(definition, this); + appendChild(image); + + if (m_hidden) + return; + + qCDebug(lcLottieQtBodymovinParser) << "BMImageLayer::BMImageLayer()" + << m_name; + + QJsonArray maskProps = definition.value(QLatin1String("maskProperties")).toArray(); + QJsonArray::const_iterator propIt = maskProps.constBegin(); + while (propIt != maskProps.constEnd()) { + m_maskProperties.append((*propIt).toVariant().toInt()); + ++propIt; + } + + QJsonObject trans = definition.value(QLatin1String("ks")).toObject(); + m_layerTransform = new BMBasicTransform(trans, this); + + QJsonArray items = definition.value(QLatin1String("shapes")).toArray(); + QJsonArray::const_iterator itemIt = items.constEnd(); + while (itemIt != items.constBegin()) { + itemIt--; + BMShape *shape = BMShape::construct((*itemIt).toObject(), this); + if (shape) + appendChild(shape); + } + + if (m_maskProperties.length()) + qCWarning(lcLottieQtBodymovinParser) + << "BM Image Layer: mask properties found, but not supported" + << m_maskProperties; +} + +BMImageLayer::~BMImageLayer() +{ + if (m_layerTransform) + delete m_layerTransform; +} + +BMBase *BMImageLayer::clone() const +{ + return new BMImageLayer(*this); +} + +void BMImageLayer::updateProperties(int frame) +{ + BMLayer::updateProperties(frame); + + m_layerTransform->updateProperties(frame); + + for (BMBase *child : children()) { + if (child->hidden()) + continue; + + BMShape *shape = dynamic_cast<BMShape*>(child); + + if (!shape) + continue; + + if (shape->type() == BM_SHAPE_TRIM_IX) { + BMTrimPath *trim = static_cast<BMTrimPath*>(shape); + if (m_appliedTrim) + m_appliedTrim->applyTrim(*trim); + else + m_appliedTrim = trim; + } else if (m_appliedTrim) { + if (shape->acceptsTrim()) + shape->applyTrim(*m_appliedTrim); + } + } +} + +void BMImageLayer::render(LottieRenderer &renderer) const +{ + renderer.saveState(); + + renderEffects(renderer); + + // In case there is a linked layer, apply its transform first + // as it affects transforms of this layer too + if (BMLayer *ll = linkedLayer()) + renderer.render(*ll->transform()); + + renderer.render(*this); + + m_layerTransform->render(renderer); + + for (BMBase *child : children()) { + if (child->hidden()) + continue; + child->render(renderer); + } + + if (m_appliedTrim && !m_appliedTrim->hidden()) + m_appliedTrim->render(renderer); + + renderer.restoreState();} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmimagelayer_p.h b/src/bodymovin/bmimagelayer_p.h new file mode 100644 index 0000000..f7a4fd5 --- /dev/null +++ b/src/bodymovin/bmimagelayer_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef BMIMAGELAYER_P_H +#define BMIMAGELAYER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtBodymovin/private/bmlayer_p.h> + +QT_BEGIN_NAMESPACE + +class QJsonObject; + +class LottieRenderer; +class BMShape; +class BMTrimPath; +class BMBasicTransform; + +class BODYMOVIN_EXPORT BMImageLayer : public BMLayer +{ +public: + BMImageLayer() = default; + explicit BMImageLayer(const BMImageLayer &other); + BMImageLayer(const QJsonObject &definition); + ~BMImageLayer() override; + + BMBase *clone() const override; + + void updateProperties(int frame) override; + void render(LottieRenderer &render) const override; + +protected: + QList<int> m_maskProperties; + +private: + BMTrimPath *m_appliedTrim = nullptr; +}; + +QT_END_NAMESPACE + +#endif // BMIMAGELAYER_P_H diff --git a/src/bodymovin/bmlayer.cpp b/src/bodymovin/bmlayer.cpp index 57a88ff..12878b0 100644 --- a/src/bodymovin/bmlayer.cpp +++ b/src/bodymovin/bmlayer.cpp @@ -34,6 +34,7 @@ #include <QJsonValue> #include <QLoggingCategory> +#include "bmimagelayer_p.h" #include "bmshapelayer_p.h" #include "bmfilleffect_p.h" #include "bmbasictransform_p.h" @@ -79,6 +80,10 @@ BMLayer *BMLayer::construct(QJsonObject definition) BMLayer *layer = nullptr; int type = definition.value(QLatin1String("ty")).toInt(); switch (type) { + case 2: + qCDebug(lcLottieQtBodymovinParser) << "Parse image layer"; + layer = new BMImageLayer(definition); + break; case 4: qCDebug(lcLottieQtBodymovinParser) << "Parse shape layer"; layer = new BMShapeLayer(definition); diff --git a/src/bodymovin/bodymovin.pro b/src/bodymovin/bodymovin.pro index 5661c4a..1dc4d34 100644 --- a/src/bodymovin/bodymovin.pro +++ b/src/bodymovin/bodymovin.pro @@ -31,10 +31,12 @@ SOURCES += \ bmlayer.cpp \ bmshape.cpp \ bmshapelayer.cpp \ + bmimagelayer.cpp \ bmrect.cpp \ bmfill.cpp \ bmgfill.cpp \ bmgroup.cpp \ + bmimage.cpp \ bmstroke.cpp \ bmbasictransform.cpp \ bmshapetransform.cpp \ @@ -61,6 +63,7 @@ HEADERS += \ bmfreeformshape_p.h \ bmgfill_p.h \ bmgroup_p.h \ + bmimage_p.h \ bmlayer_p.h \ bmproperty_p.h \ bmrect_p.h \ @@ -69,6 +72,7 @@ HEADERS += \ bmround_p.h \ bmshape_p.h \ bmshapelayer_p.h \ + bmimagelayer_p.h \ bmshapetransform_p.h \ bmspatialproperty_p.h \ bmstroke_p.h \ diff --git a/src/bodymovin/lottierenderer_p.h b/src/bodymovin/lottierenderer_p.h index 4089e8a..27490f5 100644 --- a/src/bodymovin/lottierenderer_p.h +++ b/src/bodymovin/lottierenderer_p.h @@ -52,6 +52,7 @@ class BMLayer; class BMRect; class BMFill; class BMGFill; +class BMImage; class BMStroke; class BMBasicTransform; class BMLayerTransform; @@ -84,6 +85,7 @@ public: virtual void render(const BMRound &round) = 0; virtual void render(const BMFill &fill) = 0; virtual void render(const BMGFill &fill) = 0; + virtual void render(const BMImage &image) = 0; virtual void render(const BMStroke &stroke) = 0; virtual void render(const BMBasicTransform &trans) = 0; virtual void render(const BMShapeTransform &trans) = 0; diff --git a/src/imports/lottieanimation.cpp b/src/imports/lottieanimation.cpp index 6d72151..27fe96f 100644 --- a/src/imports/lottieanimation.cpp +++ b/src/imports/lottieanimation.cpp @@ -695,9 +695,6 @@ int LottieAnimation::parse(QByteArray jsonSource) ++markerIt; } - if (rootObj.value(QLatin1String("assets")).toArray().count()) - qCWarning(lcLottieQtBodymovinParser) << "assets not supported"; - if (rootObj.value(QLatin1String("chars")).toArray().count()) qCWarning(lcLottieQtBodymovinParser) << "chars not supported"; diff --git a/src/imports/rasterrenderer/batchrenderer.cpp b/src/imports/rasterrenderer/batchrenderer.cpp index 84e5a2f..e1ad3c5 100644 --- a/src/imports/rasterrenderer/batchrenderer.cpp +++ b/src/imports/rasterrenderer/batchrenderer.cpp @@ -32,6 +32,7 @@ #include <QImage> #include <QPainter> #include <QHash> +#include <QMap> #include <QMutexLocker> #include <QLoggingCategory> #include <QThread> @@ -41,6 +42,7 @@ #include <QtBodymovin/private/bmconstants_p.h> #include <QtBodymovin/private/bmbase_p.h> +#include <QtBodymovin/private/bmimagelayer_p.h> #include <QtBodymovin/private/bmlayer_p.h> #include "lottieanimation.h" @@ -92,6 +94,10 @@ void BatchRenderer::registerAnimator(LottieAnimation *animator) << static_cast<void*>(animator); Entry *&entry = m_animData[animator]; + if (entry) { + delete (entry); + entry = nullptr; + } Q_ASSERT(entry == nullptr); entry = new Entry; entry->animator = animator; @@ -232,11 +238,27 @@ int BatchRenderer::parse(BMBase *rootElement, const QByteArray &jsonSource) cons if (rootObj.empty()) return -1; + QMap<QString, QJsonObject> assets; QJsonArray jsonLayers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonArray jsonAssets = rootObj.value(QLatin1String("assets")).toArray(); + QJsonArray::const_iterator jsonAssetsIt = jsonAssets.constBegin(); + while (jsonAssetsIt != jsonAssets.constEnd()) { + QJsonObject jsonAsset = (*jsonAssetsIt).toObject(); + + jsonAsset.insert(QLatin1String("fileSource"), QJsonValue::fromVariant(m_animData.keys().last()->source())); + QString id = jsonAsset.value(QLatin1String("id")).toString(); + assets.insert(id, jsonAsset); + jsonAssetsIt++; + } + QJsonArray::const_iterator jsonLayerIt = jsonLayers.constEnd(); while (jsonLayerIt != jsonLayers.constBegin()) { jsonLayerIt--; QJsonObject jsonLayer = (*jsonLayerIt).toObject(); + if (jsonLayer.value("ty").toInt() == 2) { + QString refId = jsonLayer.value("refId").toString(); + jsonLayer.insert("asset", assets.value(refId)); + } BMLayer *layer = BMLayer::construct(jsonLayer); if (layer) { layer->setParent(rootElement); diff --git a/src/imports/rasterrenderer/lottierasterrenderer.cpp b/src/imports/rasterrenderer/lottierasterrenderer.cpp index 4224027..12790b5 100644 --- a/src/imports/rasterrenderer/lottierasterrenderer.cpp +++ b/src/imports/rasterrenderer/lottierasterrenderer.cpp @@ -39,6 +39,7 @@ #include <QtBodymovin/private/bmshape_p.h> #include <QtBodymovin/private/bmfill_p.h> #include <QtBodymovin/private/bmgfill_p.h> +#include <QtBodymovin/private/bmimage_p.h> #include <QtBodymovin/private/bmbasictransform_p.h> #include <QtBodymovin/private/bmshapetransform_p.h> #include <QtBodymovin/private/bmrect_p.h> @@ -154,6 +155,22 @@ void LottieRasterRenderer::render(const BMEllipse &ellipse) m_painter->restore(); } +void LottieRasterRenderer::render(const BMImage &image) +{ + m_painter->save(); + + for (int i = 0; i < m_repeatCount; i++) { + qCDebug(lcLottieQtBodymovinRender) << "Image" << image.name(); + + applyRepeaterTransform(i); + QPointF center = image.getCenter(); + m_painter->drawImage(center.x(), center.y(), image.getImage()); + } + + m_painter->restore(); +} + + void LottieRasterRenderer::render(const BMRound &round) { m_painter->save(); diff --git a/src/imports/rasterrenderer/lottierasterrenderer.h b/src/imports/rasterrenderer/lottierasterrenderer.h index 557762a..92254b3 100644 --- a/src/imports/rasterrenderer/lottierasterrenderer.h +++ b/src/imports/rasterrenderer/lottierasterrenderer.h @@ -56,6 +56,7 @@ public: void render(const BMRound &round) override; void render(const BMFill &fill) override; void render(const BMGFill &shape) override; + void render(const BMImage &image) override; void render(const BMStroke &stroke) override; void render(const BMBasicTransform &transform) override; void render(const BMShapeTransform &transform) override; |