From 7db53599e81e61fd5708d8f2b200ad5e1c5238f8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 27 Mar 2020 15:40:50 +0400 Subject: [PATCH] Use Data::DocumentMedia to store good thumbnails. --- Telegram/CMakeLists.txt | 2 + Telegram/SourceFiles/data/data_document.cpp | 86 +++++--- Telegram/SourceFiles/data/data_document.h | 34 ++- .../SourceFiles/data/data_document_media.cpp | 202 ++++++++++++++++++ .../SourceFiles/data/data_document_media.h | 43 ++++ .../SourceFiles/data/data_media_types.cpp | 17 -- Telegram/SourceFiles/data/data_session.cpp | 8 +- Telegram/SourceFiles/data/data_web_page.cpp | 12 +- .../history/history_inner_widget.cpp | 2 +- .../history/view/history_view_element.cpp | 6 + .../history/view/history_view_element.h | 3 +- .../history/view/media/history_view_gif.cpp | 31 ++- .../history/view/media/history_view_gif.h | 13 +- .../history/view/media/history_view_media.h | 2 + .../view/media/history_view_media_grouped.cpp | 6 + .../view/media/history_view_media_grouped.h | 1 + .../view/media/history_view_media_unwrapped.h | 5 + .../view/media/history_view_sticker.cpp | 47 ++-- .../history/view/media/history_view_sticker.h | 10 +- .../media/history_view_theme_document.cpp | 8 +- .../view/media/history_view_theme_document.h | 9 +- .../streaming/media_streaming_document.cpp | 83 +++---- .../media/view/media_view_overlay_widget.cpp | 4 +- .../media/view/media_view_overlay_widget.h | 1 + .../SourceFiles/media/view/media_view_pip.cpp | 19 +- .../SourceFiles/media/view/media_view_pip.h | 9 +- .../SourceFiles/overview/overview_layout.cpp | 24 ++- .../SourceFiles/overview/overview_layout.h | 10 +- Telegram/SourceFiles/storage/file_upload.cpp | 16 +- 29 files changed, 545 insertions(+), 168 deletions(-) create mode 100644 Telegram/SourceFiles/data/data_document_media.cpp create mode 100644 Telegram/SourceFiles/data/data_document_media.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index d67d7cc7a..7fc53cb36 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -360,6 +360,8 @@ PRIVATE data/data_document.h data/data_document_good_thumbnail.cpp data/data_document_good_thumbnail.h + data/data_document_media.cpp + data/data_document_media.h data/data_drafts.cpp data/data_drafts.h data/data_folder.cpp diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 6c50e8aab..6afefe96e 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_streaming.h" #include "data/data_document_good_thumbnail.h" +#include "data/data_document_media.h" #include "lang/lang_keys.h" #include "inline_bots/inline_bot_layout_item.h" #include "main/main_session.h" @@ -569,7 +570,6 @@ void DocumentData::setattributes( _additional = nullptr; } } - validateGoodThumbnail(); if (isAudioFile() || isAnimation() || isVoiceMessage()) { setMaybeSupportsStreaming(true); } @@ -612,7 +612,6 @@ bool DocumentData::checkWallPaperProperties() { return false; // #TODO themes support svg patterns } type = WallPaperDocument; - validateGoodThumbnail(); return true; } @@ -661,48 +660,65 @@ Storage::Cache::Key DocumentData::goodThumbnailCacheKey() const { return Data::DocumentThumbCacheKey(_dc, id); } -Image *DocumentData::goodThumbnail() const { - return _goodThumbnail.get(); +bool DocumentData::goodThumbnailChecked() const { + return (_goodThumbnailState & GoodThumbnailFlag::Mask) + == GoodThumbnailFlag::Checked; } -void DocumentData::validateGoodThumbnail() { - if (!isVideoFile() - && !isAnimation() - && !isWallPaper() - && !isTheme() - && (!sticker() || !sticker()->animated)) { - _goodThumbnail = nullptr; - } else if (!_goodThumbnail && hasRemoteLocation()) { - _goodThumbnail = std::make_unique( - std::make_unique(this)); - } +bool DocumentData::goodThumbnailGenerating() const { + return (_goodThumbnailState & GoodThumbnailFlag::Mask) + == GoodThumbnailFlag::Generating; } -void DocumentData::refreshGoodThumbnail() { - if (_goodThumbnail && hasRemoteLocation()) { - replaceGoodThumbnail(std::make_unique(this)); - } +bool DocumentData::goodThumbnailNoData() const { + return (_goodThumbnailState & GoodThumbnailFlag::Mask) + == GoodThumbnailFlag::NoData; } -void DocumentData::replaceGoodThumbnail( - std::unique_ptr &&source) { - _goodThumbnail->replaceSource(std::move(source)); +void DocumentData::setGoodThumbnailGenerating() { + _goodThumbnailState = (_goodThumbnailState & ~GoodThumbnailFlag::Mask) + | GoodThumbnailFlag::Generating; } -void DocumentData::setGoodThumbnailOnUpload( - QImage &&image, - QByteArray &&bytes) { - Expects(uploadingData != nullptr); +void DocumentData::setGoodThumbnailDataReady() { + _goodThumbnailState = GoodThumbnailFlag::DataReady + | (goodThumbnailNoData() + ? GoodThumbnailFlag(0) + : (_goodThumbnailState & GoodThumbnailFlag::Mask)); +} - if (image.isNull()) { +void DocumentData::setGoodThumbnailChecked(bool hasData) { + if (!hasData && (_goodThumbnailState & GoodThumbnailFlag::DataReady)) { + _goodThumbnailState &= ~GoodThumbnailFlag::DataReady; + _goodThumbnailState &= ~GoodThumbnailFlag::Mask; + Data::DocumentMedia::CheckGoodThumbnail(this); return; } - _goodThumbnail = std::make_unique( - std::make_unique( - QString(), - std::move(bytes), - sticker() ? "WEBP" : "JPG", - std::move(image))); + _goodThumbnailState = (_goodThumbnailState & ~GoodThumbnailFlag::Mask) + | (hasData + ? GoodThumbnailFlag::Checked + : GoodThumbnailFlag::NoData); +} + +std::shared_ptr DocumentData::createMediaView() { + if (auto result = activeMediaView()) { + return result; + } + auto result = std::make_shared(this); + _media = result; + return result; +} + +std::shared_ptr DocumentData::activeMediaView() { + return _media.lock(); +} + +void DocumentData::setGoodThumbnailPhoto(not_null photo) { + _goodThumbnailPhoto = photo; +} + +PhotoData *DocumentData::goodThumbnailPhoto() const { + return _goodThumbnailPhoto; } auto DocumentData::bigFileBaseCacheKey() const @@ -817,7 +833,8 @@ bool DocumentData::loaded(FilePathResolve resolve) const { ActiveCache().increment(ComputeUsage(that->sticker())); } - that->refreshGoodThumbnail(); + that->setGoodThumbnailDataReady(); + Data::DocumentMedia::CheckGoodThumbnail(that); destroyLoader(); if (!that->_data.isEmpty() || that->getStickerLarge()) { @@ -1606,7 +1623,6 @@ void DocumentData::setRemoteLocation( } } } - validateGoodThumbnail(); } void DocumentData::setContentUrl(const QString &url) { diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index 3fee6eb38..305895bc3 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -32,6 +32,7 @@ class Loader; namespace Data { class Session; +class DocumentMedia; } // namespace Data namespace Main { @@ -180,11 +181,18 @@ public: ImagePtr thumbnailInline, ImagePtr thumbnail); - [[nodiscard]] Image *goodThumbnail() const; [[nodiscard]] Storage::Cache::Key goodThumbnailCacheKey() const; - void setGoodThumbnailOnUpload(QImage &&image, QByteArray &&bytes); - void refreshGoodThumbnail(); - void replaceGoodThumbnail(std::unique_ptr &&source); + [[nodiscard]] bool goodThumbnailChecked() const; + [[nodiscard]] bool goodThumbnailGenerating() const; + [[nodiscard]] bool goodThumbnailNoData() const; + void setGoodThumbnailGenerating(); + void setGoodThumbnailDataReady(); + void setGoodThumbnailChecked(bool hasData); + + [[nodiscard]] std::shared_ptr createMediaView(); + [[nodiscard]] std::shared_ptr activeMediaView(); + void setGoodThumbnailPhoto(not_null photo); + [[nodiscard]] PhotoData *goodThumbnailPhoto() const; [[nodiscard]] auto bigFileBaseCacheKey() const -> std::optional; @@ -258,6 +266,17 @@ private: using Flags = base::flags; friend constexpr bool is_flag_type(Flag) { return true; }; + enum class GoodThumbnailFlag : uchar { + Checked = 0x01, + Generating = 0x02, + NoData = 0x03, + Mask = 0x03, + + DataReady = 0x04, + }; + using GoodThumbnailState = base::flags; + friend constexpr bool is_flag_type(GoodThumbnailFlag) { return true; }; + static constexpr Flags kStreamingSupportedMask = Flags() | Flag::StreamingMaybeYes | Flag::StreamingMaybeNo; @@ -272,9 +291,8 @@ private: friend class Serialize::Document; - LocationType locationType() const; + [[nodiscard]] LocationType locationType() const; void validateLottieSticker(); - void validateGoodThumbnail(); void setMaybeSupportsStreaming(bool supports); void setLoadedInMediaCacheLocation(); @@ -294,8 +312,9 @@ private: ImagePtr _thumbnailInline; ImagePtr _thumbnail; - std::unique_ptr _goodThumbnail; Data::ReplyPreview _replyPreview; + std::weak_ptr _media; + PhotoData *_goodThumbnailPhoto = nullptr; not_null _owner; @@ -304,6 +323,7 @@ private: std::unique_ptr _additional; int32 _duration = -1; mutable Flags _flags = kStreamingSupportedUnknown; + GoodThumbnailState _goodThumbnailState = GoodThumbnailState(); mutable std::unique_ptr _loader; }; diff --git a/Telegram/SourceFiles/data/data_document_media.cpp b/Telegram/SourceFiles/data/data_document_media.cpp new file mode 100644 index 000000000..31d3ffe22 --- /dev/null +++ b/Telegram/SourceFiles/data/data_document_media.cpp @@ -0,0 +1,202 @@ +/* +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 "data/data_document_media.h" + +#include "data/data_document.h" +#include "data/data_document_good_thumbnail.h" +#include "data/data_session.h" +#include "data/data_cloud_themes.h" +#include "media/clip/media_clip_reader.h" +#include "main/main_session.h" +#include "lottie/lottie_animation.h" +#include "window/themes/window_theme_preview.h" +#include "ui/image/image.h" +#include "app.h" + +#include +#include + +namespace Data { +namespace { + +constexpr auto kReadAreaLimit = 12'032 * 9'024; +constexpr auto kWallPaperThumbnailLimit = 960; +constexpr auto kMaxVideoFrameArea = 7'680 * 4'320; +constexpr auto kGoodThumbQuality = 87; + +enum class FileType { + Video, + AnimatedSticker, + WallPaper, + Theme, +}; + +[[nodiscard]] bool MayHaveGoodThumbnail(not_null owner) { + return owner->isVideoFile() + || owner->isAnimation() + || owner->isWallPaper() + || owner->isTheme() + || (owner->sticker() && owner->sticker()->animated); +} + +[[nodiscard]] QImage PrepareGoodThumbnail( + const QString &path, + QByteArray data, + FileType type) { + if (type == FileType::Video) { + return ::Media::Clip::PrepareForSending(path, data).thumbnail; + } else if (type == FileType::AnimatedSticker) { + return Lottie::ReadThumbnail(Lottie::ReadContent(data, path)); + } else if (type == FileType::Theme) { + return Window::Theme::GeneratePreview(data, path); + } + auto buffer = QBuffer(&data); + auto file = QFile(path); + auto device = data.isEmpty() ? static_cast(&file) : &buffer; + auto reader = QImageReader(device); + const auto size = reader.size(); + if (!reader.canRead() + || (size.width() * size.height() > kReadAreaLimit)) { + return QImage(); + } + auto result = reader.read(); + if (!result.width() || !result.height()) { + return QImage(); + } + return (result.width() > kWallPaperThumbnailLimit + || result.height() > kWallPaperThumbnailLimit) + ? result.scaled( + kWallPaperThumbnailLimit, + kWallPaperThumbnailLimit, + Qt::KeepAspectRatio, + Qt::SmoothTransformation) + : result; +} + +} // namespace + +DocumentMedia::DocumentMedia(not_null owner) +: _owner(owner) { +} + +DocumentMedia::~DocumentMedia() = default; + +void DocumentMedia::goodThumbnailWanted() { + _flags |= Flag::GoodThumbnailWanted; +} + +Image *DocumentMedia::goodThumbnail() const { + Expects((_flags & Flag::GoodThumbnailWanted) != 0); + + if (!_goodThumbnail) { + ReadOrGenerateThumbnail(_owner); + } + return _goodThumbnail.get(); +} + +void DocumentMedia::setGoodThumbnail(QImage thumbnail) { + if (!(_flags & Flag::GoodThumbnailWanted)) { + return; + } + _goodThumbnail = std::make_unique( + std::make_unique(std::move(thumbnail), "PNG")); + _owner->session().downloaderTaskFinished().notify(); +} + +void DocumentMedia::GenerateGoodThumbnail(not_null document) { + const auto data = document->data(); + const auto type = document->isWallPaper() + ? FileType::WallPaper + : document->isTheme() + ? FileType::Theme + : document->sticker() + ? FileType::AnimatedSticker + : FileType::Video; + auto location = document->location().isEmpty() + ? nullptr + : std::make_unique(document->location()); + if (data.isEmpty() && !location) { + document->setGoodThumbnailChecked(false); + return; + } + const auto guard = base::make_weak(&document->owner().session()); + crl::async([=, location = std::move(location)] { + const auto filepath = (location && location->accessEnable()) + ? location->name() + : QString(); + auto result = PrepareGoodThumbnail(filepath, data, type); + auto bytes = QByteArray(); + if (!result.isNull()) { + auto buffer = QBuffer(&bytes); + const auto format = (type == FileType::AnimatedSticker) + ? "WEBP" + : (type == FileType::WallPaper && result.hasAlphaChannel()) + ? "PNG" + : "JPG"; + result.save(&buffer, format, kGoodThumbQuality); + } + if (!filepath.isEmpty()) { + location->accessDisable(); + } + const auto cache = bytes.isEmpty() ? QByteArray("(failed)") : bytes; + crl::on_main(guard, [=] { + document->setGoodThumbnailChecked(true); + if (const auto active = document->activeMediaView()) { + active->setGoodThumbnail(result); + } + document->owner().cache().put( + document->goodThumbnailCacheKey(), + Storage::Cache::Database::TaggedValue{ + base::duplicate(cache), + kImageCacheTag }); + }); + }); +} + +void DocumentMedia::CheckGoodThumbnail(not_null document) { + if (!document->goodThumbnailChecked()) { + ReadOrGenerateThumbnail(document); + } +} + +void DocumentMedia::ReadOrGenerateThumbnail( + not_null document) { + if (document->goodThumbnailGenerating() + || document->goodThumbnailNoData() + || !MayHaveGoodThumbnail(document)) { + return; + } + document->setGoodThumbnailGenerating(); + + const auto guard = base::make_weak(&document->session()); + const auto active = document->activeMediaView(); + const auto got = [=](QByteArray value) { + if (value.isEmpty()) { + crl::on_main(guard, [=] { + GenerateGoodThumbnail(document); + }); + } else if (active) { + crl::async([=] { + const auto image = App::readImage(value, nullptr, false); + crl::on_main(guard, [=] { + document->setGoodThumbnailChecked(true); + if (const auto active = document->activeMediaView()) { + active->setGoodThumbnail(image); + } + }); + }); + } else { + crl::on_main(guard, [=] { + document->setGoodThumbnailChecked(true); + }); + } + }; + document->owner().cache().get(document->goodThumbnailCacheKey(), got); +} + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_document_media.h b/Telegram/SourceFiles/data/data_document_media.h new file mode 100644 index 000000000..cdd543daa --- /dev/null +++ b/Telegram/SourceFiles/data/data_document_media.h @@ -0,0 +1,43 @@ +/* +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 "base/flags.h" + +namespace Data { + +class DocumentMedia final { +public: + explicit DocumentMedia(not_null owner); + ~DocumentMedia(); + + void goodThumbnailWanted(); + [[nodiscard]] Image *goodThumbnail() const; + void setGoodThumbnail(QImage thumbnail); + + // For DocumentData. + void validateGoodThumbnail(); + static void CheckGoodThumbnail(not_null document); + +private: + enum class Flag : uchar { + GoodThumbnailWanted = 0x01, + }; + inline constexpr bool is_flag_type(Flag) { return true; }; + using Flags = base::flags; + + static void ReadOrGenerateThumbnail(not_null document); + static void GenerateGoodThumbnail(not_null document); + + const not_null _owner; + std::unique_ptr _goodThumbnail; + Flags _flags; + +}; + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index bfff04b27..16e3ab789 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -750,23 +750,6 @@ bool MediaFile::updateSentMedia(const MTPMessageMedia &media) { return false; } parent()->history()->owner().documentConvert(_document, *content); - - if (const auto good = _document->goodThumbnail()) { - auto bytes = good->bytesForCache(); - if (const auto length = bytes.size()) { - if (length > Storage::kMaxFileInMemory) { - LOG(("App Error: Bad thumbnail data for saving to cache.")); - } else { - parent()->history()->owner().cache().putIfEmpty( - _document->goodThumbnailCacheKey(), - Storage::Cache::Database::TaggedValue( - std::move(bytes), - Data::kImageCacheTag)); - _document->refreshGoodThumbnail(); - } - } - } - return true; } diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 1d7d4aa49..e2c8ee5d2 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -2421,6 +2421,7 @@ void Session::documentConvert( }(); const auto oldKey = original->mediaKey(); const auto oldCacheKey = original->cacheKey(); + const auto oldGoodKey = original->goodThumbnailCacheKey(); const auto idChanged = (original->id != id); const auto sentSticker = idChanged && (original->sticker() != nullptr); if (idChanged) { @@ -2444,6 +2445,7 @@ void Session::documentConvert( documentApplyFields(original, data); if (idChanged) { cache().moveIfEmpty(oldCacheKey, original->cacheKey()); + cache().moveIfEmpty(oldGoodKey, original->goodThumbnailCacheKey()); if (savedGifs().indexOf(original) >= 0) { Local::writeSavedGifs(); } @@ -3180,10 +3182,10 @@ void Session::unregisterPlayingVideoFile(not_null view) { if (i != _playingVideoFiles.end()) { if (!--i->second) { _playingVideoFiles.erase(i); - unregisterHeavyViewPart(view); + view->checkHeavyPart(); } } else { - unregisterHeavyViewPart(view); + view->checkHeavyPart(); } } @@ -3205,7 +3207,7 @@ void Session::checkPlayingVideoFiles() { continue; } } - unregisterHeavyViewPart(view); + view->checkHeavyPart(); } } diff --git a/Telegram/SourceFiles/data/data_web_page.cpp b/Telegram/SourceFiles/data/data_web_page.cpp index d4818683b..f5b68f3a1 100644 --- a/Telegram/SourceFiles/data/data_web_page.cpp +++ b/Telegram/SourceFiles/data/data_web_page.cpp @@ -237,15 +237,7 @@ bool WebPageData::applyChanges( } void WebPageData::replaceDocumentGoodThumbnail() { - if (!document || !photo || !document->goodThumbnail()) { - return; + if (document && photo) { + document->setGoodThumbnailPhoto(photo); } - const auto &location = photo->large()->location(); - if (location.valid()) { - document->replaceGoodThumbnail( - std::make_unique( - location, - photo->large()->bytesSize())); - } - } diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 85494767c..d86c1af23 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -66,7 +66,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { constexpr auto kScrollDateHideTimeout = 1000; -constexpr auto kUnloadHeavyPartsPages = 1; +constexpr auto kUnloadHeavyPartsPages = 2; // Helper binary search for an item in a list that is not completely // above the given top of the visible area or below the given bottom of the visible area diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 042aab3f4..00edb1698 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -606,6 +606,12 @@ auto Element::verticalRepaintRange() const -> VerticalRepaintRange { }; } +void Element::checkHeavyPart() { + if (_media) { + _media->checkHeavyPart(); + } +} + void Element::unloadHeavyPart() { if (_media) { _media->unloadHeavyPart(); diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 8b6577479..e5a8d3a78 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -261,7 +261,8 @@ public: }; [[nodiscard]] virtual VerticalRepaintRange verticalRepaintRange() const; - virtual void unloadHeavyPart(); + void checkHeavyPart(); + void unloadHeavyPart(); // Legacy blocks structure. HistoryBlock *block(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 19dab0ea5..331a555dd 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_streaming.h" #include "data/data_document.h" #include "data/data_file_origin.h" +#include "data/data_document_media.h" #include "app.h" #include "styles/style_history.h" @@ -92,6 +93,8 @@ Gif::~Gif() { _data->owner().streaming().keepAlive(_data); setStreamed(nullptr); } + _dataMedia = nullptr; + checkHeavyPart(); } QSize Gif::sizeForAspectRatio() const { @@ -404,7 +407,8 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms } } } else { - const auto good = _data->goodThumbnail(); + ensureDataMediaCreated(); + const auto good = _dataMedia->goodThumbnail(); if (good && good->loaded()) { p.drawPixmap(rthumb.topLeft(), good->pixSingle({}, _thumbw, _thumbh, usew, painth, roundRadius, roundCorners)); } else { @@ -1071,6 +1075,15 @@ TextState Gif::getStateGrouped( : _savel); } +void Gif::ensureDataMediaCreated() const { + if (_dataMedia) { + return; + } + _dataMedia = _data->createMediaView(); + _dataMedia->goodThumbnailWanted(); + history()->owner().registerHeavyViewPart(_parent); +} + bool Gif::uploading() const { return _data->uploading(); } @@ -1116,7 +1129,10 @@ void Gif::validateGroupedCache( not_null cacheKey, not_null cache) const { using Option = Images::Option; - const auto good = _data->goodThumbnail(); + + ensureDataMediaCreated(); + + const auto good = _dataMedia->goodThumbnail(); const auto useGood = (good && good->loaded()); const auto thumb = _data->thumbnail(); const auto useThumb = (thumb && thumb->loaded()); @@ -1245,6 +1261,17 @@ void Gif::parentTextUpdated() { } } +void Gif::checkHeavyPart() { + if (!_dataMedia && !_streamed) { + history()->owner().unregisterHeavyViewPart(_parent); + } +} + +void Gif::unloadHeavyPart() { + stopAnimation(); + _dataMedia = nullptr; +} + void Gif::refreshParentId(not_null realParent) { File::refreshParentId(realParent); if (_parent->media() == this) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.h b/Telegram/SourceFiles/history/view/media/history_view_gif.h index 141fb6bdd..44a9b69f5 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.h +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.h @@ -15,6 +15,10 @@ struct HistoryMessageReply; struct HistoryMessageForwarded; class Painter; +namespace Data { +class DocumentMedia; +} // namespace Data + namespace Media { namespace View { class PlaybackProgress; @@ -100,9 +104,8 @@ public: void parentTextUpdated() override; - void unloadHeavyPart() override { - stopAnimation(); - } + void checkHeavyPart() override; + void unloadHeavyPart() override; void refreshParentId(not_null realParent) override; @@ -113,6 +116,7 @@ private: bool dataFinished() const override; bool dataLoaded() const override; + void ensureDataMediaCreated() const; void refreshCaption(); [[nodiscard]] bool autoplayEnabled() const; @@ -161,11 +165,12 @@ private: StateRequest request, QPoint position) const; - not_null _data; + const not_null _data; int _thumbw = 1; int _thumbh = 1; Ui::Text::String _caption; std::unique_ptr _streamed; + mutable std::shared_ptr _dataMedia; QString _downloadSize; diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.h b/Telegram/SourceFiles/history/view/media/history_view_media.h index 0708ca2a0..fd61228eb 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media.h @@ -253,6 +253,8 @@ public: crl::time ms) const { } + virtual void checkHeavyPart() { + } virtual void unloadHeavyPart() { } diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp index 13ab8e046..b4f65ce98 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp @@ -422,6 +422,12 @@ int GroupedMedia::checkAnimationCount() { return result; } +void GroupedMedia::checkHeavyPart() { + for (auto &part : _parts) { + part.content->checkHeavyPart(); + } +} + void GroupedMedia::unloadHeavyPart() { for (auto &part : _parts) { part.content->unloadHeavyPart(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h index c042f9ac4..897cfa1c5 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h @@ -88,6 +88,7 @@ public: void stopAnimation() override; int checkAnimationCount() override; + void checkHeavyPart() override; void unloadHeavyPart() override; void parentTextUpdated() override; diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h index 71d3cb2b0..17fe8fe1e 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h @@ -34,6 +34,8 @@ public: } virtual void clearStickerLoopPlayed() { } + virtual void checkHeavyPart() { + } virtual void unloadHeavyPart() { } virtual void refreshLink() { @@ -82,6 +84,9 @@ public: _content->clearStickerLoopPlayed(); } + void checkHeavyPart() override { + _content->checkHeavyPart(); + } void unloadHeavyPart() override { _content->unloadHeavyPart(); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp index 56e913809..044d75305 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp @@ -24,6 +24,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 "data/data_document_media.h" #include "data/data_file_origin.h" #include "lottie/lottie_single_player.h" #include "styles/style_history.h" @@ -55,12 +56,12 @@ namespace { Sticker::Sticker( not_null parent, - not_null document, + not_null data, const Lottie::ColorReplacements *replacements) : _parent(parent) -, _document(document) +, _data(data) , _replacements(replacements) { - _document->loadThumbnail(parent->data()->fullId()); + _data->loadThumbnail(parent->data()->fullId()); } Sticker::~Sticker() { @@ -72,9 +73,9 @@ bool Sticker::isEmojiSticker() const { } void Sticker::initSize() { - _size = _document->dimensions; + _size = _data->dimensions; if (isEmojiSticker() || _diceIndex >= 0) { - _size = GetAnimatedEmojiSize(&_document->session(), _size); + _size = GetAnimatedEmojiSize(&_data->session(), _size); [[maybe_unused]] bool result = readyToDrawLottie(); } else { _size = DownscaledSize( @@ -92,13 +93,13 @@ bool Sticker::readyToDrawLottie() { if (!_lastDiceFrame.isNull()) { return true; } - const auto sticker = _document->sticker(); + const auto sticker = _data->sticker(); if (!sticker) { return false; } - _document->checkStickerLarge(); - const auto loaded = _document->loaded(); + _data->checkStickerLarge(); + const auto loaded = _data->loaded(); if (sticker->animated && !_lottie && loaded) { setupLottie(); } @@ -123,8 +124,8 @@ QSize Sticker::GetAnimatedEmojiSize( void Sticker::draw(Painter &p, const QRect &r, bool selected) { if (readyToDrawLottie()) { paintLottie(p, r, selected); - } else if (_document->sticker() - && (!_document->sticker()->animated || !_replacements)) { + } else if (_data->sticker() + && (!_data->sticker()->animated || !_replacements)) { paintPixmap(p, r, selected); } } @@ -166,7 +167,7 @@ void Sticker::paintLottie(Painter &p, const QRect &r, bool selected) { : (_diceIndex == 0) ? false : (isEmojiSticker() - || !_document->session().settings().loopAnimatedStickers()); + || !_data->session().settings().loopAnimatedStickers()); const auto count = _lottie->information().framesCount; _atTheEnd = (frame.index + 1 == count); _nextLastDiceFrame = !paused @@ -197,22 +198,24 @@ void Sticker::paintPixmap(Painter &p, const QRect &r, bool selected) { } QPixmap Sticker::paintedPixmap(bool selected) const { + ensureDataMediaCreated(); + const auto o = _parent->data()->fullId(); const auto w = _size.width(); const auto h = _size.height(); const auto &c = st::msgStickerOverlay; - const auto good = _document->goodThumbnail(); + const auto good = _dataMedia->goodThumbnail(); if (good && !good->loaded()) { good->load({}); } - if (const auto image = _document->getStickerLarge()) { + if (const auto image = _data->getStickerLarge()) { return selected ? image->pixColored(o, c, w, h) : image->pix(o, w, h); // // Inline thumbnails can't have alpha channel. // - //} else if (const auto blurred = _document->thumbnailInline()) { + //} else if (const auto blurred = _data->thumbnailInline()) { // return selected // ? blurred->pixBlurredColored(o, c, w, h) // : blurred->pixBlurred(o, w, h); @@ -220,7 +223,7 @@ QPixmap Sticker::paintedPixmap(bool selected) const { return selected ? good->pixColored(o, c, w, h) : good->pix(o, w, h); - } else if (const auto thumbnail = _document->thumbnail()) { + } else if (const auto thumbnail = _data->thumbnail()) { return selected ? thumbnail->pixBlurredColored(o, c, w, h) : thumbnail->pixBlurred(o, w, h); @@ -232,7 +235,7 @@ void Sticker::refreshLink() { if (_link) { return; } - const auto sticker = _document->sticker(); + const auto sticker = _data->sticker(); if (isEmojiSticker()) { const auto weak = base::make_weak(this); _link = std::make_shared([weak] { @@ -245,12 +248,20 @@ void Sticker::refreshLink() { that->_parent); }); } else if (sticker && sticker->set.type() != mtpc_inputStickerSetEmpty) { - _link = std::make_shared([document = _document] { + _link = std::make_shared([document = _data] { StickerSetBox::Show(App::wnd()->sessionController(), document); }); } } +void Sticker::ensureDataMediaCreated() const { + if (_dataMedia) { + return; + } + _dataMedia = _data->createMediaView(); + _dataMedia->goodThumbnailWanted(); +} + void Sticker::setDiceIndex(const QString &emoji, int index) { _diceEmoji = emoji; _diceIndex = index; @@ -258,7 +269,7 @@ void Sticker::setDiceIndex(const QString &emoji, int index) { void Sticker::setupLottie() { _lottie = Stickers::LottiePlayerFromDocument( - _document, + _data, _replacements, Stickers::LottieSize::MessageHistory, _size * cIntRetinaFactor(), diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.h b/Telegram/SourceFiles/history/view/media/history_view_sticker.h index e9e09c7c3..b68a0e598 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.h +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.h @@ -16,6 +16,7 @@ class Session; namespace Data { struct FileOrigin; +class DocumentMedia; } // namespace Data namespace Lottie { @@ -31,7 +32,7 @@ class Sticker final public: Sticker( not_null parent, - not_null document, + not_null data, const Lottie::ColorReplacements *replacements = nullptr); ~Sticker(); @@ -43,7 +44,7 @@ public: } DocumentData *document() override { - return _document; + return _data; } void clearStickerLoopPlayed() override { _lottieOncePlayed = false; @@ -71,13 +72,16 @@ private: void paintPixmap(Painter &p, const QRect &r, bool selected); [[nodiscard]] QPixmap paintedPixmap(bool selected) const; + void ensureDataMediaCreated() const; + void setupLottie(); void unloadLottie(); const not_null _parent; - const not_null _document; + const not_null _data; const Lottie::ColorReplacements *_replacements = nullptr; std::unique_ptr _lottie; + mutable std::shared_ptr _dataMedia; ClickHandlerPtr _link; QSize _size; QImage _lastDiceFrame; diff --git a/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp index 95ff8ddfb..acc030e4f 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_element.h" #include "history/view/history_view_cursor_state.h" #include "data/data_document.h" +#include "data/data_document_media.h" #include "data/data_file_origin.h" #include "base/qthelp_url.h" #include "window/themes/window_theme.h" @@ -181,8 +182,11 @@ void ThemeDocument::validateThumbnail() const { if (_thumbnailGood > 0) { return; } - const auto good = _data->goodThumbnail(); - if (good) { + if (!_dataMedia) { + _dataMedia = _data->createMediaView(); + _dataMedia->goodThumbnailWanted(); + } + if (const auto good = _dataMedia->goodThumbnail()) { if (good->loaded()) { prepareThumbnailFrom(good, 1); return; diff --git a/Telegram/SourceFiles/history/view/media/history_view_theme_document.h b/Telegram/SourceFiles/history/view/media/history_view_theme_document.h index 421280994..586aad2db 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_theme_document.h +++ b/Telegram/SourceFiles/history/view/media/history_view_theme_document.h @@ -9,9 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_file.h" +namespace Data { +class DocumentMedia; +} // namespace Data + namespace HistoryView { -class ThemeDocument : public File { +class ThemeDocument final : public File { public: ThemeDocument( not_null parent, @@ -54,11 +58,12 @@ private: void validateThumbnail() const; void prepareThumbnailFrom(not_null image, int good) const; - not_null _data; + const not_null _data; int _pixw = 1; int _pixh = 1; mutable QPixmap _thumbnail; mutable int _thumbnailGood = -1; // -1 inline, 0 thumbnail, 1 good + mutable std::shared_ptr _dataMedia; // For wallpaper documents. QColor _background; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_document.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_document.cpp index 74560cac0..ee6ce4594 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_document.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_document.cpp @@ -10,7 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/streaming/media_streaming_instance.h" #include "data/data_session.h" #include "data/data_document.h" +#include "data/data_document_media.h" #include "data/data_file_origin.h" +#include "main/main_session.h" #include "storage/file_download.h" // Storage::kMaxFileInMemory. #include "styles/style_widgets.h" @@ -192,48 +194,53 @@ void Document::waitingChange(bool waiting) { } void Document::validateGoodThumbnail() { - const auto good = _document->goodThumbnail(); - if (_info.video.cover.isNull() - || (good && good->loaded()) - || _document->uploading()) { + if (_info.video.cover.isNull() || _document->goodThumbnailChecked()) { return; } - auto image = [&] { - auto result = _info.video.cover; - if (_info.video.rotation != 0) { - auto transform = QTransform(); - transform.rotate(_info.video.rotation); - result = result.transformed(transform); + const auto document = _document; + const auto information = _info.video; + const auto key = document->goodThumbnailCacheKey(); + const auto guard = base::make_weak(&document->session()); + document->owner().cache().get(key, [=](QByteArray value) { + if (!value.isEmpty()) { + return; } - if (result.size() != _info.video.size) { - result = result.scaled( - _info.video.size, - Qt::IgnoreAspectRatio, - Qt::SmoothTransformation); + const auto image = [&] { + auto result = information.cover; + if (information.rotation != 0) { + auto transform = QTransform(); + transform.rotate(information.rotation); + result = result.transformed(transform); + } + if (result.size() != information.size) { + result = result.scaled( + information.size, + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + } + return result; + }(); + auto bytes = QByteArray(); + { + auto buffer = QBuffer(&bytes); + image.save(&buffer, "JPG", kGoodThumbnailQuality); } - return result; - }(); - - auto bytes = QByteArray(); - { - auto buffer = QBuffer(&bytes); - image.save(&buffer, "JPG", kGoodThumbnailQuality); - } - const auto length = bytes.size(); - if (!length || length > Storage::kMaxFileInMemory) { - LOG(("App Error: Bad thumbnail data for saving to cache.")); - } else if (_document->uploading()) { - _document->setGoodThumbnailOnUpload( - std::move(image), - std::move(bytes)); - } else { - _document->owner().cache().putIfEmpty( - _document->goodThumbnailCacheKey(), - Storage::Cache::Database::TaggedValue( - std::move(bytes), - Data::kImageCacheTag)); - _document->refreshGoodThumbnail(); - } + const auto length = bytes.size(); + if (!length || length > Storage::kMaxFileInMemory) { + LOG(("App Error: Bad thumbnail data for saving to cache.")); + bytes = "(failed)"; + } + crl::on_main(guard, [=] { + if (const auto active = document->activeMediaView()) { + active->setGoodThumbnail(image); + } + document->owner().cache().putIfEmpty( + document->goodThumbnailCacheKey(), + Storage::Cache::Database::TaggedValue( + base::duplicate(bytes), + Data::kImageCacheTag)); + }); + }); } void Document::waitingCallback() { diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index b44f0e538..a61943579 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "data/data_file_origin.h" #include "data/data_media_rotation.h" +#include "data/data_document_media.h" #include "window/themes/window_theme_preview.h" #include "window/window_peer_menu.h" #include "window/window_session_controller.h" @@ -2177,7 +2178,8 @@ void OverlayWidget::startStreamingPlayer() { void OverlayWidget::initStreamingThumbnail() { Expects(_doc != nullptr); - const auto good = _doc->goodThumbnail(); + const auto media = _doc->activeMediaView(); + const auto good = media ? media->goodThumbnail() : nullptr; const auto useGood = (good && good->loaded()); const auto thumb = _doc->thumbnail(); const auto useThumb = (thumb && thumb->loaded()); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index c998abd7e..a665e48ad 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -348,6 +348,7 @@ private: PhotoData *_photo = nullptr; DocumentData *_doc = nullptr; + std::shared_ptr _docMedia; int _rotation = 0; std::unique_ptr _sharedMedia; std::optional _sharedMediaData; diff --git a/Telegram/SourceFiles/media/view/media_view_pip.cpp b/Telegram/SourceFiles/media/view/media_view_pip.cpp index 639f47f37..33e8dbe7d 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip.cpp +++ b/Telegram/SourceFiles/media/view/media_view_pip.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/view/media_view_playback_progress.h" #include "media/audio/media_audio.h" #include "data/data_document.h" +#include "data/data_document_media.h" #include "data/data_file_origin.h" #include "data/data_session.h" #include "data/data_media_rotation.h" @@ -806,20 +807,20 @@ void PipPanel::updateDecorations() { Pip::Pip( not_null delegate, - not_null document, + not_null data, FullMsgId contextId, std::shared_ptr shared, FnMut closeAndContinue, FnMut destroy) : _delegate(delegate) -, _document(document) +, _data(data) , _contextId(contextId) , _instance(std::move(shared), [=] { waitingAnimationCallback(); }) , _panel( _delegate->pipParentWidget(), [=](QPainter &p, const FrameRequest &request) { paint(p, request); }) , _playbackProgress(std::make_unique()) -, _rotation(document->owner().mediaRotation().get(document)) +, _rotation(data->owner().mediaRotation().get(data)) , _roundRect(ImageRoundRadius::Large, st::radialBg) , _closeAndContinue(std::move(closeAndContinue)) , _destroy(std::move(destroy)) { @@ -835,9 +836,10 @@ void Pip::setupPanel() { if (!_instance.info().video.size.isEmpty()) { return _instance.info().video.size; } - const auto good = _document->goodThumbnail(); + const auto media = _data->activeMediaView(); + const auto good = media ? media->goodThumbnail() : nullptr; const auto useGood = (good && good->loaded()); - const auto original = useGood ? good->size() : _document->dimensions; + const auto original = useGood ? good->size() : _data->dimensions; return original.isEmpty() ? QSize(1, 1) : original; }(); _panel.setAspectRatio(FlipSizeByRotation(size, _rotation)); @@ -1369,11 +1371,12 @@ QImage Pip::videoFrame(const FrameRequest &request) const { return _instance.frame(request); } const auto &cover = _instance.info().video.cover; - const auto good = _document->goodThumbnail(); + const auto media = _data->activeMediaView(); + const auto good = media ? media->goodThumbnail() : nullptr; const auto useGood = (good && good->loaded()); - const auto thumb = _document->thumbnail(); + const auto thumb = _data->thumbnail(); const auto useThumb = (thumb && thumb->loaded()); - const auto blurred = _document->thumbnailInline(); + const auto blurred = _data->thumbnailInline(); const auto state = !cover.isNull() ? ThumbState::Cover : useGood diff --git a/Telegram/SourceFiles/media/view/media_view_pip.h b/Telegram/SourceFiles/media/view/media_view_pip.h index 4385d8d40..6552b1bed 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip.h +++ b/Telegram/SourceFiles/media/view/media_view_pip.h @@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include +namespace Data { +class DocumentMedia; +} // namespace Data + namespace Ui { class IconButton; template @@ -121,7 +125,7 @@ public: Pip( not_null delegate, - not_null document, + not_null data, FullMsgId contextId, std::shared_ptr shared, FnMut closeAndContinue, @@ -197,12 +201,13 @@ private: void seekFinish(float64 value); const not_null _delegate; - not_null _document; + not_null _data; FullMsgId _contextId; Streaming::Instance _instance; PipPanel _panel; QSize _size; std::unique_ptr _playbackProgress; + std::shared_ptr _dataMedia; bool _showPause = false; bool _startPaused = false; diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 442b5bf5a..27014fbbe 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_media_types.h" #include "data/data_peer.h" #include "data/data_file_origin.h" +#include "data/data_document_media.h" #include "styles/style_overview.h" #include "styles/style_history.h" #include "core/file_utilities.h" @@ -417,13 +418,10 @@ Video::Video( , _duration(formatDurationText(_data->getDuration())) { setDocumentLinks(_data); _data->loadThumbnail(parent->fullId()); - if (_data->hasThumbnail() && !_data->thumbnail()->loaded()) { - if (const auto good = _data->goodThumbnail()) { - good->load({}); - } - } } +Video::~Video() = default; + void Video::initDimensions() { _maxw = 2 * st::overviewPhotoMinSize; _minh = _maxw; @@ -436,12 +434,14 @@ int32 Video::resizeGetHeight(int32 width) { } void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) { + ensureDataMediaCreated(); + const auto selected = (selection == FullSelection); const auto blurred = _data->thumbnailInline(); - const auto goodLoaded = _data->goodThumbnail() - && _data->goodThumbnail()->loaded(); const auto thumbLoaded = _data->hasThumbnail() && _data->thumbnail()->loaded(); + const auto goodLoaded = _dataMedia->goodThumbnail() + && _dataMedia->goodThumbnail()->loaded(); bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); if (displayLoading) { @@ -459,7 +459,7 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const || (_pixBlurred && (thumbLoaded || goodLoaded)))) { auto size = _width * cIntRetinaFactor(); auto img = goodLoaded - ? _data->goodThumbnail()->original() + ? _dataMedia->goodThumbnail()->original() : thumbLoaded ? _data->thumbnail()->original() : Images::prepareBlur(blurred->original()); @@ -543,6 +543,14 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const paintCheckbox(p, { checkLeft, checkTop }, selected, context); } +void Video::ensureDataMediaCreated() const { + if (_dataMedia) { + return; + } + _dataMedia = _data->createMediaView(); + _dataMedia->goodThumbnailWanted(); +} + float64 Video::dataProgress() const { return _data->progress(); } diff --git a/Telegram/SourceFiles/overview/overview_layout.h b/Telegram/SourceFiles/overview/overview_layout.h index a18f96bb7..9dca446c4 100644 --- a/Telegram/SourceFiles/overview/overview_layout.h +++ b/Telegram/SourceFiles/overview/overview_layout.h @@ -19,6 +19,7 @@ struct RoundCheckbox; namespace Data { class Media; +class DocumentMedia; } // namespace Data namespace Overview { @@ -220,11 +221,12 @@ private: }; -class Video : public RadialProgressItem { +class Video final : public RadialProgressItem { public: Video( not_null parent, not_null video); + ~Video(); void initDimensions() override; int32 resizeGetHeight(int32 width) override; @@ -240,15 +242,17 @@ protected: bool iconAnimated() const override; private: + void ensureDataMediaCreated() const; + void updateStatusText(); + not_null _data; + mutable std::shared_ptr _dataMedia; StatusText _status; QString _duration; QPixmap _pix; bool _pixBlurred = true; - void updateStatusText(); - }; class Voice : public RadialProgressItem { diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index 4263328fd..66d06db5e 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localimageloader.h" #include "storage/file_download.h" #include "data/data_document.h" +#include "data/data_document_media.h" #include "data/data_photo.h" #include "data/data_session.h" #include "main/main_session.h" @@ -193,9 +194,18 @@ void Uploader::upload( std::move(file->thumb)); document->uploadingData = std::make_unique( document->size); - document->setGoodThumbnailOnUpload( - std::move(file->goodThumbnail), - std::move(file->goodThumbnailBytes)); + if (!file->goodThumbnail.isNull()) { + if (const auto active = document->activeMediaView()) { + active->setGoodThumbnail(std::move(file->goodThumbnail)); + } + } + if (!file->goodThumbnailBytes.isEmpty()) { + document->owner().cache().putIfEmpty( + document->goodThumbnailCacheKey(), + Storage::Cache::Database::TaggedValue( + std::move(file->goodThumbnailBytes), + Data::kImageCacheTag)); + } if (!file->content.isEmpty()) { document->setDataAndCache(file->content); if (file->type == SendMediaType::ThemeFile) {