diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index 36fcac597..885209984 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -69,6 +69,8 @@ private: Ui::Animations::Simple overAnimation; }; + QSize boundingBoxSize() const; + void paintSticker(Painter &p, int index, QPoint position) const; void setupLottie(int index); @@ -505,13 +507,20 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) { } } +QSize StickerSetBox::Inner::boundingBoxSize() const { + return QSize( + st::stickersSize.width() - st::buttonRadius * 2, + st::stickersSize.height() - st::buttonRadius * 2); +} + void StickerSetBox::Inner::setupLottie(int index) { auto &element = _elements[index]; const auto document = element.document; - element.animated = document->data().isEmpty() - ? Lottie::FromFile(document->filepath()) - : Lottie::FromData(document->data()); + element.animated = Stickers::LottieFromDocument( + document, + Stickers::LottieSize::StickerSet, + boundingBoxSize() * cIntRetinaFactor()); const auto animation = element.animated.get(); animation->updates( @@ -550,16 +559,15 @@ void StickerSetBox::Inner::paintSticker( if (h < 1) h = 1; QPoint ppos = position + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2); if (element.animated && element.animated->ready()) { - const auto size = QSize(w, h); auto request = Lottie::FrameRequest(); - request.resize = size * cIntRetinaFactor(); + request.box = boundingBoxSize() * cIntRetinaFactor(); const auto paused = _controller->isGifPausedAtLeastFor( Window::GifPauseReason::Layer); if (!paused) { element.animated->markFrameShown(); } p.drawImage( - QRect(ppos, size), + QRect(ppos, QSize(w, h)), element.animated->frame(request)); } else if (const auto image = document->getStickerSmall()) { p.drawPixmapLeft( @@ -581,7 +589,7 @@ bool StickerSetBox::Inner::notInstalled() const { if ((it == Auth().data().stickerSets().cend()) || !(it->flags & MTPDstickerSet::Flag::f_installed_date) || (it->flags & MTPDstickerSet::Flag::f_archived)) { - return _pack.size() > 0; + return !_pack.empty(); } return false; } diff --git a/Telegram/SourceFiles/chat_helpers/stickers.cpp b/Telegram/SourceFiles/chat_helpers/stickers.cpp index 7a6726b08..694a70905 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers.cpp @@ -19,6 +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 "styles/style_chat_helpers.h" namespace Stickers { @@ -1086,4 +1087,24 @@ RecentStickerPack &GetRecentPack() { return cRefRecentStickers(); } +std::unique_ptr LottieFromDocument( + not_null document, + LottieSize sizeTag, + QSize box) { + const auto data = document->data(); + const auto filepath = document->filepath(); + if (const auto key = document->bigFileBaseCacheKey()) { + return Lottie::FromCached( + &document->session().data().cacheBigFile(), + Storage::Cache::Key{ key->high, key->low + int(sizeTag) }, + data, + filepath, + box); + } else if (!data.isEmpty()) { + return Lottie::FromData(data); + } else { + return Lottie::FromFile(filepath); + } +} + } // namespace Stickers diff --git a/Telegram/SourceFiles/chat_helpers/stickers.h b/Telegram/SourceFiles/chat_helpers/stickers.h index 88f67fce1..a1a6fe6eb 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.h +++ b/Telegram/SourceFiles/chat_helpers/stickers.h @@ -9,6 +9,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/sender.h" +class DocumentData; + +namespace Lottie { +class Animation; +} // namespace Lottie + namespace Stickers { constexpr auto DefaultSetId = 0; // for backward compatibility @@ -103,4 +109,17 @@ QString GetSetTitle(const MTPDstickerSet &s); RecentStickerPack &GetRecentPack(); +enum class LottieSize : uchar { + MessageHistory, + StickerSet, + StickersPanel, + StickersColumn, + MediaPreview, +}; + +std::unique_ptr LottieFromDocument( + not_null document, + LottieSize sizeTag, + QSize box); + } // namespace Stickers diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 2df12bb29..9c69ab10b 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -1380,6 +1380,12 @@ void StickersListWidget::setupLottie(Set &set, int section, int index) { }, lifetime()); } +QSize StickersListWidget::boundingBoxSize() const { + return QSize( + _singleSize.width() - st::buttonRadius * 2, + _singleSize.height() - st::buttonRadius * 2); +} + void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section, int index, bool selected, bool deleteSelected) { auto &sticker = set.stickers[index]; const auto document = sticker.document; @@ -1410,16 +1416,15 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section, auto h = qMax(qRound(coef * document->dimensions.height()), 1); auto ppos = pos + QPoint((_singleSize.width() - w) / 2, (_singleSize.height() - h) / 2); if (sticker.animated && sticker.animated->ready()) { - const auto size = QSize(w, h); auto request = Lottie::FrameRequest(); - request.resize = size * cIntRetinaFactor(); + request.box = boundingBoxSize() * cIntRetinaFactor(); const auto paused = controller()->isGifPausedAtLeastFor( Window::GifPauseReason::SavedGifs); if (!paused) { sticker.animated->markFrameShown(); } p.drawImage( - QRect(ppos, size), + QRect(ppos, QSize(w, h)), sticker.animated->frame(request)); } else if (const auto image = document->getStickerSmall()) { if (image->loaded()) { diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index ac210400b..7d5d51135 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -165,6 +165,8 @@ private: static std::vector PrepareStickers(const Stickers::Pack &pack); + QSize boundingBoxSize() const; + template bool enumerateSections(Callback callback) const; SectionInfo sectionInfo(int section) const; diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index f1645718c..626004cce 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -668,6 +668,21 @@ void DocumentData::setGoodThumbnailOnUpload( QString(), std::move(bytes), "JPG", std::move(image))); } +auto DocumentData::bigFileBaseCacheKey() const +-> std::optional { + if (hasRemoteLocation()) { + return StorageFileLocation( + _dc, + session().userId(), + MTP_inputDocumentFileLocation( + MTP_long(id), + MTP_long(_access), + MTP_bytes(_fileReference), + MTP_string(QString()))).bigFileBaseCacheKey(); + } + return std::nullopt; +} + bool DocumentData::saveToCache() const { return (type == StickerDocument && size < Storage::kMaxStickerInMemory) || (isAnimation() && size < Storage::kMaxAnimationInMemory) diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index b0d23519e..371fe17c5 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -181,6 +181,9 @@ public: void refreshGoodThumbnail(); void replaceGoodThumbnail(std::unique_ptr &&source); + [[nodiscard]] auto bigFileBaseCacheKey() const + -> std::optional; + void setRemoteLocation( int32 dc, uint64 access, diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 902716381..c32ae05ef 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -1101,7 +1101,7 @@ std::shared_ptr<::Media::Streaming::Reader> Session::documentStreamedReader( return nullptr; } auto result = std::make_shared<::Media::Streaming::Reader>( - this, + &cacheBigFile(), std::move(loader)); if (!PruneDestroyedAndSet(_streamedReaders, document, result)) { _streamedReaders.emplace_or_assign(document, result); diff --git a/Telegram/SourceFiles/history/media/history_media_sticker.cpp b/Telegram/SourceFiles/history/media/history_media_sticker.cpp index ce054cdb4..7dfa891cb 100644 --- a/Telegram/SourceFiles/history/media/history_media_sticker.cpp +++ b/Telegram/SourceFiles/history/media/history_media_sticker.cpp @@ -184,7 +184,8 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, c pixmap); } else if (lottieReady) { auto request = Lottie::FrameRequest(); - request.resize = QSize(_pixw, _pixh) * cIntRetinaFactor(); + request.box = QSize(st::maxStickerSize, st::maxStickerSize) + * cIntRetinaFactor(); if (selected) { request.colored = st::msgStickerOverlay->c; } diff --git a/Telegram/SourceFiles/lottie/lottie_animation.cpp b/Telegram/SourceFiles/lottie/lottie_animation.cpp index 64f620d7e..12e79e664 100644 --- a/Telegram/SourceFiles/lottie/lottie_animation.cpp +++ b/Telegram/SourceFiles/lottie/lottie_animation.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lottie/lottie_animation.h" #include "lottie/lottie_frame_renderer.h" +#include "storage/cache/storage_cache_database.h" #include "base/algorithm.h" #include "zlib.h" #include "logs.h" @@ -70,6 +71,17 @@ std::unique_ptr FromData(const QByteArray &data) { return std::make_unique(base::duplicate(data)); } +std::unique_ptr FromCached( + not_null cache, + Storage::Cache::Key key, + const QByteArray &data, + const QString &filepath, + QSize box) { + return data.isEmpty() + ? Lottie::FromFile(filepath) + : Lottie::FromData(data); +} + auto Init(QByteArray &&content) -> base::variant, Error> { if (content.size() > kMaxFileSize) { diff --git a/Telegram/SourceFiles/lottie/lottie_animation.h b/Telegram/SourceFiles/lottie/lottie_animation.h index 3e01c2b22..68f32bca4 100644 --- a/Telegram/SourceFiles/lottie/lottie_animation.h +++ b/Telegram/SourceFiles/lottie/lottie_animation.h @@ -23,6 +23,13 @@ class QImage; class QString; class QByteArray; +namespace Storage { +namespace Cache { +class Database; +struct Key; +} // namespace Cache +} // namespace Storage + namespace Lottie { constexpr auto kMaxFileSize = 1024 * 1024; @@ -33,6 +40,12 @@ class FrameRenderer; std::unique_ptr FromFile(const QString &path); std::unique_ptr FromData(const QByteArray &data); +std::unique_ptr FromCached( + not_null cache, + Storage::Cache::Key key, + const QByteArray &data, + const QString &filepath, + QSize box); QImage ReadThumbnail(QByteArray &&content); diff --git a/Telegram/SourceFiles/lottie/lottie_common.h b/Telegram/SourceFiles/lottie/lottie_common.h index 21dbb1898..ee9d268b7 100644 --- a/Telegram/SourceFiles/lottie/lottie_common.h +++ b/Telegram/SourceFiles/lottie/lottie_common.h @@ -47,18 +47,26 @@ enum class Error { }; struct FrameRequest { - QSize resize; + QSize box; std::optional colored; - bool empty() const { - return resize.isEmpty(); + [[nodiscard]] bool empty() const { + return box.isEmpty(); + } + [[nodiscard]] QSize size(const QSize &original) const { + Expects(!box.isEmpty()); + + const auto result = original.scaled(box, Qt::KeepAspectRatio); + return QSize( + std::max(result.width(), 1), + std::max(result.height(), 1)); } - bool operator==(const FrameRequest &other) const { - return (resize == other.resize) + [[nodiscard]] bool operator==(const FrameRequest &other) const { + return (box == other.box) && (colored == other.colored); } - bool operator!=(const FrameRequest &other) const { + [[nodiscard]] bool operator!=(const FrameRequest &other) const { return !(*this == other); } }; diff --git a/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp b/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp index ce0c03195..1538a403e 100644 --- a/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp +++ b/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp @@ -77,22 +77,25 @@ private: [[nodiscard]] bool GoodForRequest( const QImage &image, const FrameRequest &request) { - if (request.resize.isEmpty()) { + if (request.box.isEmpty()) { return true; } else if (request.colored.has_value()) { return false; } - return (request.resize == image.size()); + const auto size = image.size(); + return (request.box.width() == size.width()) + || (request.box.height() == size.height()); } [[nodiscard]] QImage PrepareByRequest( const QImage &original, const FrameRequest &request, QImage storage) { - Expects(!request.resize.isEmpty()); + Expects(!request.box.isEmpty()); - if (!GoodStorageForFrame(storage, request.resize)) { - storage = CreateFrameStorage(request.resize); + const auto size = request.size(original.size()); + if (!GoodStorageForFrame(storage, size)) { + storage = CreateFrameStorage(size); } storage.fill(Qt::transparent); @@ -101,7 +104,7 @@ private: p.setRenderHint(QPainter::Antialiasing); p.setRenderHint(QPainter::SmoothPixmapTransform); p.setRenderHint(QPainter::HighQualityAntialiasing); - p.drawImage(QRect(QPoint(), request.resize), original); + p.drawImage(QRect(QPoint(), size), original); } if (request.colored.has_value()) { storage = Images::prepareColored(*request.colored, std::move(storage)); @@ -211,7 +214,7 @@ void SharedState::renderFrame( return; } - const auto size = request.resize.isEmpty() ? _size : request.resize; + const auto size = request.box.isEmpty() ? _size : request.size(_size); if (!GoodStorageForFrame(image, size)) { image = CreateFrameStorage(size); } diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_reader.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_reader.cpp index 3702dbfbd..c1e1705d5 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_reader.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_reader.cpp @@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/streaming/media_streaming_common.h" #include "media/streaming/media_streaming_loader.h" #include "storage/cache/storage_cache_database.h" -#include "data/data_session.h" namespace Media { namespace Streaming { @@ -846,9 +845,9 @@ Reader::SerializedSlice Reader::Slices::unloadToCache() { } Reader::Reader( - not_null owner, + not_null cache, std::unique_ptr loader) -: _owner(owner) +: _cache(cache) , _loader(std::move(loader)) , _cacheHelper(InitCacheHelper(_loader->baseCacheKey())) , _slices(_loader->size(), _cacheHelper != nullptr) { @@ -1140,7 +1139,7 @@ void Reader::readFromCache(int sliceNumber) { for (auto i = 0; i != count; ++i) { keys.push_back(_cacheHelper->key(i + 1)); } - _owner->cacheBigFile().getWithSizes(key, std::move(keys), ready); + _cache->getWithSizes(key, std::move(keys), ready); } bool Reader::readFromCacheForDownloader(int sliceNumber) { @@ -1158,9 +1157,7 @@ void Reader::putToCache(SerializedSlice &&slice) { Expects(_cacheHelper != nullptr); Expects(slice.number >= 0); - _owner->cacheBigFile().put( - _cacheHelper->key(slice.number), - std::move(slice.data)); + _cache->put(_cacheHelper->key(slice.number), std::move(slice.data)); } int Reader::size() const { @@ -1359,7 +1356,7 @@ void Reader::finalizeCache() { putToCache(std::move(toCache)); toCache = _slices.unloadToCache(); } - _owner->cacheBigFile().sync(); + _cache->sync(); } Reader::~Reader() { diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_reader.h b/Telegram/SourceFiles/media/streaming/media_streaming_reader.h index 4501b2242..7ea333987 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_reader.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_reader.h @@ -19,13 +19,10 @@ class StreamedFileDownloader; namespace Storage { namespace Cache { struct Key; +class Database; } // namespace Cache } // namespace Storage -namespace Data { -class Session; -} // namespace Data - namespace Media { namespace Streaming { @@ -36,7 +33,9 @@ enum class Error; class Reader final : public base::has_weak_ptr { public: // Main thread. - Reader(not_null owner, std::unique_ptr loader); + Reader( + not_null cache, + std::unique_ptr loader); // Any thread. [[nodiscard]] int size() const; @@ -222,7 +221,7 @@ private: static std::shared_ptr InitCacheHelper( std::optional baseKey); - const not_null _owner; + const not_null _cache; const std::unique_ptr _loader; const std::shared_ptr _cacheHelper; diff --git a/Telegram/SourceFiles/window/layer_widget.cpp b/Telegram/SourceFiles/window/layer_widget.cpp index bd86a882e..a1accbc73 100644 --- a/Telegram/SourceFiles/window/layer_widget.cpp +++ b/Telegram/SourceFiles/window/layer_widget.cpp @@ -880,7 +880,7 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) { return QImage(); } auto request = Lottie::FrameRequest(); - request.resize = currentDimensions() * cIntRetinaFactor(); + request.box = currentDimensions() * cIntRetinaFactor(); _lottie->markFrameShown(); return _lottie->frame(request); }(); diff --git a/Telegram/gyp/lib_lottie.gyp b/Telegram/gyp/lib_lottie.gyp index b5fadcde4..a6ca9ede3 100644 --- a/Telegram/gyp/lib_lottie.gyp +++ b/Telegram/gyp/lib_lottie.gyp @@ -29,11 +29,13 @@ 'crl.gyp:crl', 'lib_base.gyp:lib_base', 'lib_rlottie.gyp:lib_rlottie', + 'lib_storage.gyp:lib_storage', ], 'export_dependent_settings': [ 'crl.gyp:crl', 'lib_base.gyp:lib_base', 'lib_rlottie.gyp:lib_rlottie', + 'lib_storage.gyp:lib_storage', ], 'defines': [ 'LOT_BUILD',