From 9c23de7f1a9d3d62488b1085fc46f9a96ae00271 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 24 Oct 2023 11:47:11 +0400 Subject: [PATCH] Display reply background emoji. --- Telegram/SourceFiles/data/data_peer.cpp | 10 +- Telegram/SourceFiles/data/data_peer.h | 5 +- Telegram/SourceFiles/data/data_session.cpp | 4 +- .../history/history_item_components.cpp | 157 +++++++++++++++++- Telegram/SourceFiles/ui/chat/chat_style.cpp | 19 +++ Telegram/SourceFiles/ui/chat/chat_style.h | 26 +++ 6 files changed, 211 insertions(+), 10 deletions(-) diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index d89b23984..edda209ad 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -627,9 +627,9 @@ bool PeerData::changeColorIndex( : clearColorIndex(); } -bool PeerData::changeBackgroundEmoji( +bool PeerData::changeBackgroundEmojiId( const tl::conditional &cloudBackgroundEmoji) { - return changeBackgroundEmoji(cloudBackgroundEmoji + return changeBackgroundEmojiId(cloudBackgroundEmoji ? cloudBackgroundEmoji->v : DocumentId()); } @@ -869,7 +869,11 @@ bool PeerData::clearColorIndex() { return true; } -bool PeerData::changeBackgroundEmoji(uint64 id) { +DocumentId PeerData::backgroundEmojiId() const { + return _backgroundEmojiId; +} + +bool PeerData::changeBackgroundEmojiId(DocumentId id) { if (_backgroundEmojiId == id) { return false; } diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index c0d84e718..33a772741 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -169,7 +169,8 @@ public: } bool changeColorIndex(uint8 index); bool clearColorIndex(); - bool changeBackgroundEmoji(uint64 id); + [[nodiscard]] DocumentId backgroundEmojiId() const; + bool changeBackgroundEmojiId(DocumentId id); [[nodiscard]] bool isUser() const { return peerIsUser(id); @@ -361,7 +362,7 @@ public: void setSettings(const MTPPeerSettings &data); bool changeColorIndex(const tl::conditional &cloudColorIndex); - bool changeBackgroundEmoji( + bool changeBackgroundEmojiId( const tl::conditional &cloudBackgroundEmoji); enum class BlockStatus : char { diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 3f8b7796f..4c4223895 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -711,7 +711,7 @@ not_null Session::processUser(const MTPUser &data) { flags |= UpdateFlag::Color; decorationsUpdated = true; } - if (result->changeBackgroundEmoji(data.vbackground_emoji_id())) { + if (result->changeBackgroundEmojiId(data.vbackground_emoji_id())) { flags |= UpdateFlag::BackgroundEmoji; decorationsUpdated = true; } @@ -997,7 +997,7 @@ not_null Session::processChat(const MTPChat &data) { flags |= UpdateFlag::Color; decorationsUpdated = true; } - if (result->changeBackgroundEmoji(data.vbackground_emoji_id())) { + if (result->changeBackgroundEmojiId(data.vbackground_emoji_id())) { flags |= UpdateFlag::BackgroundEmoji; decorationsUpdated = true; } diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index c8b3acece..a21b77b58 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "media/audio/media_audio.h" #include "media/player/media_player_instance.h" +#include "data/stickers/data_custom_emoji.h" #include "data/data_media_types.h" #include "data/data_session.h" #include "data/data_user.h" @@ -55,6 +56,127 @@ namespace { const auto kPsaForwardedPrefix = "cloud_lng_forwarded_psa_"; +void ValidateBackgroundEmoji( + DocumentId backgroundEmojiId, + not_null data, + not_null cache, + not_null quote, + not_null holder) { + if (data->firstFrameMask.isNull()) { + if (!cache->frames[0].isNull()) { + for (auto &frame : cache->frames) { + frame = QImage(); + } + } + const auto tag = Data::CustomEmojiSizeTag::Isolated; + if (!data->emoji) { + const auto owner = &holder->history()->owner(); + const auto repaint = crl::guard(holder, [=] { + holder->history()->owner().requestViewRepaint(holder); + }); + data->emoji = owner->customEmojiManager().create( + backgroundEmojiId, + repaint, + tag); + } + if (!data->emoji->ready()) { + return; + } + const auto size = Data::FrameSizeFromTag(tag); + data->firstFrameMask = QImage( + QSize(size, size), + QImage::Format_ARGB32_Premultiplied); + data->firstFrameMask.fill(Qt::transparent); + data->firstFrameMask.setDevicePixelRatio(style::DevicePixelRatio()); + auto p = Painter(&data->firstFrameMask); + data->emoji->paint(p, { + .textColor = QColor(255, 255, 255), + .position = QPoint(0, 0), + .internal = { + .forceFirstFrame = true, + }, + }); + p.end(); + + data->emoji = nullptr; + } + if (!cache->frames[0].isNull() && cache->color == quote->icon) { + return; + } + cache->color = quote->icon; + const auto ratio = style::DevicePixelRatio(); + auto colorized = QImage( + data->firstFrameMask.size(), + QImage::Format_ARGB32_Premultiplied); + colorized.setDevicePixelRatio(ratio); + style::colorizeImage( + data->firstFrameMask, + cache->color, + &colorized, + QRect(), // src + QPoint(), // dst + true); // use alpha + const auto make = [&](int size) { + size = style::ConvertScale(size) * ratio; + auto result = colorized.scaled( + size, + size, + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + result.setDevicePixelRatio(ratio); + return result; + }; + + constexpr auto kSize1 = 12; + constexpr auto kSize2 = 16; + constexpr auto kSize3 = 20; + cache->frames[0] = make(kSize1); + cache->frames[1] = make(kSize2); + cache->frames[2] = make(kSize3); +} + +void FillBackgroundEmoji( + Painter &p, + const QRect &rect, + bool quote, + const Ui::BackgroundEmojiCache &cache) { + p.setClipRect(rect); + + const auto &frames = cache.frames; + const auto right = rect.x() + rect.width(); + const auto paint = [&](int x, int y, int index, float64 opacity) { + y = style::ConvertScale(y); + if (y >= rect.height()) { + return; + } + p.setOpacity(opacity); + p.drawImage( + right - style::ConvertScale(x + (quote ? 12 : 0)), + rect.y() + y, + frames[index]); + }; + + paint(28, 4, 2, 0.32); + paint(51, 15, 1, 0.32); + paint(64, -2, 0, 0.28); + paint(87, 11, 1, 0.24); + paint(125, -2, 2, 0.16); + + paint(28, 31, 1, 0.24); + paint(72, 33, 2, 0.2); + + paint(46, 52, 1, 0.24); + paint(24, 55, 2, 0.18); + + if (quote) { + paint(4, 23, 1, 0.28); + paint(0, 48, 0, 0.24); + } + + p.setClipping(false); + p.setOpacity(1.); +} + } // namespace void HistoryMessageVia::create( @@ -676,10 +798,18 @@ void HistoryMessageReply::paint( const auto rect = QRect(x, y, w, _height); const auto hasQuote = !_fields.quote.empty(); const auto selected = context.selected(); - const auto colorIndexPlusOne = resolvedMessage - ? (resolvedMessage->colorIndex() + 1) + const auto colorPeer = resolvedMessage + ? resolvedMessage->displayFrom() : resolvedStory - ? (resolvedStory->peer()->colorIndex() + 1) + ? resolvedStory->peer().get() + : nullptr; + const auto backgroundEmojiId = colorPeer + ? colorPeer->backgroundEmojiId() + : DocumentId(); + const auto colorIndexPlusOne = colorPeer + ? (colorPeer->colorIndex() + 1) + : resolvedMessage + ? (resolvedMessage->hiddenSenderInfo()->colorIndex + 1) : 0; const auto useColorIndex = colorIndexPlusOne && !context.outbg; const auto twoColored = colorIndexPlusOne @@ -698,12 +828,33 @@ void HistoryMessageReply::paint( const auto "eSt = hasQuote ? st::messageTextStyle.blockquote : st::messageQuoteStyle; + const auto backgroundEmoji = backgroundEmojiId + ? st->backgroundEmojiData(backgroundEmojiId).get() + : nullptr; + const auto backgroundEmojiCache = backgroundEmoji + ? &backgroundEmoji->caches[Ui::BackgroundEmojiData::CacheIndex( + selected, + context.outbg, + inBubble, + colorIndexPlusOne)] + : nullptr; const auto rippleColor = cache->bg; if (!inBubble) { cache->bg = QColor(0, 0, 0, 0); } Ui::Text::ValidateQuotePaintCache(*cache, quoteSt); Ui::Text::FillQuotePaint(p, rect, *cache, quoteSt); + if (backgroundEmoji) { + ValidateBackgroundEmoji( + backgroundEmojiId, + backgroundEmoji, + backgroundEmojiCache, + cache, + holder); + if (!backgroundEmojiCache->frames[0].isNull()) { + FillBackgroundEmoji(p, rect, hasQuote, *backgroundEmojiCache); + } + } if (!inBubble) { cache->bg = rippleColor; } diff --git a/Telegram/SourceFiles/ui/chat/chat_style.cpp b/Telegram/SourceFiles/ui/chat/chat_style.cpp index 17524b4ee..1ca60e7a3 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_style.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/chat_theme.h" #include "ui/image/image_prepare.h" // ImageRoundRadius +#include "ui/text/text_custom_emoji.h" #include "ui/color_contrast.h" #include "ui/painter.h" #include "ui/ui_utility.h" @@ -190,6 +191,17 @@ ColorIndexValues SimpleColorIndexValues(QColor color, bool twoColored) { }; } +int BackgroundEmojiData::CacheIndex( + bool selected, + bool outbg, + bool inbubble, + uint8 colorIndexPlusOne) { + const auto base = colorIndexPlusOne + ? (colorIndexPlusOne - 1) + : (kColorIndexCount + (!inbubble ? 0 : outbg ? 1 : 2)); + return (base * 2) + (selected ? 1 : 0); +}; + ChatStyle::ChatStyle() { finalize(); make(_historyPsaForwardPalette, st::historyPsaForwardPalette); @@ -553,6 +565,8 @@ ChatStyle::ChatStyle(not_null isolated) assignPalette(isolated); } +ChatStyle::~ChatStyle() = default; + void ChatStyle::apply(not_null theme) { applyCustomPalette(theme->palette()); } @@ -802,6 +816,11 @@ const style::TextPalette &ChatStyle::coloredTextPalette( return result.data; } +not_null ChatStyle::backgroundEmojiData( + uint64 id) const { + return &_backgroundEmojis[id]; +} + not_null ChatStyle::coloredQuoteCache( bool selected, uint8 colorIndex) const { diff --git a/Telegram/SourceFiles/ui/chat/chat_style.h b/Telegram/SourceFiles/ui/chat/chat_style.h index 6d7367b44..8f2b7639d 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.h +++ b/Telegram/SourceFiles/ui/chat/chat_style.h @@ -21,6 +21,10 @@ struct TwoIconButton; struct ScrollArea; } // namespace style +namespace Ui::Text { +class CustomEmoji; +} // namespace Ui::Text + namespace Ui { class ChatTheme; @@ -112,6 +116,23 @@ struct ReactionPaintInfo { Fn effectPaint; }; +struct BackgroundEmojiCache { + QColor color; + std::array frames; +}; + +struct BackgroundEmojiData { + std::unique_ptr emoji; + QImage firstFrameMask; + std::array caches; + + [[nodiscard]] static int CacheIndex( + bool selected, + bool outbg, + bool inbubble, + uint8 colorIndexPlusOne); +}; + struct ChatPaintContext { not_null st; const BubblePattern *bubblesPattern = nullptr; @@ -185,6 +206,7 @@ class ChatStyle final : public style::palette { public: ChatStyle(); explicit ChatStyle(not_null isolated); + ~ChatStyle(); void apply(not_null theme); void applyCustomPalette(const style::palette *palette); @@ -242,6 +264,9 @@ public: bool selected, uint8 colorIndex) const; + [[nodiscard]] not_null backgroundEmojiData( + uint64 id) const; + [[nodiscard]] const CornersPixmaps &msgBotKbOverBgAddCornersSmall() const; [[nodiscard]] const CornersPixmaps &msgBotKbOverBgAddCornersLarge() const; [[nodiscard]] const CornersPixmaps &msgSelectOverlayCorners( @@ -408,6 +433,7 @@ private: mutable std::array< ColoredPalette, 2 * kColorIndexCount> _coloredTextPalettes; + mutable base::flat_map _backgroundEmojis; style::TextPalette _historyPsaForwardPalette; style::TextPalette _imgReplyTextPalette;