From bdd3c51ab8e27cc269f04b53136dd4f6c30432ae Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 8 Apr 2020 19:09:29 +0400 Subject: [PATCH] Move inline thumbnail image to DocumentMedia. --- Telegram/CMakeLists.txt | 2 + .../chat_helpers/gifs_list_widget.cpp | 2 +- Telegram/SourceFiles/data/data_document.cpp | 51 +++------ Telegram/SourceFiles/data/data_document.h | 17 +-- .../SourceFiles/data/data_document_media.cpp | 13 +++ .../SourceFiles/data/data_document_media.h | 4 +- Telegram/SourceFiles/data/data_photo.cpp | 34 ++---- Telegram/SourceFiles/data/data_photo.h | 6 +- .../SourceFiles/data/data_reply_preview.cpp | 101 ++++++++++++++++++ .../SourceFiles/data/data_reply_preview.h | 36 +++++++ Telegram/SourceFiles/data/data_session.cpp | 29 ++--- Telegram/SourceFiles/data/data_session.h | 4 +- Telegram/SourceFiles/data/data_types.cpp | 73 ------------- Telegram/SourceFiles/data/data_types.h | 27 ----- .../view/media/history_view_document.cpp | 23 +++- .../view/media/history_view_document.h | 10 ++ .../history/view/media/history_view_gif.cpp | 4 +- .../media/history_view_theme_document.cpp | 4 +- .../inline_bot_layout_internal.cpp | 50 ++++++++- .../inline_bots/inline_bot_layout_internal.h | 52 +++++---- .../inline_bots/inline_bot_layout_item.h | 2 +- .../inline_bots/inline_results_widget.cpp | 2 +- .../media/view/media_view_overlay_widget.cpp | 5 +- .../SourceFiles/media/view/media_view_pip.cpp | 5 +- .../SourceFiles/overview/overview_layout.cpp | 26 ++++- .../SourceFiles/overview/overview_layout.h | 13 ++- Telegram/SourceFiles/storage/localstorage.cpp | 2 +- .../storage/serialize_document.cpp | 2 +- Telegram/SourceFiles/ui/image/image.cpp | 99 ++++++++--------- Telegram/SourceFiles/ui/image/image.h | 2 + .../window/window_media_preview.cpp | 10 +- .../SourceFiles/window/window_media_preview.h | 5 + 32 files changed, 432 insertions(+), 283 deletions(-) create mode 100644 Telegram/SourceFiles/data/data_reply_preview.cpp create mode 100644 Telegram/SourceFiles/data/data_reply_preview.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 7fc53cb36..0fcffa305 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -396,6 +396,8 @@ PRIVATE data/data_poll.h data/data_pts_waiter.cpp data/data_pts_waiter.h + data/data_reply_preview.cpp + data/data_reply_preview.h data/data_search_controller.cpp data/data_search_controller.h data/data_session.cpp diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index 30d8a84bb..0023af14c 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -442,7 +442,7 @@ void GifsListWidget::processPanelHideFinished() { if (const auto result = item->getResult()) { result->unload(); } - item->unloadAnimation(); + item->unloadHeavyPart(); }; // Preserve panel state through visibility toggles. //clearInlineRows(false); diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 6afefe96e..95771cc16 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_streaming.h" #include "data/data_document_good_thumbnail.h" #include "data/data_document_media.h" +#include "data/data_reply_preview.h" #include "lang/lang_keys.h" #include "inline_bots/inline_bot_layout_item.h" #include "main/main_session.h" @@ -470,6 +471,12 @@ DocumentData::DocumentData(not_null owner, DocumentId id) , _owner(owner) { } +DocumentData::~DocumentData() { + destroyLoader(); + unload(); + ActiveCache().remove(this); +} + Data::Session &DocumentData::owner() const { return *_owner; } @@ -616,10 +623,11 @@ bool DocumentData::checkWallPaperProperties() { } void DocumentData::updateThumbnails( - ImagePtr thumbnailInline, + const QByteArray &inlineThumbnailBytes, ImagePtr thumbnail) { - if (thumbnailInline && !_thumbnailInline) { - _thumbnailInline = thumbnailInline; + if (!inlineThumbnailBytes.isEmpty() + && _inlineThumbnailBytes.isEmpty()) { + _inlineThumbnailBytes = inlineThumbnailBytes; } if (thumbnail && (!_thumbnail @@ -642,10 +650,6 @@ bool DocumentData::hasThumbnail() const { return !_thumbnail->isNull(); } -Image *DocumentData::thumbnailInline() const { - return _thumbnailInline ? _thumbnailInline.get() : nullptr; -} - Image *DocumentData::thumbnail() const { return _thumbnail ? _thumbnail.get() : nullptr; } @@ -750,7 +754,6 @@ void DocumentData::unload() { // Also, you can't unload() images that you don't own // from the destructor, because they're already destroyed. // - //_thumbnailInline->unload(); //_thumbnail->unload(); if (sticker()) { if (sticker()->image) { @@ -758,7 +761,7 @@ void DocumentData::unload() { sticker()->image = nullptr; } } - _replyPreview.clear(); + _replyPreview = nullptr; if (!_data.isEmpty()) { ActiveCache().decrement(_data.size()); _data.clear(); @@ -1192,28 +1195,10 @@ bool DocumentData::isStickerSetInstalled() const { Image *DocumentData::getReplyPreview(Data::FileOrigin origin) { if (!_thumbnail) { return nullptr; - } else if (_replyPreview - && (_replyPreview.good() || !_thumbnail->loaded())) { - return _replyPreview.image(); + } else if (!_replyPreview) { + _replyPreview = std::make_unique(this); } - const auto option = isVideoMessage() - ? Images::Option::Circled - : Images::Option::None; - if (_thumbnail->loaded()) { - _replyPreview.prepare( - _thumbnail.get(), - origin, - option); - } else { - _thumbnail->load(origin); - if (_thumbnailInline) { - _replyPreview.prepare( - _thumbnailInline.get(), - origin, - option | Images::Option::Blurred); - } - } - return _replyPreview.image(); + return _replyPreview->image(origin); } StickerData *DocumentData::sticker() const { @@ -1653,12 +1638,6 @@ void DocumentData::collectLocalData(not_null local) { } } -DocumentData::~DocumentData() { - destroyLoader(); - unload(); - ActiveCache().remove(this); -} - QString DocumentData::ComposeNameString( const QString &filename, const QString &songTitle, diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index 305895bc3..45dd5e99d 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -33,6 +33,7 @@ class Loader; namespace Data { class Session; class DocumentMedia; +class ReplyPreview; } // namespace Data namespace Main { @@ -84,9 +85,10 @@ namespace Serialize { class Document; } // namespace Serialize; -class DocumentData { +class DocumentData final { public: DocumentData(not_null owner, DocumentId id); + ~DocumentData(); [[nodiscard]] Data::Session &owner() const; [[nodiscard]] Main::Session &session() const; @@ -175,12 +177,15 @@ public: [[nodiscard]] bool hasThumbnail() const; void loadThumbnail(Data::FileOrigin origin); - [[nodiscard]] Image *thumbnailInline() const; [[nodiscard]] Image *thumbnail() const; void updateThumbnails( - ImagePtr thumbnailInline, + const QByteArray &inlineThumbnailBytes, ImagePtr thumbnail); + [[nodiscard]] QByteArray inlineThumbnailBytes() const { + return _inlineThumbnailBytes; + } + [[nodiscard]] Storage::Cache::Key goodThumbnailCacheKey() const; [[nodiscard]] bool goodThumbnailChecked() const; [[nodiscard]] bool goodThumbnailGenerating() const; @@ -242,8 +247,6 @@ public: void setInappPlaybackFailed(); [[nodiscard]] bool inappPlaybackFailed() const; - ~DocumentData(); - DocumentId id = 0; DocumentType type = FileDocument; QSize dimensions; @@ -310,9 +313,9 @@ private: QString _mimeString; WebFileLocation _urlLocation; - ImagePtr _thumbnailInline; + QByteArray _inlineThumbnailBytes; ImagePtr _thumbnail; - Data::ReplyPreview _replyPreview; + std::unique_ptr _replyPreview; std::weak_ptr _media; PhotoData *_goodThumbnailPhoto = nullptr; diff --git a/Telegram/SourceFiles/data/data_document_media.cpp b/Telegram/SourceFiles/data/data_document_media.cpp index 31d3ffe22..25fac8e40 100644 --- a/Telegram/SourceFiles/data/data_document_media.cpp +++ b/Telegram/SourceFiles/data/data_document_media.cpp @@ -108,6 +108,19 @@ void DocumentMedia::setGoodThumbnail(QImage thumbnail) { _owner->session().downloaderTaskFinished().notify(); } +Image *DocumentMedia::thumbnailInline() const { + if (!_inlineThumbnail) { + auto image = Images::FromInlineBytes(_owner->inlineThumbnailBytes()); + if (!image.isNull()) { + _inlineThumbnail = std::make_unique( + std::make_unique( + std::move(image), + "PNG")); + } + } + return _inlineThumbnail.get(); +} + void DocumentMedia::GenerateGoodThumbnail(not_null document) { const auto data = document->data(); const auto type = document->isWallPaper() diff --git a/Telegram/SourceFiles/data/data_document_media.h b/Telegram/SourceFiles/data/data_document_media.h index cdd543daa..b917ecf25 100644 --- a/Telegram/SourceFiles/data/data_document_media.h +++ b/Telegram/SourceFiles/data/data_document_media.h @@ -20,8 +20,9 @@ public: [[nodiscard]] Image *goodThumbnail() const; void setGoodThumbnail(QImage thumbnail); + [[nodiscard]] Image *thumbnailInline() const; + // For DocumentData. - void validateGoodThumbnail(); static void CheckGoodThumbnail(not_null document); private: @@ -36,6 +37,7 @@ private: const not_null _owner; std::unique_ptr _goodThumbnail; + mutable std::unique_ptr _inlineThumbnail; Flags _flags; }; diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index 100d905e4..0452337d5 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_file_origin.h" +#include "data/data_reply_preview.h" #include "ui/image/image.h" #include "ui/image/image_source.h" #include "main/main_session.h" @@ -22,6 +23,8 @@ PhotoData::PhotoData(not_null owner, PhotoId id) , _owner(owner) { } +PhotoData::~PhotoData() = default; + Data::Session &PhotoData::owner() const { return *_owner; } @@ -105,37 +108,14 @@ void PhotoData::unload() { _thumbnailSmall->unload(); _thumbnail->unload(); _large->unload(); - _replyPreview.clear(); + _replyPreview = nullptr; } Image *PhotoData::getReplyPreview(Data::FileOrigin origin) { - if (_replyPreview - && (_replyPreview.good() || !_thumbnailSmall->loaded())) { - return _replyPreview.image(); + if (!_replyPreview) { + _replyPreview = std::make_unique(this); } - if (_thumbnailSmall->isDelayedStorageImage() - && !_large->isNull() - && !_large->isDelayedStorageImage() - && _large->loaded()) { - _replyPreview.prepare( - _large.get(), - origin, - Images::Option(0)); - } else if (_thumbnailSmall->loaded()) { - _replyPreview.prepare( - _thumbnailSmall.get(), - origin, - Images::Option(0)); - } else { - _thumbnailSmall->load(origin); - if (_thumbnailInline) { - _replyPreview.prepare( - _thumbnailInline.get(), - origin, - Images::Option::Blurred); - } - } - return _replyPreview.image(); + return _replyPreview->image(origin); } void PhotoData::setRemoteLocation( diff --git a/Telegram/SourceFiles/data/data_photo.h b/Telegram/SourceFiles/data/data_photo.h index c9baaafb8..772d1ecb4 100644 --- a/Telegram/SourceFiles/data/data_photo.h +++ b/Telegram/SourceFiles/data/data_photo.h @@ -15,11 +15,13 @@ class Session; namespace Data { class Session; +class ReplyPreview; } // namespace Data -class PhotoData { +class PhotoData final { public: PhotoData(not_null owner, PhotoId id); + ~PhotoData(); [[nodiscard]] Data::Session &owner() const; [[nodiscard]] Main::Session &session() const; @@ -97,7 +99,7 @@ private: int32 _dc = 0; uint64 _access = 0; QByteArray _fileReference; - Data::ReplyPreview _replyPreview; + std::unique_ptr _replyPreview; not_null _owner; diff --git a/Telegram/SourceFiles/data/data_reply_preview.cpp b/Telegram/SourceFiles/data/data_reply_preview.cpp new file mode 100644 index 000000000..78e1dccce --- /dev/null +++ b/Telegram/SourceFiles/data/data_reply_preview.cpp @@ -0,0 +1,101 @@ +/* +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_reply_preview.h" + +#include "data/data_file_origin.h" +#include "data/data_document.h" +#include "data/data_document_media.h" +#include "data/data_photo.h" +#include "ui/image/image.h" +#include "ui/image/image_source.h" + +namespace Data { + +ReplyPreview::ReplyPreview(not_null document) +: _document(document) +, _documentMedia(_document->createMediaView()) { +} + +ReplyPreview::ReplyPreview(not_null photo) +: _photo(photo) { +} + +void ReplyPreview::prepare(not_null image, Images::Options options) { + if (image->isNull() || !image->loaded()) { + return; + } + int w = image->width(), h = image->height(); + if (w <= 0) w = 1; + if (h <= 0) h = 1; + auto thumbSize = (w > h) + ? QSize( + w * st::msgReplyBarSize.height() / h, + st::msgReplyBarSize.height()) + : QSize( + st::msgReplyBarSize.height(), + h * st::msgReplyBarSize.height() / w); + thumbSize *= cIntRetinaFactor(); + const auto prepareOptions = Images::Option::Smooth + | Images::Option::TransparentBackground + | options; + auto outerSize = st::msgReplyBarSize.height(); + auto bitmap = image->pixNoCache( + FileOrigin(), + thumbSize.width(), + thumbSize.height(), + prepareOptions, + outerSize, + outerSize); + _image = std::make_unique( + std::make_unique( + bitmap.toImage(), + "PNG")); + _good = ((options & Images::Option::Blurred) == 0); +} + +Image *ReplyPreview::image(Data::FileOrigin origin) { + if (_document) { + const auto thumbnail = _document->thumbnail(); + Assert(thumbnail != nullptr); + if (!_image || (!_good && thumbnail->loaded())) { + const auto option = _document->isVideoMessage() + ? Images::Option::Circled + : Images::Option::None; + if (thumbnail->loaded()) { + prepare(thumbnail, option); + } else { + thumbnail->load(origin); + if (const auto image = _documentMedia->thumbnailInline()) { + prepare(image, option | Images::Option::Blurred); + } + } + } + } else { + Assert(_photo != nullptr); + const auto small = _photo->thumbnailSmall(); + const auto large = _photo->large(); + if (!_image || (!_good && (small->loaded() || large->loaded()))) { + if (small->isDelayedStorageImage() + && !large->isNull() + && !large->isDelayedStorageImage() + && large->loaded()) { + prepare(large, Images::Option(0)); + } else if (small->loaded()) { + prepare(small, Images::Option(0)); + } else { + small->load(origin); + if (const auto blurred = _photo->thumbnailInline()) { + prepare(blurred, Images::Option::Blurred); + } + } + } + } + return _image.get(); +} + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_reply_preview.h b/Telegram/SourceFiles/data/data_reply_preview.h new file mode 100644 index 000000000..a1df48449 --- /dev/null +++ b/Telegram/SourceFiles/data/data_reply_preview.h @@ -0,0 +1,36 @@ +/* +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 + +class DocumentData; +class PhotoData; + +namespace Data { + +class DocumentMedia; +struct FileOrigin; + +class ReplyPreview { +public: + explicit ReplyPreview(not_null document); + explicit ReplyPreview(not_null photo); + + [[nodiscard]] Image *image(Data::FileOrigin origin); + +private: + void prepare(not_null image, Images::Options options); + + std::unique_ptr _image; + bool _good = false; + DocumentData *_document = nullptr; + PhotoData *_photo = nullptr; + std::shared_ptr _documentMedia; + +}; + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index e2c8ee5d2..6d2e3fd37 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -126,19 +126,20 @@ std::vector ExtractUnavailableReasons( }) | ranges::to_vector; } -MTPPhotoSize FindDocumentInlineThumbnail(const MTPDdocument &data) { +QByteArray FindDocumentInlineThumbnail(const MTPDdocument &data) { const auto thumbs = data.vthumbs(); if (!thumbs) { - return MTP_photoSizeEmpty(MTP_string()); + return QByteArray(); } const auto &list = thumbs->v; const auto i = ranges::find( list, mtpc_photoStrippedSize, &MTPPhotoSize::type); - return (i != list.end()) - ? (*i) - : MTPPhotoSize(MTP_photoSizeEmpty(MTP_string())); + if (i == list.end()) { + return QByteArray(); + } + return i->c_photoStrippedSize().vbytes().v; } MTPPhotoSize FindDocumentThumbnail(const MTPDdocument &data) { @@ -2371,7 +2372,7 @@ not_null Session::processDocument( fields.vdate().v, fields.vattributes().v, mime, - ImagePtr(), + QByteArray(), Images::Create(std::move(thumb), format), fields.vdc_id().v, fields.vsize().v, @@ -2388,7 +2389,7 @@ not_null Session::document( TimeId date, const QVector &attributes, const QString &mime, - const ImagePtr &thumbnailInline, + const QByteArray &inlineThumbnailBytes, const ImagePtr &thumbnail, int32 dc, int32 size, @@ -2401,7 +2402,7 @@ not_null Session::document( date, attributes, mime, - thumbnailInline, + inlineThumbnailBytes, thumbnail, dc, size, @@ -2476,7 +2477,7 @@ DocumentData *Session::documentFromWeb( base::unixtime::now(), data.vattributes().v, data.vmime_type().v, - ImagePtr(), + QByteArray(), thumb, MTP::maindc(), int32(0), // data.vsize().v @@ -2497,7 +2498,7 @@ DocumentData *Session::documentFromWeb( base::unixtime::now(), data.vattributes().v, data.vmime_type().v, - ImagePtr(), + QByteArray(), thumb, MTP::maindc(), int32(0), // data.vsize().v @@ -2517,7 +2518,7 @@ void Session::documentApplyFields( void Session::documentApplyFields( not_null document, const MTPDdocument &data) { - const auto thumbnailInline = FindDocumentInlineThumbnail(data); + const auto inlineThumbnailBytes = FindDocumentInlineThumbnail(data); const auto thumbnailSize = FindDocumentThumbnail(data); const auto thumbnail = Images::Create(data, thumbnailSize); documentApplyFields( @@ -2527,7 +2528,7 @@ void Session::documentApplyFields( data.vdate().v, data.vattributes().v, qs(data.vmime_type()), - Images::Create(data, thumbnailInline), + inlineThumbnailBytes, thumbnail, data.vdc_id().v, data.vsize().v, @@ -2541,7 +2542,7 @@ void Session::documentApplyFields( TimeId date, const QVector &attributes, const QString &mime, - const ImagePtr &thumbnailInline, + const QByteArray &inlineThumbnailBytes, const ImagePtr &thumbnail, int32 dc, int32 size, @@ -2551,7 +2552,7 @@ void Session::documentApplyFields( } document->date = date; document->setMimeString(mime); - document->updateThumbnails(thumbnailInline, thumbnail); + document->updateThumbnails(inlineThumbnailBytes, thumbnail); document->size = size; document->setattributes(attributes); diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 9a5cd1b28..41670f668 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -494,7 +494,7 @@ public: TimeId date, const QVector &attributes, const QString &mime, - const ImagePtr &thumbnailInline, + const QByteArray &inlineThumbnailBytes, const ImagePtr &thumbnail, int32 dc, int32 size, @@ -749,7 +749,7 @@ private: TimeId date, const QVector &attributes, const QString &mime, - const ImagePtr &thumbnailInline, + const QByteArray &inlineThumbnailBytes, const ImagePtr &thumbnail, int32 dc, int32 size, diff --git a/Telegram/SourceFiles/data/data_types.cpp b/Telegram/SourceFiles/data/data_types.cpp index 413f6428c..8f21a9f94 100644 --- a/Telegram/SourceFiles/data/data_types.cpp +++ b/Telegram/SourceFiles/data/data_types.cpp @@ -8,9 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_types.h" #include "data/data_document.h" -#include "data/data_file_origin.h" #include "data/data_session.h" -#include "ui/image/image_source.h" #include "ui/widgets/input_fields.h" #include "storage/cache/storage_cache_types.h" #include "base/openssl_help.h" @@ -34,20 +32,6 @@ constexpr auto kGeoPointCacheMask = 0x000000FFFFFFFFFFULL; } // namespace -struct ReplyPreview::Data { - Data(std::unique_ptr &&source, bool good); - - Image image; - bool good = false; -}; - -ReplyPreview::Data::Data( - std::unique_ptr &&source, - bool good) -: image(std::move(source)) -, good(good) { -} - Storage::Cache::Key DocumentCacheKey(int32 dcId, uint64 id) { return Storage::Cache::Key{ Data::kDocumentCacheTag | (uint64(dcId) & Data::kDocumentCacheMask), @@ -109,63 +93,6 @@ Storage::Cache::Key GeoPointCacheKey(const GeoPointLocation &location) { }; } -ReplyPreview::ReplyPreview() = default; - -ReplyPreview::ReplyPreview(ReplyPreview &&other) = default; - -ReplyPreview &ReplyPreview::operator=(ReplyPreview &&other) = default; - -ReplyPreview::~ReplyPreview() = default; - -void ReplyPreview::prepare( - not_null image, - FileOrigin origin, - Images::Options options) { - int w = image->width(), h = image->height(); - if (w <= 0) w = 1; - if (h <= 0) h = 1; - auto thumbSize = (w > h) - ? QSize( - w * st::msgReplyBarSize.height() / h, - st::msgReplyBarSize.height()) - : QSize( - st::msgReplyBarSize.height(), - h * st::msgReplyBarSize.height() / w); - thumbSize *= cIntRetinaFactor(); - const auto prepareOptions = Images::Option::Smooth - | Images::Option::TransparentBackground - | options; - auto outerSize = st::msgReplyBarSize.height(); - auto bitmap = image->pixNoCache( - origin, - thumbSize.width(), - thumbSize.height(), - prepareOptions, - outerSize, - outerSize); - _data = std::make_unique( - std::make_unique( - bitmap.toImage(), - "PNG"), - ((options & Images::Option::Blurred) == 0)); -} - -void ReplyPreview::clear() { - _data = nullptr; -} - -Image *ReplyPreview::image() const { - return _data ? &_data->image : nullptr; -} - -bool ReplyPreview::good() const { - return !empty() && _data->good; -} - -bool ReplyPreview::empty() const { - return !_data; -} - } // namespace Data uint32 AudioMsgId::CreateExternalPlayId() { diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 6a45b7a89..43a2c45b9 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -56,33 +56,6 @@ constexpr auto kAnimationCacheTag = uint8(0x05); struct FileOrigin; -class ReplyPreview { -public: - ReplyPreview(); - ReplyPreview(ReplyPreview &&other); - ReplyPreview &operator=(ReplyPreview &&other); - ~ReplyPreview(); - - void prepare( - not_null image, - FileOrigin origin, - Images::Options options); - void clear(); - - [[nodiscard]] Image *image() const; - [[nodiscard]] bool good() const; - [[nodiscard]] bool empty() const; - - [[nodiscard]] explicit operator bool() const { - return !empty(); - } - -private: - struct Data; - std::unique_ptr _data; - -}; - } // namespace Data struct MessageGroupId { diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index 980c5dac2..87cc280d2 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "data/data_session.h" #include "data/data_document.h" +#include "data/data_document_media.h" #include "data/data_media_types.h" #include "data/data_file_origin.h" #include "app.h" @@ -262,6 +263,8 @@ void Document::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus; int nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0, bottom = 0; if (auto thumbed = Get()) { + ensureDataMediaCreated(); + nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right(); nametop = st::msgFileThumbNameTop - topMinus; nameright = st::msgFileThumbPadding.left(); @@ -278,7 +281,7 @@ void Document::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti thumb = normal->pixSingle(_realParent->fullId(), thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize, roundRadius); } else { _data->loadThumbnail(_realParent->fullId()); - if (const auto blurred = _data->thumbnailInline()) { + if (const auto blurred = _dataMedia->thumbnailInline()) { thumb = blurred->pixBlurredSingle(_realParent->fullId(), thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize, roundRadius); } } @@ -491,6 +494,24 @@ void Document::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti } } +void Document::checkHeavyPart() { + if (!_dataMedia) { + history()->owner().unregisterHeavyViewPart(_parent); + } +} + +void Document::unloadHeavyPart() { + _dataMedia = nullptr; +} + +void Document::ensureDataMediaCreated() const { + if (_dataMedia) { + return; + } + _dataMedia = _data->createMediaView(); + history()->owner().registerHeavyViewPart(_parent); +} + bool Document::downloadInCorner() const { return _data->isAudioFile() && _data->canBeStreamed() diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.h b/Telegram/SourceFiles/history/view/media/history_view_document.h index 2783d9544..1d2615e43 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.h +++ b/Telegram/SourceFiles/history/view/media/history_view_document.h @@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL struct HistoryDocumentNamed; +namespace Data { +class DocumentMedia; +} // namespace Data + namespace Ui { namespace Text { class String; @@ -63,6 +67,9 @@ public: void refreshParentId(not_null realParent) override; void parentTextUpdated() override; + void checkHeavyPart() override; + void unloadHeavyPart() override; + protected: float64 dataProgress() const override; bool dataFinished() const override; @@ -75,6 +82,8 @@ private: int realDuration = 0; }; + void ensureDataMediaCreated() const; + [[nodiscard]] Ui::Text::String createCaption(); QSize countOptimalSize() override; @@ -93,6 +102,7 @@ private: StateRequest request) const; not_null _data; + mutable std::shared_ptr _dataMedia; }; diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 331a555dd..c786ad9ae 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -425,7 +425,7 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms } } else { _data->loadThumbnail(_realParent->fullId()); - if (const auto blurred = _data->thumbnailInline()) { + if (const auto blurred = _dataMedia->thumbnailInline()) { p.drawPixmap(rthumb.topLeft(), blurred->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, usew, painth, roundRadius, roundCorners)); } else if (!isRound) { const auto roundTop = (roundCorners & RectPart::TopLeft); @@ -1140,7 +1140,7 @@ void Gif::validateGroupedCache( ? good : useThumb ? thumb - : _data->thumbnailInline(); + : _dataMedia->thumbnailInline(); const auto blur = !useGood && (!useThumb || (thumb->width() < kUseNonBlurredThreshold 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 3d1eb8439..5b8740f5e 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp @@ -207,8 +207,8 @@ void ThemeDocument::validateThumbnail() const { } if (_data->thumbnail()->loaded()) { prepareThumbnailFrom(_data->thumbnail(), 0); - } else if (const auto blurred = _data->thumbnailInline()) { - if (_thumbnail.isNull()) { + } else if (_thumbnail.isNull()) { + if (const auto blurred = _dataMedia->thumbnailInline()) { prepareThumbnailFrom(blurred, -1); } } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 34bc88d35..7e3c1b25f 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_session.h" #include "data/data_file_origin.h" +#include "data/data_document_media.h" #include "styles/style_overview.h" #include "styles/style_history.h" #include "styles/style_chat_helpers.h" @@ -325,13 +326,21 @@ void Gif::validateThumbnail( void Gif::prepareThumbnail(QSize size, QSize frame) const { if (const auto document = getShownDocument()) { + ensureDataMediaCreated(document); validateThumbnail(document->thumbnail(), size, frame, true); - validateThumbnail(document->thumbnailInline(), size, frame, false); + validateThumbnail(_dataMedia->thumbnailInline(), size, frame, false); } else { validateThumbnail(getResultThumb(), size, frame, true); } } +void Gif::ensureDataMediaCreated(not_null document) const { + if (_dataMedia) { + return; + } + _dataMedia = document->createMediaView(); +} + void Gif::ensureAnimation() const { if (!_animation) { _animation = std::make_unique([=](crl::time now) { @@ -367,9 +376,14 @@ void Gif::radialAnimationCallback(crl::time now) const { } } +void Gif::unloadHeavyPart() { + unloadAnimation(); + getShownDocument()->unload(); + _dataMedia = nullptr; +} + void Gif::unloadAnimation() { _gif.reset(); - getShownDocument()->unload(); } void Gif::clipCallback(Media::Clip::Notification notification) { @@ -1380,11 +1394,19 @@ void Game::prepareThumbnail(QSize size) const { validateThumbnail(photo->thumbnail(), size, true); validateThumbnail(photo->thumbnailInline(), size, false); } else if (const auto document = getResultDocument()) { + ensureDataMediaCreated(document); validateThumbnail(document->thumbnail(), size, true); - validateThumbnail(document->thumbnailInline(), size, false); + validateThumbnail(_dataMedia->thumbnailInline(), size, false); } } +void Game::ensureDataMediaCreated(not_null document) const { + if (_dataMedia) { + return; + } + _dataMedia = document->createMediaView(); +} + void Game::validateThumbnail(Image *image, QSize size, bool good) const { if (!image || (_thumbGood && !good)) { return; @@ -1450,9 +1472,14 @@ void Game::radialAnimationCallback(crl::time now) const { } } +void Game::unloadHeavyPart() { + unloadAnimation(); + getResultDocument()->unload(); + _dataMedia = nullptr; +} + void Game::unloadAnimation() { _gif.reset(); - getResultDocument()->unload(); } void Game::clipCallback(Media::Clip::Notification notification) { @@ -1464,7 +1491,20 @@ void Game::clipCallback(Media::Clip::Notification notification) { _gif.setBad(); getResultDocument()->unload(); } else if (_gif->ready() && !_gif->started()) { - _gif->start(_frameSize.width(), _frameSize.height(), st::inlineThumbSize, st::inlineThumbSize, ImageRoundRadius::None, RectPart::None); + if (_gif->width() * _gif->height() > kMaxInlineArea) { + getResultDocument()->dimensions = QSize( + _gif->width(), + _gif->height()); + unloadAnimation(); + } else { + _gif->start( + _frameSize.width(), + _frameSize.height(), + st::inlineThumbSize, + st::inlineThumbSize, + ImageRoundRadius::None, + RectPart::None); + } } else if (_gif->autoPausedGif() && !context()->inlineItemVisible(this)) { unloadAnimation(); } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h index a496f8535..bb36fe24c 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h @@ -18,6 +18,10 @@ namespace Lottie { class SinglePlayer; } // namespace Lottie +namespace Data { +class DocumentMedia; +} // namespace Data + namespace InlineBots { namespace Layout { namespace internal { @@ -75,23 +79,30 @@ public: int resizeGetHeight(int width) override; - void unloadAnimation() override; + void unloadHeavyPart() override; private: - QSize countFrameSize() const; - enum class StateFlag { - Over = (1 << 0), + Over = (1 << 0), DeleteOver = (1 << 1), }; using StateFlags = base::flags; friend inline constexpr auto is_flag_type(StateFlag) { return true; }; - StateFlags _state; - Media::Clip::ReaderPointer _gif; - ClickHandlerPtr _delete; - mutable QPixmap _thumb; - mutable bool _thumbGood = false; + struct AnimationData { + template + AnimationData(Callback &&callback) + : radial(std::forward(callback)) { + } + bool over = false; + Ui::Animations::Simple _a_over; + Ui::RadialAnimation radial; + }; + + void ensureDataMediaCreated(not_null document) const; + void unloadAnimation(); + QSize countFrameSize() const; + void validateThumbnail( Image *image, QSize size, @@ -105,15 +116,15 @@ private: void clipCallback(Media::Clip::Notification notification); - struct AnimationData { - template - AnimationData(Callback &&callback) - : radial(std::forward(callback)) { - } - bool over = false; - Ui::Animations::Simple _a_over; - Ui::RadialAnimation radial; - }; + StateFlags _state; + + Media::Clip::ReaderPointer _gif; + ClickHandlerPtr _delete; + mutable QPixmap _thumb; + mutable bool _thumbGood = false; + + mutable std::shared_ptr _dataMedia; + mutable std::unique_ptr _animation; mutable Ui::Animations::Simple _a_deleteOver; @@ -372,9 +383,11 @@ public: QPoint point, StateRequest request) const override; - void unloadAnimation() override; + void unloadHeavyPart() override; private: + void ensureDataMediaCreated(not_null document) const; + void unloadAnimation(); void countFrameSize(); void prepareThumbnail(QSize size) const; @@ -386,6 +399,7 @@ private: void clipCallback(Media::Clip::Notification notification); Media::Clip::ReaderPointer _gif; + mutable std::shared_ptr _dataMedia; mutable QPixmap _thumb; mutable bool _thumbGood = false; mutable std::unique_ptr _radial; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.h b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.h index 3ee685426..86e6e0b5c 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.h @@ -80,7 +80,7 @@ public: PhotoData *getPreviewPhoto() const; virtual void preload() const; - virtual void unloadAnimation() { + virtual void unloadHeavyPart() { } void update() const; diff --git a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp index 83ce40017..ef22540ae 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp @@ -304,7 +304,7 @@ void Inner::hideFinish(bool completely) { if (const auto result = item->getResult()) { result->unload(); } - item->unloadAnimation(); + item->unloadHeavyPart(); }; clearInlineRows(false); for (const auto &[result, layout] : _inlineLayouts) { diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index a61943579..43372c26d 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -2183,7 +2183,10 @@ void OverlayWidget::initStreamingThumbnail() { const auto useGood = (good && good->loaded()); const auto thumb = _doc->thumbnail(); const auto useThumb = (thumb && thumb->loaded()); - const auto blurred = _doc->thumbnailInline(); + + // #TODO optimize + const auto blurred = media ? media->thumbnailInline() : nullptr; + if (good && !useGood) { good->load({}); } else if (thumb && !useThumb) { diff --git a/Telegram/SourceFiles/media/view/media_view_pip.cpp b/Telegram/SourceFiles/media/view/media_view_pip.cpp index 33e8dbe7d..a6f564ea8 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip.cpp +++ b/Telegram/SourceFiles/media/view/media_view_pip.cpp @@ -1376,7 +1376,10 @@ QImage Pip::videoFrame(const FrameRequest &request) const { const auto useGood = (good && good->loaded()); const auto thumb = _data->thumbnail(); const auto useThumb = (thumb && thumb->loaded()); - const auto blurred = _data->thumbnailInline(); + + // #TODO optimize always use when available + const auto blurred = media ? media->thumbnailInline() : nullptr; + const auto state = !cover.isNull() ? ThumbState::Cover : useGood diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 27014fbbe..0c1de4de1 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -437,7 +437,7 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const ensureDataMediaCreated(); const auto selected = (selection == FullSelection); - const auto blurred = _data->thumbnailInline(); + const auto blurred = _dataMedia->thumbnailInline(); const auto thumbLoaded = _data->hasThumbnail() && _data->thumbnail()->loaded(); const auto goodLoaded = _dataMedia->goodThumbnail() @@ -672,10 +672,15 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const _st.songThumbSize, _width); if (clip.intersects(inner)) { + if (_data->hasThumbnail()) { + ensureDataMediaCreated(); + } p.setPen(Qt::NoPen); const auto thumbLoaded = _data->hasThumbnail() && _data->thumbnail()->loaded(); - const auto blurred = _data->thumbnailInline(); + const auto blurred = _dataMedia + ? _dataMedia->thumbnailInline() + : nullptr; if (thumbLoaded || blurred) { const auto thumb = thumbLoaded ? _data->thumbnail()->pixCircled( @@ -826,6 +831,13 @@ TextState Voice::getState( return result; } +void Voice::ensureDataMediaCreated() const { + if (_dataMedia) { + return; + } + _dataMedia = _data->createMediaView(); +} + float64 Voice::dataProgress() const { return _data->progress(); } @@ -1023,8 +1035,9 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con QRect rthumb(style::rtlrect(0, st::linksBorder + _st.filePadding.top(), _st.fileThumbSize, _st.fileThumbSize, _width)); if (clip.intersects(rthumb)) { if (wthumb) { + ensureDataMediaCreated(); const auto thumbLoaded = _data->thumbnail()->loaded(); - const auto blurred = _data->thumbnailInline(); + const auto blurred = _dataMedia->thumbnailInline(); if (thumbLoaded || blurred) { if (_thumb.isNull() || (thumbLoaded && !_thumbLoaded)) { _thumbLoaded = thumbLoaded; @@ -1279,6 +1292,13 @@ const style::RoundCheckbox &Document::checkboxStyle() const { return st::overviewSmallCheck; } +void Document::ensureDataMediaCreated() const { + if (_dataMedia) { + return; + } + _dataMedia = _data->createMediaView(); +} + float64 Document::dataProgress() const { return _data->progress(); } diff --git a/Telegram/SourceFiles/overview/overview_layout.h b/Telegram/SourceFiles/overview/overview_layout.h index 9dca446c4..abe9e42a4 100644 --- a/Telegram/SourceFiles/overview/overview_layout.h +++ b/Telegram/SourceFiles/overview/overview_layout.h @@ -255,7 +255,7 @@ private: }; -class Voice : public RadialProgressItem { +class Voice final : public RadialProgressItem { public: Voice( not_null parent, @@ -276,9 +276,11 @@ protected: const style::RoundCheckbox &checkboxStyle() const override; private: + void ensureDataMediaCreated() const; int duration() const; not_null _data; + mutable std::shared_ptr _dataMedia; StatusText _status; ClickHandlerPtr _namel; @@ -292,7 +294,7 @@ private: }; -class Document : public RadialProgressItem { +class Document final : public RadialProgressItem { public: Document( not_null parent, @@ -305,7 +307,7 @@ public: QPoint point, StateRequest request) const override; - virtual DocumentData *getDocument() const override { + DocumentData *getDocument() const override { return _data; } @@ -323,7 +325,10 @@ private: QPoint point, StateRequest request) const; + void ensureDataMediaCreated() const; + not_null _data; + mutable std::shared_ptr _dataMedia; StatusText _status; ClickHandlerPtr _msgl, _namel; @@ -342,7 +347,7 @@ private: }; -class Link : public ItemBase { +class Link final : public ItemBase { public: Link( not_null parent, diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index ddc1e1a58..d0b80ffac 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -3942,7 +3942,7 @@ void importOldRecentStickers() { date, attributes, mime, - ImagePtr(), + QByteArray(), ImagePtr(), dc, size, diff --git a/Telegram/SourceFiles/storage/serialize_document.cpp b/Telegram/SourceFiles/storage/serialize_document.cpp index c82a0ca72..0be4e1592 100644 --- a/Telegram/SourceFiles/storage/serialize_document.cpp +++ b/Telegram/SourceFiles/storage/serialize_document.cpp @@ -146,7 +146,7 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream & date, attributes, mime, - ImagePtr(), + QByteArray(), Images::Create(*thumb), dc, size, diff --git a/Telegram/SourceFiles/ui/image/image.cpp b/Telegram/SourceFiles/ui/image/image.cpp index 8b38382bf..7d3e09fce 100644 --- a/Telegram/SourceFiles/ui/image/image.cpp +++ b/Telegram/SourceFiles/ui/image/image.cpp @@ -61,6 +61,56 @@ uint64 SinglePixKey(Options options) { } // namespace +QImage FromInlineBytes(const QByteArray &bytes) { + if (bytes.size() < 3 || bytes[0] != '\x01') { + return QImage(); + } + const char header[] = "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49" +"\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c" +"\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37" +"\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3" +"\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff" +"\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35" +"\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8" +"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8" +"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8" +"\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00" +"\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01" +"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08" +"\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05" +"\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06" +"\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52" +"\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28" +"\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53" +"\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75" +"\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96" +"\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6" +"\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6" +"\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4" +"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01" +"\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08" +"\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05" +"\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41" +"\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33" +"\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26" +"\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a" +"\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74" +"\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94" +"\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4" +"\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4" +"\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4" +"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00" +"\x3f\x00"; + const char footer[] = "\xff\xd9"; + auto real = QByteArray(header, sizeof(header) - 1); + real[164] = bytes[1]; + real[166] = bytes[2]; + const auto ready = real + + bytes.mid(3) + + QByteArray::fromRawData(footer, sizeof(footer) - 1); + return App::readImage(ready); +} + void ClearRemote() { base::take(StorageImages); base::take(WebUrlImages); @@ -251,54 +301,7 @@ ImagePtr CreateFromPhotoSize( data.vh().v), bytes); }, [&](const MTPDphotoStrippedSize &data) { - const auto bytes = qba(data.vbytes()); - if (bytes.size() < 3 || bytes[0] != '\x01') { - return ImagePtr(); - } - const char header[] = "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49" -"\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c" -"\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37" -"\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3" -"\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff" -"\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35" -"\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8" -"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8" -"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8" -"\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00" -"\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01" -"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08" -"\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05" -"\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06" -"\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52" -"\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28" -"\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53" -"\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75" -"\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96" -"\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6" -"\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6" -"\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4" -"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01" -"\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08" -"\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05" -"\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41" -"\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33" -"\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26" -"\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a" -"\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74" -"\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94" -"\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4" -"\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4" -"\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4" -"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00" -"\x3f\x00"; - const char footer[] = "\xff\xd9"; - auto real = QByteArray(header, sizeof(header) - 1); - real[164] = bytes[1]; - real[166] = bytes[2]; - const auto ready = real - + bytes.mid(3) - + QByteArray::fromRawData(footer, sizeof(footer) - 1); - auto image = App::readImage(ready); + auto image = FromInlineBytes(qba(data.vbytes())); return !image.isNull() ? Images::Create(std::move(image), "JPG") : ImagePtr(); diff --git a/Telegram/SourceFiles/ui/image/image.h b/Telegram/SourceFiles/ui/image/image.h index 5d0352076..cc548f271 100644 --- a/Telegram/SourceFiles/ui/image/image.h +++ b/Telegram/SourceFiles/ui/image/image.h @@ -13,6 +13,8 @@ class HistoryItem; namespace Images { +[[nodiscard]] QImage FromInlineBytes(const QByteArray &bytes); + void ClearRemote(); void ClearAll(); diff --git a/Telegram/SourceFiles/window/window_media_preview.cpp b/Telegram/SourceFiles/window/window_media_preview.cpp index c2affbe9e..d0bd24edb 100644 --- a/Telegram/SourceFiles/window/window_media_preview.cpp +++ b/Telegram/SourceFiles/window/window_media_preview.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo.h" #include "data/data_document.h" +#include "data/data_document_media.h" #include "ui/image/image.h" #include "ui/emoji_config.h" #include "lottie/lottie_single_player.h" @@ -114,6 +115,7 @@ void MediaPreviewWidget::showPreview( _origin = origin; _photo = nullptr; _document = document; + _documentMedia = _document->createMediaView(); fillEmojiString(); resetGifAndCache(); } @@ -125,6 +127,7 @@ void MediaPreviewWidget::showPreview( _origin = origin; _photo = photo; _document = nullptr; + _documentMedia = nullptr; fillEmojiString(); resetGifAndCache(); } @@ -137,7 +140,7 @@ void MediaPreviewWidget::startShow() { _controller->enableGifPauseReason(Window::GifPauseReason::MediaPreview); } _hiding = false; - _a_shown.start([this] { update(); }, 0., 1., st::stickerPreviewDuration); + _a_shown.start([=] { update(); }, 0., 1., st::stickerPreviewDuration); } else { update(); } @@ -149,9 +152,10 @@ void MediaPreviewWidget::hidePreview() { } if (_gif) _cache = currentImage(); _hiding = true; - _a_shown.start([this] { update(); }, 1., 0., st::stickerPreviewDuration); + _a_shown.start([=] { update(); }, 1., 0., st::stickerPreviewDuration); _photo = nullptr; _document = nullptr; + _documentMedia = nullptr; resetGifAndCache(); } @@ -279,7 +283,7 @@ QPixmap MediaPreviewWidget::currentImage() const { if (_document->thumbnail()->loaded()) { _cache = _document->thumbnail()->pixBlurred(_origin, s.width(), s.height()); _cacheStatus = CacheThumbLoaded; - } else if (const auto blurred = _document->thumbnailInline()) { + } else if (const auto blurred = _documentMedia->thumbnailInline()) { _cache = _document->thumbnail()->pixBlurred(_origin, s.width(), s.height()); _cacheStatus = CacheThumbLoaded; } else { diff --git a/Telegram/SourceFiles/window/window_media_preview.h b/Telegram/SourceFiles/window/window_media_preview.h index 7dc4cc350..dee706e58 100644 --- a/Telegram/SourceFiles/window/window_media_preview.h +++ b/Telegram/SourceFiles/window/window_media_preview.h @@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "ui/rp_widget.h" +namespace Data { +class DocumentMedia; +} // namespace Data + namespace Lottie { class SinglePlayer; } // namespace Lottie @@ -55,6 +59,7 @@ private: bool _hiding = false; Data::FileOrigin _origin; DocumentData *_document = nullptr; + std::shared_ptr _documentMedia; PhotoData *_photo = nullptr; Media::Clip::ReaderPointer _gif; std::unique_ptr _lottie;