diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp index 6322ccd2b..98d1facee 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp @@ -150,22 +150,25 @@ auto EmojiPack::stickerForEmoji(EmojiPtr emoji) -> Sticker { return { i->second.get(), nullptr }; } if (!emoji->colored()) { - return Sticker(); + return {}; } const auto j = _map.find(emoji->original()); if (j != end(_map)) { const auto index = emoji->variantIndex(emoji); return { j->second.get(), ColorReplacements(index) }; } - return Sticker(); + return {}; } auto EmojiPack::stickerForEmoji(const IsolatedEmoji &emoji) -> Sticker { Expects(!emoji.empty()); - return (emoji.items[1] != nullptr) - ? Sticker() - : stickerForEmoji(emoji.items[0]); + if (!v::is_null(emoji.items[1])) { + return {}; + } else if (const auto regular = std::get_if(&emoji.items[0])) { + return stickerForEmoji(*regular); + } + return {}; } std::shared_ptr EmojiPack::image(EmojiPtr emoji) { diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp index d725af94b..05663becf 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_block.h" #include "ui/ui_utility.h" #include "apiwrap.h" +#include "styles/style_chat.h" #include "data/stickers/data_stickers.h" #include "ui/widgets/input_fields.h" @@ -36,7 +37,8 @@ using SizeTag = CustomEmojiManager::SizeTag; using LottieSize = ChatHelpers::StickerLottieSize; switch (tag) { case SizeTag::Normal: return LottieSize::MessageHistory; - case SizeTag::Large: return LottieSize::EmojiInteraction; + case SizeTag::Large: return LottieSize::StickersPanel; + case SizeTag::Isolated: return LottieSize::EmojiInteraction; } Unexpected("SizeTag value in CustomEmojiManager-LottieSizeFromTag."); } @@ -45,6 +47,9 @@ using SizeTag = CustomEmojiManager::SizeTag; switch (tag) { case SizeTag::Normal: return Ui::Emoji::GetSizeNormal(); case SizeTag::Large: return Ui::Emoji::GetSizeLarge(); + case SizeTag::Isolated: + return (st::largeEmojiSize + 2 * st::largeEmojiOutline) + * style::DevicePixelRatio(); } Unexpected("SizeTag value in CustomEmojiManager-SizeFromTag."); } @@ -396,20 +401,25 @@ std::unique_ptr CustomEmojiManager::create( Ui::CustomEmoji::Preview CustomEmojiManager::prepareNonExactPreview( DocumentId documentId, SizeTag tag) const { - const auto &other = _instances[1 - SizeIndex(tag)]; - const auto j = other.find(documentId); - if (j == end(other)) { - return {}; - } else if (const auto nonExact = j->second->imagePreview()) { - const auto size = SizeFromTag(tag); - return { - nonExact.image().scaled( - size, - size, - Qt::IgnoreAspectRatio, - Qt::SmoothTransformation), - false, - }; + for (auto i = _instances.size(); i != 0;) { + if (SizeIndex(tag) == --i) { + continue; + } + const auto &other = _instances[i]; + const auto j = other.find(documentId); + if (j == end(other)) { + continue; + } else if (const auto nonExact = j->second->imagePreview()) { + const auto size = SizeFromTag(tag); + return { + nonExact.image().scaled( + size, + size, + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation), + false, + }; + } } return {}; } @@ -535,7 +545,7 @@ void CustomEmojiManager::requestSetFor(not_null document) { int CustomEmojiManager::SizeIndex(SizeTag tag) { const auto result = static_cast(tag); - Ensures(result >= 0 && result < 2); + Ensures(result >= 0 && result < kSizeCount); return result; } diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h index f4a1c6696..74666add1 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h @@ -33,6 +33,9 @@ public: enum class SizeTag { Normal, Large, + Isolated, + + kCount, }; CustomEmojiManager(not_null owner); @@ -64,6 +67,8 @@ public: [[nodiscard]] Session &owner() const; private: + static constexpr auto kSizeCount = int(SizeTag::kCount); + struct RepaintBunch { crl::time when = 0; std::vector> instances; @@ -91,12 +96,16 @@ private: const not_null _owner; - base::flat_map< - uint64, - std::unique_ptr> _instances[2]; - base::flat_map< - uint64, - std::vector>> _loaders[2]; + std::array< + base::flat_map< + uint64, + std::unique_ptr>, + kSizeCount> _instances; + std::array< + base::flat_map< + uint64, + std::vector>>, + kSizeCount> _loaders; base::flat_set _pendingForRequest; mtpRequestId _requestId = 0; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 93be919ea..9092d0fd3 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -1308,7 +1308,7 @@ TextWithEntities HistoryItem::inReplyText() const { } Ui::Text::IsolatedEmoji HistoryItem::isolatedEmoji() const { - return Ui::Text::IsolatedEmoji(); + return {}; } HistoryItem::~HistoryItem() { diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index c932f83bd..f09f891e5 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -419,15 +419,18 @@ void Element::customEmojiRepaint() { } } +void Element::clearCustomEmojiRepaint() const { + _customEmojiRepaintScheduled = false; + data()->_customEmojiRepaintScheduled = false; +} + void Element::prepareCustomEmojiPaint( Painter &p, const Ui::Text::String &text) const { - const auto item = data(); if (!text.hasCustomEmoji()) { return; } - _customEmojiRepaintScheduled = false; - item->_customEmojiRepaintScheduled = false; + clearCustomEmojiRepaint(); p.setInactive(delegate()->elementIsGifPaused()); if (!_heavyCustomEmoji) { _heavyCustomEmoji = true; diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 975280bb6..6bd3ae0a6 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -431,6 +431,7 @@ public: void prepareCustomEmojiPaint( Painter &p, const Ui::Text::String &text) const; + void clearCustomEmojiRepaint() const; [[nodiscard]] ClickHandlerPtr fromPhotoLink() const { return fromLink(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_large_emoji.cpp b/Telegram/SourceFiles/history/view/media/history_view_large_emoji.cpp index ebce8d94b..7dcb83aa6 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_large_emoji.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_large_emoji.cpp @@ -14,20 +14,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "ui/image/image.h" #include "ui/chat/chat_style.h" +#include "data/data_session.h" #include "data/data_file_origin.h" +#include "data/stickers/data_custom_emoji.h" #include "styles/style_chat.h" namespace HistoryView { namespace { -using EmojiImage = Stickers::LargeEmojiImage; +using Stickers::LargeEmojiImage; +using ImagePtr = std::shared_ptr; +using CustomPtr = std::unique_ptr; auto ResolveImages( not_null session, + Fn customEmojiRepaint, const Ui::Text::IsolatedEmoji &emoji) --> std::array, Ui::Text::kIsolatedEmojiLimit> { - const auto single = [&](EmojiPtr emoji) { - return emoji ? session->emojiStickersPack().image(emoji) : nullptr; +-> std::array { + const auto single = [&](Ui::Text::IsolatedEmoji::Item item) + -> LargeEmojiMedia { + if (const auto regular = std::get_if(&item)) { + return session->emojiStickersPack().image(*regular); + } else if (const auto custom = std::get_if(&item)) { + return session->data().customEmojiManager().create( + *custom, + customEmojiRepaint, + Data::CustomEmojiManager::SizeTag::Isolated); + } + return v::null; }; return { { single(emoji.items[0]), @@ -35,28 +49,25 @@ auto ResolveImages( single(emoji.items[2]) } }; } -auto NonEmpty(const std::array, Ui::Text::kIsolatedEmojiLimit> &images) { - using namespace rpl::mappers; - - return images | ranges::views::filter(_1 != nullptr); -} - } // namespace LargeEmoji::LargeEmoji( not_null parent, const Ui::Text::IsolatedEmoji &emoji) : _parent(parent) -, _images(ResolveImages(&parent->data()->history()->session(), emoji)) { +, _images(ResolveImages( + &parent->data()->history()->session(), + [=] { parent->customEmojiRepaint(); }, + emoji)) { } QSize LargeEmoji::size() { using namespace rpl::mappers; - const auto count = ranges::distance(NonEmpty(_images)); - Assert(count > 0); + const auto count = _images.size() + - ranges::count(_images, LargeEmojiMedia()); - const auto single = EmojiImage::Size() / cIntRetinaFactor(); + const auto single = LargeEmojiImage::Size() / cIntRetinaFactor(); const auto skip = st::largeEmojiSkip - 2 * st::largeEmojiOutline; const auto inner = count * single.width() + (count - 1) * skip; const auto &padding = st::largeEmojiPadding; @@ -70,23 +81,91 @@ void LargeEmoji::draw( Painter &p, const PaintContext &context, const QRect &r) { - auto &&images = NonEmpty(_images); + _parent->clearCustomEmojiRepaint(); + const auto &padding = st::largeEmojiPadding; auto x = r.x() + (r.width() - _size.width()) / 2 + padding.left(); const auto y = r.y() + (r.height() - _size.height()) / 2 + padding.top(); const auto skip = st::largeEmojiSkip - 2 * st::largeEmojiOutline; - const auto size = EmojiImage::Size() / cIntRetinaFactor(); - for (const auto &image : images) { - if (const auto &prepared = image->image) { - const auto colored = context.selected() - ? &context.st->msgStickerOverlay() - : nullptr; - p.drawPixmap(x, y, prepared->pix(size, { .colored = colored })); - } else if (image->load) { - image->load(); + const auto size = LargeEmojiImage::Size() / cIntRetinaFactor(); + const auto paused = _parent->delegate()->elementIsGifPaused(); + const auto selected = context.selected(); + if (!selected) { + _selectedFrame = QImage(); + } + for (const auto &media : _images) { + if (const auto image = std::get_if(&media)) { + if (const auto &prepared = (*image)->image) { + const auto colored = selected + ? &context.st->msgStickerOverlay() + : nullptr; + p.drawPixmap( + x, + y, + prepared->pix(size, { .colored = colored })); + } else if ((*image)->load) { + (*image)->load(); + } + } else if (const auto custom = std::get_if(&media)) { + paintCustom(p, x, y, custom->get(), context, paused); + } else { + continue; } x += size.width() + skip; } } +void LargeEmoji::paintCustom( + QPainter &p, + int x, + int y, + not_null emoji, + const PaintContext &context, + bool paused) { + if (!_hasHeavyPart) { + _hasHeavyPart = true; + _parent->history()->owner().registerHeavyViewPart(_parent); + } + const auto inner = st::largeEmojiSize + 2 * st::largeEmojiOutline; + const auto outer = Ui::Text::AdjustCustomEmojiSize(inner); + const auto skip = (inner - outer) / 2; + const auto preview = context.imageStyle()->msgServiceBg->c; + if (context.selected()) { + const auto factor = style::DevicePixelRatio(); + const auto size = QSize(outer, outer) * factor; + if (_selectedFrame.size() != size) { + _selectedFrame = QImage( + size, + QImage::Format_ARGB32_Premultiplied); + _selectedFrame.setDevicePixelRatio(factor); + } + _selectedFrame.fill(Qt::transparent); + auto q = QPainter(&_selectedFrame); + emoji->paint(q, 0, 0, context.now, preview, paused); + q.end(); + + _selectedFrame = Images::Colored( + std::move(_selectedFrame), + context.st->msgStickerOverlay()->c); + p.drawImage(x + skip, y + skip, _selectedFrame); + } else { + emoji->paint(p, x + skip, y + skip, context.now, preview, paused); + } +} + +bool LargeEmoji::hasHeavyPart() const { + return _hasHeavyPart; +} + +void LargeEmoji::unloadHeavyPart() { + if (_hasHeavyPart) { + _hasHeavyPart = false; + for (auto &media : _images) { + if (const auto custom = std::get_if(&media)) { + (*custom)->unload(); + } + } + } +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_large_emoji.h b/Telegram/SourceFiles/history/view/media/history_view_large_emoji.h index 7ac0f32ff..d3541ffc7 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_large_emoji.h +++ b/Telegram/SourceFiles/history/view/media/history_view_large_emoji.h @@ -20,6 +20,11 @@ struct LargeEmojiImage; namespace HistoryView { +using LargeEmojiMedia = std::variant< + v::null_t, + std::shared_ptr, + std::unique_ptr>; + class LargeEmoji final : public UnwrappedMedia::Content { public: LargeEmoji( @@ -39,12 +44,23 @@ public: return true; } + bool hasHeavyPart() const override; + void unloadHeavyPart() override; + private: + void paintCustom( + QPainter &p, + int x, + int y, + not_null emoji, + const PaintContext &context, + bool paused); + const not_null _parent; - const std::array< - std::shared_ptr, - Ui::Text::kIsolatedEmojiLimit> _images; + const std::array _images; + QImage _selectedFrame; QSize _size; + bool _hasHeavyPart = false; }; diff --git a/Telegram/SourceFiles/ui/text/custom_emoji_instance.cpp b/Telegram/SourceFiles/ui/text/custom_emoji_instance.cpp index b6fdf18c3..c1e82faa9 100644 --- a/Telegram/SourceFiles/ui/text/custom_emoji_instance.cpp +++ b/Telegram/SourceFiles/ui/text/custom_emoji_instance.cpp @@ -19,7 +19,7 @@ namespace Ui::CustomEmoji { namespace { constexpr auto kMaxSize = 128; -constexpr auto kMaxFrames = 512; +constexpr auto kMaxFrames = 180; constexpr auto kMaxFrameDuration = 86400 * crl::time(1000); constexpr auto kCacheVersion = 1; constexpr auto kPreloadFrames = 3; @@ -415,7 +415,7 @@ void Renderer::frameReady( } if (const auto count = generator->count()) { if (!_cache.frames()) { - _cache.reserve(count); + _cache.reserve(std::max(count, kMaxFrames)); } } const auto current = _cache.currentFrame(); @@ -425,7 +425,7 @@ void Renderer::frameReady( if (explicitRepaint && _repaint) { _repaint(); } - if (!duration) { + if (!duration || total + 1 >= kMaxFrames) { finish(); } else if (current + kPreloadFrames > total) { renderNext(std::move(generator), std::move(frame)); diff --git a/Telegram/lib_ui b/Telegram/lib_ui index c5b32c53e..a5d7b23a6 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit c5b32c53efbc1481f3942d28ef7e4f5eaca5df1f +Subproject commit a5d7b23a638e6c2d9bffa97bf40d5d7559a926c8