diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index 8852099848..a07ce4a8f4 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -21,7 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/text/text_utilities.h" #include "ui/emoji_config.h" -#include "lottie/lottie_animation.h" +#include "lottie/lottie_single_player.h" #include "window/window_session_controller.h" #include "auth_session.h" #include "apiwrap.h" @@ -65,7 +65,7 @@ protected: private: struct Element { not_null document; - std::unique_ptr animated; + std::unique_ptr animated; Ui::Animations::Simple overAnimation; }; @@ -517,7 +517,7 @@ void StickerSetBox::Inner::setupLottie(int index) { auto &element = _elements[index]; const auto document = element.document; - element.animated = Stickers::LottieFromDocument( + element.animated = Stickers::LottiePlayerFromDocument( document, Stickers::LottieSize::StickerSet, boundingBoxSize() * cIntRetinaFactor()); @@ -566,9 +566,10 @@ void StickerSetBox::Inner::paintSticker( if (!paused) { element.animated->markFrameShown(); } + const auto frame = element.animated->frame(request); p.drawImage( - QRect(ppos, QSize(w, h)), - element.animated->frame(request)); + QRect(ppos, frame.size() / cIntRetinaFactor()), + frame); } else if (const auto image = document->getStickerSmall()) { p.drawPixmapLeft( ppos, diff --git a/Telegram/SourceFiles/chat_helpers/stickers.cpp b/Telegram/SourceFiles/chat_helpers/stickers.cpp index 2635cdc881..45bda32934 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers.cpp @@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "ui/toast/toast.h" #include "ui/emoji_config.h" -#include "lottie/lottie_animation.h" +#include "lottie/lottie_single_player.h" #include "styles/style_chat_helpers.h" namespace Stickers { @@ -1092,7 +1092,7 @@ RecentStickerPack &GetRecentPack() { return cRefRecentStickers(); } -std::unique_ptr LottieFromDocument( +std::unique_ptr LottiePlayerFromDocument( not_null document, LottieSize sizeTag, QSize box) { @@ -1100,9 +1100,8 @@ std::unique_ptr LottieFromDocument( const auto filepath = document->filepath(); if (box.width() & box.height() > kDontCacheLottieAfterArea) { // Don't use frame caching for large stickers. - return Lottie::FromContent( - data, - filepath, + return std::make_unique( + Lottie::ReadContent(data, filepath), Lottie::FrameRequest{ box }); } if (const auto baseKey = document->bigFileBaseCacheKey()) { @@ -1121,14 +1120,15 @@ std::unique_ptr LottieFromDocument( weak->data().cacheBigFile().put(key, std::move(data)); }); }; - return Lottie::FromCached( + return std::make_unique( get, put, - data, - filepath, + Lottie::ReadContent(data, filepath), Lottie::FrameRequest{ box }); } - return Lottie::FromContent(data, filepath, Lottie::FrameRequest{ box }); + return std::make_unique( + Lottie::ReadContent(data, filepath), + Lottie::FrameRequest{ box }); } } // namespace Stickers diff --git a/Telegram/SourceFiles/chat_helpers/stickers.h b/Telegram/SourceFiles/chat_helpers/stickers.h index a1a6fe6eb9..939f9f157a 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.h +++ b/Telegram/SourceFiles/chat_helpers/stickers.h @@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class DocumentData; namespace Lottie { -class Animation; +class SinglePlayer; } // namespace Lottie namespace Stickers { @@ -113,11 +113,9 @@ enum class LottieSize : uchar { MessageHistory, StickerSet, StickersPanel, - StickersColumn, - MediaPreview, }; -std::unique_ptr LottieFromDocument( +std::unique_ptr LottiePlayerFromDocument( not_null document, LottieSize sizeTag, QSize box); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 4943d51329..beaa39bf29 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/animations.h" #include "ui/effects/ripple_animation.h" #include "ui/image/image.h" -#include "lottie/lottie_animation.h" +#include "lottie/lottie_single_player.h" #include "boxes/stickers_box.h" #include "inline_bots/inline_bot_result.h" #include "chat_helpers/stickers.h" @@ -1368,9 +1368,9 @@ void StickersListWidget::setupLottie(Set &set, int section, int index) { auto &sticker = set.stickers[index]; const auto document = sticker.document; - sticker.animated = Stickers::LottieFromDocument( + sticker.animated = Stickers::LottiePlayerFromDocument( document, - Stickers::LottieSize::StickersColumn, // #TODO stickers + Stickers::LottieSize::StickersPanel, boundingBoxSize() * cIntRetinaFactor()); const auto animation = sticker.animated.get(); @@ -1424,9 +1424,10 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section, if (!paused) { sticker.animated->markFrameShown(); } + const auto frame = sticker.animated->frame(request); p.drawImage( - QRect(ppos, QSize(w, h)), - sticker.animated->frame(request)); + QRect(ppos, frame.size() / cIntRetinaFactor()), + frame); } else if (const auto image = document->getStickerSmall()) { if (image->loaded()) { p.drawPixmapLeft( diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index 7d5d511359..9f7495aa4b 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -22,7 +22,7 @@ class RippleAnimation; } // namespace Ui namespace Lottie { -class Animation; +class SinglePlayer; } // namespace Lottie namespace ChatHelpers { @@ -98,20 +98,22 @@ private: }; struct OverSticker { - int section; - int index; - bool overDelete; + int section = 0; + int index = 0; + bool overDelete = false; }; struct OverSet { - int section; + int section = 0; }; struct OverButton { - int section; + int section = 0; }; struct OverGroupAdd { }; friend inline bool operator==(OverSticker a, OverSticker b) { - return (a.section == b.section) && (a.index == b.index) && (a.overDelete == b.overDelete); + return (a.section == b.section) + && (a.index == b.index) + && (a.overDelete == b.overDelete); } friend inline bool operator==(OverSet a, OverSet b) { return (a.section == b.section); @@ -122,7 +124,11 @@ private: friend inline bool operator==(OverGroupAdd a, OverGroupAdd b) { return true; } - using OverState = base::optional_variant; + using OverState = base::optional_variant< + OverSticker, + OverSet, + OverButton, + OverGroupAdd>; struct SectionInfo { int section = 0; @@ -135,7 +141,7 @@ private: struct Sticker { not_null document; - std::unique_ptr animated; + std::unique_ptr animated; }; struct Set { diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp index 59fa446c50..67e2156937 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp @@ -45,14 +45,16 @@ TabbedPanel::TabbedPanel( , _maxContentHeight(st::emojiPanMaxHeight) { _selector->setParent(this); _selector->setRoundRadius(st::buttonRadius); - _selector->setAfterShownCallback([this](SelectorTab tab) { - if (tab == SelectorTab::Gifs) { - _controller->enableGifPauseReason(Window::GifPauseReason::SavedGifs); + _selector->setAfterShownCallback([=](SelectorTab tab) { + if (tab == SelectorTab::Gifs || tab == SelectorTab::Stickers) { + _controller->enableGifPauseReason( + Window::GifPauseReason::SavedGifs); } }); - _selector->setBeforeHidingCallback([this](SelectorTab tab) { - if (tab == SelectorTab::Gifs) { - _controller->disableGifPauseReason(Window::GifPauseReason::SavedGifs); + _selector->setBeforeHidingCallback([=](SelectorTab tab) { + if (tab == SelectorTab::Gifs || tab == SelectorTab::Stickers) { + _controller->disableGifPauseReason( + Window::GifPauseReason::SavedGifs); } }); _selector->showRequests( diff --git a/Telegram/SourceFiles/history/media/history_media_sticker.cpp b/Telegram/SourceFiles/history/media/history_media_sticker.cpp index 357d03a370..0192c1854f 100644 --- a/Telegram/SourceFiles/history/media/history_media_sticker.cpp +++ b/Telegram/SourceFiles/history/media/history_media_sticker.cpp @@ -20,7 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" // isGifPausedAtLeastFor. #include "data/data_session.h" #include "data/data_document.h" -#include "lottie/lottie_animation.h" +#include "lottie/lottie_single_player.h" #include "styles/style_history.h" namespace { @@ -97,7 +97,7 @@ QSize HistorySticker::countCurrentSize(int newWidth) { } void HistorySticker::setupLottie() { - _lottie = Stickers::LottieFromDocument( + _lottie = Stickers::LottiePlayerFromDocument( _data, Stickers::LottieSize::MessageHistory, QSize(st::maxStickerSize, st::maxStickerSize) * cIntRetinaFactor()); diff --git a/Telegram/SourceFiles/history/media/history_media_sticker.h b/Telegram/SourceFiles/history/media/history_media_sticker.h index 484045251d..e92919b64f 100644 --- a/Telegram/SourceFiles/history/media/history_media_sticker.h +++ b/Telegram/SourceFiles/history/media/history_media_sticker.h @@ -15,7 +15,7 @@ struct HistoryMessageReply; struct HistoryMessageForwarded; namespace Lottie { -class Animation; +class SinglePlayer; } // namespace Lottie class HistorySticker : public HistoryMedia { @@ -75,7 +75,7 @@ private: ClickHandlerPtr _packLink; not_null _data; QString _emoji; - std::unique_ptr _lottie; + std::unique_ptr _lottie; rpl::lifetime _lifetime; }; diff --git a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp index 9b5ae28fba..ad46776311 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp @@ -932,7 +932,8 @@ Widget::~Widget() = default; void Widget::hideFinished() { hide(); - _controller->disableGifPauseReason(Window::GifPauseReason::InlineResults); + _controller->disableGifPauseReason( + Window::GifPauseReason::InlineResults); _inner->hideFinish(true); _a_show.stop(); @@ -953,7 +954,8 @@ void Widget::showStarted() { recountContentMaxHeight(); _inner->preloadImages(); show(); - _controller->enableGifPauseReason(Window::GifPauseReason::InlineResults); + _controller->enableGifPauseReason( + Window::GifPauseReason::InlineResults); startShowAnimation(); } else if (_hiding) { startOpacityAnimation(false); diff --git a/Telegram/SourceFiles/lottie/lottie_animation.cpp b/Telegram/SourceFiles/lottie/lottie_animation.cpp index aa4b97ecbf..2af8da3b14 100644 --- a/Telegram/SourceFiles/lottie/lottie_animation.cpp +++ b/Telegram/SourceFiles/lottie/lottie_animation.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lottie/lottie_frame_renderer.h" #include "lottie/lottie_cache.h" +#include "lottie/lottie_player.h" #include "base/algorithm.h" #include "zlib.h" #include "logs.h" @@ -56,17 +57,6 @@ std::string UnpackGzip(const QByteArray &bytes) { return result; } -QByteArray ReadFile(const QString &filepath) { - auto f = QFile(filepath); - return (f.size() <= kMaxFileSize && f.open(QIODevice::ReadOnly)) - ? f.readAll() - : QByteArray(); -} - -QByteArray ReadContent(const QByteArray &data, const QString &filepath) { - return data.isEmpty() ? ReadFile(filepath) : base::duplicate(data); -} - std::optional ContentError(const QByteArray &content) { if (content.size() > kMaxFileSize) { qWarning() << "Lottie Error: Too large file: " << content.size(); @@ -140,26 +130,6 @@ std::unique_ptr CreateFromContent( } // namespace details -std::unique_ptr FromContent( - const QByteArray &data, - const QString &filepath, - const FrameRequest &request) { - return std::make_unique(ReadContent(data, filepath), request); -} - -std::unique_ptr FromCached( - FnMut)> get, // Main thread. - FnMut put, // Unknown thread. - const QByteArray &data, - const QString &filepath, - const FrameRequest &request) { - return std::make_unique( - std::move(get), - std::move(put), - ReadContent(data, filepath), - request); -} - QImage ReadThumbnail(const QByteArray &content) { return Init(content, FrameRequest()).match([]( const std::unique_ptr &state) { @@ -169,8 +139,11 @@ QImage ReadThumbnail(const QByteArray &content) { }); } -Animation::Animation(const QByteArray &content, const FrameRequest &request) -: _timer([=] { checkNextFrameRender(); }) { +Animation::Animation( + not_null player, + const QByteArray &content, + const FrameRequest &request) +: _player(player) { const auto weak = base::make_weak(this); crl::async([=] { crl::on_main(weak, [=, data = Init(content, request)]() mutable { @@ -180,11 +153,12 @@ Animation::Animation(const QByteArray &content, const FrameRequest &request) } Animation::Animation( + not_null player, FnMut)> get, // Main thread. FnMut put, // Unknown thread. const QByteArray &content, const FrameRequest &request) -: _timer([=] { checkNextFrameRender(); }) { +: _player(player) { const auto weak = base::make_weak(this); get([=, put = std::move(put)](QByteArray &&cached) mutable { crl::async([=, put = std::move(put)]() mutable { @@ -196,11 +170,8 @@ Animation::Animation( }); } -Animation::~Animation() { - if (_renderer) { - Assert(_state != nullptr); - _renderer->remove(_state); - } +bool Animation::ready() const { + return (_state != nullptr); } void Animation::initDone(details::InitData &&data) { @@ -214,97 +185,24 @@ void Animation::initDone(details::InitData &&data) { void Animation::parseDone(std::unique_ptr state) { Expects(state != nullptr); - auto information = state->information(); _state = state.get(); - _state->start(this, crl::now()); - _renderer = FrameRenderer::Instance(); - _renderer->append(std::move(state)); - _updates.fire({ std::move(information) }); - - crl::on_main_update_requests( - ) | rpl::start_with_next([=] { - checkStep(); - }, _lifetime); + _player->start(this, std::move(state)); } void Animation::parseFailed(Error error) { - _updates.fire_error(std::move(error)); + _player->failed(this, error); } QImage Animation::frame(const FrameRequest &request) const { - Expects(_renderer != nullptr); + Expects(_state != nullptr); const auto frame = _state->frameForPaint(); const auto changed = (frame->request != request); if (changed) { frame->request = request; - _renderer->updateFrameRequest(_state, request); + _player->updateFrameRequest(this, request); } return PrepareFrameByRequest(frame, !changed); } -rpl::producer Animation::updates() const { - return _updates.events(); -} - -bool Animation::ready() const { - return (_renderer != nullptr); -} - -crl::time Animation::markFrameDisplayed(crl::time now) { - Expects(_renderer != nullptr); - - const auto result = _state->markFrameDisplayed(now); - - return result; -} - -crl::time Animation::markFrameShown() { - Expects(_renderer != nullptr); - - const auto result = _state->markFrameShown(); - _renderer->frameShown(_state); - - return result; -} - -void Animation::checkStep() { - if (_nextFrameTime != kTimeUnknown) { - checkNextFrameRender(); - } else { - checkNextFrameAvailability(); - } -} - -void Animation::checkNextFrameAvailability() { - Expects(_renderer != nullptr); - - _nextFrameTime = _state->nextFrameDisplayTime(); - if (_nextFrameTime != kTimeUnknown) { - checkStep(); - } -} - -void Animation::checkNextFrameRender() { - Expects(_nextFrameTime != kTimeUnknown); - - const auto now = crl::now(); - if (now < _nextFrameTime) { - if (!_timer.isActive()) { - _timer.callOnce(_nextFrameTime - now); - } - } else { - _timer.cancel(); - - _nextFrameTime = kTimeUnknown; - const auto position = markFrameDisplayed(now); - _updates.fire({ DisplayFrameRequest{ position } }); - } -} - -//void Animation::play(const PlaybackOptions &options) { -// _options = options; -// _started = crl::now(); -//} - } // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_animation.h b/Telegram/SourceFiles/lottie/lottie_animation.h index 3c4271d422..21e64288b0 100644 --- a/Telegram/SourceFiles/lottie/lottie_animation.h +++ b/Telegram/SourceFiles/lottie/lottie_animation.h @@ -7,11 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "base/basic_types.h" -#include "base/flat_map.h" -#include "base/weak_ptr.h" -#include "base/timer.h" #include "lottie/lottie_common.h" +#include "base/weak_ptr.h" #include #include @@ -29,23 +26,10 @@ class Animation; namespace Lottie { -inline constexpr auto kMaxFileSize = 1024 * 1024; - +class Player; class SharedState; -class Animation; class FrameRenderer; -std::unique_ptr FromContent( - const QByteArray &data, - const QString &filepath, - const FrameRequest &request); -std::unique_ptr FromCached( - FnMut)> get, // Main thread. - FnMut put, // Unknown thread. - const QByteArray &data, - const QString &filepath, - const FrameRequest &request); - QImage ReadThumbnail(const QByteArray &content); namespace details { @@ -59,47 +43,27 @@ std::unique_ptr CreateFromContent( class Animation final : public base::has_weak_ptr { public: - explicit Animation( + Animation( + not_null player, const QByteArray &content, const FrameRequest &request); Animation( + not_null player, FnMut)> get, // Main thread. FnMut put, // Unknown thread. const QByteArray &content, const FrameRequest &request); - ~Animation(); - - //void play(const PlaybackOptions &options); - - [[nodiscard]] QImage frame(const FrameRequest &request) const; - - [[nodiscard]] rpl::producer updates() const; [[nodiscard]] bool ready() const; - - // Returns frame position, if any frame was marked as displayed. - crl::time markFrameDisplayed(crl::time now); - crl::time markFrameShown(); - - void checkStep(); + [[nodiscard]] QImage frame(const FrameRequest &request) const; private: void initDone(details::InitData &&data); void parseDone(std::unique_ptr state); void parseFailed(Error error); - void checkNextFrameAvailability(); - void checkNextFrameRender(); - - //crl::time _started = 0; - //PlaybackOptions _options; - - base::Timer _timer; - crl::time _nextFrameTime = kTimeUnknown; + not_null _player; SharedState *_state = nullptr; - std::shared_ptr _renderer; - rpl::event_stream _updates; - rpl::lifetime _lifetime; }; diff --git a/Telegram/SourceFiles/lottie/lottie_common.cpp b/Telegram/SourceFiles/lottie/lottie_common.cpp new file mode 100644 index 0000000000..32dadff9cc --- /dev/null +++ b/Telegram/SourceFiles/lottie/lottie_common.cpp @@ -0,0 +1,30 @@ +/* +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_common.h" + +#include "base/algorithm.h" + +#include + +namespace Lottie { +namespace { + +QByteArray ReadFile(const QString &filepath) { + auto f = QFile(filepath); + return (f.size() <= kMaxFileSize && f.open(QIODevice::ReadOnly)) + ? f.readAll() + : QByteArray(); +} + +} // namespace + +QByteArray ReadContent(const QByteArray &data, const QString &filepath) { + return data.isEmpty() ? ReadFile(filepath) : base::duplicate(data); +} + +} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_common.h b/Telegram/SourceFiles/lottie/lottie_common.h index c832fdde3a..fc6d3f1419 100644 --- a/Telegram/SourceFiles/lottie/lottie_common.h +++ b/Telegram/SourceFiles/lottie/lottie_common.h @@ -16,15 +16,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Lottie { -constexpr auto kTimeUnknown = std::numeric_limits::min(); +inline constexpr auto kTimeUnknown = std::numeric_limits::min(); +inline constexpr auto kMaxFileSize = 1024 * 1024; class Animation; -struct PlaybackOptions { - float64 speed = 1.; - bool loop = true; -}; - struct Information { int frameRate = 0; int framesCount = 0; @@ -73,4 +69,6 @@ struct FrameRequest { } }; +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 dc4af1879e..885da69444 100644 --- a/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp +++ b/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "lottie/lottie_frame_renderer.h" +#include "lottie/lottie_player.h" #include "lottie/lottie_animation.h" #include "lottie/lottie_cache.h" #include "logs.h" @@ -290,7 +291,7 @@ void SharedState::init(QImage cover, const FrameRequest &request) { _counter.store(0, std::memory_order_release); } -void SharedState::start(not_null owner, crl::time now) { +void SharedState::start(not_null owner, crl::time now) { _owner = owner; _started = now; } diff --git a/Telegram/SourceFiles/lottie/lottie_frame_renderer.h b/Telegram/SourceFiles/lottie/lottie_frame_renderer.h index 5f881fb308..81343b30c0 100644 --- a/Telegram/SourceFiles/lottie/lottie_frame_renderer.h +++ b/Telegram/SourceFiles/lottie/lottie_frame_renderer.h @@ -27,7 +27,7 @@ inline constexpr auto kMaxFrameRate = 120; inline constexpr auto kMaxSize = 3096; inline constexpr auto kMaxFramesCount = 600; -class Animation; +class Player; class Cache; struct Frame { @@ -55,7 +55,7 @@ public: std::unique_ptr cache, const FrameRequest &request); - void start(not_null owner, crl::time now); + void start(not_null owner, crl::time now); [[nodiscard]] Information information() const; [[nodiscard]] bool initialized() const; @@ -91,7 +91,7 @@ private: static constexpr auto kFramesCount = 4; std::array _frames; - base::weak_ptr _owner; + base::weak_ptr _owner; crl::time _started = kTimeUnknown; crl::time _duration = kTimeUnknown; int _frameIndex = 0; @@ -111,6 +111,7 @@ public: static std::shared_ptr Instance(); void append(std::unique_ptr entry); + void updateFrameRequest( not_null entry, const FrameRequest &request); diff --git a/Telegram/SourceFiles/lottie/lottie_player.h b/Telegram/SourceFiles/lottie/lottie_player.h new file mode 100644 index 0000000000..47b5aaad43 --- /dev/null +++ b/Telegram/SourceFiles/lottie/lottie_player.h @@ -0,0 +1,42 @@ +/* +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 +*/ +#pragma once + +#include "lottie/lottie_common.h" +#include "base/weak_ptr.h" + +#include + +namespace Lottie { + +class SharedState; + +class Player : public base::has_weak_ptr { +public: + virtual void start( + not_null animation, + std::unique_ptr state) = 0; + virtual void failed(not_null animation, Error error) = 0; + + [[nodiscard]] virtual rpl::producer updates() = 0; + + virtual void updateFrameRequest( + not_null animation, + const FrameRequest &request) = 0; + + // Returns frame position, if any frame was marked as displayed. + virtual crl::time markFrameDisplayed(crl::time now) = 0; + virtual crl::time markFrameShown() = 0; + + virtual void checkStep() = 0; + + virtual ~Player() = default; + +}; + +} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_single_player.cpp b/Telegram/SourceFiles/lottie/lottie_single_player.cpp new file mode 100644 index 0000000000..a618cc67a8 --- /dev/null +++ b/Telegram/SourceFiles/lottie/lottie_single_player.cpp @@ -0,0 +1,135 @@ +/* +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_single_player.h" + +#include "lottie/lottie_frame_renderer.h" + +namespace Lottie { +namespace { + +} // namespace + +SinglePlayer::SinglePlayer( + const QByteArray &content, + const FrameRequest &request) +: _animation(this, content, request) +, _timer([=] { checkNextFrameRender(); }) +, _renderer(FrameRenderer::Instance()) { +} + +SinglePlayer::SinglePlayer( + FnMut)> get, // Main thread. + FnMut put, // Unknown thread. + const QByteArray &content, + const FrameRequest &request) +: _animation(this, std::move(get), std::move(put), content, request) +, _timer([=] { checkNextFrameRender(); }) +, _renderer(FrameRenderer::Instance()) { +} + +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()); + _renderer = FrameRenderer::Instance(); + _renderer->append(std::move(state)); + _updates.fire({ std::move(information) }); + + crl::on_main_update_requests( + ) | rpl::start_with_next([=] { + checkStep(); + }, _lifetime); +} + +void SinglePlayer::failed(not_null animation, Error error) { + Expects(animation == &_animation); + + _updates.fire_error(std::move(error)); +} + +SinglePlayer::~SinglePlayer() { + if (_state) { + _renderer->remove(_state); + } +} + +rpl::producer SinglePlayer::updates() { + return _updates.events(); +} + +bool SinglePlayer::ready() const { + return _animation.ready(); +} + +QImage SinglePlayer::frame(const FrameRequest &request) const { + return _animation.frame(request); +} + +void SinglePlayer::checkStep() { + if (_nextFrameTime != kTimeUnknown) { + checkNextFrameRender(); + } else { + checkNextFrameAvailability(); + } +} + +void SinglePlayer::checkNextFrameAvailability() { + Expects(_state != nullptr); + + _nextFrameTime = _state->nextFrameDisplayTime(); + if (_nextFrameTime != kTimeUnknown) { + checkStep(); + } +} + +void SinglePlayer::checkNextFrameRender() { + Expects(_nextFrameTime != kTimeUnknown); + + const auto now = crl::now(); + if (now < _nextFrameTime) { + if (!_timer.isActive()) { + _timer.callOnce(_nextFrameTime - now); + } + } else { + _timer.cancel(); + + _nextFrameTime = kTimeUnknown; + const auto position = markFrameDisplayed(now); + _updates.fire({ DisplayFrameRequest{ position } }); + } +} + +void SinglePlayer::updateFrameRequest( + not_null animation, + const FrameRequest &request) { + Expects(animation == &_animation); + Expects(_state != nullptr); + + _renderer->updateFrameRequest(_state, request); +} + +crl::time SinglePlayer::markFrameDisplayed(crl::time now) { + Expects(_state != nullptr); + + return _state->markFrameDisplayed(now); +} + +crl::time SinglePlayer::markFrameShown() { + Expects(_renderer != nullptr); + + const auto result = _state->markFrameShown(); + _renderer->frameShown(_state); + + return result; +} + +} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_single_player.h b/Telegram/SourceFiles/lottie/lottie_single_player.h new file mode 100644 index 0000000000..efc6adbc8e --- /dev/null +++ b/Telegram/SourceFiles/lottie/lottie_single_player.h @@ -0,0 +1,62 @@ +/* +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 +*/ +#pragma once + +#include "lottie/lottie_player.h" +#include "lottie/lottie_animation.h" +#include "base/timer.h" + +namespace Lottie { + +class SinglePlayer final : public Player { +public: + SinglePlayer( + const QByteArray &content, + const FrameRequest &request); + SinglePlayer( + FnMut)> get, // Main thread. + FnMut put, // Unknown thread. + const QByteArray &content, + const FrameRequest &request); + ~SinglePlayer(); + + void start( + not_null animation, + std::unique_ptr state) override; + void failed(not_null animation, Error error) override; + + rpl::producer updates() override; + + [[nodiscard]] bool ready() const; + [[nodiscard]] QImage frame(const FrameRequest &request) const; + + void updateFrameRequest( + not_null animation, + const FrameRequest &request) override; + + // Returns frame position, if any frame was marked as displayed. + crl::time markFrameDisplayed(crl::time now) override; + crl::time markFrameShown() override; + + void checkStep() override; + +private: + void checkNextFrameAvailability(); + void checkNextFrameRender(); + + Animation _animation; + base::Timer _timer; + std::shared_ptr _renderer; + SharedState *_state = nullptr; + crl::time _nextFrameTime = kTimeUnknown; + rpl::event_stream _updates; + rpl::lifetime _lifetime; + +}; + +} // namespace Lottie diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 9dc054f3d3..040825117a 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -27,7 +27,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/streaming/media_streaming_player.h" #include "media/streaming/media_streaming_reader.h" #include "media/player/media_player_instance.h" -#include "lottie/lottie_animation.h" #include "history/history.h" #include "history/history_message.h" #include "data/data_media_types.h" @@ -43,7 +42,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "auth_session.h" #include "layout.h" #include "storage/file_download.h" -#include "lottie/lottie_animation.h" #include "calls/calls_instance.h" #include "styles/style_mediaview.h" #include "styles/style_history.h" @@ -195,12 +193,6 @@ struct OverlayWidget::Streamed { bool resumeOnCallEnd = false; }; -struct OverlayWidget::LottieFile { - LottieFile(std::unique_ptr data); - - std::unique_ptr data; -}; - template OverlayWidget::Streamed::Streamed( not_null owner, @@ -215,11 +207,6 @@ OverlayWidget::Streamed::Streamed( st::mediaviewStreamingRadial) { } -OverlayWidget::LottieFile::LottieFile( - std::unique_ptr data) -: data(std::move(data)) { -} - OverlayWidget::OverlayWidget() : OverlayParent(nullptr) , _transparentBrush(style::transparentPlaceholderBrush()) @@ -438,7 +425,6 @@ bool OverlayWidget::documentBubbleShown() const { || (_doc && !_themePreviewShown && !_streamed - && !_lottie && _current.isNull()); } @@ -447,10 +433,6 @@ void OverlayWidget::clearStreaming() { _streamed = nullptr; } -void OverlayWidget::clearLottie() { - _lottie = nullptr; -} - void OverlayWidget::documentUpdated(DocumentData *doc) { if (_doc && _doc == doc) { if (documentBubbleShown()) { @@ -980,7 +962,6 @@ void OverlayWidget::clearData() { _animationOpacities.clear(); } clearStreaming(); - clearLottie(); delete _menu; _menu = nullptr; setContext(std::nullopt); @@ -1776,7 +1757,6 @@ void OverlayWidget::displayPhoto(not_null photo, HistoryItem *item) } clearStreaming(); - clearLottie(); destroyThemePreview(); _doc = nullptr; _fullScreenVideo = false; @@ -1828,7 +1808,6 @@ void OverlayWidget::displayDocument(DocumentData *doc, HistoryItem *item) { _fullScreenVideo = false; _current = QPixmap(); clearStreaming(); - clearLottie(); destroyThemePreview(); _doc = doc; _photo = nullptr; @@ -1861,13 +1840,6 @@ void OverlayWidget::displayDocument(DocumentData *doc, HistoryItem *item) { const auto &path = location.name(); if (QImageReader(path).canRead()) { _current = PrepareStaticImage(path); - //} else if (auto lottie = Lottie::FromFile(path)) { - // _lottie = std::make_unique( - // std::move(lottie)); - // _lottie->data->updates( - // ) | rpl::start_with_next([=] { - // update(); - // }, lifetime()); } } location.accessDisable(); @@ -2561,8 +2533,6 @@ void OverlayWidget::paintEvent(QPaintEvent *e) { } } else if (_themePreviewShown) { paintThemePreview(p, r); - } else if (_lottie) { - paintLottieFrame(p, r); } else if (documentBubbleShown()) { if (_docRect.intersects(r)) { p.fillRect(_docRect, st::mediaviewFileBg); @@ -2929,20 +2899,6 @@ void OverlayWidget::paintThemePreview(Painter &p, QRect clip) { } } -void OverlayWidget::paintLottieFrame(Painter &p, QRect clip) { - Expects(_lottie != nullptr); - - if (_lottie->data->ready()) { - _lottie->data->markFrameShown(); - const auto frame = _lottie->data->frame(Lottie::FrameRequest()); - const auto x = (width() - frame.width()) / 2; - const auto y = (height() - frame.height()) / 2; - const auto background = _lottieDark ? Qt::black : Qt::white; - p.fillRect(x, y, frame.width(), frame.height(), background); - p.drawImage(x, y, frame); - } -} - void OverlayWidget::keyPressEvent(QKeyEvent *e) { const auto ctrl = e->modifiers().testFlag(Qt::ControlModifier); if (_streamed) { @@ -2996,7 +2952,6 @@ void OverlayWidget::keyPressEvent(QKeyEvent *e) { } else if (e->key() == Qt::Key_0) { zoomReset(); } else if (e->key() == Qt::Key_I) { - _lottieDark = !_lottieDark; update(); } } @@ -3191,7 +3146,6 @@ bool OverlayWidget::moveToEntity(const Entity &entity, int preloadDelta) { setContext(std::nullopt); } clearStreaming(); - clearLottie(); _streamingStartPaused = false; if (auto photo = base::get_if>(&entity.data)) { displayPhoto(*photo, entity.item); @@ -3686,7 +3640,6 @@ void OverlayWidget::setVisibleHook(bool visible) { QCoreApplication::instance()->removeEventFilter(this); clearStreaming(); - clearLottie(); destroyThemePreview(); _radial.stop(); _current = QPixmap(); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 8817a2e78e..a418c85659 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -120,7 +120,6 @@ private slots: private: struct Streamed; - struct LottieFile; enum OverState { OverNone, @@ -313,9 +312,6 @@ private: void paintTransformedVideoFrame(Painter &p); void clearStreaming(); - void paintLottieFrame(Painter &p, QRect clip); - void clearLottie(); - QBrush _transparentBrush; PhotoData *_photo = nullptr; @@ -364,8 +360,6 @@ private: bool _blurred = true; std::unique_ptr _streamed; - std::unique_ptr _lottie; - bool _lottieDark = false; const style::icon *_docIcon = nullptr; style::color _docIconColor; diff --git a/Telegram/SourceFiles/window/layer_widget.cpp b/Telegram/SourceFiles/window/layer_widget.cpp index 32db4fd8ed..4c151e3e81 100644 --- a/Telegram/SourceFiles/window/layer_widget.cpp +++ b/Telegram/SourceFiles/window/layer_widget.cpp @@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/emoji_config.h" #include "window/window_main_menu.h" -#include "lottie/lottie_animation.h" +#include "lottie/lottie_single_player.h" #include "auth_session.h" #include "chat_helpers/stickers.h" #include "window/window_session_controller.h" @@ -1049,9 +1049,8 @@ QSize MediaPreviewWidget::currentDimensions() const { void MediaPreviewWidget::setupLottie() { Expects(_document != nullptr); - _lottie = Lottie::FromContent( - _document->data(), - _document->filepath(), + _lottie = std::make_unique( + Lottie::ReadContent(_document->data(), _document->filepath()), Lottie::FrameRequest{ currentDimensions() * cIntRetinaFactor() }); _lottie->updates( diff --git a/Telegram/SourceFiles/window/layer_widget.h b/Telegram/SourceFiles/window/layer_widget.h index c195675165..ab7549ef0f 100644 --- a/Telegram/SourceFiles/window/layer_widget.h +++ b/Telegram/SourceFiles/window/layer_widget.h @@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" namespace Lottie { -class Animation; +class SinglePlayer; } // namespace Lottie namespace Window { @@ -236,7 +236,7 @@ private: DocumentData *_document = nullptr; PhotoData *_photo = nullptr; Media::Clip::ReaderPointer _gif; - std::unique_ptr _lottie; + std::unique_ptr _lottie; int _emojiSize; std::vector> _emojiList; diff --git a/Telegram/gyp/lib_lottie.gyp b/Telegram/gyp/lib_lottie.gyp index a6bd6f7951..ef9b2b9cfc 100644 --- a/Telegram/gyp/lib_lottie.gyp +++ b/Telegram/gyp/lib_lottie.gyp @@ -60,9 +60,13 @@ '<(src_loc)/lottie/lottie_animation.h', '<(src_loc)/lottie/lottie_cache.cpp', '<(src_loc)/lottie/lottie_cache.h', + '<(src_loc)/lottie/lottie_common.cpp', '<(src_loc)/lottie/lottie_common.h', '<(src_loc)/lottie/lottie_frame_renderer.cpp', '<(src_loc)/lottie/lottie_frame_renderer.h', + '<(src_loc)/lottie/lottie_player.h', + '<(src_loc)/lottie/lottie_single_player.cpp', + '<(src_loc)/lottie/lottie_single_player.h', ], 'conditions': [[ 'build_macold', { 'xcode_settings': {