// 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 namespace Lottie { SinglePlayer::SinglePlayer( const QByteArray &content, const FrameRequest &request, Quality quality, const ColorReplacements *replacements, std::shared_ptr renderer) : _timer([=] { checkNextFrameRender(); }) , _renderer(renderer ? renderer : FrameRenderer::Instance()) , _animation(this, content, request, quality, replacements) { } SinglePlayer::SinglePlayer( FnMut)> get, // Main thread. FnMut put, // Unknown thread. const QByteArray &content, const FrameRequest &request, Quality quality, const ColorReplacements *replacements, std::shared_ptr 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)> get, FnMut put, const QByteArray &content, const FrameRequest &request, Quality quality, const ColorReplacements *replacements, std::shared_ptr 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 SinglePlayer::SharedProvider( int keysCount, FnMut)> get, FnMut put, const QByteArray &content, const FrameRequest &request, Quality quality, const ColorReplacements *replacements) { auto factory = [=, get = std::move(get), put = std::move(put)]( FnMut)> done) mutable { #ifdef LOTTIE_USE_CACHE struct State { std::atomic left = 0; std::vector caches; FnMut put; FnMut)> done; }; const auto state = std::make_shared(); 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( 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(quality); done(provider->load(content, replacements) ? std::move(provider) : nullptr); }); #endif // LOTTIE_USE_CACHE }; return std::make_shared(std::move(factory)); } SinglePlayer::SinglePlayer( std::shared_ptr provider, const FrameRequest &request, std::shared_ptr renderer) : _timer([=] { checkNextFrameRender(); }) , _renderer(renderer ? renderer : FrameRenderer::Instance()) , _animation(this, std::move(provider), request) { } void SinglePlayer::start( not_null animation, std::unique_ptr 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::start_with_next([=] { checkStep(); }, _lifetime); // This may destroy the player. _updates.fire({ std::move(information) }); } void SinglePlayer::failed(not_null animation, Error error) { Expects(animation == &_animation); _updates.fire_error(std::move(error)); } rpl::producer 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 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