Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
274 lines
6.9 KiB
C++
274 lines
6.9 KiB
C++
// This file is part of Desktop App Toolkit,
|
|
// a set of libraries for developing nice desktop applications.
|
|
//
|
|
// For license and copyright information please follow this link:
|
|
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
|
//
|
|
#include "lottie/lottie_single_player.h"
|
|
|
|
#include "lottie/details/lottie_frame_renderer.h"
|
|
#include "lottie/details/lottie_frame_provider_shared.h"
|
|
#include "lottie/details/lottie_frame_provider_direct.h"
|
|
|
|
#ifdef LOTTIE_USE_CACHE
|
|
#include "lottie/details/lottie_frame_provider_cached_multi.h"
|
|
#endif // LOTTIE_USE_CACHE
|
|
|
|
#include <crl/crl_async.h>
|
|
|
|
namespace Lottie {
|
|
|
|
SinglePlayer::SinglePlayer(
|
|
const QByteArray &content,
|
|
const FrameRequest &request,
|
|
Quality quality,
|
|
const ColorReplacements *replacements,
|
|
std::shared_ptr<FrameRenderer> renderer)
|
|
: _timer([=] { checkNextFrameRender(); })
|
|
, _renderer(renderer ? renderer : FrameRenderer::Instance())
|
|
, _animation(this, content, request, quality, replacements) {
|
|
}
|
|
|
|
SinglePlayer::SinglePlayer(
|
|
FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
|
|
FnMut<void(QByteArray &&cached)> put, // Unknown thread.
|
|
const QByteArray &content,
|
|
const FrameRequest &request,
|
|
Quality quality,
|
|
const ColorReplacements *replacements,
|
|
std::shared_ptr<FrameRenderer> renderer)
|
|
: _timer([=] { checkNextFrameRender(); })
|
|
, _renderer(renderer ? renderer : FrameRenderer::Instance())
|
|
, _animation(
|
|
this,
|
|
std::move(get),
|
|
std::move(put),
|
|
content,
|
|
request,
|
|
quality,
|
|
replacements) {
|
|
}
|
|
|
|
SinglePlayer::SinglePlayer(
|
|
int keysCount,
|
|
FnMut<void(int, FnMut<void(QByteArray &&)>)> get,
|
|
FnMut<void(int, QByteArray &&)> put,
|
|
const QByteArray &content,
|
|
const FrameRequest &request,
|
|
Quality quality,
|
|
const ColorReplacements *replacements,
|
|
std::shared_ptr<FrameRenderer> renderer)
|
|
: _timer([=] { checkNextFrameRender(); })
|
|
, _renderer(renderer ? renderer : FrameRenderer::Instance())
|
|
, _animation(
|
|
this,
|
|
keysCount,
|
|
std::move(get),
|
|
std::move(put),
|
|
content,
|
|
request,
|
|
quality,
|
|
replacements) {
|
|
}
|
|
|
|
SinglePlayer::~SinglePlayer() {
|
|
if (_state) {
|
|
_renderer->remove(_state);
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<FrameProvider> SinglePlayer::SharedProvider(
|
|
int keysCount,
|
|
FnMut<void(int, FnMut<void(QByteArray &&)>)> get,
|
|
FnMut<void(int, QByteArray &&)> put,
|
|
const QByteArray &content,
|
|
const FrameRequest &request,
|
|
Quality quality,
|
|
const ColorReplacements *replacements) {
|
|
auto factory = [=, get = std::move(get), put = std::move(put)](
|
|
FnMut<void(std::unique_ptr<FrameProvider>)> done) mutable {
|
|
#ifdef LOTTIE_USE_CACHE
|
|
struct State {
|
|
std::atomic<int> left = 0;
|
|
std::vector<QByteArray> caches;
|
|
FnMut<void(int, QByteArray &&cached)> put;
|
|
FnMut<void(std::unique_ptr<FrameProvider>)> done;
|
|
};
|
|
const auto state = std::make_shared<State>();
|
|
state->left = keysCount;
|
|
state->put = std::move(put);
|
|
state->done = std::move(done);
|
|
state->caches.resize(keysCount);
|
|
for (auto i = 0; i != keysCount; ++i) {
|
|
get(i, [=](QByteArray &&cached) {
|
|
state->caches[i] = std::move(cached);
|
|
if (--state->left) {
|
|
return;
|
|
}
|
|
crl::async([=, done = std::move(state->done)]() mutable {
|
|
if (const auto error = ContentError(content)) {
|
|
done(nullptr);
|
|
return;
|
|
}
|
|
auto provider = std::make_unique<FrameProviderCachedMulti>(
|
|
content,
|
|
std::move(state->put),
|
|
std::move(state->caches),
|
|
request,
|
|
quality,
|
|
replacements);
|
|
done(provider->valid() ? std::move(provider) : nullptr);
|
|
});
|
|
});
|
|
}
|
|
#else // LOTTIE_USE_CACHE
|
|
crl::async([=, done = std::move(done)]() mutable {
|
|
if (const auto error = ContentError(content)) {
|
|
done(nullptr);
|
|
return;
|
|
}
|
|
auto provider = std::make_unique<FrameProviderDirect>(quality);
|
|
done(provider->load(content, replacements)
|
|
? std::move(provider)
|
|
: nullptr);
|
|
});
|
|
#endif // LOTTIE_USE_CACHE
|
|
};
|
|
return std::make_shared<FrameProviderShared>(std::move(factory));
|
|
}
|
|
|
|
SinglePlayer::SinglePlayer(
|
|
std::shared_ptr<FrameProvider> provider,
|
|
const FrameRequest &request,
|
|
std::shared_ptr<FrameRenderer> renderer)
|
|
: _timer([=] { checkNextFrameRender(); })
|
|
, _renderer(renderer ? renderer : FrameRenderer::Instance())
|
|
, _animation(this, std::move(provider), request) {
|
|
}
|
|
|
|
void SinglePlayer::start(
|
|
not_null<Animation*> animation,
|
|
std::unique_ptr<SharedState> state) {
|
|
Expects(animation == &_animation);
|
|
|
|
_state = state.get();
|
|
auto information = state->information();
|
|
state->start(this, crl::now());
|
|
const auto request = state->frameForPaint()->request;
|
|
_renderer->append(std::move(state), request);
|
|
|
|
crl::on_main_update_requests(
|
|
) | rpl::on_next([=] {
|
|
checkStep();
|
|
}, _lifetime);
|
|
|
|
// This may destroy the player.
|
|
_updates.fire({ std::move(information) });
|
|
}
|
|
|
|
void SinglePlayer::failed(not_null<Animation*> animation, Error error) {
|
|
Expects(animation == &_animation);
|
|
|
|
_updates.fire_error(std::move(error));
|
|
}
|
|
|
|
rpl::producer<Update, Error> SinglePlayer::updates() const {
|
|
return _updates.events();
|
|
}
|
|
|
|
bool SinglePlayer::ready() const {
|
|
return _animation.ready();
|
|
}
|
|
|
|
QImage SinglePlayer::frame() const {
|
|
return _animation.frame();
|
|
}
|
|
|
|
QImage SinglePlayer::frame(const FrameRequest &request) const {
|
|
return _animation.frame(request);
|
|
}
|
|
|
|
Animation::FrameInfo SinglePlayer::frameInfo(
|
|
const FrameRequest &request) const {
|
|
return _animation.frameInfo(request);
|
|
}
|
|
|
|
int SinglePlayer::frameIndex() const {
|
|
return _animation.frameIndex();
|
|
}
|
|
|
|
int SinglePlayer::framesCount() const {
|
|
return _animation.framesCount();
|
|
}
|
|
|
|
Information SinglePlayer::information() const {
|
|
return _animation.information();
|
|
}
|
|
|
|
void SinglePlayer::checkStep() {
|
|
if (_nextFrameTime == kFrameDisplayTimeAlreadyDone) {
|
|
return;
|
|
} else if (_nextFrameTime != kTimeUnknown) {
|
|
checkNextFrameRender();
|
|
} else {
|
|
checkNextFrameAvailability();
|
|
}
|
|
}
|
|
|
|
void SinglePlayer::checkNextFrameAvailability() {
|
|
Expects(_state != nullptr);
|
|
Expects(_nextFrameTime == kTimeUnknown);
|
|
|
|
_nextFrameTime = _state->nextFrameDisplayTime();
|
|
Assert(_nextFrameTime != kFrameDisplayTimeAlreadyDone);
|
|
if (_nextFrameTime != kTimeUnknown) {
|
|
checkNextFrameRender();
|
|
}
|
|
}
|
|
|
|
void SinglePlayer::checkNextFrameRender() {
|
|
Expects(_nextFrameTime != kTimeUnknown);
|
|
|
|
const auto now = crl::now();
|
|
if (now < _nextFrameTime) {
|
|
if (!_timer.isActive()) {
|
|
_timer.callOnce(_nextFrameTime - now);
|
|
}
|
|
} else {
|
|
_timer.cancel();
|
|
renderFrame(now);
|
|
}
|
|
}
|
|
|
|
void SinglePlayer::renderFrame(crl::time now) {
|
|
_state->markFrameDisplayed(now);
|
|
_state->addTimelineDelay(now - _nextFrameTime);
|
|
|
|
_nextFrameTime = kFrameDisplayTimeAlreadyDone;
|
|
_updates.fire({ DisplayFrameRequest() });
|
|
}
|
|
|
|
void SinglePlayer::updateFrameRequest(
|
|
not_null<const Animation*> animation,
|
|
const FrameRequest &request) {
|
|
Expects(animation == &_animation);
|
|
Expects(_state != nullptr);
|
|
|
|
_renderer->updateFrameRequest(_state, request);
|
|
}
|
|
|
|
bool SinglePlayer::markFrameShown() {
|
|
Expects(_state != nullptr);
|
|
|
|
if (_nextFrameTime == kFrameDisplayTimeAlreadyDone) {
|
|
_nextFrameTime = kTimeUnknown;
|
|
}
|
|
if (_state->markFrameShown()) {
|
|
_renderer->frameShown();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace Lottie
|