From 51c2bc734950822c45cea2cd641752e5d32f040b Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 22 Jun 2020 23:01:19 +0400 Subject: [PATCH] Create only one EmojiImageLoader to fix a crash. --- Telegram/CMakeLists.txt | 2 + .../stickers_emoji_image_loader.cpp | 97 ++++++++++++ .../stickers_emoji_image_loader.h | 34 ++++ .../chat_helpers/stickers_emoji_pack.cpp | 147 +----------------- .../chat_helpers/stickers_emoji_pack.h | 8 - Telegram/SourceFiles/core/application.cpp | 50 ++++++ Telegram/SourceFiles/core/application.h | 17 ++ Telegram/SourceFiles/main/main_domain.cpp | 1 - .../window/notifications_manager.cpp | 2 +- .../SourceFiles/window/window_main_menu.cpp | 28 +++- 10 files changed, 234 insertions(+), 152 deletions(-) create mode 100644 Telegram/SourceFiles/chat_helpers/stickers_emoji_image_loader.cpp create mode 100644 Telegram/SourceFiles/chat_helpers/stickers_emoji_image_loader.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index cbd0ed302..ee7a2907d 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -296,6 +296,8 @@ PRIVATE chat_helpers/message_field.h chat_helpers/spellchecker_common.cpp chat_helpers/spellchecker_common.h + chat_helpers/stickers_emoji_image_loader.cpp + chat_helpers/stickers_emoji_image_loader.h chat_helpers/stickers_emoji_pack.cpp chat_helpers/stickers_emoji_pack.h chat_helpers/stickers_dice_pack.cpp diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_image_loader.cpp b/Telegram/SourceFiles/chat_helpers/stickers_emoji_image_loader.cpp new file mode 100644 index 000000000..fa47ce5f7 --- /dev/null +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_image_loader.cpp @@ -0,0 +1,97 @@ +/* +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 "chat_helpers/stickers_emoji_image_loader.h" + +#include "styles/style_history.h" + +namespace Stickers { + +EmojiImageLoader::EmojiImageLoader(crl::weak_on_queue weak) +: _weak(std::move(weak)) { +} + +void EmojiImageLoader::init( + std::shared_ptr images, + bool largeEnabled) { + Expects(images != nullptr); + + _images = std::move(images); + if (largeEnabled) { + _images->ensureLoaded(); + } +} + +QImage EmojiImageLoader::prepare(EmojiPtr emoji) const { + const auto loaded = _images->ensureLoaded(); + const auto factor = cIntRetinaFactor(); + const auto side = st::largeEmojiSize + 2 * st::largeEmojiOutline; + auto tinted = QImage( + QSize(st::largeEmojiSize, st::largeEmojiSize) * factor, + QImage::Format_ARGB32_Premultiplied); + tinted.fill(Qt::white); + if (loaded) { + QPainter p(&tinted); + p.setCompositionMode(QPainter::CompositionMode_DestinationIn); + _images->draw( + p, + emoji, + st::largeEmojiSize * factor, + 0, + 0); + } + auto result = QImage( + QSize(side, side) * factor, + QImage::Format_ARGB32_Premultiplied); + result.fill(Qt::transparent); + if (loaded) { + QPainter p(&result); + const auto delta = st::largeEmojiOutline * factor; + const auto planar = std::array{ { + { 0, -1 }, + { -1, 0 }, + { 1, 0 }, + { 0, 1 }, + } }; + for (const auto &shift : planar) { + for (auto i = 0; i != delta; ++i) { + p.drawImage(QPoint(delta, delta) + shift * (i + 1), tinted); + } + } + const auto diagonal = std::array{ { + { -1, -1 }, + { 1, -1 }, + { -1, 1 }, + { 1, 1 }, + } }; + const auto corrected = int(std::round(delta / sqrt(2.))); + for (const auto &shift : diagonal) { + for (auto i = 0; i != corrected; ++i) { + p.drawImage(QPoint(delta, delta) + shift * (i + 1), tinted); + } + } + _images->draw( + p, + emoji, + st::largeEmojiSize * factor, + delta, + delta); + } + return result; +} + +void EmojiImageLoader::switchTo(std::shared_ptr images) { + _images = std::move(images); +} + +auto EmojiImageLoader::releaseImages() -> std::shared_ptr { + return std::exchange( + _images, + std::make_shared(_images->id())); +} + +} // namespace Stickers diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_image_loader.h b/Telegram/SourceFiles/chat_helpers/stickers_emoji_image_loader.h new file mode 100644 index 000000000..09aa1310e --- /dev/null +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_image_loader.h @@ -0,0 +1,34 @@ +/* +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 "ui/emoji_config.h" + +namespace Stickers { + +class EmojiImageLoader { +public: + using UniversalImages = Ui::Emoji::UniversalImages; + + explicit EmojiImageLoader(crl::weak_on_queue weak); + + void init( + std::shared_ptr images, + bool largeEnabled); + + [[nodiscard]] QImage prepare(EmojiPtr emoji) const; + void switchTo(std::shared_ptr images); + std::shared_ptr releaseImages(); + +private: + crl::weak_on_queue _weak; + std::shared_ptr _images; + +}; + +} // namespace Stickers diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp index 93cd1604c..9c4be2153 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "chat_helpers/stickers_emoji_pack.h" +#include "chat_helpers/stickers_emoji_image_loader.h" #include "history/history_item.h" #include "lottie/lottie_common.h" #include "ui/emoji_config.h" @@ -26,31 +27,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include namespace Stickers { -namespace details { - -using UniversalImages = Ui::Emoji::UniversalImages; - -class EmojiImageLoader { -public: - EmojiImageLoader( - crl::weak_on_queue weak, - std::shared_ptr images, - bool largeEnabled); - - [[nodiscard]] QImage prepare(EmojiPtr emoji); - void switchTo(std::shared_ptr images); - std::shared_ptr releaseImages(); - -private: - crl::weak_on_queue _weak; - std::shared_ptr _images; - -}; - namespace { constexpr auto kRefreshTimeout = 7200 * crl::time(1000); -constexpr auto kClearSourceTimeout = 10 * crl::time(1000); [[nodiscard]] QSize SingleSize() { const auto single = st::largeEmojiSize; @@ -121,97 +100,12 @@ constexpr auto kClearSourceTimeout = 10 * crl::time(1000); } // namespace -EmojiImageLoader::EmojiImageLoader( - crl::weak_on_queue weak, - std::shared_ptr images, - bool largeEnabled) -: _weak(std::move(weak)) -, _images(std::move(images)) { - Expects(_images != nullptr); - - if (largeEnabled) { - _images->ensureLoaded(); - } -} - -QImage EmojiImageLoader::prepare(EmojiPtr emoji) { - const auto loaded = _images->ensureLoaded(); - const auto factor = cIntRetinaFactor(); - const auto side = st::largeEmojiSize + 2 * st::largeEmojiOutline; - auto tinted = QImage( - QSize(st::largeEmojiSize, st::largeEmojiSize) * factor, - QImage::Format_ARGB32_Premultiplied); - tinted.fill(Qt::white); - if (loaded) { - QPainter p(&tinted); - p.setCompositionMode(QPainter::CompositionMode_DestinationIn); - _images->draw( - p, - emoji, - st::largeEmojiSize * factor, - 0, - 0); - } - auto result = QImage( - QSize(side, side) * factor, - QImage::Format_ARGB32_Premultiplied); - result.fill(Qt::transparent); - if (loaded) { - QPainter p(&result); - const auto delta = st::largeEmojiOutline * factor; - const auto planar = std::array{ { - { 0, -1 }, - { -1, 0 }, - { 1, 0 }, - { 0, 1 }, - } }; - for (const auto &shift : planar) { - for (auto i = 0; i != delta; ++i) { - p.drawImage(QPoint(delta, delta) + shift * (i + 1), tinted); - } - } - const auto diagonal = std::array{ { - { -1, -1 }, - { 1, -1 }, - { -1, 1 }, - { 1, 1 }, - } }; - const auto corrected = int(std::round(delta / sqrt(2.))); - for (const auto &shift : diagonal) { - for (auto i = 0; i != corrected; ++i) { - p.drawImage(QPoint(delta, delta) + shift * (i + 1), tinted); - } - } - _images->draw( - p, - emoji, - st::largeEmojiSize * factor, - delta, - delta); - } - return result; -} - -void EmojiImageLoader::switchTo(std::shared_ptr images) { - _images = std::move(images); -} - -std::shared_ptr EmojiImageLoader::releaseImages() { - return std::exchange( - _images, - std::make_shared(_images->id())); -} - -} // namespace details - QSize LargeEmojiImage::Size() { - return details::SingleSize(); + return SingleSize(); } EmojiPack::EmojiPack(not_null session) -: _session(session) -, _imageLoader(prepareSourceImages(), Core::App().settings().largeEmoji()) -, _clearTimer([=] { clearSourceImages(); }) { +: _session(session) { refresh(); session->data().itemRemoved( @@ -223,22 +117,12 @@ EmojiPack::EmojiPack(not_null session) Core::App().settings().largeEmojiChanges( ) | rpl::start_with_next([=](bool large) { - if (large) { - _clearTimer.cancel(); - } else { - _clearTimer.callOnce(details::kClearSourceTimeout); - } refreshAll(); }, _lifetime); Ui::Emoji::Updated( ) | rpl::start_with_next([=] { _images.clear(); - _imageLoader.with([ - source = prepareSourceImages() - ](details::EmojiImageLoader &loader) mutable { - loader.switchTo(std::move(source)); - }); refreshAll(); }, _lifetime); } @@ -286,7 +170,7 @@ auto EmojiPack::stickerForEmoji(const IsolatedEmoji &emoji) -> Sticker { const auto j = _map.find(first->original()); if (j != end(_map)) { const auto index = first->variantIndex(first); - return { j->second.get(), details::ColorReplacements(index) }; + return { j->second.get(), ColorReplacements(index) }; } return Sticker(); } @@ -302,7 +186,8 @@ std::shared_ptr EmojiPack::image(EmojiPtr emoji) { const auto raw = result.get(); const auto weak = base::make_weak(_session.get()); raw->load = [=] { - _imageLoader.with([=](details::EmojiImageLoader &loader) mutable { + Core::App().emojiImageLoader().with([=]( + const EmojiImageLoader &loader) { crl::on_main(weak, [ =, image = loader.prepare(emoji) @@ -390,24 +275,6 @@ void EmojiPack::refreshItems( } } -auto EmojiPack::prepareSourceImages() --> std::shared_ptr { - const auto &images = Ui::Emoji::SourceImages(); - if (Core::App().settings().largeEmoji()) { - return images; - } - Ui::Emoji::ClearSourceImages(images); - return std::make_shared(images->id()); -} - -void EmojiPack::clearSourceImages() { - _imageLoader.with([](details::EmojiImageLoader &loader) { - crl::on_main([images = loader.releaseImages()]{ - Ui::Emoji::ClearSourceImages(images); - }); - }); -} - void EmojiPack::applyPack( const MTPDstickerPack &data, const base::flat_map> &map) { @@ -442,7 +309,7 @@ base::flat_map> EmojiPack::collectStickers( } void EmojiPack::refreshDelayed() { - base::call_delayed(details::kRefreshTimeout, _session, [=] { + base::call_delayed(kRefreshTimeout, _session, [=] { refresh(); }); } diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.h b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.h index 506171bff..06c55028f 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.h @@ -34,9 +34,6 @@ class UniversalImages; } // namespace Ui namespace Stickers { -namespace details { -class EmojiImageLoader; -} // namespace details using IsolatedEmoji = Ui::Text::IsolatedEmoji; @@ -84,8 +81,6 @@ private: void refreshAll(); void refreshItems(EmojiPtr emoji); void refreshItems(const base::flat_set> &list); - std::shared_ptr prepareSourceImages(); - void clearSourceImages(); not_null _session; base::flat_map> _map; @@ -95,9 +90,6 @@ private: base::flat_map> _images; mtpRequestId _requestId = 0; - crl::object_on_queue _imageLoader; - base::Timer _clearTimer; - rpl::lifetime _lifetime; }; diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 05d86122e..09c2eea64 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/launcher.h" #include "core/ui_integration.h" #include "chat_helpers/emoji_keywords.h" +#include "chat_helpers/stickers_emoji_image_loader.h" #include "base/platform/base_platform_info.h" #include "platform/platform_specific.h" #include "mainwindow.h" @@ -80,6 +81,7 @@ namespace { constexpr auto kQuitPreventTimeoutMs = crl::time(1500); constexpr auto kAutoLockTimeoutLateMs = crl::time(3000); +constexpr auto kClearEmojiImageSourceTimeout = 10 * crl::time(1000); } // namespace @@ -96,6 +98,7 @@ Application::Application(not_null launcher) , _private(std::make_unique()) , _databases(std::make_unique()) , _animationsManager(std::make_unique()) +, _clearEmojiImageLoaderTimer([=] { clearEmojiSourceImages(); }) , _fallbackProductionConfig( std::make_unique(MTP::Environment::Production)) , _domain(std::make_unique(cDataFile())) @@ -207,6 +210,7 @@ void Application::run() { style::startManager(cScale()); Ui::InitTextOptions(); Ui::Emoji::Init(); + startEmojiImageLoader(); Media::Player::start(_audio.get()); style::ShortAnimationPlaying( @@ -275,6 +279,24 @@ void Application::run() { } } +auto Application::prepareEmojiSourceImages() +-> std::shared_ptr { + const auto &images = Ui::Emoji::SourceImages(); + if (_settings.largeEmoji()) { + return images; + } + Ui::Emoji::ClearSourceImages(images); + return std::make_shared(images->id()); +} + +void Application::clearEmojiSourceImages() { + _emojiImageLoader.with([](Stickers::EmojiImageLoader &loader) { + crl::on_main([images = loader.releaseImages()]{ + Ui::Emoji::ClearSourceImages(images); + }); + }); +} + bool Application::hideMediaView() { if (_mediaView && !_mediaView->isHidden()) { _mediaView->hide(); @@ -466,6 +488,34 @@ void Application::startLocalStorage() { _saveSettingsTimer.setCallback([=] { saveSettings(); }); } +void Application::startEmojiImageLoader() { + _emojiImageLoader.with([ + source = prepareEmojiSourceImages(), + large = _settings.largeEmoji() + ](Stickers::EmojiImageLoader &loader) mutable { + loader.init(std::move(source), large); + }); + + _settings.largeEmojiChanges( + ) | rpl::start_with_next([=](bool large) { + if (large) { + _clearEmojiImageLoaderTimer.cancel(); + } else { + _clearEmojiImageLoaderTimer.callOnce( + kClearEmojiImageSourceTimeout); + } + }, _lifetime); + + Ui::Emoji::Updated( + ) | rpl::start_with_next([=] { + _emojiImageLoader.with([ + source = prepareEmojiSourceImages() + ](Stickers::EmojiImageLoader &loader) mutable { + loader.switchTo(std::move(source)); + }); + }, _lifetime); +} + void Application::logout(Main::Account *account) { if (account) { account->logOut(); diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index fefedeb62..71896d9a3 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -51,6 +51,9 @@ namespace Ui { namespace Animations { class Manager; } // namespace Animations +namespace Emoji { +class UniversalImages; +} // namespace Emoji class BoxContent; } // namespace Ui @@ -81,6 +84,10 @@ namespace Data { struct CloudTheme; } // namespace Data +namespace Stickers { +class EmojiImageLoader; +} // namespace Stickers + namespace Core { class Launcher; @@ -195,6 +202,10 @@ public: [[nodiscard]] ChatHelpers::EmojiKeywords &emojiKeywords() { return *_emojiKeywords; } + [[nodiscard]] auto emojiImageLoader() const + -> const crl::object_on_queue & { + return _emojiImageLoader; + } // Internal links. void checkStartUrl(); @@ -256,8 +267,12 @@ private: friend bool IsAppLaunched(); friend Application &App(); + void clearEmojiSourceImages(); + [[nodiscard]] auto prepareEmojiSourceImages() + -> std::shared_ptr; void startLocalStorage(); void startShortcuts(); + void startEmojiImageLoader(); void stateChanged(Qt::ApplicationState state); @@ -293,6 +308,8 @@ private: const std::unique_ptr _databases; const std::unique_ptr _animationsManager; + crl::object_on_queue _emojiImageLoader; + base::Timer _clearEmojiImageLoaderTimer; mutable std::unique_ptr _fallbackProductionConfig; const std::unique_ptr _domain; std::unique_ptr _window; diff --git a/Telegram/SourceFiles/main/main_domain.cpp b/Telegram/SourceFiles/main/main_domain.cpp index 93796d3e8..b84ca49ae 100644 --- a/Telegram/SourceFiles/main/main_domain.cpp +++ b/Telegram/SourceFiles/main/main_domain.cpp @@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtproto_dc_options.h" #include "storage/storage_domain.h" #include "storage/localstorage.h" -#include "window/notifications_manager.h" #include "facades.h" namespace Main { diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index c84a55527..ba728b09f 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -200,7 +200,7 @@ void System::clearFromHistory(not_null history) { void System::clearFromSession(not_null session) { _manager->clearFromSession(session); - for (auto i = _whenMaps.begin(), e = _whenMaps.end(); i != e;) { + for (auto i = _whenMaps.begin(); i != _whenMaps.end();) { const auto history = i->first; if (&history->session() == session) { history->clearNotifications(); diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index a1a599b4a..0b50dc952 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_utilities.h" #include "ui/special_buttons.h" #include "ui/empty_userpic.h" +#include "dialogs/dialogs_layout.h" #include "base/call_delayed.h" #include "mainwindow.h" #include "storage/localstorage.h" @@ -91,6 +92,8 @@ private: const not_null _account; const style::Menu &_st; std::shared_ptr _userpicView; + + Dialogs::Layout::UnreadBadgeStyle _unreadSt; int _unreadBadge = 0; bool _unreadBadgeMuted = true; @@ -161,8 +164,6 @@ void MainMenu::AccountButton::paintEvent(QPaintEvent *e) { p.fillRect(rect(), over ? _st.itemBgOver : _st.itemBg); paintRipple(p, 0, 0); - const auto available = width() - 2 * _st.itemPadding.left(); - session.user()->paintUserpicLeft( p, _userpicView, @@ -171,6 +172,29 @@ void MainMenu::AccountButton::paintEvent(QPaintEvent *e) { width(), height() - 2 * _st.itemIconPosition.y()); + auto available = width() - _st.itemPadding.left(); + if (_unreadBadge && _account != &Core::App().domain().active()) { + _unreadSt.muted = _unreadBadgeMuted; + const auto string = (_unreadBadge > 99) + ? "99+" + : QString::number(_unreadBadge); + auto unreadWidth = 0; + const auto skip = _st.itemPadding.right() + - st::mainMenu.itemToggleShift; + const auto unreadRight = width() - skip; + const auto unreadTop = (height() - _unreadSt.size) / 2; + Dialogs::Layout::paintUnreadCount( + p, + string, + unreadRight, + unreadTop, + _unreadSt, + &unreadWidth); + available -= unreadWidth + skip + st::mainMenu.itemStyle.font->spacew; + } else { + available -= _st.itemPadding.right(); + } + p.setPen(over ? _st.itemFgOver : _st.itemFg); session.user()->nameText().drawElided( p,