QtLottie: Add BMScene root class.

This commit is contained in:
John Preston 2019-05-09 15:11:38 +03:00
parent 6abf74530d
commit 2c422dcd73
6 changed files with 127 additions and 122 deletions

View File

@ -26,9 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QThread>
#include <math.h>
#include <QtBodymovin/private/bmbase_p.h>
#include <QtBodymovin/private/bmlayer_p.h>
#include <QtBodymovin/private/bmasset_p.h>
#include <QtBodymovin/private/bmscene_p.h>
#include "rasterrenderer/lottierasterrenderer.h"
@ -64,12 +62,14 @@ Animation::~Animation() {
}
QImage Animation::frame(crl::time now) const {
if (_startFrame == _endFrame || _realWidth <= 0 || _realHeight <= 0) {
if (_scene->startFrame() == _scene->endFrame()
|| _scene->width() <= 0
|| _scene->height() <= 0) {
return QImage();
}
auto result = QImage(
qCeil(_realWidth),
qCeil(_realHeight),
_scene->width(),
_scene->height(),
QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::transparent);
@ -79,36 +79,26 @@ QImage Animation::frame(crl::time now) const {
p.setRenderHints(QPainter::SmoothPixmapTransform);
const auto position = now;
const auto elapsed = int((_frameRate * position + 500) / 1000);
const auto frames = (_endFrame - _startFrame);
const auto elapsed = int((_scene->frameRate() * position + 500) / 1000);
const auto frames = (_scene->endFrame() - _scene->startFrame());
const auto frame = _options.loop
? (_startFrame + (elapsed % frames))
: std::min(_startFrame + elapsed, _endFrame);
? (_scene->startFrame() + (elapsed % frames))
: std::min(_scene->startFrame() + elapsed, _scene->endFrame());
auto tree = BMBase(*_treeBlueprint);
for (const auto element : tree.children()) {
if (element->active(frame)) {
element->updateProperties(frame);
}
}
_scene->updateProperties(frame);
LottieRasterRenderer renderer(&p);
for (const auto element : tree.children()) {
if (element->active(frame)) {
element->render(renderer, frame);
}
}
_scene->render(renderer, frame);
}
return result;
}
int Animation::frameRate() const {
return _frameRate;
return _scene->frameRate();
}
crl::time Animation::duration() const {
return (_endFrame - _startFrame) * crl::time(1000) / _frameRate;
return (_scene->endFrame() - _scene->startFrame()) * crl::time(1000) / _scene->frameRate();
}
void Animation::play(const PlaybackOptions &options) {
@ -125,87 +115,8 @@ void Animation::parse(const QByteArray &content) {
return;
}
_startFrame = root.value(qstr("ip")).toVariant().toInt();
_endFrame = root.value(qstr("op")).toVariant().toInt();
_frameRate = root.value(qstr("fr")).toVariant().toInt();
_realWidth = root.value(qstr("w")).toVariant().toReal();
_realHeight = root.value(qstr("h")).toVariant().toReal();
const auto markers = root.value(qstr("markers")).toArray();
for (const auto &entry : markers) {
const auto object = entry.toObject();
const auto name = object.value(qstr("cm")).toString();
const auto frame = object.value(qstr("tm")).toInt();
_markers.emplace(name, frame);
if (object.value(qstr("dr")).toInt()) {
_unsupported = true;
}
}
const auto assets = root.value(qstr("assets")).toArray();
for (const auto &entry : assets) {
if (const auto asset = BMAsset::construct(entry.toObject())) {
_assetIndexById.emplace(asset->id(), _assets.size());
_assets.emplace_back(asset);
} else {
_unsupported = true;
}
}
if (root.value(qstr("chars")).toArray().count()) {
_unsupported = true;
}
_treeBlueprint = std::make_unique<BMBase>();
const auto blueprint = _treeBlueprint.get();
const auto layers = root.value(QLatin1String("layers")).toArray();
for (auto i = layers.end(); i != layers.begin();) {
const auto &entry = *(--i);
if (const auto layer = BMLayer::construct(entry.toObject())) {
layer->setParent(blueprint);
// Mask layers must be rendered before the layers they affect to
// although they appear before in layer hierarchy. For this reason
// move a mask after the affected layers, so it will be rendered first
if (layer->isMaskLayer()) {
blueprint->prependChild(layer);
} else {
blueprint->appendChild(layer);
}
} else {
_unsupported = true;
}
}
resolveAssets();
}
void Animation::resolveAssets() {
if (_assets.empty()) {
return;
}
std::function<BMAsset*(QString)> resolver = [&](const QString &refId)
-> BMAsset* {
const auto i = _assetIndexById.find(refId);
if (i == end(_assetIndexById)) {
return nullptr;
}
const auto result = _assets[i->second].get();
result->resolveAssets(resolver);
return result->clone();
};
for (const auto &asset : _assets) {
asset->resolveAssets(resolver);
}
_treeBlueprint->resolveAssets([&](const QString &refId) {
const auto i = _assetIndexById.find(refId);
return (i != end(_assetIndexById))
? _assets[i->second]->clone()
: nullptr;
});
_scene = std::make_unique<BMScene>();
_scene->parse(root);
}
} // namespace Lottie

View File

@ -17,8 +17,7 @@ class QImage;
class QString;
class QByteArray;
class BMBase;
class BMAsset;
class BMScene;
namespace Lottie {
@ -63,14 +62,6 @@ public:
private:
void parse(const QByteArray &content);
void resolveAssets();
int _startFrame = 0;
int _endFrame = 0;
int _frameRate = 30;
qreal _realWidth = 0;
qreal _realHeight = 0;
base::flat_map<QString, int> _markers;
bool _initialized = false;
bool _unsupported = false;
@ -79,9 +70,7 @@ private:
crl::time _started = 0;
PlaybackOptions _options;
std::unique_ptr<BMBase> _treeBlueprint;
std::vector<std::unique_ptr<BMAsset>> _assets;
base::flat_map<QString, int> _assetIndexById;
std::unique_ptr<BMScene> _scene;
};

@ -1 +1 @@
Subproject commit 9ab16cc8fda724421f13656a2f0821abdeac30c7
Subproject commit bfc7dc4606bbba1f5c42f4868e39b48e4b5764b5

View File

@ -54,6 +54,7 @@
QT_BEGIN_NAMESPACE
class BMAsset;
class BMScene;
class BODYMOVIN_EXPORT BMBase
{
@ -93,8 +94,8 @@ public:
virtual void resolveAssets(const std::function<BMAsset*(QString)> &resolver);
protected:
void resolveTopRoot();
BMBase *topRoot() const;
virtual BMScene *resolveTopRoot() const;
BMScene *topRoot() const;
const QJsonObject resolveExpression(const QJsonObject& definition);
protected:
@ -114,7 +115,7 @@ private:
// Handle to the topmost element on which this element resides
// Will be resolved when traversing effects
BMBase *m_topRoot = nullptr;
mutable BMScene *m_topRoot = nullptr;
};
QT_END_NAMESPACE

View File

@ -0,0 +1,102 @@
/****************************************************************************
**
** 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 BMSCENE_P_H
#define BMSCENE_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 <QJsonObject>
#include <QList>
#include <vector>
#include <memory>
#include <QtBodymovin/private/bmbase_p.h>
QT_BEGIN_NAMESPACE
class BMAsset;
class BODYMOVIN_EXPORT BMScene : public BMBase
{
public:
BMScene();
BMScene(const BMScene &other) = delete;
BMScene &operator=(const BMScene &other) = delete;
virtual ~BMScene();
BMBase *clone() const override;
void parse(const QJsonObject &definition) override;
void updateProperties(int frame) override;
void render(LottieRenderer &renderer, int frame) const override;
int startFrame() const;
int endFrame() const;
int frameRate() const;
int width() const;
int height() const;
protected:
BMScene *resolveTopRoot() const override;
private:
void resolveAllAssets();
std::vector<std::unique_ptr<BMAsset>> _assets;
QHash<QString, int> _assetIndexById;
std::unique_ptr<BMBase> _blueprint;
std::unique_ptr<BMBase> _current;
int _startFrame = 0;
int _endFrame = 0;
int _frameRate = 30;
int _width = 0;
int _height = 0;
QHash<QString, int> _markers;
bool _unsupported = false;
};
QT_END_NAMESPACE
#endif // BMSCENE_P_H

View File

@ -127,11 +127,13 @@
'<(lottie_loc)/bodymovin/bmprecompasset.cpp',
'<(lottie_loc)/bodymovin/bmnulllayer.cpp',
'<(lottie_loc)/bodymovin/bmprecomplayer.cpp',
'<(lottie_loc)/bodymovin/bmscene.cpp',
'<(lottie_loc)/bodymovin/bmasset_p.h',
'<(lottie_loc)/bodymovin/bmprecompasset_p.h',
'<(lottie_loc)/bodymovin/bmnulllayer_p.h',
'<(lottie_loc)/bodymovin/bmprecomplayer_p.h',
'<(lottie_loc)/bodymovin/bmscene_p.h',
],
'conditions': [[ 'build_macold', {
'xcode_settings': {