From b334a1f4fda6cd126d00ef20db4a5e98e20d60bf Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 28 Dec 2022 14:07:38 +0400 Subject: [PATCH] Support spoilers in chats list media previews. --- .../SourceFiles/data/data_media_types.cpp | 71 +++++++++++++------ .../dialogs/ui/dialogs_message_view.cpp | 19 ++++- .../dialogs/ui/dialogs_message_view.h | 2 + .../history/view/history_view_item_preview.h | 4 ++ 4 files changed, 72 insertions(+), 24 deletions(-) diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 1fe1b35d66..b6b881b22a 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -101,7 +101,8 @@ using ItemPreviewImage = HistoryView::ItemPreviewImage; [[nodiscard]] QImage PreparePreviewImage( not_null image, - ImageRoundRadius radius = ImageRoundRadius::Small) { + ImageRoundRadius radius, + bool spoiler) { const auto original = image->original(); if (original.width() * 10 < original.height() || original.height() * 10 < original.width()) { @@ -119,6 +120,11 @@ using ItemPreviewImage = HistoryView::ItemPreviewImage; size, size ).convertToFormat(QImage::Format_ARGB32_Premultiplied); + if (spoiler) { + square = Images::BlurLargeImage( + std::move(square), + style::ConvertScale(3) * factor); + } if (radius == ImageRoundRadius::Small) { struct Cache { base::flat_map> all; @@ -146,27 +152,33 @@ using ItemPreviewImage = HistoryView::ItemPreviewImage; return square; } +template +[[nodiscard]] uint64 CountCacheKey(not_null data, bool spoiler) { + return (reinterpret_cast(data.get()) & ~1) | (spoiler ? 1 : 0); +} + [[nodiscard]] ItemPreviewImage PreparePhotoPreview( not_null item, const std::shared_ptr &media, - ImageRoundRadius radius) { + ImageRoundRadius radius, + bool spoiler) { const auto photo = media->owner(); - const auto readyCacheKey = reinterpret_cast(photo.get()); + const auto counted = CountCacheKey(photo, spoiler); if (const auto small = media->image(PhotoSize::Small)) { - return { PreparePreviewImage(small, radius), readyCacheKey }; + return { PreparePreviewImage(small, radius, spoiler), counted }; } else if (const auto thumbnail = media->image(PhotoSize::Thumbnail)) { - return { PreparePreviewImage(thumbnail, radius), readyCacheKey }; + return { PreparePreviewImage(thumbnail, radius, spoiler), counted }; } else if (const auto large = media->image(PhotoSize::Large)) { - return { PreparePreviewImage(large, radius), readyCacheKey }; + return { PreparePreviewImage(large, radius, spoiler), counted }; } const auto allowedToDownload = media->autoLoadThumbnailAllowed( item->history()->peer); - const auto cacheKey = allowedToDownload ? 0 : readyCacheKey; + const auto cacheKey = allowedToDownload ? 0 : counted; if (allowedToDownload) { media->owner()->load(PhotoSize::Small, item->fullId()); } if (const auto blurred = media->thumbnailInline()) { - return { PreparePreviewImage(blurred, radius), cacheKey }; + return { PreparePreviewImage(blurred, radius, spoiler), cacheKey }; } return { QImage(), allowedToDownload ? 0 : cacheKey }; } @@ -174,17 +186,21 @@ using ItemPreviewImage = HistoryView::ItemPreviewImage; [[nodiscard]] ItemPreviewImage PrepareFilePreviewImage( not_null item, const std::shared_ptr &media, - ImageRoundRadius radius) { + ImageRoundRadius radius, + bool spoiler) { Expects(media->owner()->hasThumbnail()); const auto document = media->owner(); - const auto readyCacheKey = reinterpret_cast(document.get()); + const auto readyCacheKey = CountCacheKey(document, spoiler); if (const auto thumbnail = media->thumbnail()) { - return { PreparePreviewImage(thumbnail, radius), readyCacheKey }; + return { + PreparePreviewImage(thumbnail, radius, spoiler), + readyCacheKey, + }; } document->loadThumbnail(item->fullId()); if (const auto blurred = media->thumbnailInline()) { - return { PreparePreviewImage(blurred, radius), 0 }; + return { PreparePreviewImage(blurred, radius, spoiler), 0 }; } return { QImage(), 0 }; } @@ -204,8 +220,9 @@ using ItemPreviewImage = HistoryView::ItemPreviewImage; [[nodiscard]] ItemPreviewImage PrepareFilePreview( not_null item, const std::shared_ptr &media, - ImageRoundRadius radius) { - auto result = PrepareFilePreviewImage(item, media, radius); + ImageRoundRadius radius, + bool spoiler) { + auto result = PrepareFilePreviewImage(item, media, radius, spoiler); const auto document = media->owner(); if (!result.data.isNull() && (document->isVideoFile() || document->isVideoMessage())) { @@ -223,13 +240,14 @@ using ItemPreviewImage = HistoryView::ItemPreviewImage; template [[nodiscard]] ItemPreviewImage FindCachedPreview( const std::vector *existing, - not_null data) { + not_null data, + bool spoiler) { if (!existing) { return {}; } const auto i = ranges::find( *existing, - reinterpret_cast(data.get()), + CountCacheKey(data, spoiler), &ItemPreviewImage::cacheKey); return (i != end(*existing)) ? *i : ItemPreviewImage(); } @@ -619,14 +637,18 @@ ItemPreview MediaPhoto::toPreview(ToPreviewOptions options) const { } auto images = std::vector(); auto context = std::any(); - if (auto cached = FindCachedPreview(options.existing, _photo)) { - images.push_back(std::move(cached)); + if (auto found = FindCachedPreview(options.existing, _photo, _spoiler)) { + images.push_back(std::move(found)); } else { const auto media = _photo->createMediaView(); const auto radius = _chat ? ImageRoundRadius::Ellipse : ImageRoundRadius::Small; - if (auto prepared = PreparePhotoPreview(parent(), media, radius) + if (auto prepared = PreparePhotoPreview( + parent(), + media, + radius, + _spoiler) ; prepared || !prepared.cacheKey) { images.push_back(std::move(prepared)); if (!prepared.cacheKey) { @@ -848,14 +870,19 @@ ItemPreview MediaFile::toPreview(ToPreviewOptions options) const { } auto images = std::vector(); auto context = std::any(); - if (auto cached = FindCachedPreview(options.existing, _document)) { - images.push_back(std::move(cached)); + const auto existing = options.existing; + if (auto found = FindCachedPreview(existing, _document, _spoiler)) { + images.push_back(std::move(found)); } else if (TryFilePreview(_document)) { const auto media = _document->createMediaView(); const auto radius = _document->isVideoMessage() ? ImageRoundRadius::Ellipse : ImageRoundRadius::Small; - if (auto prepared = PrepareFilePreview(parent(), media, radius) + if (auto prepared = PrepareFilePreview( + parent(), + media, + radius, + _spoiler) ; prepared || !prepared.cacheKey) { images.push_back(std::move(prepared)); if (!prepared.cacheKey) { diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp index 7159aa2d81..986ec35817 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "dialogs/ui/dialogs_layout.h" #include "dialogs/ui/dialogs_topics_view.h" +#include "ui/effects/spoiler_mess.h" #include "ui/text/text_options.h" #include "ui/text/text_utilities.h" #include "ui/image/image.h" @@ -185,6 +186,11 @@ void MessageView::prepare( context); _textCachedFor = item; _imagesCache = std::move(preview.images); + if (!ranges::any_of(_imagesCache, &ItemPreviewImage::hasSpoiler)) { + _spoiler = nullptr; + } else if (!_spoiler) { + _spoiler = std::make_unique(customEmojiRepaint); + } if (preview.loadingContext.has_value()) { if (!_loadingContext) { _loadingContext = std::make_unique(); @@ -302,10 +308,19 @@ void MessageView::paint( if (rect.width() < st::dialogsMiniPreview) { break; } - p.drawImage( + const auto mini = QRect( rect.x(), rect.y() + st::dialogsMiniPreviewTop, - image.data); + st::dialogsMiniPreview, + st::dialogsMiniPreview); + if (!image.data.isNull()) { + p.drawImage(mini, image.data); + if (image.hasSpoiler()) { + const auto frame = DefaultImageSpoiler().frame( + _spoiler->index(context.now, context.paused)); + FillSpoilerRect(p, mini, frame); + } + } rect.setLeft(rect.x() + st::dialogsMiniPreview + st::dialogsMiniPreviewSkip); diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h index 7718f3c641..4ef089131c 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h @@ -18,6 +18,7 @@ struct DialogRow; } // namespace style namespace Ui { +class SpoilerAnimation; } // namespace Ui namespace Data { @@ -88,6 +89,7 @@ private: mutable std::unique_ptr _topics; mutable Text::String _textCache; mutable std::vector _imagesCache; + mutable std::unique_ptr _spoiler; mutable std::unique_ptr _loadingContext; }; diff --git a/Telegram/SourceFiles/history/view/history_view_item_preview.h b/Telegram/SourceFiles/history/view/history_view_item_preview.h index 6bf76ecf97..a2229cddf4 100644 --- a/Telegram/SourceFiles/history/view/history_view_item_preview.h +++ b/Telegram/SourceFiles/history/view/history_view_item_preview.h @@ -13,6 +13,10 @@ struct ItemPreviewImage { QImage data; uint64 cacheKey = 0; + [[nodiscard]] bool hasSpoiler() const { + return (cacheKey & 1); + } + explicit operator bool() const { return !data.isNull(); }