From 1b1b1780db1498c179d98f7b7dd2ee2b54c4427d Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Mon, 5 Aug 2019 15:08:20 +0100
Subject: [PATCH] Reuse global emoji large images.

---
 .../chat_helpers/stickers_emoji_pack.cpp      | 76 ++++++++++++++-----
 .../chat_helpers/stickers_emoji_pack.h        |  7 ++
 Telegram/SourceFiles/main/main_session.cpp    |  4 +
 Telegram/SourceFiles/main/main_session.h      |  1 +
 Telegram/SourceFiles/ui/emoji_config.cpp      | 27 ++++++-
 Telegram/SourceFiles/ui/emoji_config.h        |  5 +-
 Telegram/gyp/generate.py                      |  2 +-
 7 files changed, 98 insertions(+), 24 deletions(-)

diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp
index 4706014c5..915dd6d1d 100644
--- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp
+++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp
@@ -15,34 +15,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_file_origin.h"
 #include "data/data_session.h"
 #include "data/data_document.h"
-#include "base/concurrent_timer.h"
 #include "apiwrap.h"
 #include "styles/style_history.h"
 
 namespace Stickers {
 namespace details {
 
+using UniversalImages = Ui::Emoji::UniversalImages;
+
 class EmojiImageLoader {
 public:
 	EmojiImageLoader(
 		crl::weak_on_queue<EmojiImageLoader> weak,
-		int id);
+		std::shared_ptr<UniversalImages> images,
+		bool largeEnabled);
 
 	[[nodiscard]] QImage prepare(EmojiPtr emoji);
-	void switchTo(int id);
+	void switchTo(std::shared_ptr<UniversalImages> images);
+	std::shared_ptr<UniversalImages> releaseImages();
 
 private:
 	crl::weak_on_queue<EmojiImageLoader> _weak;
-	std::optional<Ui::Emoji::UniversalImages> _images;
-
-	base::ConcurrentTimer _unloadTimer;
+	std::shared_ptr<UniversalImages> _images;
 
 };
 
 namespace {
 
 constexpr auto kRefreshTimeout = TimeId(7200);
-constexpr auto kUnloadTimeout = 86400 * crl::time(1000);
+constexpr auto kClearSourceTimeout = 10 * crl::time(1000);
 
 [[nodiscard]] QSize SingleSize() {
 	const auto single = st::largeEmojiSize;
@@ -245,15 +246,18 @@ QByteArray ImageSource::bytesForCache() {
 
 EmojiImageLoader::EmojiImageLoader(
 	crl::weak_on_queue<EmojiImageLoader> weak,
-	int id)
+	std::shared_ptr<UniversalImages> images,
+	bool largeEnabled)
 : _weak(std::move(weak))
-, _images(std::in_place, id)
-, _unloadTimer(_weak.runner(), [=] { _images->clear(); }) {
+, _images(std::move(images)) {
+	Expects(_images != nullptr);
+
+	if (largeEnabled) {
+		_images->ensureLoaded();
+	}
 }
 
 QImage EmojiImageLoader::prepare(EmojiPtr emoji) {
-	Expects(_images.has_value());
-
 	_images->ensureLoaded();
 	const auto factor = cIntRetinaFactor();
 	const auto side = st::largeEmojiSize + 2 * st::largeEmojiOutline;
@@ -300,19 +304,25 @@ QImage EmojiImageLoader::prepare(EmojiPtr emoji) {
 			delta,
 			delta);
 	}
-	_unloadTimer.callOnce(kUnloadTimeout);
 	return result;
 }
 
-void EmojiImageLoader::switchTo(int id) {
-	_images.emplace(id);
+void EmojiImageLoader::switchTo(std::shared_ptr<UniversalImages> images) {
+	_images = std::move(images);
+}
+
+std::shared_ptr<UniversalImages> EmojiImageLoader::releaseImages() {
+	return std::exchange(
+		_images,
+		std::make_shared<UniversalImages>(_images->id()));
 }
 
 } // namespace details
 
 EmojiPack::EmojiPack(not_null<Main::Session*> session)
 : _session(session)
-, _imageLoader(Ui::Emoji::CurrentSetId()) {
+, _imageLoader(prepareSourceImages(), session->settings().largeEmoji())
+, _clearTimer([=] { clearSourceImages(); }) {
 	refresh();
 
 	session->data().itemRemoved(
@@ -322,17 +332,23 @@ EmojiPack::EmojiPack(not_null<Main::Session*> session)
 		remove(item);
 	}, _lifetime);
 
-	session->settings().largeEmojiChanges(
-	) | rpl::start_with_next([=] {
+	_session->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([=] {
-		const auto id = Ui::Emoji::CurrentSetId();
 		_images.clear();
-		_imageLoader.with([=](details::EmojiImageLoader &loader) {
-			loader.switchTo(id);
+		_imageLoader.with([
+			source = prepareSourceImages()
+		](details::EmojiImageLoader &loader) mutable {
+			loader.switchTo(std::move(source));
 		});
 		refreshAll();
 	}, _lifetime);
@@ -450,6 +466,24 @@ void EmojiPack::refreshItems(
 	}
 }
 
+auto EmojiPack::prepareSourceImages()
+-> std::shared_ptr<Ui::Emoji::UniversalImages> {
+	const auto &images = Ui::Emoji::SourceImages();
+	if (_session->settings().largeEmoji()) {
+		return images;
+	}
+	Ui::Emoji::ClearSourceImages(images);
+	return std::make_shared<Ui::Emoji::UniversalImages>(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<uint64, not_null<DocumentData*>> &map) {
diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.h b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.h
index b098b0775..68ed3c3e3 100644
--- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.h
+++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.h
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #pragma once
 
 #include "ui/text/text_isolated_emoji.h"
+#include "base/timer.h"
 
 #include <crl/crl_object_on_queue.h>
 
@@ -22,6 +23,9 @@ namespace Ui {
 namespace Text {
 class String;
 } // namespace Text
+namespace Emoji {
+class UniversalImages;
+} // namespace Emoji
 } // namespace Ui
 
 namespace Stickers {
@@ -56,6 +60,8 @@ private:
 	void refreshAll();
 	void refreshItems(EmojiPtr emoji);
 	void refreshItems(const base::flat_set<not_null<HistoryItem*>> &list);
+	std::shared_ptr<Ui::Emoji::UniversalImages> prepareSourceImages();
+	void clearSourceImages();
 
 	not_null<Main::Session*> _session;
 	base::flat_map<EmojiPtr, not_null<DocumentData*>> _map;
@@ -66,6 +72,7 @@ private:
 	mtpRequestId _requestId = 0;
 
 	crl::object_on_queue<details::EmojiImageLoader> _imageLoader;
+	base::Timer _clearTimer;
 
 	rpl::lifetime _lifetime;
 
diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp
index d4a0662c2..3f76336e2 100644
--- a/Telegram/SourceFiles/main/main_session.cpp
+++ b/Telegram/SourceFiles/main/main_session.cpp
@@ -481,6 +481,10 @@ bool Settings::largeEmoji() const {
 	return _variables.largeEmoji.current();
 }
 
+rpl::producer<bool> Settings::largeEmojiValue() const {
+	return _variables.largeEmoji.value();
+}
+
 rpl::producer<bool> Settings::largeEmojiChanges() const {
 	return _variables.largeEmoji.changes();
 }
diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h
index c75848753..7f6afc3dd 100644
--- a/Telegram/SourceFiles/main/main_session.h
+++ b/Telegram/SourceFiles/main/main_session.h
@@ -249,6 +249,7 @@ public:
 	}
 	void setLargeEmoji(bool value);
 	[[nodiscard]] bool largeEmoji() const;
+	[[nodiscard]] rpl::producer<bool> largeEmojiValue() const;
 	[[nodiscard]] rpl::producer<bool> largeEmojiChanges() const;
 	void setReplaceEmoji(bool value);
 	[[nodiscard]] bool replaceEmoji() const;
diff --git a/Telegram/SourceFiles/ui/emoji_config.cpp b/Telegram/SourceFiles/ui/emoji_config.cpp
index 9f123ae78..e1e105b81 100644
--- a/Telegram/SourceFiles/ui/emoji_config.cpp
+++ b/Telegram/SourceFiles/ui/emoji_config.cpp
@@ -68,6 +68,7 @@ auto SpritesCount = -1;
 auto InstanceNormal = std::unique_ptr<Instance>();
 auto InstanceLarge = std::unique_ptr<Instance>();
 auto Universal = std::shared_ptr<UniversalImages>();
+auto CanClearUniversal = false;
 auto Updates = rpl::event_stream<>();
 auto UpdatesRecent = rpl::event_stream<>();
 
@@ -148,6 +149,7 @@ void SwitchToSetPrepared(int id, std::shared_ptr<UniversalImages> images) {
 		stream << qint32(id);
 	}
 	Universal = std::move(images);
+	CanClearUniversal = false;
 	MainEmojiMap.clear();
 	OtherEmojiMap.clear();
 	Updates.fire({});
@@ -383,7 +385,10 @@ EmojiPtr FindReplacement(const QChar *start, const QChar *end, int *outLength) {
 void ClearUniversalChecked() {
 	Expects(InstanceNormal != nullptr && InstanceLarge != nullptr);
 
-	if (InstanceNormal->cached() && InstanceLarge->cached() && Universal) {
+	if (CanClearUniversal
+		&& Universal
+		&& InstanceNormal->cached()
+		&& InstanceLarge->cached()) {
 		Universal->clear();
 	}
 }
@@ -507,6 +512,7 @@ void Init() {
 	SizeNormal = ConvertScale(18, cScale() * cIntRetinaFactor());
 	SizeLarge = int(ConvertScale(18 * 4 / 3., cScale() * cIntRetinaFactor()));
 	Universal = std::make_shared<UniversalImages>(ReadCurrentSetId());
+	CanClearUniversal = false;
 
 	InstanceNormal = std::make_unique<Instance>(SizeNormal);
 	InstanceLarge = std::make_unique<Instance>(SizeLarge);
@@ -1007,5 +1013,24 @@ void Instance::pushSprite(QImage &&data) {
 	_sprites.back().setDevicePixelRatio(cRetinaFactor());
 }
 
+const std::shared_ptr<UniversalImages> &SourceImages() {
+	return Universal;
+}
+
+void ClearSourceImages(const std::shared_ptr<UniversalImages> &images) {
+	if (images == Universal) {
+		CanClearUniversal = true;
+		ClearUniversalChecked();
+	}
+}
+
+void ReplaceSourceImages(std::shared_ptr<UniversalImages> images) {
+	Expects(images != nullptr);
+
+	if (Universal->id() == images->id()) {
+		Universal = std::move(images);
+	}
+}
+
 } // namespace Emoji
 } // namespace Ui
diff --git a/Telegram/SourceFiles/ui/emoji_config.h b/Telegram/SourceFiles/ui/emoji_config.h
index fb056e6a9..e4eeaf0d3 100644
--- a/Telegram/SourceFiles/ui/emoji_config.h
+++ b/Telegram/SourceFiles/ui/emoji_config.h
@@ -182,10 +182,13 @@ public:
 	QImage generate(int size, int index) const;
 
 private:
-	int _id = 0;
+	const int _id = 0;
 	std::vector<QImage> _sprites;
 
 };
 
+const std::shared_ptr<UniversalImages> &SourceImages();
+void ClearSourceImages(const std::shared_ptr<UniversalImages> &images);
+
 } // namespace Emoji
 } // namespace Ui
diff --git a/Telegram/gyp/generate.py b/Telegram/gyp/generate.py
index cf7b44707..4194e1666 100644
--- a/Telegram/gyp/generate.py
+++ b/Telegram/gyp/generate.py
@@ -96,7 +96,7 @@ if sys.platform == 'win32':
 elif sys.platform == 'darwin':
     # use patched gyp with Xcode project generator
     gypScript = '../../../Libraries/gyp/gyp'
-    gypArguments.append('-Gxcode_upgrade_check_project_version=1020')
+    gypArguments.append('-Gxcode_upgrade_check_project_version=1030')
     gypFormats.append('xcode')
 else:
     gypScript = '../../../Libraries/gyp/gyp'