mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 02:01:40 -05:00
Render animated stickers using rlottie.
This commit is contained in:
parent
b36f7dfdb1
commit
b10e6b3508
3 changed files with 61 additions and 39 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue