tdesktop/Telegram/SourceFiles/lottie/lottie_animation.cpp

173 lines
4.1 KiB
C++
Raw Normal View History

2019-04-27 09:12:53 +00:00
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "lottie/lottie_animation.h"
#include "lottie/lottie_frame_renderer.h"
#include "base/algorithm.h"
#include <crl/crl_async.h>
#include <crl/crl_on_main.h>
2019-04-27 09:12:53 +00:00
#include <QJsonDocument>
2019-05-16 10:29:29 +00:00
#include <QJsonObject>
2019-04-27 09:12:53 +00:00
#include <QFile>
2019-05-15 09:09:09 +00:00
#include <rapidjson/document.h>
2019-05-14 14:04:10 +00:00
#include "logs.h"
#include "rasterrenderer/rasterrenderer.h"
2019-04-27 09:12:53 +00:00
namespace Lottie {
bool ValidateFile(const QString &path) {
if (!path.endsWith(qstr(".json"), Qt::CaseInsensitive)) {
return false;
}
return true;
}
std::unique_ptr<Animation> FromFile(const QString &path) {
if (!path.endsWith(qstr(".json"), Qt::CaseInsensitive)) {
return nullptr;
}
auto f = QFile(path);
if (!f.open(QIODevice::ReadOnly)) {
return nullptr;
}
const auto content = f.readAll();
if (content.isEmpty()) {
return nullptr;
}
return FromData(content);
}
std::unique_ptr<Animation> FromData(const QByteArray &data) {
return std::make_unique<Animation>(data);
2019-04-27 09:12:53 +00:00
}
Animation::Animation(const QByteArray &content)
: _timer([=] { checkNextFrame(); }) {
const auto weak = base::make_weak(this);
crl::async([=] {
2019-05-14 14:04:10 +00:00
const auto now = crl::now();
auto error = QJsonParseError();
const auto document = QJsonDocument::fromJson(content, &error);
2019-05-14 14:04:10 +00:00
const auto parsed = crl::now();
2019-05-15 09:09:09 +00:00
auto test = rapidjson::Document();
test.Parse(content.data());
const auto second = crl::now();
if (error.error != QJsonParseError::NoError) {
qWarning()
<< "Lottie Error: Parse failed with code "
<< error.error
<< "( " << error.errorString() << ")";
crl::on_main(weak, [=] {
parseFailed();
});
} else {
auto state = std::make_unique<SharedState>(document.object());
crl::on_main(weak, [this, result = std::move(state)]() mutable {
parseDone(std::move(result));
});
}
2019-05-14 14:04:10 +00:00
const auto finish = crl::now();
2019-05-15 09:09:09 +00:00
LOG(("INIT: %1 (PARSE %2, RAPIDJSON %3)").arg(finish - now).arg(parsed - now).arg(second - parsed));
});
2019-04-27 09:12:53 +00:00
}
Animation::~Animation() {
if (_renderer) {
Assert(_state != nullptr);
_renderer->remove(_state);
}
2019-04-27 09:12:53 +00:00
}
void Animation::parseDone(std::unique_ptr<SharedState> state) {
Expects(state != nullptr);
auto information = state->information();
if (!information.frameRate
|| information.framesCount <= 0
|| information.size.isEmpty()) {
_updates.fire_error(Error::NotSupported);
} else {
_state = state.get();
_state->start(this, crl::now());
_renderer = FrameRenderer::Instance();
_renderer->append(std::move(state));
_updates.fire({ std::move(information) });
2019-04-27 09:12:53 +00:00
}
}
void Animation::parseFailed() {
_updates.fire_error(Error::ParseFailed);
}
QImage Animation::frame(const FrameRequest &request) const {
Expects(_renderer != nullptr);
const auto frame = _state->frameForPaint();
const auto changed = (frame->request != request)
&& (request.strict || !frame->request.strict);
if (changed) {
frame->request = request;
_renderer->updateFrameRequest(_state, request);
2019-04-27 09:12:53 +00:00
}
return PrepareFrameByRequest(frame, !changed);
2019-04-27 09:12:53 +00:00
}
rpl::producer<Update, Error> Animation::updates() const {
return _updates.events();
2019-04-27 09:12:53 +00:00
}
bool Animation::ready() const {
return (_renderer != nullptr);
2019-05-02 12:42:47 +00:00
}
crl::time Animation::markFrameDisplayed(crl::time now) {
Expects(_renderer != nullptr);
const auto result = _state->markFrameDisplayed(now);
return result;
2019-04-27 09:12:53 +00:00
}
crl::time Animation::markFrameShown() {
Expects(_renderer != nullptr);
const auto result = _state->markFrameShown();
_renderer->frameShown(_state);
return result;
}
2019-04-27 09:12:53 +00:00
void Animation::checkNextFrame() {
Expects(_renderer != nullptr);
const auto time = _state->nextFrameDisplayTime();
if (time == kTimeUnknown) {
2019-04-27 09:12:53 +00:00
return;
}
const auto now = crl::now();
if (time > now) {
_timer.callOnce(time - now);
} else {
_timer.cancel();
const auto position = markFrameDisplayed(now);
_updates.fire({ DisplayFrameRequest{ position } });
}
2019-04-27 09:12:53 +00:00
}
//void Animation::play(const PlaybackOptions &options) {
// _options = options;
// _started = crl::now();
//}
2019-04-27 09:12:53 +00:00
} // namespace Lottie