From 198de85ce59350d2acc3d63fe9530e1ba93fc363 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Jul 2019 19:15:25 +0200 Subject: [PATCH] Limit to 30 FPS in small stickers. --- .../SourceFiles/boxes/sticker_set_box.cpp | 1 + .../chat_helpers/field_autocomplete.cpp | 1 + .../SourceFiles/chat_helpers/stickers.cpp | 3 +- Telegram/SourceFiles/chat_helpers/stickers.h | 2 ++ .../chat_helpers/stickers_list_widget.cpp | 1 + .../history/media/history_media_sticker.cpp | 3 +- .../SourceFiles/lottie/lottie_animation.cpp | 24 ++++++++----- .../SourceFiles/lottie/lottie_animation.h | 6 ++-- Telegram/SourceFiles/lottie/lottie_cache.cpp | 8 ++--- Telegram/SourceFiles/lottie/lottie_common.h | 5 +++ .../lottie/lottie_frame_renderer.cpp | 36 +++++++++++++++---- .../lottie/lottie_frame_renderer.h | 7 ++-- .../lottie/lottie_multi_player.cpp | 13 ++++--- .../SourceFiles/lottie/lottie_multi_player.h | 5 ++- .../lottie/lottie_single_player.cpp | 6 ++-- .../SourceFiles/lottie/lottie_single_player.h | 2 ++ Telegram/SourceFiles/window/layer_widget.cpp | 3 +- 17 files changed, 91 insertions(+), 35 deletions(-) diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index fc4cb05fb..b8e602bb8 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -485,6 +485,7 @@ void StickerSetBox::Inner::showPreview() { not_null StickerSetBox::Inner::getLottiePlayer() { if (!_lottiePlayer) { _lottiePlayer = std::make_unique( + Lottie::Quality::Default, Lottie::MakeFrameRenderer()); _lottiePlayer->updates( ) | rpl::start_with_next([=] { diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 7798a9b07..e9558b71a 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -971,6 +971,7 @@ void FieldAutocompleteInner::setupLottie(StickerSuggestion &suggestion) { st::stickerPanSize.width() - st::buttonRadius * 2, st::stickerPanSize.height() - st::buttonRadius * 2 ) * cIntRetinaFactor(), + Lottie::Quality::Default, getLottieRenderer()); suggestion.animated->updates( diff --git a/Telegram/SourceFiles/chat_helpers/stickers.cpp b/Telegram/SourceFiles/chat_helpers/stickers.cpp index e35d6643a..5aa2109b4 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers.cpp @@ -1170,10 +1170,11 @@ std::unique_ptr LottiePlayerFromDocument( not_null document, LottieSize sizeTag, QSize box, + Lottie::Quality quality, std::shared_ptr renderer) { const auto method = [&](auto &&...args) { return std::make_unique( - std::forward(args)..., std::move(renderer)); + std::forward(args)..., quality, std::move(renderer)); }; return LottieFromDocument(method, document, sizeTag, box); } diff --git a/Telegram/SourceFiles/chat_helpers/stickers.h b/Telegram/SourceFiles/chat_helpers/stickers.h index 993635b86..f5bd29985 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.h +++ b/Telegram/SourceFiles/chat_helpers/stickers.h @@ -24,6 +24,7 @@ class SinglePlayer; class MultiPlayer; class FrameRenderer; class Animation; +enum class Quality : char; } // namespace Lottie namespace Stickers { @@ -133,6 +134,7 @@ enum class LottieSize : uchar { not_null document, LottieSize sizeTag, QSize box, + Lottie::Quality quality = Lottie::Quality(), std::shared_ptr renderer = nullptr); [[nodiscard]] not_null LottieAnimationFromDocument( not_null player, diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 7bff8b309..3196311c6 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -1593,6 +1593,7 @@ void StickersListWidget::ensureLottiePlayer(Set &set) { const auto [i, ok] = _lottieData.emplace( set.id, LottieSet{ std::make_unique( + Lottie::Quality::Default, getLottieRenderer()) }); Assert(ok); const auto raw = set.lottiePlayer = i->second.player.get(); diff --git a/Telegram/SourceFiles/history/media/history_media_sticker.cpp b/Telegram/SourceFiles/history/media/history_media_sticker.cpp index d9b1f731c..7b95707f9 100644 --- a/Telegram/SourceFiles/history/media/history_media_sticker.cpp +++ b/Telegram/SourceFiles/history/media/history_media_sticker.cpp @@ -100,7 +100,8 @@ void HistorySticker::setupLottie() { _lottie = Stickers::LottiePlayerFromDocument( _data, Stickers::LottieSize::MessageHistory, - QSize(st::maxStickerSize, st::maxStickerSize) * cIntRetinaFactor()); + QSize(st::maxStickerSize, st::maxStickerSize) * cIntRetinaFactor(), + Lottie::Quality::High); _parent->data()->history()->owner().registerHeavyViewPart(_parent); _lottie->updates( diff --git a/Telegram/SourceFiles/lottie/lottie_animation.cpp b/Telegram/SourceFiles/lottie/lottie_animation.cpp index d8e8b34ec..ab21f635a 100644 --- a/Telegram/SourceFiles/lottie/lottie_animation.cpp +++ b/Telegram/SourceFiles/lottie/lottie_animation.cpp @@ -78,7 +78,8 @@ details::InitData CheckSharedState(std::unique_ptr state) { details::InitData Init( const QByteArray &content, - const FrameRequest &request) { + const FrameRequest &request, + Quality quality) { if (const auto error = ContentError(content)) { return *error; } @@ -86,7 +87,8 @@ details::InitData Init( return animation ? CheckSharedState(std::make_unique( std::move(animation), - request)) + request, + quality)) : Error::ParseFailed; } @@ -94,7 +96,8 @@ details::InitData Init( const QByteArray &content, FnMut put, const QByteArray &cached, - const FrameRequest &request) { + const FrameRequest &request, + Quality quality) { if (const auto error = ContentError(content)) { return *error; } @@ -107,7 +110,8 @@ details::InitData Init( content, std::move(animation), std::move(cache), - request)) + request, + quality)) : Error::ParseFailed; } @@ -134,7 +138,7 @@ std::shared_ptr MakeFrameRenderer() { } QImage ReadThumbnail(const QByteArray &content) { - return Init(content, FrameRequest()).match([]( + return Init(content, FrameRequest(), Quality::High).match([]( const std::unique_ptr &state) { return state->frameForPaint()->original; }, [](Error) { @@ -145,11 +149,12 @@ QImage ReadThumbnail(const QByteArray &content) { Animation::Animation( not_null player, const QByteArray &content, - const FrameRequest &request) + const FrameRequest &request, + Quality quality) : _player(player) { const auto weak = base::make_weak(this); crl::async([=] { - crl::on_main(weak, [=, data = Init(content, request)]() mutable { + crl::on_main(weak, [=, data = Init(content, request, quality)]() mutable { initDone(std::move(data)); }); }); @@ -160,12 +165,13 @@ Animation::Animation( FnMut)> get, // Main thread. FnMut put, // Unknown thread. const QByteArray &content, - const FrameRequest &request) + const FrameRequest &request, + Quality quality) : _player(player) { const auto weak = base::make_weak(this); get([=, put = std::move(put)](QByteArray &&cached) mutable { crl::async([=, put = std::move(put)]() mutable { - auto result = Init(content, std::move(put), cached, request); + auto result = Init(content, std::move(put), cached, request, quality); crl::on_main(weak, [=, data = std::move(result)]() mutable { initDone(std::move(data)); }); diff --git a/Telegram/SourceFiles/lottie/lottie_animation.h b/Telegram/SourceFiles/lottie/lottie_animation.h index 0e3c4b185..c0b84bbb0 100644 --- a/Telegram/SourceFiles/lottie/lottie_animation.h +++ b/Telegram/SourceFiles/lottie/lottie_animation.h @@ -42,13 +42,15 @@ public: Animation( not_null player, const QByteArray &content, - const FrameRequest &request); + const FrameRequest &request, + Quality quality); Animation( not_null player, FnMut)> get, // Main thread. FnMut put, // Unknown thread. const QByteArray &content, - const FrameRequest &request); + const FrameRequest &request, + Quality quality); [[nodiscard]] bool ready() const; [[nodiscard]] QImage frame() const; diff --git a/Telegram/SourceFiles/lottie/lottie_cache.cpp b/Telegram/SourceFiles/lottie/lottie_cache.cpp index c2f025e43..10eb55b35 100644 --- a/Telegram/SourceFiles/lottie/lottie_cache.cpp +++ b/Telegram/SourceFiles/lottie/lottie_cache.cpp @@ -580,11 +580,9 @@ void Cache::writeHeader() { void Cache::updateFramesReadyCount() { Expects(_data.size() >= headerSize()); - const auto serialized = qint32(_framesReady); - const auto offset = headerSize() - sizeof(qint32); - bytes::copy( - bytes::make_detached_span(_data).subspan(offset), - bytes::object_as_span(&serialized)); + QDataStream stream(&_data, QIODevice::ReadWrite); + stream.device()->seek(headerSize() - sizeof(qint32)); + stream << qint32(_framesReady); } void Cache::prepareBuffers() { diff --git a/Telegram/SourceFiles/lottie/lottie_common.h b/Telegram/SourceFiles/lottie/lottie_common.h index 6d1e5631a..7239644bd 100644 --- a/Telegram/SourceFiles/lottie/lottie_common.h +++ b/Telegram/SourceFiles/lottie/lottie_common.h @@ -50,6 +50,11 @@ struct FrameRequest { } }; +enum class Quality : char { + Default, + High, +}; + QByteArray ReadContent(const QByteArray &data, const QString &filepath); } // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp b/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp index 11eea316e..5fa32aba2 100644 --- a/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp +++ b/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp @@ -40,6 +40,22 @@ QImage CreateFrameStorage(QSize size) { return QImage(size, kImageFormat); } +int GetLottieFrameRate(not_null animation, Quality quality) { + const auto rate = int(qRound(animation->frameRate())); + return (quality == Quality::Default && rate == 60) ? (rate / 2) : rate; +} + +int GetLottieFramesCount(not_null animation, Quality quality) { + const auto rate = int(qRound(animation->frameRate())); + const auto count = int(animation->totalFrame()); + return (quality == Quality::Default && rate == 60) ? (count / 2) : count; +} + +int GetLottieFrameIndex(not_null animation, Quality quality, int index) { + const auto rate = int(qRound(animation->frameRate())); + return (quality == Quality::Default && rate == 60) ? (index * 2) : index; +} + } // namespace class FrameRendererObject final { @@ -196,8 +212,10 @@ void FrameRendererObject::queueGenerateFrames() { SharedState::SharedState( std::unique_ptr animation, - const FrameRequest &request) -: _animation(std::move(animation)) { + const FrameRequest &request, + Quality quality) +: _animation(std::move(animation)) +, _quality(quality) { construct(request); } @@ -205,9 +223,11 @@ SharedState::SharedState( const QByteArray &content, std::unique_ptr animation, std::unique_ptr cache, - const FrameRequest &request) + const FrameRequest &request, + Quality quality) : _content(content) , _animation(std::move(animation)) +, _quality(quality) , _cache(std::move(cache)) { construct(request); } @@ -241,16 +261,16 @@ void SharedState::calculateProperties() { height = _cache->originalSize().height(); } const auto rate = _animation - ? _animation->frameRate() + ? GetLottieFrameRate(_animation.get(), _quality) : _cache->frameRate(); const auto count = _animation - ? _animation->totalFrame() + ? GetLottieFramesCount(_animation.get(), _quality) : _cache->framesCount(); _size = QSize( (width > 0 && width < kMaxSize) ? int(width) : 0, (height > 0 && height < kMaxSize) ? int(height) : 0); - _frameRate = (rate >= 1. && rate <= kMaxFrameRate) ? int(rate) : 0; + _frameRate = (rate > 0 && rate <= kMaxFrameRate) ? int(rate) : 0; _framesCount = (count > 0 && count <= kMaxFramesCount) ? int(count) : 0; } @@ -282,7 +302,9 @@ void SharedState::renderFrame( image.width(), image.height(), image.bytesPerLine()); - _animation->renderSync(index, surface); + _animation->renderSync( + GetLottieFrameIndex(_animation.get(), _quality, index), + surface); if (_cache) { _cache->appendFrame(image, request, index); if (_cache->framesReady() == _cache->framesCount()) { diff --git a/Telegram/SourceFiles/lottie/lottie_frame_renderer.h b/Telegram/SourceFiles/lottie/lottie_frame_renderer.h index 07d0ae90d..94e8928cb 100644 --- a/Telegram/SourceFiles/lottie/lottie_frame_renderer.h +++ b/Telegram/SourceFiles/lottie/lottie_frame_renderer.h @@ -51,12 +51,14 @@ class SharedState { public: SharedState( std::unique_ptr animation, - const FrameRequest &request); + const FrameRequest &request, + Quality quality); SharedState( const QByteArray &content, std::unique_ptr animation, std::unique_ptr cache, - const FrameRequest &request); + const FrameRequest &request, + Quality quality); void start( not_null owner, @@ -98,6 +100,7 @@ private: QByteArray _content; std::unique_ptr _animation; + Quality _quality = Quality::Default; // crl::queue changes 0,2,4,6 to 1,3,5,7. // main thread changes 1,3,5,7 to 2,4,6,0. diff --git a/Telegram/SourceFiles/lottie/lottie_multi_player.cpp b/Telegram/SourceFiles/lottie/lottie_multi_player.cpp index 6c650cdca..b3f845f62 100644 --- a/Telegram/SourceFiles/lottie/lottie_multi_player.cpp +++ b/Telegram/SourceFiles/lottie/lottie_multi_player.cpp @@ -15,8 +15,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Lottie { -MultiPlayer::MultiPlayer(std::shared_ptr renderer) -: _timer([=] { checkNextFrameRender(); }) +MultiPlayer::MultiPlayer( + Quality quality, + std::shared_ptr renderer) +: _quality(quality) +, _timer([=] { checkNextFrameRender(); }) , _renderer(renderer ? std::move(renderer) : FrameRenderer::Instance()) { crl::on_main_update_requests( ) | rpl::start_with_next([=] { @@ -43,7 +46,8 @@ not_null MultiPlayer::append( std::move(get), std::move(put), content, - request)); + request, + _quality)); return _animations.back().get(); } @@ -53,7 +57,8 @@ not_null MultiPlayer::append( _animations.push_back(std::make_unique( this, content, - request)); + request, + _quality)); return _animations.back().get(); } diff --git a/Telegram/SourceFiles/lottie/lottie_multi_player.h b/Telegram/SourceFiles/lottie/lottie_multi_player.h index 387316ccc..4a6349268 100644 --- a/Telegram/SourceFiles/lottie/lottie_multi_player.h +++ b/Telegram/SourceFiles/lottie/lottie_multi_player.h @@ -29,7 +29,9 @@ struct MultiUpdate { class MultiPlayer final : public Player { public: - explicit MultiPlayer(std::shared_ptr renderer = nullptr); + MultiPlayer( + Quality quality = Quality::Default, + std::shared_ptr renderer = nullptr); ~MultiPlayer(); void start( @@ -89,6 +91,7 @@ private: void unpauseAndKeepUp(not_null animation); void removeNow(not_null animation); + Quality _quality = Quality::Default; base::Timer _timer; const std::shared_ptr _renderer; std::vector> _animations; diff --git a/Telegram/SourceFiles/lottie/lottie_single_player.cpp b/Telegram/SourceFiles/lottie/lottie_single_player.cpp index 849f7bd6c..2d65c67f8 100644 --- a/Telegram/SourceFiles/lottie/lottie_single_player.cpp +++ b/Telegram/SourceFiles/lottie/lottie_single_player.cpp @@ -14,8 +14,9 @@ namespace Lottie { SinglePlayer::SinglePlayer( const QByteArray &content, const FrameRequest &request, + Quality quality, std::shared_ptr renderer) -: _animation(this, content, request) +: _animation(this, content, request, quality) , _timer([=] { checkNextFrameRender(); }) , _renderer(renderer ? renderer : FrameRenderer::Instance()) { } @@ -25,8 +26,9 @@ SinglePlayer::SinglePlayer( FnMut put, // Unknown thread. const QByteArray &content, const FrameRequest &request, + Quality quality, std::shared_ptr renderer) -: _animation(this, std::move(get), std::move(put), content, request) +: _animation(this, std::move(get), std::move(put), content, request, quality) , _timer([=] { checkNextFrameRender(); }) , _renderer(renderer ? renderer : FrameRenderer::Instance()) { } diff --git a/Telegram/SourceFiles/lottie/lottie_single_player.h b/Telegram/SourceFiles/lottie/lottie_single_player.h index f5c66e713..1689402fa 100644 --- a/Telegram/SourceFiles/lottie/lottie_single_player.h +++ b/Telegram/SourceFiles/lottie/lottie_single_player.h @@ -31,12 +31,14 @@ public: SinglePlayer( const QByteArray &content, const FrameRequest &request, + Quality quality = Quality::Default, std::shared_ptr renderer = nullptr); SinglePlayer( FnMut)> get, // Main thread. FnMut put, // Unknown thread. const QByteArray &content, const FrameRequest &request, + Quality quality = Quality::Default, std::shared_ptr renderer = nullptr); ~SinglePlayer(); diff --git a/Telegram/SourceFiles/window/layer_widget.cpp b/Telegram/SourceFiles/window/layer_widget.cpp index b312e5f7b..3b0484383 100644 --- a/Telegram/SourceFiles/window/layer_widget.cpp +++ b/Telegram/SourceFiles/window/layer_widget.cpp @@ -1049,7 +1049,8 @@ void MediaPreviewWidget::setupLottie() { _lottie = std::make_unique( Lottie::ReadContent(_document->data(), _document->filepath()), - Lottie::FrameRequest{ currentDimensions() * cIntRetinaFactor() }); + Lottie::FrameRequest{ currentDimensions() * cIntRetinaFactor() }, + Lottie::Quality::High); _lottie->updates( ) | rpl::start_with_next([=](Lottie::Update update) {