Render animated stickers using rlottie.

This commit is contained in:
John Preston 2019-06-25 16:16:38 +02:00
parent b36f7dfdb1
commit b10e6b3508
3 changed files with 61 additions and 39 deletions

View File

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/algorithm.h"
#include "zlib.h"
#include "logs.h"
#include "rlottie.h"
#include <QFile>
#include <crl/crl_async.h>
@ -82,14 +83,15 @@ auto Init(QByteArray &&content)
<< content.size();
return Error::ParseFailed;
}
const auto document = JsonDocument(std::move(content));
if (const auto error = document.error()) {
auto animation = rlottie::Animation::loadFromData(
std::string(content.constData(), content.size()),
std::string());
if (!animation) {
qWarning()
<< "Lottie Error: Parse failed with code: "
<< error;
<< "Lottie Error: Parse failed.";
return Error::ParseFailed;
}
auto result = std::make_unique<SharedState>(document.root());
auto result = std::make_unique<SharedState>(std::move(animation));
auto information = result->information();
if (!information.frameRate
|| information.framesCount <= 0

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lottie/lottie_animation.h"
#include "rasterrenderer/rasterrenderer.h"
#include "logs.h"
#include "rlottie.h"
#include <range/v3/algorithm/find.hpp>
#include <range/v3/algorithm/count_if.hpp>
@ -23,6 +24,8 @@ namespace Lottie {
namespace {
constexpr auto kDisplaySkipped = crl::time(-1);
constexpr auto kMaxFrameRate = 120;
constexpr auto kMaxSize = 3096;
std::weak_ptr<FrameRenderer> GlobalInstance;
@ -171,21 +174,40 @@ void FrameRendererObject::queueGenerateFrames() {
});
}
SharedState::SharedState(const JsonObject &definition)
: _scene(definition) {
if (_scene.isValid()) {
SharedState::SharedState(std::unique_ptr<rlottie::Animation> animation)
: _animation(std::move(animation)) {
Expects(_animation != nullptr);
if (isValid()) {
auto cover = QImage();
renderFrame(cover, FrameRequest::NonStrict(), 0);
init(std::move(cover));
}
}
bool SharedState::isValid() const {
auto width = size_t(0);
auto height = size_t(0);
_animation->size(width, height);
const auto frameRate = int(_animation->frameRate());
return _animation->totalFrame() > 0
&& frameRate > 0
&& frameRate <= kMaxFrameRate
&& width > 0
&& width <= kMaxSize
&& height > 0
&& height <= kMaxSize;
}
void SharedState::renderFrame(
QImage &image,
const FrameRequest &request,
int index) {
const auto realSize = QSize(_scene.width(), _scene.height());
if (realSize.isEmpty() || _scene.endFrame() <= _scene.startFrame()) {
auto width = size_t(0);
auto height = size_t(0);
_animation->size(width, height);
const auto realSize = QSize(width, height);
if (realSize.isEmpty() || !_animation->totalFrame()) {
return;
}
@ -195,32 +217,19 @@ void SharedState::renderFrame(
}
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHints(QPainter::Antialiasing);
p.setRenderHints(QPainter::SmoothPixmapTransform);
p.setRenderHint(QPainter::TextAntialiasing);
p.setRenderHints(QPainter::HighQualityAntialiasing);
if (realSize != size) {
p.scale(
size.width() / float64(realSize.width()),
size.height() / float64(realSize.height()));
}
const auto frame = std::clamp(
_scene.startFrame() + index,
_scene.startFrame(),
_scene.endFrame() - 1);
_scene.updateProperties(frame);
RasterRenderer renderer(&p);
_scene.render(renderer, frame);
auto surface = rlottie::Surface(
reinterpret_cast<uint32_t*>(image.bits()),
image.width(),
image.height(),
image.bytesPerLine());
_animation->renderSync(index, surface);
}
void SharedState::init(QImage cover) {
Expects(!initialized());
_frameRate = _scene.frameRate();
_framesCount = _scene.endFrame() - _scene.startFrame();
_frameRate = int(_animation->frameRate());
_framesCount = int(_animation->totalFrame());
_duration = crl::time(1000) * _framesCount / _frameRate;
_frames[0].original = std::move(cover);
@ -319,13 +328,17 @@ not_null<const Frame*> SharedState::getFrame(int index) const {
}
Information SharedState::information() const {
if (!_scene.isValid()) {
if (!isValid()) {
return {};
}
auto width = size_t(0);
auto height = size_t(0);
_animation->size(width, height);
auto result = Information();
result.frameRate = _scene.frameRate();
result.size = QSize(_scene.width(), _scene.height());
result.framesCount = _scene.endFrame() - _scene.startFrame();
result.frameRate = int(_animation->frameRate());
result.size = QSize(width, height);
result.framesCount = int(_animation->totalFrame());
return result;
}
@ -421,6 +434,8 @@ crl::time SharedState::markFrameShown() {
Unexpected("Counter value in Lottie::SharedState::markFrameShown.");
}
SharedState::~SharedState() = default;
std::shared_ptr<FrameRenderer> FrameRenderer::Instance() {
if (auto result = GlobalInstance.lock()) {
return result;

View File

@ -10,14 +10,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/basic_types.h"
#include "base/weak_ptr.h"
#include "lottie/lottie_common.h"
#include "bmscene.h"
#include <QImage>
#include <crl/crl_time.h>
#include <crl/crl_object_on_queue.h>
#include <limits>
class BMBase;
namespace rlottie {
class Animation;
} // namespace rlottie
class QImage;
namespace Lottie {
@ -41,7 +43,7 @@ QImage PrepareFrameByRequest(
class SharedState {
public:
explicit SharedState(const JsonObject &definition);
explicit SharedState(std::unique_ptr<rlottie::Animation> animation);
void start(not_null<Animation*> owner, crl::time now);
@ -56,7 +58,10 @@ public:
void renderFrame(QImage &image, const FrameRequest &request, int index);
[[nodiscard]] bool renderNextFrame(const FrameRequest &request);
~SharedState();
private:
bool isValid() const;
void init(QImage cover);
void renderNextFrame(
not_null<Frame*> frame,
@ -65,7 +70,7 @@ private:
[[nodiscard]] not_null<const Frame*> getFrame(int index) const;
[[nodiscard]] int counter() const;
BMScene _scene;
std::unique_ptr<rlottie::Animation> _animation;
static constexpr auto kCounterUninitialized = -1;
std::atomic<int> _counter = kCounterUninitialized;