From 113f66529557f13761be80860c6fa500e3f516a9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 11 Oct 2018 18:54:57 +0300 Subject: [PATCH] Move Image and derived to ui/image. --- Telegram/SourceFiles/app.cpp | 44 +- Telegram/SourceFiles/app.h | 7 - Telegram/SourceFiles/boxes/background_box.cpp | 1 + Telegram/SourceFiles/boxes/confirm_box.cpp | 1 + .../SourceFiles/boxes/edit_caption_box.cpp | 1 + .../SourceFiles/boxes/sticker_set_box.cpp | 1 + Telegram/SourceFiles/boxes/stickers_box.cpp | 1 + Telegram/SourceFiles/calls/calls_panel.cpp | 1 + .../chat_helpers/field_autocomplete.cpp | 1 + .../chat_helpers/gifs_list_widget.cpp | 1 + .../chat_helpers/stickers_list_widget.cpp | 1 + Telegram/SourceFiles/core/utils.h | 23 - Telegram/SourceFiles/data/data_document.cpp | 1 + .../SourceFiles/data/data_media_types.cpp | 3 +- Telegram/SourceFiles/data/data_media_types.h | 2 + Telegram/SourceFiles/data/data_peer.cpp | 17 +- Telegram/SourceFiles/data/data_peer.h | 14 +- Telegram/SourceFiles/data/data_photo.cpp | 1 + Telegram/SourceFiles/data/data_session.cpp | 14 + Telegram/SourceFiles/data/data_session.h | 6 + .../admin_log/history_admin_log_inner.cpp | 1 + Telegram/SourceFiles/history/history.cpp | 3 +- .../history/history_inner_widget.cpp | 3 +- .../history/history_item_components.cpp | 1 + .../history/history_location_manager.cpp | 1 + .../history/history_location_manager.h | 33 +- .../history/history_media_types.cpp | 1 + .../SourceFiles/history/history_widget.cpp | 6 +- Telegram/SourceFiles/history/history_widget.h | 1 - .../view/history_view_context_menu.cpp | 1 + .../inline_bot_layout_internal.cpp | 1 + .../inline_bots/inline_bot_layout_item.cpp | 1 + .../inline_bots/inline_bot_result.cpp | 1 + .../inline_bots/inline_bot_result.h | 1 + Telegram/SourceFiles/mainwidget.cpp | 3 +- .../media/view/media_view_group_thumbs.cpp | 1 + Telegram/SourceFiles/mediaview.cpp | 1 + Telegram/SourceFiles/messenger.cpp | 3 +- .../SourceFiles/overview/overview_layout.cpp | 1 + .../SourceFiles/platform/win/specific_win.h | 2 + .../SourceFiles/settings/settings_chat.cpp | 1 + .../storage/serialize_document.cpp | 1 + Telegram/SourceFiles/ui/image.cpp | 1257 ++++++++++++++++ Telegram/SourceFiles/ui/image.h | 441 ++++++ Telegram/SourceFiles/ui/images.cpp | 1302 ++--------------- Telegram/SourceFiles/ui/images.h | 481 +----- Telegram/SourceFiles/window/layer_widget.cpp | 1 + Telegram/gyp/telegram_sources.txt | 2 + 48 files changed, 1917 insertions(+), 1776 deletions(-) create mode 100644 Telegram/SourceFiles/ui/image.cpp create mode 100644 Telegram/SourceFiles/ui/image.h diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 1d1f244256..40817dc2e4 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -7,10 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "app.h" -#ifdef OS_MAC_OLD -#include -#endif // OS_MAC_OLD - #include "styles/style_overview.h" #include "styles/style_mediaview.h" #include "styles/style_chat_helpers.h" @@ -27,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_components.h" #include "history/view/history_view_service_message.h" #include "media/media_audio.h" +#include "ui/image.h" #include "inline_bots/inline_bot_layout_item.h" #include "messenger.h" #include "application.h" @@ -46,14 +43,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/notifications_manager.h" #include "platform/platform_notifications_manager.h" +#ifdef OS_MAC_OLD +#include +#endif // OS_MAC_OLD + namespace { App::LaunchState _launchState = App::Launched; std::unordered_map> peersData; - using LocationsData = QHash; - LocationsData locationsData; - using DependentItemsSet = OrderedSet; using DependentItems = QMap; DependentItems dependentItems; @@ -1155,20 +1153,6 @@ namespace App { return nullptr; } - LocationData *location(const LocationCoords &coords) { - auto i = locationsData.constFind(coords); - if (i == locationsData.cend()) { - i = locationsData.insert(coords, new LocationData(coords)); - } - return i.value(); - } - - void forgetMedia() { - for_const (auto location, ::locationsData) { - location->thumb->forget(); - } - } - QString peerName(const PeerData *peer, bool forDialogs) { return peer ? ((forDialogs && peer->isUser() && !peer->asUser()->nameOrPhone.isEmpty()) ? peer->asUser()->nameOrPhone : peer->name) : lang(lng_deleted); } @@ -1260,9 +1244,6 @@ namespace App { delete item; } } - for (const auto data : base::take(::locationsData)) { - delete data; - } clearMousedItems(); } @@ -1470,10 +1451,8 @@ namespace App { histories().clear(); - clearStorageImages(); + Images::ClearRemote(); cSetServerBackgrounds(WallPapers()); - - serviceImageCacheSize = imageCacheSize(); } void deinitMedia() { @@ -1481,7 +1460,7 @@ namespace App { Data::clearGlobalStructures(); - clearAllImages(); + Images::ClearAll(); } void hoveredItem(HistoryView::Element *item) { @@ -1536,15 +1515,6 @@ namespace App { return ::monofont; } - void checkImageCacheSize() { - int64 nowImageCacheSize = imageCacheSize(); - if (nowImageCacheSize > serviceImageCacheSize + MemoryForImageCache) { - App::forgetMedia(); - Auth().data().forgetMedia(); - serviceImageCacheSize = imageCacheSize(); - } - } - void quit() { if (quitting()) { return; diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 3f58f44210..8a18f6f3e8 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -14,8 +14,6 @@ enum NewMessageType : char; class Messenger; class MainWindow; class MainWidget; -class LocationCoords; -struct LocationData; class HistoryItem; class History; class Histories; @@ -138,9 +136,6 @@ namespace App { PeerData *peerByName(const QString &username); QString peerName(const PeerData *peer, bool forDialogs = false); - LocationData *location(const LocationCoords &coords); - void forgetMedia(); - Histories &histories(); not_null history(const PeerId &peer); History *historyLoaded(const PeerId &peer); @@ -192,8 +187,6 @@ namespace App { void initMedia(); void deinitMedia(); - void checkImageCacheSize(); - enum LaunchState { Launched = 0, QuitRequested = 1, diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp index aa549b06bb..f1381d2708 100644 --- a/Telegram/SourceFiles/boxes/background_box.cpp +++ b/Telegram/SourceFiles/boxes/background_box.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_overview.h" #include "styles/style_boxes.h" #include "ui/effects/round_checkbox.h" +#include "ui/image.h" #include "auth_session.h" class BackgroundBox::Inner : public TWidget, public RPCSender, private base::Subscriber { diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index ec212a82b5..07b4a64d8c 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/toast/toast.h" #include "ui/empty_userpic.h" +#include "ui/image.h" #include "core/click_handler_types.h" #include "storage/localstorage.h" #include "data/data_session.h" diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 77c8d18bdd..72fa89f74c 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/input_fields.h" #include "ui/text_options.h" +#include "ui/image.h" #include "media/media_clip_reader.h" #include "history/history.h" #include "history/history_item.h" diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index 297e4d71d9..f39eb4848d 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" #include "ui/emoji_config.h" +#include "ui/image.h" #include "auth_session.h" #include "messenger.h" diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index bd2fcabd12..8340c27db4 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/slide_animation.h" #include "ui/widgets/discrete_sliders.h" #include "ui/widgets/input_fields.h" +#include "ui/image.h" #include "auth_session.h" #include "messenger.h" diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index a8f3fc63e7..8fe51edc46 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/fade_wrap.h" #include "ui/empty_userpic.h" #include "ui/emoji_config.h" +#include "ui/image.h" #include "messenger.h" #include "mainwindow.h" #include "lang/lang_keys.h" diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index d085a66bfa..02158f7767 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "storage/localstorage.h" #include "ui/widgets/scroll_area.h" +#include "ui/image.h" #include "styles/style_history.h" #include "styles/style_widgets.h" #include "styles/style_chat_helpers.h" diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index 84415770da..c67b5d7bdc 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" #include "ui/effects/ripple_animation.h" +#include "ui/image.h" #include "boxes/stickers_box.h" #include "inline_bots/inline_bot_result.h" #include "chat_helpers/stickers.h" diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index e9588370cf..e113fb8e58 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "ui/widgets/buttons.h" #include "ui/effects/ripple_animation.h" +#include "ui/image.h" #include "boxes/stickers_box.h" #include "inline_bots/inline_bot_result.h" #include "chat_helpers/stickers.h" diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 219dc22981..4bd51a30ba 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -318,29 +318,6 @@ inline T snap(const T &v, const T &_min, const T &_max) { return (v < _min) ? _min : ((v > _max) ? _max : v); } -template -class ManagedPtr { -public: - ManagedPtr() = default; - ManagedPtr(T *p) : _data(p) { - } - T *operator->() const { - return _data; - } - T *v() const { - return _data; - } - - explicit operator bool() const { - return _data != nullptr; - } - -protected: - using Parent = ManagedPtr; - T *_data = nullptr; - -}; - QString translitRusEng(const QString &rus); QString rusKeyboardLayoutSwitch(const QString &from); diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index f1d095ab34..bf00dccddd 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_media_types.h" #include "window/window_controller.h" #include "storage/cache/storage_cache_database.h" +#include "ui/image.h" #include "auth_session.h" #include "mainwindow.h" #include "messenger.h" diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index f5241d182f..e65532e6f2 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_element.h" #include "ui/text_options.h" #include "ui/emoji_config.h" +#include "ui/image.h" #include "storage/storage_shared_media.h" #include "storage/localstorage.h" #include "data/data_session.h" @@ -765,7 +766,7 @@ MediaLocation::MediaLocation( const QString &title, const QString &description) : Media(parent) -, _location(App::location(coords)) +, _location(Auth().data().location(coords)) , _title(title) , _description(description) { } diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index 4a854bfb38..e91a00f62b 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class HistoryItem; class HistoryMedia; +class LocationCoords; +struct LocationData; namespace base { template diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index ad2b4fe334..735d4c3782 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "ui/empty_userpic.h" #include "ui/text_options.h" +#include "ui/image.h" namespace { @@ -221,6 +222,20 @@ void PeerData::paintUserpicSquare(Painter &p, int x, int y, int size) const { } } +void PeerData::loadUserpic(bool loadFirst, bool prior) { + _userpic->load(userpicPhotoOrigin(), loadFirst, prior); +} + +bool PeerData::userpicLoaded() const { + return _userpic->loaded(); +} + +bool PeerData::useEmptyUserpic() const { + return _userpicLocation.isNull() + || !_userpic + || !_userpic->loaded(); +} + StorageKey PeerData::userpicUniqueKey() const { if (useEmptyUserpic()) { return _userpicEmpty->uniqueKey(); @@ -296,7 +311,7 @@ void PeerData::setUserpicChecked( const StorageImageLocation &location, ImagePtr userpic) { if (_userpicPhotoId != photoId - || _userpic.v() != userpic.v() + || _userpic.get() != userpic.get() || _userpicLocation != location) { setUserpic(photoId, location, userpic); Notify::peerUpdatedDelayed(this, UpdateFlag::PhotoChanged); diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 3d99024645..17d1bae884 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -165,17 +165,9 @@ public: int x, int y, int size) const; - void loadUserpic(bool loadFirst = false, bool prior = true) { - _userpic->load(userpicPhotoOrigin(), loadFirst, prior); - } - bool userpicLoaded() const { - return _userpic->loaded(); - } - bool useEmptyUserpic() const { - return _userpicLocation.isNull() - || !_userpic - || !_userpic->loaded(); - } + void loadUserpic(bool loadFirst = false, bool prior = true); + bool userpicLoaded() const; + bool useEmptyUserpic() const; StorageKey userpicUniqueKey() const; void saveUserpic(const QString &path, int size) const; void saveUserpicRounded(const QString &path, int size) const; diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index 78067c15c9..0dc545982e 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo.h" #include "data/data_session.h" +#include "ui/image.h" #include "mainwidget.h" #include "history/history_media_types.h" #include "auth_session.h" diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 10ba615234..0d5ecf5c93 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "auth_session.h" #include "apiwrap.h" #include "messenger.h" +#include "ui/image.h" #include "export/export_controller.h" #include "export/view/export_view_panel_controller.h" #include "window/notifications_manager.h" @@ -1478,6 +1479,16 @@ void Session::gameApplyFields( notifyGameUpdateDelayed(game); } +not_null Session::location(const LocationCoords &coords) { + auto i = _locations.find(coords); + if (i == _locations.cend()) { + i = _locations.emplace( + coords, + std::make_unique(coords)).first; + } + return i->second.get(); +} + void Session::registerPhotoItem( not_null photo, not_null item) { @@ -2000,6 +2011,9 @@ void Session::forgetMedia() { for (const auto &[id, document] : _documents) { document->forget(); } + for (const auto &[coords, location] : _locations) { + location->thumb->forget(); + } } void Session::setMimeForwardIds(MessageIdsList &&list) { diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index da2099e01c..2f9d8a89d9 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/stickers.h" #include "dialogs/dialogs_key.h" #include "data/data_groups.h" +#include "history/history_location_manager.h" #include "base/timer.h" class HistoryItem; @@ -323,6 +324,8 @@ public: not_null original, const MTPGame &data); + not_null location(const LocationCoords &coords); + void registerPhotoItem( not_null photo, not_null item); @@ -593,6 +596,9 @@ private: std::unordered_map< WebPageId, std::unique_ptr> _webpages; + std::unordered_map< + LocationCoords, + std::unique_ptr> _locations; std::map< not_null, base::flat_set>> _webpageItems; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 82dd04c93b..433b903456 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_controller.h" #include "auth_session.h" #include "ui/widgets/popup_menu.h" +#include "ui/image.h" #include "core/file_utilities.h" #include "core/tl_help.h" #include "base/overload.h" diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 0370399c3e..a3eb322bdb 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel_admins.h" #include "data/data_feed.h" #include "ui/text_options.h" +#include "ui/image.h" #include "core/crash_reports.h" namespace { @@ -1261,7 +1262,7 @@ void History::mainViewRemoved( } void History::newItemAdded(not_null item) { - App::checkImageCacheSize(); + Images::CheckCacheSize(); item->indexAsNewItem(); if (const auto from = item->from() ? item->from()->asUser() : nullptr) { if (from == item->author()) { diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index f5f98e0480..79c8c2c0cd 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -19,8 +19,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_message.h" #include "history/view/history_view_service_message.h" #include "history/view/history_view_cursor_state.h" -#include "ui/text_options.h" #include "ui/widgets/popup_menu.h" +#include "ui/text_options.h" +#include "ui/image.h" #include "window/window_controller.h" #include "window/window_peer_menu.h" #include "boxes/confirm_box.h" diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index 207be8c64c..3ebaf16d9e 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "ui/effects/ripple_animation.h" #include "ui/text_options.h" +#include "ui/image.h" #include "history/history_message.h" #include "history/history_media.h" #include "history/history_media_types.h" diff --git a/Telegram/SourceFiles/history/history_location_manager.cpp b/Telegram/SourceFiles/history/history_location_manager.cpp index 79e3592d37..81c8213399 100644 --- a/Telegram/SourceFiles/history/history_location_manager.cpp +++ b/Telegram/SourceFiles/history/history_location_manager.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "lang/lang_keys.h" +#include "ui/image.h" #include "platform/platform_specific.h" namespace { diff --git a/Telegram/SourceFiles/history/history_location_manager.h b/Telegram/SourceFiles/history/history_location_manager.h index 2b0aa15f92..e1a5a81106 100644 --- a/Telegram/SourceFiles/history/history_location_manager.h +++ b/Telegram/SourceFiles/history/history_location_manager.h @@ -39,6 +39,18 @@ public: return _access; } + inline size_t hash() const { +#ifndef OS_MAC_OLD + return QtPrivate::QHashCombine().operator()( + std::hash()(_lat), + _lon); +#else // OS_MAC_OLD + const auto h1 = std::hash()(_lat); + const auto h2 = std::hash()(_lon); + return ((h1 << 16) | (h1 >> 16)) ^ h2; +#endif // OS_MAC_OLD + } + private: static QString asString(float64 value) { constexpr auto kPrecision = 6; @@ -53,22 +65,23 @@ private: return (a._lat < b._lat) || ((a._lat == b._lat) && (a._lon < b._lon)); } - friend inline uint qHash(const LocationCoords &t, uint seed = 0) { -#ifndef OS_MAC_OLD - return qHash(QtPrivate::QHashCombine().operator()(qHash(t._lat), t._lon), seed); -#else // OS_MAC_OLD - uint h1 = qHash(t._lat, seed); - uint h2 = qHash(t._lon, seed); - return ((h1 << 16) | (h1 >> 16)) ^ h2 ^ seed; -#endif // OS_MAC_OLD - } - float64 _lat = 0; float64 _lon = 0; uint64 _access = 0; }; +namespace std { + +template <> +struct hash { + size_t operator()(const LocationCoords &value) const { + return value.hash(); + } +}; + +} // namespace std + struct LocationData { LocationData(const LocationCoords &coords); diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 86dd829788..688e325088 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/grouped_layout.h" #include "ui/text_options.h" #include "ui/emoji_config.h" +#include "ui/image.h" #include "data/data_session.h" #include "data/data_media_types.h" diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 3375044e2b..0043ef9c7a 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -21,6 +21,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/widgets/shadow.h" #include "ui/effects/ripple_animation.h" +#include "ui/special_buttons.h" +#include "ui/image.h" #include "inline_bots/inline_bot_result.h" #include "data/data_drafts.h" #include "data/data_session.h" @@ -1920,9 +1922,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re _nonEmptySelection = false; if (_peer) { - App::forgetMedia(); Auth().data().forgetMedia(); - _serviceImageCacheSize = imageCacheSize(); Auth().downloader().clearPriorities(); _history = App::history(_peer); @@ -2856,7 +2856,7 @@ void HistoryWidget::delayedShowAt(MsgId showAtMsgId) { } void HistoryWidget::onScroll() { - App::checkImageCacheSize(); + Images::CheckCacheSize(); preloadHistoryIfNeeded(); visibleAreaUpdated(); if (!_synteticScrollEvent) { diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index abfd3f8bee..417ec9dcc5 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -867,7 +867,6 @@ private: TextUpdateEvents _textUpdateEvents = (TextUpdateEvents() | TextUpdateEvent::SaveDraft | TextUpdateEvent::SendTyping); - int64 _serviceImageCacheSize = 0; QString _confirmSource; Animation _a_show; diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 2b68f4e4e7..51b4a44474 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_text.h" #include "history/history_media_types.h" #include "ui/widgets/popup_menu.h" +#include "ui/image.h" #include "chat_helpers/message_field.h" #include "boxes/confirm_box.h" #include "data/data_photo.h" diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 562e09f8e6..71285996ea 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_location_manager.h" #include "history/view/history_view_cursor_state.h" #include "storage/localstorage.h" +#include "ui/image.h" #include "auth_session.h" #include "apiwrap.h" #include "lang/lang_keys.h" diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp index 3a96f41e62..0abd8bf5a3 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "mainwidget.h" #include "ui/empty_userpic.h" +#include "ui/image.h" namespace InlineBots { namespace Layout { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index f27f65718a..affdd24fd0 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/file_download.h" #include "core/file_utilities.h" #include "core/mime_type.h" +#include "ui/image.h" #include "mainwidget.h" #include "auth_session.h" diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.h b/Telegram/SourceFiles/inline_bots/inline_bot_result.h index 346bc1ac48..b0d992c22a 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once class FileLoader; +class LocationCoords; namespace InlineBots { diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 32af33cf28..9a9b9f1858 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -25,12 +25,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/section_memento.h" #include "window/section_widget.h" #include "window/window_connecting_widget.h" +#include "ui/toast/toast.h" #include "ui/widgets/dropdown_menu.h" #include "ui/focus_persister.h" #include "ui/resize_area.h" #include "ui/text_options.h" #include "ui/emoji_config.h" -#include "ui/toast/toast.h" +#include "ui/image.h" #include "chat_helpers/message_field.h" #include "chat_helpers/stickers.h" #include "info/info_memento.h" diff --git a/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp b/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp index 5ed4f0842f..6d68d627ad 100644 --- a/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp +++ b/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "history/history.h" #include "history/history_media.h" +#include "ui/image.h" #include "auth_session.h" #include "styles/style_mediaview.h" diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index fa171f9bf9..f3ec078cb7 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/popup_menu.h" #include "ui/widgets/buttons.h" #include "ui/text_options.h" +#include "ui/image.h" #include "media/media_clip_reader.h" #include "media/view/media_clip_controller.h" #include "media/view/media_view_group_thumbs.h" diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp index ce488432af..901386b2e8 100644 --- a/Telegram/SourceFiles/messenger.cpp +++ b/Telegram/SourceFiles/messenger.cpp @@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/tooltip.h" #include "ui/text_options.h" #include "ui/emoji_config.h" +#include "ui/image.h" #include "storage/serialize_common.h" #include "window/window_controller.h" #include "base/qthelp_regex.h" @@ -1123,7 +1124,7 @@ void Messenger::loggedOut() { Local::reset(); cSetOtherOnline(0); - clearStorageImages(); + Images::ClearRemote(); } QPoint Messenger::getPointForCallPanelCenter() const { diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index af3938f53e..316e8d300f 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_cursor_state.h" #include "ui/effects/round_checkbox.h" #include "ui/text_options.h" +#include "ui/image.h" namespace Overview { namespace Layout { diff --git a/Telegram/SourceFiles/platform/win/specific_win.h b/Telegram/SourceFiles/platform/win/specific_win.h index 58ba3ade67..5483e178bb 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.h +++ b/Telegram/SourceFiles/platform/win/specific_win.h @@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include +class LocationCoords; + namespace Platform { inline void SetWatchingMediaKeys(bool watching) { diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index bc1b7eb672..464421547a 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/effects/radial_animation.h" #include "ui/toast/toast.h" +#include "ui/image.h" #include "lang/lang_keys.h" #include "window/themes/window_theme_editor.h" #include "window/themes/window_theme.h" diff --git a/Telegram/SourceFiles/storage/serialize_document.cpp b/Telegram/SourceFiles/storage/serialize_document.cpp index c73747b4d3..266aad55f1 100644 --- a/Telegram/SourceFiles/storage/serialize_document.cpp +++ b/Telegram/SourceFiles/storage/serialize_document.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/serialize_common.h" #include "chat_helpers/stickers.h" #include "data/data_session.h" +#include "ui/image.h" #include "auth_session.h" namespace { diff --git a/Telegram/SourceFiles/ui/image.cpp b/Telegram/SourceFiles/ui/image.cpp new file mode 100644 index 0000000000..b23910d66f --- /dev/null +++ b/Telegram/SourceFiles/ui/image.cpp @@ -0,0 +1,1257 @@ +/* +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 "ui/image.h" + +#include "storage/file_download.h" +#include "data/data_session.h" +#include "storage/cache/storage_cache_database.h" +#include "history/history_item.h" +#include "history/history.h" +#include "auth_session.h" + +namespace { + +using LocalImages = QMap; +LocalImages localImages; + +using WebImages = QMap; +WebImages webImages; + +using StorageImages = QMap; +StorageImages storageImages; + +using WebFileImages = QMap; +WebFileImages webFileImages; + +using GeoPointImages = QMap; +GeoPointImages geoPointImages; + +int64 GlobalAcquiredSize = 0; +int64 LocalAcquiredSize = 0; + +uint64 PixKey(int width, int height, Images::Options options) { + return static_cast(width) | (static_cast(height) << 24) | (static_cast(options) << 48); +} + +uint64 SinglePixKey(Images::Options options) { + return PixKey(0, 0, options); +} + +} // namespace + +namespace Images { + +void ClearRemote() { + for (auto image : base::take(storageImages)) { + delete image; + } + for (auto image : base::take(webImages)) { + delete image; + } + for (auto image : base::take(webFileImages)) { + delete image; + } + for (auto image : base::take(geoPointImages)) { + delete image; + } + + LocalAcquiredSize = GlobalAcquiredSize; +} + +void ClearAll() { + for (auto image : base::take(localImages)) { + delete image; + } + ClearRemote(); +} + +void CheckCacheSize() { + const auto now = GlobalAcquiredSize; + if (GlobalAcquiredSize > LocalAcquiredSize + MemoryForImageCache) { + Auth().data().forgetMedia(); + LocalAcquiredSize = GlobalAcquiredSize; + } +} + +} // namespace Images + +Image::Image(const QString &file, QByteArray fmt) { + _data = App::pixmapFromImageInPlace(App::readImage(file, &fmt, false, 0, &_saved)); + _format = fmt; + if (!_data.isNull()) { + GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; + } +} + +Image::Image(const QByteArray &filecontent, QByteArray fmt) { + _data = App::pixmapFromImageInPlace(App::readImage(filecontent, &fmt, false)); + _format = fmt; + _saved = filecontent; + if (!_data.isNull()) { + GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; + } +} + +Image::Image(const QPixmap &pixmap, QByteArray format) : _format(format), _data(pixmap) { + if (!_data.isNull()) { + GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; + } +} + +Image::Image(const QByteArray &filecontent, QByteArray fmt, const QPixmap &pixmap) : _saved(filecontent), _format(fmt), _data(pixmap) { + _data = pixmap; + _format = fmt; + _saved = filecontent; + if (!_data.isNull()) { + GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; + } +} + +Image *Image::Blank() { + static const auto blankImage = [] { + const auto factor = cIntRetinaFactor(); + auto data = QImage( + factor, + factor, + QImage::Format_ARGB32_Premultiplied); + data.fill(Qt::transparent); + data.setDevicePixelRatio(cRetinaFactor()); + return Images::details::Create( + App::pixmapFromImageInPlace(std::move(data)), + "GIF"); + }(); + return blankImage; +} + +bool Image::isNull() const { + return (this == Blank()); +} + +const QPixmap &Image::pix( + Data::FileOrigin origin, + int32 w, + int32 h) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width(); + } else { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + auto options = Images::Option::Smooth | Images::Option::None; + auto k = PixKey(w, h, options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + auto p = pixNoCache(origin, w, h, options); + p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + GlobalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixRounded( + Data::FileOrigin origin, + int32 w, + int32 h, + ImageRoundRadius radius, + RectParts corners) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width(); + } else { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + auto options = Images::Option::Smooth | Images::Option::None; + auto cornerOptions = [](RectParts corners) { + return (corners & RectPart::TopLeft ? Images::Option::RoundedTopLeft : Images::Option::None) + | (corners & RectPart::TopRight ? Images::Option::RoundedTopRight : Images::Option::None) + | (corners & RectPart::BottomLeft ? Images::Option::RoundedBottomLeft : Images::Option::None) + | (corners & RectPart::BottomRight ? Images::Option::RoundedBottomRight : Images::Option::None); + }; + if (radius == ImageRoundRadius::Large) { + options |= Images::Option::RoundedLarge | cornerOptions(corners); + } else if (radius == ImageRoundRadius::Small) { + options |= Images::Option::RoundedSmall | cornerOptions(corners); + } else if (radius == ImageRoundRadius::Ellipse) { + options |= Images::Option::Circled | cornerOptions(corners); + } + auto k = PixKey(w, h, options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + auto p = pixNoCache(origin, w, h, options); + p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + GlobalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixCircled( + Data::FileOrigin origin, + int32 w, + int32 h) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width(); + } else { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + auto options = Images::Option::Smooth | Images::Option::Circled; + auto k = PixKey(w, h, options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + auto p = pixNoCache(origin, w, h, options); + p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + GlobalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixBlurredCircled( + Data::FileOrigin origin, + int32 w, + int32 h) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width(); + } else { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + auto options = Images::Option::Smooth | Images::Option::Circled | Images::Option::Blurred; + auto k = PixKey(w, h, options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + auto p = pixNoCache(origin, w, h, options); + p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + GlobalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixBlurred( + Data::FileOrigin origin, + int32 w, + int32 h) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + auto options = Images::Option::Smooth | Images::Option::Blurred; + auto k = PixKey(w, h, options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + auto p = pixNoCache(origin, w, h, options); + p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + GlobalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixColored( + Data::FileOrigin origin, + style::color add, + int32 w, + int32 h) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + auto options = Images::Option::Smooth | Images::Option::Colored; + auto k = PixKey(w, h, options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + auto p = pixColoredNoCache(origin, add, w, h, true); + p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + GlobalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixBlurredColored( + Data::FileOrigin origin, + style::color add, + int32 w, + int32 h) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + auto options = Images::Option::Blurred | Images::Option::Smooth | Images::Option::Colored; + auto k = PixKey(w, h, options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + auto p = pixBlurredColoredNoCache(origin, add, w, h); + p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + GlobalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixSingle( + Data::FileOrigin origin, + int32 w, + int32 h, + int32 outerw, + int32 outerh, + ImageRoundRadius radius, + RectParts corners, + const style::color *colored) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + + auto options = Images::Option::Smooth | Images::Option::None; + auto cornerOptions = [](RectParts corners) { + return (corners & RectPart::TopLeft ? Images::Option::RoundedTopLeft : Images::Option::None) + | (corners & RectPart::TopRight ? Images::Option::RoundedTopRight : Images::Option::None) + | (corners & RectPart::BottomLeft ? Images::Option::RoundedBottomLeft : Images::Option::None) + | (corners & RectPart::BottomRight ? Images::Option::RoundedBottomRight : Images::Option::None); + }; + if (radius == ImageRoundRadius::Large) { + options |= Images::Option::RoundedLarge | cornerOptions(corners); + } else if (radius == ImageRoundRadius::Small) { + options |= Images::Option::RoundedSmall | cornerOptions(corners); + } else if (radius == ImageRoundRadius::Ellipse) { + options |= Images::Option::Circled | cornerOptions(corners); + } + if (colored) { + options |= Images::Option::Colored; + } + + auto k = SinglePixKey(options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend() || i->width() != (outerw * cIntRetinaFactor()) || i->height() != (outerh * cIntRetinaFactor())) { + if (i != _sizesCache.cend()) { + GlobalAcquiredSize -= int64(i->width()) * i->height() * 4; + } + auto p = pixNoCache(origin, w, h, options, outerw, outerh, colored); + p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + GlobalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixBlurredSingle( + Data::FileOrigin origin, + int w, + int h, + int32 outerw, + int32 outerh, + ImageRoundRadius radius, + RectParts corners) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + + auto options = Images::Option::Smooth | Images::Option::Blurred; + auto cornerOptions = [](RectParts corners) { + return (corners & RectPart::TopLeft ? Images::Option::RoundedTopLeft : Images::Option::None) + | (corners & RectPart::TopRight ? Images::Option::RoundedTopRight : Images::Option::None) + | (corners & RectPart::BottomLeft ? Images::Option::RoundedBottomLeft : Images::Option::None) + | (corners & RectPart::BottomRight ? Images::Option::RoundedBottomRight : Images::Option::None); + }; + if (radius == ImageRoundRadius::Large) { + options |= Images::Option::RoundedLarge | cornerOptions(corners); + } else if (radius == ImageRoundRadius::Small) { + options |= Images::Option::RoundedSmall | cornerOptions(corners); + } else if (radius == ImageRoundRadius::Ellipse) { + options |= Images::Option::Circled | cornerOptions(corners); + } + + auto k = SinglePixKey(options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend() || i->width() != (outerw * cIntRetinaFactor()) || i->height() != (outerh * cIntRetinaFactor())) { + if (i != _sizesCache.cend()) { + GlobalAcquiredSize -= int64(i->width()) * i->height() * 4; + } + auto p = pixNoCache(origin, w, h, options, outerw, outerh); + p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + GlobalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +QPixmap Image::pixNoCache( + Data::FileOrigin origin, + int w, + int h, + Images::Options options, + int outerw, + int outerh, + const style::color *colored) const { + if (!loading()) const_cast(this)->load(origin); + restore(); + + if (_data.isNull()) { + if (h <= 0 && height() > 0) { + h = qRound(width() * w / float64(height())); + } + return Blank()->pixNoCache(origin, w, h, options, outerw, outerh); + } + + if (isNull() && outerw > 0 && outerh > 0) { + outerw *= cIntRetinaFactor(); + outerh *= cIntRetinaFactor(); + + QImage result(outerw, outerh, QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + + { + QPainter p(&result); + if (w < outerw) { + p.fillRect(0, 0, (outerw - w) / 2, result.height(), st::imageBg); + p.fillRect(((outerw - w) / 2) + w, 0, result.width() - (((outerw - w) / 2) + w), result.height(), st::imageBg); + } + if (h < outerh) { + p.fillRect(qMax(0, (outerw - w) / 2), 0, qMin(result.width(), w), (outerh - h) / 2, st::imageBg); + p.fillRect(qMax(0, (outerw - w) / 2), ((outerh - h) / 2) + h, qMin(result.width(), w), result.height() - (((outerh - h) / 2) + h), st::imageBg); + } + p.fillRect(qMax(0, (outerw - w) / 2), qMax(0, (outerh - h) / 2), qMin(result.width(), w), qMin(result.height(), h), st::imageBgTransparent); + } + + auto corners = [](Images::Options options) { + return ((options & Images::Option::RoundedTopLeft) ? RectPart::TopLeft : RectPart::None) + | ((options & Images::Option::RoundedTopRight) ? RectPart::TopRight : RectPart::None) + | ((options & Images::Option::RoundedBottomLeft) ? RectPart::BottomLeft : RectPart::None) + | ((options & Images::Option::RoundedBottomRight) ? RectPart::BottomRight : RectPart::None); + }; + if (options & Images::Option::Circled) { + Images::prepareCircle(result); + } else if (options & Images::Option::RoundedLarge) { + Images::prepareRound(result, ImageRoundRadius::Large, corners(options)); + } else if (options & Images::Option::RoundedSmall) { + Images::prepareRound(result, ImageRoundRadius::Small, corners(options)); + } + if (options & Images::Option::Colored) { + Assert(colored != nullptr); + result = Images::prepareColored(*colored, std::move(result)); + } + return App::pixmapFromImageInPlace(std::move(result)); + } + + return Images::pixmap(_data.toImage(), w, h, options, outerw, outerh, colored); +} + +QPixmap Image::pixColoredNoCache( + Data::FileOrigin origin, + style::color add, + int32 w, + int32 h, + bool smooth) const { + const_cast(this)->load(origin); + restore(); + if (_data.isNull()) { + return Blank()->pix(origin); + } + + auto img = _data.toImage(); + if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) { + return App::pixmapFromImageInPlace(Images::prepareColored(add, std::move(img))); + } + if (h <= 0) { + return App::pixmapFromImageInPlace(Images::prepareColored(add, img.scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation))); + } + return App::pixmapFromImageInPlace(Images::prepareColored(add, img.scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation))); +} + +QPixmap Image::pixBlurredColoredNoCache( + Data::FileOrigin origin, + style::color add, + int32 w, + int32 h) const { + const_cast(this)->load(origin); + restore(); + if (_data.isNull()) { + return Blank()->pix(origin); + } + + auto img = Images::prepareBlur(_data.toImage()); + if (h <= 0) { + img = img.scaledToWidth(w, Qt::SmoothTransformation); + } else { + img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + + return App::pixmapFromImageInPlace(Images::prepareColored(add, img)); +} + +void Image::forget() const { + if (_forgot) return; + + checkload(); + if (_data.isNull()) return; + + invalidateSizeCache(); + /*if (hasLocalCopy()) { + _saved.clear(); + } else */if (_saved.isEmpty()) { + QBuffer buffer(&_saved); + if (!_data.save(&buffer, _format)) { + if (_data.save(&buffer, "PNG")) { + _format = "PNG"; + } else { + return; + } + } + } + GlobalAcquiredSize -= int64(_data.width()) * _data.height() * 4; + _data = QPixmap(); + _forgot = true; +} + +void Image::restore() const { + if (!_forgot) return; + + QBuffer buffer(&_saved); + QImageReader reader(&buffer, _format); +#ifndef OS_MAC_OLD + reader.setAutoTransform(true); +#endif // OS_MAC_OLD + _data = QPixmap::fromImageReader(&reader, Qt::ColorOnly); + + if (!_data.isNull()) { + GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; + } + _forgot = false; +} + +std::optional Image::cacheKey() const { + return std::nullopt; +} + +void Image::invalidateSizeCache() const { + for (auto &pix : _sizesCache) { + if (!pix.isNull()) { + GlobalAcquiredSize -= int64(pix.width()) * pix.height() * 4; + } + } + _sizesCache.clear(); +} + +Image::~Image() { + invalidateSizeCache(); + if (!_data.isNull()) { + GlobalAcquiredSize -= int64(_data.width()) * _data.height() * 4; + } +} + +void RemoteImage::doCheckload() const { + if (!amLoading() || !_loader->finished()) return; + + QPixmap data = _loader->imagePixmap(shrinkBox()); + if (data.isNull()) { + destroyLoaderDelayed(CancelledFileLoader); + return; + } + + if (!_data.isNull()) { + GlobalAcquiredSize -= int64(_data.width()) * _data.height() * 4; + } + + _format = _loader->imageFormat(shrinkBox()); + _data = data; + _saved = _loader->bytes(); + const_cast(this)->setInformation(_saved.size(), _data.width(), _data.height()); + GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; + + invalidateSizeCache(); + + destroyLoaderDelayed(); + + _forgot = false; +} + +void RemoteImage::destroyLoaderDelayed(FileLoader *newValue) const { + _loader->stop(); + auto loader = std::unique_ptr(std::exchange(_loader, newValue)); + Auth().downloader().delayedDestroyLoader(std::move(loader)); +} + +void RemoteImage::loadLocal() { + if (loaded() || amLoading()) return; + + _loader = createLoader(std::nullopt, LoadFromLocalOnly, true); + if (_loader) _loader->start(); +} + +void RemoteImage::setImageBytes( + const QByteArray &bytes, + const QByteArray &bytesFormat) { + if (!_data.isNull()) { + GlobalAcquiredSize -= int64(_data.width()) * _data.height() * 4; + } + QByteArray fmt(bytesFormat); + _data = App::pixmapFromImageInPlace(App::readImage(bytes, &fmt, false)); + if (!_data.isNull()) { + GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; + setInformation(bytes.size(), _data.width(), _data.height()); + } + + invalidateSizeCache(); + if (amLoading()) { + destroyLoaderDelayed(); + } + _saved = bytes; + _format = fmt; + _forgot = false; + + const auto location = this->location(); + if (!location.isNull() + && !bytes.isEmpty() + && bytes.size() <= Storage::kMaxFileInMemory) { + Auth().data().cache().putIfEmpty( + Data::StorageCacheKey(location), + Storage::Cache::Database::TaggedValue( + base::duplicate(bytes), + Data::kImageCacheTag)); + } +} + +bool RemoteImage::amLoading() const { + return _loader && _loader != CancelledFileLoader; +} + +void RemoteImage::automaticLoad( + Data::FileOrigin origin, + const HistoryItem *item) { + if (loaded()) return; + + if (_loader != CancelledFileLoader && item) { + bool loadFromCloud = false; + if (item->history()->peer->isUser()) { + loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoPrivate); + } else { + loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoGroups); + } + + if (_loader) { + if (loadFromCloud) _loader->permitLoadFromCloud(); + } else { + _loader = createLoader( + origin, + loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly, + true); + if (_loader) _loader->start(); + } + } +} + +void RemoteImage::automaticLoadSettingsChanged() { + if (loaded() || _loader != CancelledFileLoader) return; + _loader = 0; +} + +void RemoteImage::load( + Data::FileOrigin origin, + bool loadFirst, + bool prior) { + if (loaded()) return; + + if (!_loader) { + _loader = createLoader(origin, LoadFromCloudOrLocal, false); + } + if (amLoading()) { + _loader->start(loadFirst, prior); + } +} + +void RemoteImage::loadEvenCancelled( + Data::FileOrigin origin, + bool loadFirst, + bool prior) { + if (_loader == CancelledFileLoader) { + _loader = nullptr; + } + return load(origin, loadFirst, prior); +} + +RemoteImage::~RemoteImage() { + if (!_data.isNull()) { + GlobalAcquiredSize -= int64(_data.width()) * _data.height() * 4; + } + if (amLoading()) { + destroyLoaderDelayed(); + } +} + +bool RemoteImage::loaded() const { + doCheckload(); + return (!_data.isNull() || !_saved.isNull()); +} + +bool RemoteImage::displayLoading() const { + return amLoading() && (!_loader->loadingLocal() || !_loader->autoLoading()); +} + +void RemoteImage::cancel() { + if (!amLoading()) return; + + auto loader = std::exchange(_loader, CancelledFileLoader); + loader->cancel(); + loader->stop(); + Auth().downloader().delayedDestroyLoader(std::unique_ptr(loader)); +} + +float64 RemoteImage::progress() const { + return amLoading() ? _loader->currentProgress() : (loaded() ? 1 : 0); +} + +int32 RemoteImage::loadOffset() const { + return amLoading() ? _loader->currentOffset() : 0; +} + +StorageImage::StorageImage(const StorageImageLocation &location, int32 size) +: _location(location) +, _size(size) { +} + +StorageImage::StorageImage( + const StorageImageLocation &location, + const QByteArray &bytes) +: _location(location) +, _size(bytes.size()) { + setImageBytes(bytes); +} + +std::optional StorageImage::cacheKey() const { + return _location.isNull() + ? std::nullopt + : base::make_optional(Data::StorageCacheKey(_location)); +} + +int32 StorageImage::countWidth() const { + return _location.width(); +} + +int32 StorageImage::countHeight() const { + return _location.height(); +} + +void StorageImage::setInformation(int32 size, int32 width, int32 height) { + _size = size; + _location.setSize(width, height); +} + +FileLoader *StorageImage::createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) { + if (_location.isNull()) { + return nullptr; + } + return new mtpFileLoader( + &_location, + origin, + _size, + fromCloud, + autoLoading, + Data::kImageCacheTag); +} + +WebFileImage::WebFileImage( + const WebFileLocation &location, + QSize box, + int size) +: _location(location) +, _box(box) +, _width(0) +, _height(0) +, _size(size) { +} + +WebFileImage::WebFileImage( + const WebFileLocation &location, + int width, + int height, + int size) +: _location(location) +, _width(width) +, _height(height) +, _size(size) { +} + +std::optional WebFileImage::cacheKey() const { + return _location.isNull() + ? std::nullopt + : base::make_optional(Data::WebDocumentCacheKey(_location)); +} + +int WebFileImage::countWidth() const { + return _width; +} + +int WebFileImage::countHeight() const { + return _height; +} + +void WebFileImage::setInformation(int size, int width, int height) { + _size = size; + _width = width; + _height = height; +} + +FileLoader *WebFileImage::createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) { + return _location.isNull() + ? nullptr + : new mtpFileLoader( + &_location, + _size, + fromCloud, + autoLoading, + Data::kImageCacheTag); +} + +GeoPointImage::GeoPointImage(const GeoPointLocation &location) +: _location(location) { +} + +std::optional GeoPointImage::cacheKey() const { + return Data::GeoPointCacheKey(_location); +} + +int GeoPointImage::countWidth() const { + return _location.width * _location.scale; +} + +int GeoPointImage::countHeight() const { + return _location.height * _location.scale; +} + +void GeoPointImage::setInformation(int size, int width, int height) { + _size = size; + _location.width = width; + _location.height = height; +} + +FileLoader *GeoPointImage::createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) { + return new mtpFileLoader( + &_location, + _size, + fromCloud, + autoLoading, + Data::kImageCacheTag); +} + +DelayedStorageImage::DelayedStorageImage() : StorageImage(StorageImageLocation()) +, _loadRequested(false) +, _loadCancelled(false) +, _loadFromCloud(false) { +} + +DelayedStorageImage::DelayedStorageImage(int32 w, int32 h) +: StorageImage(StorageImageLocation(w, h, 0, 0, 0, 0, {})) +, _loadRequested(false) +, _loadCancelled(false) +, _loadFromCloud(false) { +} +// +//DelayedStorageImage::DelayedStorageImage(QByteArray &bytes) +//: StorageImage(StorageImageLocation(), bytes) +//, _loadRequested(false) +//, _loadCancelled(false) +//, _loadFromCloud(false) { +//} + +void DelayedStorageImage::setStorageLocation( + Data::FileOrigin origin, + const StorageImageLocation location) { + _location = location; + if (_loadRequested) { + if (!_loadCancelled) { + if (_loadFromCloud) { + load(origin); + } else { + loadLocal(); + } + } + _loadRequested = false; + } +} + +void DelayedStorageImage::automaticLoad( + Data::FileOrigin origin, + const HistoryItem *item) { + if (_location.isNull()) { + if (!_loadCancelled && item) { + bool loadFromCloud = false; + if (item->history()->peer->isUser()) { + loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoPrivate); + } else { + loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoGroups); + } + + if (_loadRequested) { + if (loadFromCloud) _loadFromCloud = loadFromCloud; + } else { + _loadFromCloud = loadFromCloud; + _loadRequested = true; + } + } + } else { + StorageImage::automaticLoad(origin, item); + } +} + +void DelayedStorageImage::automaticLoadSettingsChanged() { + if (_loadCancelled) _loadCancelled = false; + StorageImage::automaticLoadSettingsChanged(); +} + +void DelayedStorageImage::load( + Data::FileOrigin origin, + bool loadFirst, + bool prior) { + if (_location.isNull()) { + _loadRequested = _loadFromCloud = true; + } else { + StorageImage::load(origin, loadFirst, prior); + } +} + +void DelayedStorageImage::loadEvenCancelled( + Data::FileOrigin origin, + bool loadFirst, + bool prior) { + _loadCancelled = false; + StorageImage::loadEvenCancelled(origin, loadFirst, prior); +} + +bool DelayedStorageImage::displayLoading() const { + return _location.isNull() ? true : StorageImage::displayLoading(); +} + +void DelayedStorageImage::cancel() { + if (_loadRequested) { + _loadRequested = false; + } + StorageImage::cancel(); +} + +WebImage::WebImage(const QString &url, QSize box) +: _url(url) +, _box(box) +, _size(0) +, _width(0) +, _height(0) { +} + +WebImage::WebImage(const QString &url, int width, int height) +: _url(url) +, _size(0) +, _width(width) +, _height(height) { +} + +std::optional WebImage::cacheKey() const { + return Data::UrlCacheKey(_url); +} + +void WebImage::setSize(int width, int height) { + _width = width; + _height = height; +} + +int32 WebImage::countWidth() const { + return _width; +} + +int32 WebImage::countHeight() const { + return _height; +} + +void WebImage::setInformation(int32 size, int32 width, int32 height) { + _size = size; + setSize(width, height); +} + +FileLoader *WebImage::createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) { + return new webFileLoader( + _url, + QString(), + fromCloud, + autoLoading, + Data::kImageCacheTag); +} + +namespace Images { +namespace details { + +Image *Create(const QString &file, QByteArray format) { + if (file.startsWith(qstr("http://"), Qt::CaseInsensitive) + || file.startsWith(qstr("https://"), Qt::CaseInsensitive)) { + QString key = file; + WebImages::const_iterator i = webImages.constFind(key); + if (i == webImages.cend()) { + i = webImages.insert(key, new WebImage(file)); + } + return i.value(); + } else { + QFileInfo f(file); + QString key = qsl("//:%1//:%2//:").arg(f.size()).arg(f.lastModified().toTime_t()) + file; + LocalImages::const_iterator i = localImages.constFind(key); + if (i == localImages.cend()) { + i = localImages.insert(key, new Image(file, format)); + } + return i.value(); + } +} + +Image *Create(const QString &url, QSize box) { + QString key = qsl("//:%1//:%2//:").arg(box.width()).arg(box.height()) + url; + auto i = webImages.constFind(key); + if (i == webImages.cend()) { + i = webImages.insert(key, new WebImage(url, box)); + } + return i.value(); +} + +Image *Create(const QString &url, int width, int height) { + QString key = url; + auto i = webImages.constFind(key); + if (i == webImages.cend()) { + i = webImages.insert(key, new WebImage(url, width, height)); + } else { + i.value()->setSize(width, height); + } + return i.value(); +} + +Image *Create(const QByteArray &filecontent, QByteArray format) { + return new Image(filecontent, format); +} + +Image *Create(const QPixmap &pixmap, QByteArray format) { + return new Image(pixmap, format); +} + +Image *Create(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap) { + return new Image(filecontent, format, pixmap); +} + +Image *Create(int32 width, int32 height) { + return new DelayedStorageImage(width, height); +} + +StorageImage *Create(const StorageImageLocation &location, int32 size) { + const auto key = storageKey(location); + auto i = storageImages.constFind(key); + if (i == storageImages.cend()) { + i = storageImages.insert(key, new StorageImage(location, size)); + } else { + i.value()->refreshFileReference(location.fileReference()); + } + return i.value(); +} + +StorageImage *Create( + const StorageImageLocation &location, + const QByteArray &bytes) { + const auto key = storageKey(location); + auto i = storageImages.constFind(key); + if (i == storageImages.cend()) { + i = storageImages.insert(key, new StorageImage(location, bytes)); + } else { + i.value()->refreshFileReference(location.fileReference()); + if (!i.value()->loaded()) { + i.value()->setImageBytes(bytes); + } + } + return i.value(); +} + +QSize getImageSize(const QVector &attributes) { + for (const auto &attribute : attributes) { + if (attribute.type() == mtpc_documentAttributeImageSize) { + auto &size = attribute.c_documentAttributeImageSize(); + return QSize(size.vw.v, size.vh.v); + } + } + return QSize(); +} + +Image *Create(const MTPDwebDocument &document) { + const auto size = getImageSize(document.vattributes.v); + if (size.isEmpty()) { + return Image::Blank(); + } + + // We don't use size from WebDocument, because it is not reliable. + // It can be > 0 and different from the real size that we get in upload.WebFile result. + auto filesize = 0; // document.vsize.v; + return Create( + WebFileLocation( + Global::WebFileDcId(), + document.vurl.v, + document.vaccess_hash.v), + size.width(), + size.height(), + filesize); +} + +Image *Create(const MTPDwebDocumentNoProxy &document) { + const auto size = getImageSize(document.vattributes.v); + if (size.isEmpty()) { + return Image::Blank(); + } + + return Create(qs(document.vurl), size.width(), size.height()); +} + +Image *Create(const MTPDwebDocument &document, QSize box) { + //const auto size = getImageSize(document.vattributes.v); + //if (size.isEmpty()) { + // return Image::Blank(); + //} + + // We don't use size from WebDocument, because it is not reliable. + // It can be > 0 and different from the real size that we get in upload.WebFile result. + auto filesize = 0; // document.vsize.v; + return Create( + WebFileLocation( + Global::WebFileDcId(), + document.vurl.v, + document.vaccess_hash.v), + box, + filesize); +} + +Image *Create(const MTPDwebDocumentNoProxy &document, QSize box) { + //const auto size = getImageSize(document.vattributes.v); + //if (size.isEmpty()) { + // return Image::Blank(); + //} + + return Create(qs(document.vurl), box); +} + +Image *Create(const MTPWebDocument &document) { + switch (document.type()) { + case mtpc_webDocument: + return Create(document.c_webDocument()); + case mtpc_webDocumentNoProxy: + return Create(document.c_webDocumentNoProxy()); + } + Unexpected("Type in getImage(MTPWebDocument)."); +} + +Image *Create(const MTPWebDocument &document, QSize box) { + switch (document.type()) { + case mtpc_webDocument: + return Create(document.c_webDocument(), box); + case mtpc_webDocumentNoProxy: + return Create(document.c_webDocumentNoProxy(), box); + } + Unexpected("Type in getImage(MTPWebDocument)."); +} + +WebFileImage *Create( + const WebFileLocation &location, + QSize box, + int size) { + auto key = storageKey(location); + auto i = webFileImages.constFind(key); + if (i == webFileImages.cend()) { + i = webFileImages.insert( + key, + new WebFileImage(location, box, size)); + } + return i.value(); +} + +WebFileImage *Create( + const WebFileLocation &location, + int width, + int height, + int size) { + auto key = storageKey(location); + auto i = webFileImages.constFind(key); + if (i == webFileImages.cend()) { + i = webFileImages.insert( + key, + new WebFileImage(location, width, height, size)); + } + return i.value(); +} + +GeoPointImage *Create(const GeoPointLocation &location) { + auto key = storageKey(location); + auto i = geoPointImages.constFind(key); + if (i == geoPointImages.cend()) { + i = geoPointImages.insert( + key, + new GeoPointImage(location)); + } + return i.value(); +} + +} // namespace detals +} // namespace Images diff --git a/Telegram/SourceFiles/ui/image.h b/Telegram/SourceFiles/ui/image.h new file mode 100644 index 0000000000..a4c37c1a7a --- /dev/null +++ b/Telegram/SourceFiles/ui/image.h @@ -0,0 +1,441 @@ +/* +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 + +namespace Images { + +void ClearRemote(); +void ClearAll(); +void CheckCacheSize(); + +} // namespace Images + +class DelayedStorageImage; +class HistoryItem; + +class Image { +public: + Image(const QString &file, QByteArray format = QByteArray()); + Image(const QByteArray &filecontent, QByteArray format = QByteArray()); + Image(const QPixmap &pixmap, QByteArray format = QByteArray()); + Image(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap); + + static Image *Blank(); + + virtual void automaticLoad( + Data::FileOrigin origin, + const HistoryItem *item) { + } + virtual void automaticLoadSettingsChanged() { + } + + virtual bool loaded() const { + return true; + } + virtual bool loading() const { + return false; + } + virtual bool displayLoading() const { + return false; + } + virtual void cancel() { + } + virtual float64 progress() const { + return 1; + } + virtual int32 loadOffset() const { + return 0; + } + + const QPixmap &pix( + Data::FileOrigin origin, + int32 w = 0, + int32 h = 0) const; + const QPixmap &pixRounded( + Data::FileOrigin origin, + int32 w = 0, + int32 h = 0, + ImageRoundRadius radius = ImageRoundRadius::None, + RectParts corners = RectPart::AllCorners) const; + const QPixmap &pixBlurred( + Data::FileOrigin origin, + int32 w = 0, + int32 h = 0) const; + const QPixmap &pixColored( + Data::FileOrigin origin, + style::color add, + int32 w = 0, + int32 h = 0) const; + const QPixmap &pixBlurredColored( + Data::FileOrigin origin, + style::color add, + int32 w = 0, + int32 h = 0) const; + const QPixmap &pixSingle( + Data::FileOrigin origin, + int32 w, + int32 h, + int32 outerw, + int32 outerh, + ImageRoundRadius radius, + RectParts corners = RectPart::AllCorners, + const style::color *colored = nullptr) const; + const QPixmap &pixBlurredSingle( + Data::FileOrigin origin, + int32 w, + int32 h, + int32 outerw, + int32 outerh, + ImageRoundRadius radius, + RectParts corners = RectPart::AllCorners) const; + const QPixmap &pixCircled( + Data::FileOrigin origin, + int32 w = 0, + int32 h = 0) const; + const QPixmap &pixBlurredCircled( + Data::FileOrigin origin, + int32 w = 0, + int32 h = 0) const; + QPixmap pixNoCache( + Data::FileOrigin origin, + int w = 0, + int h = 0, + Images::Options options = 0, + int outerw = -1, + int outerh = -1, + const style::color *colored = nullptr) const; + QPixmap pixColoredNoCache( + Data::FileOrigin origin, + style::color add, + int32 w = 0, + int32 h = 0, + bool smooth = false) const; + QPixmap pixBlurredColoredNoCache( + Data::FileOrigin origin, + style::color add, + int32 w, + int32 h = 0) const; + + int32 width() const { + return qMax(countWidth(), 1); + } + + int32 height() const { + return qMax(countHeight(), 1); + } + + virtual void load( + Data::FileOrigin origin, + bool loadFirst = false, + bool prior = true) { + } + + virtual void loadEvenCancelled( + Data::FileOrigin origin, + bool loadFirst = false, + bool prior = true) { + } + + virtual const StorageImageLocation &location() const { + return StorageImageLocation::Null; + } + virtual std::optional cacheKey() const; + + bool isNull() const; + + void forget() const; + + QByteArray savedFormat() const { + return _format; + } + QByteArray savedData() const { + return _saved; + } + + virtual DelayedStorageImage *toDelayedStorageImage() { + return 0; + } + virtual const DelayedStorageImage *toDelayedStorageImage() const { + return 0; + } + + virtual ~Image(); + +protected: + Image(QByteArray format = "PNG") : _format(format) { + } + + void restore() const; + virtual void checkload() const { + } + void invalidateSizeCache() const; + + virtual int32 countWidth() const { + restore(); + return _data.width(); + } + + virtual int32 countHeight() const { + restore(); + return _data.height(); + } + + mutable QByteArray _saved, _format; + mutable bool _forgot = false; + mutable QPixmap _data; + +private: + using Sizes = QMap; + mutable Sizes _sizesCache; + +}; + +class RemoteImage : public Image { +public: + void automaticLoad( + Data::FileOrigin origin, + const HistoryItem *item) override; // auto load photo + void automaticLoadSettingsChanged() override; + + bool loaded() const override; + bool loading() const override { + return amLoading(); + } + bool displayLoading() const override; + void cancel() override; + float64 progress() const override; + int32 loadOffset() const override; + + void setImageBytes( + const QByteArray &bytes, + const QByteArray &format = QByteArray()); + + void load( + Data::FileOrigin origin, + bool loadFirst = false, + bool prior = true) override; + void loadEvenCancelled( + Data::FileOrigin origin, + bool loadFirst = false, + bool prior = true) override; + + ~RemoteImage(); + +protected: + // If after loading the image we need to shrink it to fit into a + // specific size, you can return this size here. + virtual QSize shrinkBox() const { + return QSize(); + } + virtual void setInformation(int32 size, int32 width, int32 height) = 0; + virtual FileLoader *createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) = 0; + + void checkload() const override { + doCheckload(); + } + void loadLocal(); + +private: + mutable FileLoader *_loader = nullptr; + bool amLoading() const; + void doCheckload() const; + + void destroyLoaderDelayed(FileLoader *newValue = nullptr) const; + +}; + +class StorageImage : public RemoteImage { +public: + explicit StorageImage(const StorageImageLocation &location, int32 size = 0); + StorageImage(const StorageImageLocation &location, const QByteArray &bytes); + + const StorageImageLocation &location() const override { + return _location; + } + std::optional cacheKey() const override; + void refreshFileReference(const QByteArray &data) { + _location.refreshFileReference(data); + } + +protected: + void setInformation(int32 size, int32 width, int32 height) override; + FileLoader *createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) override; + + int32 countWidth() const override; + int32 countHeight() const override; + + StorageImageLocation _location; + int32 _size; + +}; + +class WebFileImage : public RemoteImage { +public: + WebFileImage(const WebFileLocation &location, QSize box, int size = 0); + WebFileImage( + const WebFileLocation &location, + int width, + int height, + int size = 0); + + std::optional cacheKey() const override; + +protected: + void setInformation(int size, int width, int height) override; + FileLoader *createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) override; + + QSize shrinkBox() const override { + return _box; + } + + int countWidth() const override; + int countHeight() const override; + + WebFileLocation _location; + QSize _box; + int _width = 0; + int _height = 0; + int _size = 0; + +}; + +class GeoPointImage : public RemoteImage { +public: + GeoPointImage(const GeoPointLocation &location); + + std::optional cacheKey() const override; + +protected: + void setInformation(int size, int width, int height) override; + FileLoader *createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) override; + + int countWidth() const override; + int countHeight() const override; + + GeoPointLocation _location; + int _size = 0; + +}; + +class DelayedStorageImage : public StorageImage { +public: + DelayedStorageImage(); + DelayedStorageImage(int32 w, int32 h); + //DelayedStorageImage(QByteArray &bytes); + + void setStorageLocation( + Data::FileOrigin origin, + const StorageImageLocation location); + + virtual DelayedStorageImage *toDelayedStorageImage() override { + return this; + } + virtual const DelayedStorageImage *toDelayedStorageImage() const override { + return this; + } + + void automaticLoad( + Data::FileOrigin origin, + const HistoryItem *item) override; // auto load photo + void automaticLoadSettingsChanged() override; + + bool loading() const override { + return _location.isNull() ? _loadRequested : StorageImage::loading(); + } + bool displayLoading() const override; + void cancel() override; + + void load( + Data::FileOrigin origin, + bool loadFirst = false, + bool prior = true) override; + void loadEvenCancelled( + Data::FileOrigin origin, + bool loadFirst = false, + bool prior = true) override; + +private: + bool _loadRequested, _loadCancelled, _loadFromCloud; + +}; + +class WebImage : public RemoteImage { +public: + // If !box.isEmpty() then resize the image to fit in this box. + WebImage(const QString &url, QSize box = QSize()); + WebImage(const QString &url, int width, int height); + + void setSize(int width, int height); + + std::optional cacheKey() const override; + +protected: + QSize shrinkBox() const override { + return _box; + } + void setInformation(int32 size, int32 width, int32 height) override; + FileLoader *createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) override; + + int32 countWidth() const override; + int32 countHeight() const override; + +private: + QString _url; + QSize _box; + int32 _size, _width, _height; + +}; + +namespace Images { +namespace details { + +Image *Create(const QString &file, QByteArray format); +Image *Create(const QString &url, QSize box); +Image *Create(const QString &url, int width, int height); +Image *Create(const QByteArray &filecontent, QByteArray format); +Image *Create(const QPixmap &pixmap, QByteArray format); +Image *Create( + const QByteArray &filecontent, + QByteArray format, + const QPixmap &pixmap); +Image *Create(int32 width, int32 height); +StorageImage *Create(const StorageImageLocation &location, int size = 0); +StorageImage *Create( // photoCachedSize + const StorageImageLocation &location, + const QByteArray &bytes); +Image *Create(const MTPWebDocument &location); +Image *Create(const MTPWebDocument &location, QSize box); +WebFileImage *Create( + const WebFileLocation &location, + int width, + int height, + int size = 0); +WebFileImage *Create( + const WebFileLocation &location, + QSize box, + int size = 0); +GeoPointImage *Create( + const GeoPointLocation &location); + +} // namespace details +} // namespace Images diff --git a/Telegram/SourceFiles/ui/images.cpp b/Telegram/SourceFiles/ui/images.cpp index a1a1c2f598..1d672e535b 100644 --- a/Telegram/SourceFiles/ui/images.cpp +++ b/Telegram/SourceFiles/ui/images.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/images.h" +#include "ui/image.h" #include "mainwidget.h" #include "storage/localstorage.h" #include "storage/cache/storage_cache_database.h" @@ -275,7 +276,7 @@ QImage prepareColored(style::color add, QImage image) { if (auto pix = image.bits()) { int ca = int(add->c.alphaF() * 0xFF), cr = int(add->c.redF() * 0xFF), cg = int(add->c.greenF() * 0xFF), cb = int(add->c.blueF() * 0xFF); const int w = image.width(), h = image.height(), size = w * h * 4; - for (int32 i = 0; i < size; i += 4) { + for (auto i = index_type(); i < size; i += 4) { int b = pix[i], g = pix[i + 1], r = pix[i + 2], a = pix[i + 3], aca = a * ca; pix[i + 0] = uchar(b + ((aca * (cb - b)) >> 16)); pix[i + 1] = uchar(g + ((aca * (cg - g)) >> 16)); @@ -366,46 +367,95 @@ QImage prepare(QImage img, int w, int h, Images::Options options, int outerw, in } // namespace Images -namespace { - -using LocalImages = QMap; -LocalImages localImages; - -using WebImages = QMap; -WebImages webImages; - -Image *generateBlankImage() { - auto data = QImage(cIntRetinaFactor(), cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); - data.fill(Qt::transparent); - data.setDevicePixelRatio(cRetinaFactor()); - return internal::getImage(App::pixmapFromImageInPlace(std::move(data)), "GIF"); +ImagePtr::ImagePtr() : _data(Image::Blank()) { } -Image *blank() { - static auto blankImage = generateBlankImage(); - return blankImage; +ImagePtr::ImagePtr(const QString &file, QByteArray format) +: _data(Images::details::Create(file, format)) { } -using StorageImages = QMap; -StorageImages storageImages; - -using WebFileImages = QMap; -WebFileImages webFileImages; - -using GeoPointImages = QMap; -GeoPointImages geoPointImages; - -int64 globalAcquiredSize = 0; - -uint64 PixKey(int width, int height, Images::Options options) { - return static_cast(width) | (static_cast(height) << 24) | (static_cast(options) << 48); +ImagePtr::ImagePtr(const QString &url, QSize box) +: _data(Images::details::Create(url, box)) { } -uint64 SinglePixKey(Images::Options options) { - return PixKey(0, 0, options); +ImagePtr::ImagePtr(const QString &url, int width, int height) +: _data(Images::details::Create(url, width, height)) { } -} // namespace +ImagePtr::ImagePtr(const QByteArray &filecontent, QByteArray format) +: _data(Images::details::Create(filecontent, format)) { +} + +ImagePtr::ImagePtr( + const QByteArray &filecontent, + QByteArray format, + const QPixmap &pixmap) +: _data(Images::details::Create(filecontent, format, pixmap)) { +} + +ImagePtr::ImagePtr(const QPixmap &pixmap, QByteArray format) +: _data(Images::details::Create(pixmap, format)) { +} + +ImagePtr::ImagePtr(const StorageImageLocation &location, int32 size) +: _data(Images::details::Create(location, size)) { +} + +ImagePtr::ImagePtr( + const StorageImageLocation &location, + const QByteArray &bytes) +: _data(Images::details::Create(location, bytes)) { +} + +ImagePtr::ImagePtr(const MTPWebDocument &location) +: _data(Images::details::Create(location)) { +} + +ImagePtr::ImagePtr(const MTPWebDocument &location, QSize box) +: _data(Images::details::Create(location, box)) { +} + +ImagePtr::ImagePtr( + const WebFileLocation &location, + int width, + int height, + int size) +: _data(Images::details::Create(location, width, height, size)) { +} + +ImagePtr::ImagePtr(const WebFileLocation &location, QSize box, int size) +: _data(Images::details::Create(location, box, size)) { +} + +ImagePtr::ImagePtr(const GeoPointLocation &location) +: _data(Images::details::Create(location)) { +} + +ImagePtr::ImagePtr( + int32 width, + int32 height, + const MTPFileLocation &location, + ImagePtr def) +: _data((location.type() != mtpc_fileLocation) + ? def.get() + : (Image*)(Images::details::Create( + StorageImageLocation(width, height, location.c_fileLocation())))) { +} + +ImagePtr::ImagePtr(int32 width, int32 height) +: _data(Images::details::Create(width, height)) { +} + +Image *ImagePtr::operator->() const { + return _data; +} +Image *ImagePtr::get() const { + return _data; +} + +ImagePtr::operator bool() const { + return !_data->isNull(); +} StorageImageLocation StorageImageLocation::Null; WebFileLocation WebFileLocation::Null; @@ -439,1192 +489,6 @@ StorageImageLocation::StorageImageLocation( location.vfile_reference.v) { } -bool Image::isNull() const { - return (this == blank()); -} - -ImagePtr::ImagePtr() : Parent(blank()) { -} - -ImagePtr::ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def) : - Parent((location.type() == mtpc_fileLocation) ? (Image*)(internal::getImage(StorageImageLocation(width, height, location.c_fileLocation()))) : def.v()) { -} - -Image::Image(const QString &file, QByteArray fmt) { - _data = App::pixmapFromImageInPlace(App::readImage(file, &fmt, false, 0, &_saved)); - _format = fmt; - if (!_data.isNull()) { - globalAcquiredSize += int64(_data.width()) * _data.height() * 4; - } -} - -Image::Image(const QByteArray &filecontent, QByteArray fmt) { - _data = App::pixmapFromImageInPlace(App::readImage(filecontent, &fmt, false)); - _format = fmt; - _saved = filecontent; - if (!_data.isNull()) { - globalAcquiredSize += int64(_data.width()) * _data.height() * 4; - } -} - -Image::Image(const QPixmap &pixmap, QByteArray format) : _format(format), _data(pixmap) { - if (!_data.isNull()) { - globalAcquiredSize += int64(_data.width()) * _data.height() * 4; - } -} - -Image::Image(const QByteArray &filecontent, QByteArray fmt, const QPixmap &pixmap) : _saved(filecontent), _format(fmt), _data(pixmap) { - _data = pixmap; - _format = fmt; - _saved = filecontent; - if (!_data.isNull()) { - globalAcquiredSize += int64(_data.width()) * _data.height() * 4; - } -} - -const QPixmap &Image::pix( - Data::FileOrigin origin, - int32 w, - int32 h) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width(); - } else { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - auto options = Images::Option::Smooth | Images::Option::None; - auto k = PixKey(w, h, options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend()) { - auto p = pixNoCache(origin, w, h, options); - p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixRounded( - Data::FileOrigin origin, - int32 w, - int32 h, - ImageRoundRadius radius, - RectParts corners) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width(); - } else { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - auto options = Images::Option::Smooth | Images::Option::None; - auto cornerOptions = [](RectParts corners) { - return (corners & RectPart::TopLeft ? Images::Option::RoundedTopLeft : Images::Option::None) - | (corners & RectPart::TopRight ? Images::Option::RoundedTopRight : Images::Option::None) - | (corners & RectPart::BottomLeft ? Images::Option::RoundedBottomLeft : Images::Option::None) - | (corners & RectPart::BottomRight ? Images::Option::RoundedBottomRight : Images::Option::None); - }; - if (radius == ImageRoundRadius::Large) { - options |= Images::Option::RoundedLarge | cornerOptions(corners); - } else if (radius == ImageRoundRadius::Small) { - options |= Images::Option::RoundedSmall | cornerOptions(corners); - } else if (radius == ImageRoundRadius::Ellipse) { - options |= Images::Option::Circled | cornerOptions(corners); - } - auto k = PixKey(w, h, options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend()) { - auto p = pixNoCache(origin, w, h, options); - p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixCircled( - Data::FileOrigin origin, - int32 w, - int32 h) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width(); - } else { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - auto options = Images::Option::Smooth | Images::Option::Circled; - auto k = PixKey(w, h, options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend()) { - auto p = pixNoCache(origin, w, h, options); - p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixBlurredCircled( - Data::FileOrigin origin, - int32 w, - int32 h) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width(); - } else { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - auto options = Images::Option::Smooth | Images::Option::Circled | Images::Option::Blurred; - auto k = PixKey(w, h, options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend()) { - auto p = pixNoCache(origin, w, h, options); - p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixBlurred( - Data::FileOrigin origin, - int32 w, - int32 h) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width() * cIntRetinaFactor(); - } else { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - auto options = Images::Option::Smooth | Images::Option::Blurred; - auto k = PixKey(w, h, options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend()) { - auto p = pixNoCache(origin, w, h, options); - p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixColored( - Data::FileOrigin origin, - style::color add, - int32 w, - int32 h) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width() * cIntRetinaFactor(); - } else { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - auto options = Images::Option::Smooth | Images::Option::Colored; - auto k = PixKey(w, h, options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend()) { - auto p = pixColoredNoCache(origin, add, w, h, true); - p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixBlurredColored( - Data::FileOrigin origin, - style::color add, - int32 w, - int32 h) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width() * cIntRetinaFactor(); - } else { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - auto options = Images::Option::Blurred | Images::Option::Smooth | Images::Option::Colored; - auto k = PixKey(w, h, options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend()) { - auto p = pixBlurredColoredNoCache(origin, add, w, h); - p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixSingle( - Data::FileOrigin origin, - int32 w, - int32 h, - int32 outerw, - int32 outerh, - ImageRoundRadius radius, - RectParts corners, - const style::color *colored) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width() * cIntRetinaFactor(); - } else { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - - auto options = Images::Option::Smooth | Images::Option::None; - auto cornerOptions = [](RectParts corners) { - return (corners & RectPart::TopLeft ? Images::Option::RoundedTopLeft : Images::Option::None) - | (corners & RectPart::TopRight ? Images::Option::RoundedTopRight : Images::Option::None) - | (corners & RectPart::BottomLeft ? Images::Option::RoundedBottomLeft : Images::Option::None) - | (corners & RectPart::BottomRight ? Images::Option::RoundedBottomRight : Images::Option::None); - }; - if (radius == ImageRoundRadius::Large) { - options |= Images::Option::RoundedLarge | cornerOptions(corners); - } else if (radius == ImageRoundRadius::Small) { - options |= Images::Option::RoundedSmall | cornerOptions(corners); - } else if (radius == ImageRoundRadius::Ellipse) { - options |= Images::Option::Circled | cornerOptions(corners); - } - if (colored) { - options |= Images::Option::Colored; - } - - auto k = SinglePixKey(options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend() || i->width() != (outerw * cIntRetinaFactor()) || i->height() != (outerh * cIntRetinaFactor())) { - if (i != _sizesCache.cend()) { - globalAcquiredSize -= int64(i->width()) * i->height() * 4; - } - auto p = pixNoCache(origin, w, h, options, outerw, outerh, colored); - p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixBlurredSingle( - Data::FileOrigin origin, - int w, - int h, - int32 outerw, - int32 outerh, - ImageRoundRadius radius, - RectParts corners) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width() * cIntRetinaFactor(); - } else { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - - auto options = Images::Option::Smooth | Images::Option::Blurred; - auto cornerOptions = [](RectParts corners) { - return (corners & RectPart::TopLeft ? Images::Option::RoundedTopLeft : Images::Option::None) - | (corners & RectPart::TopRight ? Images::Option::RoundedTopRight : Images::Option::None) - | (corners & RectPart::BottomLeft ? Images::Option::RoundedBottomLeft : Images::Option::None) - | (corners & RectPart::BottomRight ? Images::Option::RoundedBottomRight : Images::Option::None); - }; - if (radius == ImageRoundRadius::Large) { - options |= Images::Option::RoundedLarge | cornerOptions(corners); - } else if (radius == ImageRoundRadius::Small) { - options |= Images::Option::RoundedSmall | cornerOptions(corners); - } else if (radius == ImageRoundRadius::Ellipse) { - options |= Images::Option::Circled | cornerOptions(corners); - } - - auto k = SinglePixKey(options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend() || i->width() != (outerw * cIntRetinaFactor()) || i->height() != (outerh * cIntRetinaFactor())) { - if (i != _sizesCache.cend()) { - globalAcquiredSize -= int64(i->width()) * i->height() * 4; - } - auto p = pixNoCache(origin, w, h, options, outerw, outerh); - p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -QPixmap Image::pixNoCache( - Data::FileOrigin origin, - int w, - int h, - Images::Options options, - int outerw, - int outerh, - const style::color *colored) const { - if (!loading()) const_cast(this)->load(origin); - restore(); - - if (_data.isNull()) { - if (h <= 0 && height() > 0) { - h = qRound(width() * w / float64(height())); - } - return blank()->pixNoCache(origin, w, h, options, outerw, outerh); - } - - if (isNull() && outerw > 0 && outerh > 0) { - outerw *= cIntRetinaFactor(); - outerh *= cIntRetinaFactor(); - - QImage result(outerw, outerh, QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(cRetinaFactor()); - - { - QPainter p(&result); - if (w < outerw) { - p.fillRect(0, 0, (outerw - w) / 2, result.height(), st::imageBg); - p.fillRect(((outerw - w) / 2) + w, 0, result.width() - (((outerw - w) / 2) + w), result.height(), st::imageBg); - } - if (h < outerh) { - p.fillRect(qMax(0, (outerw - w) / 2), 0, qMin(result.width(), w), (outerh - h) / 2, st::imageBg); - p.fillRect(qMax(0, (outerw - w) / 2), ((outerh - h) / 2) + h, qMin(result.width(), w), result.height() - (((outerh - h) / 2) + h), st::imageBg); - } - p.fillRect(qMax(0, (outerw - w) / 2), qMax(0, (outerh - h) / 2), qMin(result.width(), w), qMin(result.height(), h), st::imageBgTransparent); - } - - auto corners = [](Images::Options options) { - return ((options & Images::Option::RoundedTopLeft) ? RectPart::TopLeft : RectPart::None) - | ((options & Images::Option::RoundedTopRight) ? RectPart::TopRight : RectPart::None) - | ((options & Images::Option::RoundedBottomLeft) ? RectPart::BottomLeft : RectPart::None) - | ((options & Images::Option::RoundedBottomRight) ? RectPart::BottomRight : RectPart::None); - }; - if (options & Images::Option::Circled) { - Images::prepareCircle(result); - } else if (options & Images::Option::RoundedLarge) { - Images::prepareRound(result, ImageRoundRadius::Large, corners(options)); - } else if (options & Images::Option::RoundedSmall) { - Images::prepareRound(result, ImageRoundRadius::Small, corners(options)); - } - if (options & Images::Option::Colored) { - Assert(colored != nullptr); - result = Images::prepareColored(*colored, std::move(result)); - } - return App::pixmapFromImageInPlace(std::move(result)); - } - - return Images::pixmap(_data.toImage(), w, h, options, outerw, outerh, colored); -} - -QPixmap Image::pixColoredNoCache( - Data::FileOrigin origin, - style::color add, - int32 w, - int32 h, - bool smooth) const { - const_cast(this)->load(origin); - restore(); - if (_data.isNull()) return blank()->pix(origin); - - auto img = _data.toImage(); - if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) { - return App::pixmapFromImageInPlace(Images::prepareColored(add, std::move(img))); - } - if (h <= 0) { - return App::pixmapFromImageInPlace(Images::prepareColored(add, img.scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation))); - } - return App::pixmapFromImageInPlace(Images::prepareColored(add, img.scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation))); -} - -QPixmap Image::pixBlurredColoredNoCache( - Data::FileOrigin origin, - style::color add, - int32 w, - int32 h) const { - const_cast(this)->load(origin); - restore(); - if (_data.isNull()) return blank()->pix(origin); - - auto img = Images::prepareBlur(_data.toImage()); - if (h <= 0) { - img = img.scaledToWidth(w, Qt::SmoothTransformation); - } else { - img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - } - - return App::pixmapFromImageInPlace(Images::prepareColored(add, img)); -} - -void Image::forget() const { - if (_forgot) return; - - checkload(); - if (_data.isNull()) return; - - invalidateSizeCache(); - /*if (hasLocalCopy()) { - _saved.clear(); - } else */if (_saved.isEmpty()) { - QBuffer buffer(&_saved); - if (!_data.save(&buffer, _format)) { - if (_data.save(&buffer, "PNG")) { - _format = "PNG"; - } else { - return; - } - } - } - globalAcquiredSize -= int64(_data.width()) * _data.height() * 4; - _data = QPixmap(); - _forgot = true; -} - -void Image::restore() const { - if (!_forgot) return; - - QBuffer buffer(&_saved); - QImageReader reader(&buffer, _format); -#ifndef OS_MAC_OLD - reader.setAutoTransform(true); -#endif // OS_MAC_OLD - _data = QPixmap::fromImageReader(&reader, Qt::ColorOnly); - - if (!_data.isNull()) { - globalAcquiredSize += int64(_data.width()) * _data.height() * 4; - } - _forgot = false; -} - -std::optional Image::cacheKey() const { - return std::nullopt; -} - -void Image::invalidateSizeCache() const { - for (auto &pix : _sizesCache) { - if (!pix.isNull()) { - globalAcquiredSize -= int64(pix.width()) * pix.height() * 4; - } - } - _sizesCache.clear(); -} - -Image::~Image() { - invalidateSizeCache(); - if (!_data.isNull()) { - globalAcquiredSize -= int64(_data.width()) * _data.height() * 4; - } -} - -void clearStorageImages() { - for (auto image : base::take(storageImages)) { - delete image; - } - for (auto image : base::take(webImages)) { - delete image; - } - for (auto image : base::take(webFileImages)) { - delete image; - } - for (auto image : base::take(geoPointImages)) { - delete image; - } -} - -void clearAllImages() { - for (auto image : base::take(localImages)) { - delete image; - } - clearStorageImages(); -} - -int64 imageCacheSize() { - return globalAcquiredSize; -} - -void RemoteImage::doCheckload() const { - if (!amLoading() || !_loader->finished()) return; - - QPixmap data = _loader->imagePixmap(shrinkBox()); - if (data.isNull()) { - destroyLoaderDelayed(CancelledFileLoader); - return; - } - - if (!_data.isNull()) { - globalAcquiredSize -= int64(_data.width()) * _data.height() * 4; - } - - _format = _loader->imageFormat(shrinkBox()); - _data = data; - _saved = _loader->bytes(); - const_cast(this)->setInformation(_saved.size(), _data.width(), _data.height()); - globalAcquiredSize += int64(_data.width()) * _data.height() * 4; - - invalidateSizeCache(); - - destroyLoaderDelayed(); - - _forgot = false; -} - -void RemoteImage::destroyLoaderDelayed(FileLoader *newValue) const { - _loader->stop(); - auto loader = std::unique_ptr(std::exchange(_loader, newValue)); - Auth().downloader().delayedDestroyLoader(std::move(loader)); -} - -void RemoteImage::loadLocal() { - if (loaded() || amLoading()) return; - - _loader = createLoader(std::nullopt, LoadFromLocalOnly, true); - if (_loader) _loader->start(); -} - -void RemoteImage::setImageBytes( - const QByteArray &bytes, - const QByteArray &bytesFormat) { - if (!_data.isNull()) { - globalAcquiredSize -= int64(_data.width()) * _data.height() * 4; - } - QByteArray fmt(bytesFormat); - _data = App::pixmapFromImageInPlace(App::readImage(bytes, &fmt, false)); - if (!_data.isNull()) { - globalAcquiredSize += int64(_data.width()) * _data.height() * 4; - setInformation(bytes.size(), _data.width(), _data.height()); - } - - invalidateSizeCache(); - if (amLoading()) { - destroyLoaderDelayed(); - } - _saved = bytes; - _format = fmt; - _forgot = false; - - const auto location = this->location(); - if (!location.isNull() - && !bytes.isEmpty() - && bytes.size() <= Storage::kMaxFileInMemory) { - Auth().data().cache().putIfEmpty( - Data::StorageCacheKey(location), - Storage::Cache::Database::TaggedValue( - base::duplicate(bytes), - Data::kImageCacheTag)); - } -} - -bool RemoteImage::amLoading() const { - return _loader && _loader != CancelledFileLoader; -} - -void RemoteImage::automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) { - if (loaded()) return; - - if (_loader != CancelledFileLoader && item) { - bool loadFromCloud = false; - if (item->history()->peer->isUser()) { - loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoPrivate); - } else { - loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoGroups); - } - - if (_loader) { - if (loadFromCloud) _loader->permitLoadFromCloud(); - } else { - _loader = createLoader( - origin, - loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly, - true); - if (_loader) _loader->start(); - } - } -} - -void RemoteImage::automaticLoadSettingsChanged() { - if (loaded() || _loader != CancelledFileLoader) return; - _loader = 0; -} - -void RemoteImage::load( - Data::FileOrigin origin, - bool loadFirst, - bool prior) { - if (loaded()) return; - - if (!_loader) { - _loader = createLoader(origin, LoadFromCloudOrLocal, false); - } - if (amLoading()) { - _loader->start(loadFirst, prior); - } -} - -void RemoteImage::loadEvenCancelled( - Data::FileOrigin origin, - bool loadFirst, - bool prior) { - if (_loader == CancelledFileLoader) { - _loader = nullptr; - } - return load(origin, loadFirst, prior); -} - -RemoteImage::~RemoteImage() { - if (!_data.isNull()) { - globalAcquiredSize -= int64(_data.width()) * _data.height() * 4; - } - if (amLoading()) { - destroyLoaderDelayed(); - } -} - -bool RemoteImage::loaded() const { - doCheckload(); - return (!_data.isNull() || !_saved.isNull()); -} - -bool RemoteImage::displayLoading() const { - return amLoading() && (!_loader->loadingLocal() || !_loader->autoLoading()); -} - -void RemoteImage::cancel() { - if (!amLoading()) return; - - auto loader = std::exchange(_loader, CancelledFileLoader); - loader->cancel(); - loader->stop(); - Auth().downloader().delayedDestroyLoader(std::unique_ptr(loader)); -} - -float64 RemoteImage::progress() const { - return amLoading() ? _loader->currentProgress() : (loaded() ? 1 : 0); -} - -int32 RemoteImage::loadOffset() const { - return amLoading() ? _loader->currentOffset() : 0; -} - -StorageImage::StorageImage(const StorageImageLocation &location, int32 size) -: _location(location) -, _size(size) { -} - -StorageImage::StorageImage( - const StorageImageLocation &location, - const QByteArray &bytes) -: _location(location) -, _size(bytes.size()) { - setImageBytes(bytes); -} - -std::optional StorageImage::cacheKey() const { - return _location.isNull() - ? std::nullopt - : base::make_optional(Data::StorageCacheKey(_location)); -} - -int32 StorageImage::countWidth() const { - return _location.width(); -} - -int32 StorageImage::countHeight() const { - return _location.height(); -} - -void StorageImage::setInformation(int32 size, int32 width, int32 height) { - _size = size; - _location.setSize(width, height); -} - -FileLoader *StorageImage::createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) { - if (_location.isNull()) { - return nullptr; - } - return new mtpFileLoader( - &_location, - origin, - _size, - fromCloud, - autoLoading, - Data::kImageCacheTag); -} - -WebFileImage::WebFileImage( - const WebFileLocation &location, - QSize box, - int size) -: _location(location) -, _box(box) -, _width(0) -, _height(0) -, _size(size) { -} - -WebFileImage::WebFileImage( - const WebFileLocation &location, - int width, - int height, - int size) -: _location(location) -, _width(width) -, _height(height) -, _size(size) { -} - -std::optional WebFileImage::cacheKey() const { - return _location.isNull() - ? std::nullopt - : base::make_optional(Data::WebDocumentCacheKey(_location)); -} - -int WebFileImage::countWidth() const { - return _width; -} - -int WebFileImage::countHeight() const { - return _height; -} - -void WebFileImage::setInformation(int size, int width, int height) { - _size = size; - _width = width; - _height = height; -} - -FileLoader *WebFileImage::createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) { - return _location.isNull() - ? nullptr - : new mtpFileLoader( - &_location, - _size, - fromCloud, - autoLoading, - Data::kImageCacheTag); -} - -GeoPointImage::GeoPointImage(const GeoPointLocation &location) -: _location(location) { -} - -std::optional GeoPointImage::cacheKey() const { - return Data::GeoPointCacheKey(_location); -} - -int GeoPointImage::countWidth() const { - return _location.width * _location.scale; -} - -int GeoPointImage::countHeight() const { - return _location.height * _location.scale; -} - -void GeoPointImage::setInformation(int size, int width, int height) { - _size = size; - _location.width = width; - _location.height = height; -} - -FileLoader *GeoPointImage::createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) { - return new mtpFileLoader( - &_location, - _size, - fromCloud, - autoLoading, - Data::kImageCacheTag); -} - -DelayedStorageImage::DelayedStorageImage() : StorageImage(StorageImageLocation()) -, _loadRequested(false) -, _loadCancelled(false) -, _loadFromCloud(false) { -} - -DelayedStorageImage::DelayedStorageImage(int32 w, int32 h) -: StorageImage(StorageImageLocation(w, h, 0, 0, 0, 0, {})) -, _loadRequested(false) -, _loadCancelled(false) -, _loadFromCloud(false) { -} -// -//DelayedStorageImage::DelayedStorageImage(QByteArray &bytes) -//: StorageImage(StorageImageLocation(), bytes) -//, _loadRequested(false) -//, _loadCancelled(false) -//, _loadFromCloud(false) { -//} - -void DelayedStorageImage::setStorageLocation( - Data::FileOrigin origin, - const StorageImageLocation location) { - _location = location; - if (_loadRequested) { - if (!_loadCancelled) { - if (_loadFromCloud) { - load(origin); - } else { - loadLocal(); - } - } - _loadRequested = false; - } -} - -void DelayedStorageImage::automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) { - if (_location.isNull()) { - if (!_loadCancelled && item) { - bool loadFromCloud = false; - if (item->history()->peer->isUser()) { - loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoPrivate); - } else { - loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoGroups); - } - - if (_loadRequested) { - if (loadFromCloud) _loadFromCloud = loadFromCloud; - } else { - _loadFromCloud = loadFromCloud; - _loadRequested = true; - } - } - } else { - StorageImage::automaticLoad(origin, item); - } -} - -void DelayedStorageImage::automaticLoadSettingsChanged() { - if (_loadCancelled) _loadCancelled = false; - StorageImage::automaticLoadSettingsChanged(); -} - -void DelayedStorageImage::load( - Data::FileOrigin origin, - bool loadFirst, - bool prior) { - if (_location.isNull()) { - _loadRequested = _loadFromCloud = true; - } else { - StorageImage::load(origin, loadFirst, prior); - } -} - -void DelayedStorageImage::loadEvenCancelled( - Data::FileOrigin origin, - bool loadFirst, - bool prior) { - _loadCancelled = false; - StorageImage::loadEvenCancelled(origin, loadFirst, prior); -} - -bool DelayedStorageImage::displayLoading() const { - return _location.isNull() ? true : StorageImage::displayLoading(); -} - -void DelayedStorageImage::cancel() { - if (_loadRequested) { - _loadRequested = false; - } - StorageImage::cancel(); -} - -WebImage::WebImage(const QString &url, QSize box) -: _url(url) -, _box(box) -, _size(0) -, _width(0) -, _height(0) { -} - -WebImage::WebImage(const QString &url, int width, int height) -: _url(url) -, _size(0) -, _width(width) -, _height(height) { -} - -std::optional WebImage::cacheKey() const { - return Data::UrlCacheKey(_url); -} - -void WebImage::setSize(int width, int height) { - _width = width; - _height = height; -} - -int32 WebImage::countWidth() const { - return _width; -} - -int32 WebImage::countHeight() const { - return _height; -} - -void WebImage::setInformation(int32 size, int32 width, int32 height) { - _size = size; - setSize(width, height); -} - -FileLoader *WebImage::createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) { - return new webFileLoader( - _url, - QString(), - fromCloud, - autoLoading, - Data::kImageCacheTag); -} - -namespace internal { - -Image *getImage(const QString &file, QByteArray format) { - if (file.startsWith(qstr("http://"), Qt::CaseInsensitive) || file.startsWith(qstr("https://"), Qt::CaseInsensitive)) { - QString key = file; - WebImages::const_iterator i = webImages.constFind(key); - if (i == webImages.cend()) { - i = webImages.insert(key, new WebImage(file)); - } - return i.value(); - } else { - QFileInfo f(file); - QString key = qsl("//:%1//:%2//:").arg(f.size()).arg(f.lastModified().toTime_t()) + file; - LocalImages::const_iterator i = localImages.constFind(key); - if (i == localImages.cend()) { - i = localImages.insert(key, new Image(file, format)); - } - return i.value(); - } -} - -Image *getImage(const QString &url, QSize box) { - QString key = qsl("//:%1//:%2//:").arg(box.width()).arg(box.height()) + url; - auto i = webImages.constFind(key); - if (i == webImages.cend()) { - i = webImages.insert(key, new WebImage(url, box)); - } - return i.value(); -} - -Image *getImage(const QString &url, int width, int height) { - QString key = url; - auto i = webImages.constFind(key); - if (i == webImages.cend()) { - i = webImages.insert(key, new WebImage(url, width, height)); - } else { - i.value()->setSize(width, height); - } - return i.value(); -} - -Image *getImage(const QByteArray &filecontent, QByteArray format) { - return new Image(filecontent, format); -} - -Image *getImage(const QPixmap &pixmap, QByteArray format) { - return new Image(pixmap, format); -} - -Image *getImage(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap) { - return new Image(filecontent, format, pixmap); -} - -Image *getImage(int32 width, int32 height) { - return new DelayedStorageImage(width, height); -} - -StorageImage *getImage(const StorageImageLocation &location, int32 size) { - const auto key = storageKey(location); - auto i = storageImages.constFind(key); - if (i == storageImages.cend()) { - i = storageImages.insert(key, new StorageImage(location, size)); - } else { - i.value()->refreshFileReference(location.fileReference()); - } - return i.value(); -} - -StorageImage *getImage( - const StorageImageLocation &location, - const QByteArray &bytes) { - const auto key = storageKey(location); - auto i = storageImages.constFind(key); - if (i == storageImages.cend()) { - i = storageImages.insert(key, new StorageImage(location, bytes)); - } else { - i.value()->refreshFileReference(location.fileReference()); - if (!i.value()->loaded()) { - i.value()->setImageBytes(bytes); - } - } - return i.value(); -} - -QSize getImageSize(const QVector &attributes) { - for (const auto &attribute : attributes) { - if (attribute.type() == mtpc_documentAttributeImageSize) { - auto &size = attribute.c_documentAttributeImageSize(); - return QSize(size.vw.v, size.vh.v); - } - } - return QSize(); -} - -Image *getImage(const MTPDwebDocument &document) { - const auto size = getImageSize(document.vattributes.v); - if (size.isEmpty()) { - return blank(); - } - - // We don't use size from WebDocument, because it is not reliable. - // It can be > 0 and different from the real size that we get in upload.WebFile result. - auto filesize = 0; // document.vsize.v; - return getImage( - WebFileLocation( - Global::WebFileDcId(), - document.vurl.v, - document.vaccess_hash.v), - size.width(), - size.height(), - filesize); -} - -Image *getImage(const MTPDwebDocumentNoProxy &document) { - const auto size = getImageSize(document.vattributes.v); - if (size.isEmpty()) { - return blank(); - } - - return getImage(qs(document.vurl), size.width(), size.height()); -} - -Image *getImage(const MTPDwebDocument &document, QSize box) { - //const auto size = getImageSize(document.vattributes.v); - //if (size.isEmpty()) { - // return blank(); - //} - - // We don't use size from WebDocument, because it is not reliable. - // It can be > 0 and different from the real size that we get in upload.WebFile result. - auto filesize = 0; // document.vsize.v; - return getImage( - WebFileLocation( - Global::WebFileDcId(), - document.vurl.v, - document.vaccess_hash.v), - box, - filesize); -} - -Image *getImage(const MTPDwebDocumentNoProxy &document, QSize box) { - //const auto size = getImageSize(document.vattributes.v); - //if (size.isEmpty()) { - // return blank(); - //} - - return getImage(qs(document.vurl), box); -} - -Image *getImage(const MTPWebDocument &document) { - switch (document.type()) { - case mtpc_webDocument: - return getImage(document.c_webDocument()); - case mtpc_webDocumentNoProxy: - return getImage(document.c_webDocumentNoProxy()); - } - Unexpected("Type in getImage(MTPWebDocument)."); -} - -Image *getImage(const MTPWebDocument &document, QSize box) { - switch (document.type()) { - case mtpc_webDocument: - return getImage(document.c_webDocument(), box); - case mtpc_webDocumentNoProxy: - return getImage(document.c_webDocumentNoProxy(), box); - } - Unexpected("Type in getImage(MTPWebDocument)."); -} - -WebFileImage *getImage( - const WebFileLocation &location, - QSize box, - int size) { - auto key = storageKey(location); - auto i = webFileImages.constFind(key); - if (i == webFileImages.cend()) { - i = webFileImages.insert( - key, - new WebFileImage(location, box, size)); - } - return i.value(); -} - -WebFileImage *getImage( - const WebFileLocation &location, - int width, - int height, - int size) { - auto key = storageKey(location); - auto i = webFileImages.constFind(key); - if (i == webFileImages.cend()) { - i = webFileImages.insert( - key, - new WebFileImage(location, width, height, size)); - } - return i.value(); -} - -GeoPointImage *getImage(const GeoPointLocation &location) { - auto key = storageKey(location); - auto i = geoPointImages.constFind(key); - if (i == geoPointImages.cend()) { - i = geoPointImages.insert( - key, - new GeoPointImage(location)); - } - return i.value(); -} - -} // namespace internal - ReadAccessEnabler::ReadAccessEnabler(const PsFileBookmark *bookmark) : _bookmark(bookmark) , _failed(_bookmark ? !_bookmark->enable() : false) { diff --git a/Telegram/SourceFiles/ui/images.h b/Telegram/SourceFiles/ui/images.h index 9a8571bd88..ce57d63ba7 100644 --- a/Telegram/SourceFiles/ui/images.h +++ b/Telegram/SourceFiles/ui/images.h @@ -279,181 +279,33 @@ inline bool operator!=( return !(a == b); } -class DelayedStorageImage; - -class HistoryItem; -class Image { +class Image; +class ImagePtr { public: - Image(const QString &file, QByteArray format = QByteArray()); - Image(const QByteArray &filecontent, QByteArray format = QByteArray()); - Image(const QPixmap &pixmap, QByteArray format = QByteArray()); - Image(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap); + ImagePtr(); + ImagePtr(const QString &file, QByteArray format = QByteArray()); + ImagePtr(const QString &url, QSize box); + ImagePtr(const QString &url, int width, int height); + ImagePtr(const QByteArray &filecontent, QByteArray format = QByteArray()); + ImagePtr(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap); + ImagePtr(const QPixmap &pixmap, QByteArray format); + ImagePtr(const StorageImageLocation &location, int32 size = 0); + ImagePtr(const StorageImageLocation &location, const QByteArray &bytes); + ImagePtr(const MTPWebDocument &location); + ImagePtr(const MTPWebDocument &location, QSize box); + ImagePtr(const WebFileLocation &location, int width, int height, int size = 0); + ImagePtr(const WebFileLocation &location, QSize box, int size = 0); + ImagePtr(const GeoPointLocation &location); + ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def = ImagePtr()); + ImagePtr(int32 width, int32 height); - virtual void automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) { - } - virtual void automaticLoadSettingsChanged() { - } + Image *operator->() const; + Image *get() const; - virtual bool loaded() const { - return true; - } - virtual bool loading() const { - return false; - } - virtual bool displayLoading() const { - return false; - } - virtual void cancel() { - } - virtual float64 progress() const { - return 1; - } - virtual int32 loadOffset() const { - return 0; - } - - const QPixmap &pix( - Data::FileOrigin origin, - int32 w = 0, - int32 h = 0) const; - const QPixmap &pixRounded( - Data::FileOrigin origin, - int32 w = 0, - int32 h = 0, - ImageRoundRadius radius = ImageRoundRadius::None, - RectParts corners = RectPart::AllCorners) const; - const QPixmap &pixBlurred( - Data::FileOrigin origin, - int32 w = 0, - int32 h = 0) const; - const QPixmap &pixColored( - Data::FileOrigin origin, - style::color add, - int32 w = 0, - int32 h = 0) const; - const QPixmap &pixBlurredColored( - Data::FileOrigin origin, - style::color add, - int32 w = 0, - int32 h = 0) const; - const QPixmap &pixSingle( - Data::FileOrigin origin, - int32 w, - int32 h, - int32 outerw, - int32 outerh, - ImageRoundRadius radius, - RectParts corners = RectPart::AllCorners, - const style::color *colored = nullptr) const; - const QPixmap &pixBlurredSingle( - Data::FileOrigin origin, - int32 w, - int32 h, - int32 outerw, - int32 outerh, - ImageRoundRadius radius, - RectParts corners = RectPart::AllCorners) const; - const QPixmap &pixCircled( - Data::FileOrigin origin, - int32 w = 0, - int32 h = 0) const; - const QPixmap &pixBlurredCircled( - Data::FileOrigin origin, - int32 w = 0, - int32 h = 0) const; - QPixmap pixNoCache( - Data::FileOrigin origin, - int w = 0, - int h = 0, - Images::Options options = 0, - int outerw = -1, - int outerh = -1, - const style::color *colored = nullptr) const; - QPixmap pixColoredNoCache( - Data::FileOrigin origin, - style::color add, - int32 w = 0, - int32 h = 0, - bool smooth = false) const; - QPixmap pixBlurredColoredNoCache( - Data::FileOrigin origin, - style::color add, - int32 w, - int32 h = 0) const; - - int32 width() const { - return qMax(countWidth(), 1); - } - - int32 height() const { - return qMax(countHeight(), 1); - } - - virtual void load( - Data::FileOrigin origin, - bool loadFirst = false, - bool prior = true) { - } - - virtual void loadEvenCancelled( - Data::FileOrigin origin, - bool loadFirst = false, - bool prior = true) { - } - - virtual const StorageImageLocation &location() const { - return StorageImageLocation::Null; - } - virtual std::optional cacheKey() const; - - bool isNull() const; - - void forget() const; - - QByteArray savedFormat() const { - return _format; - } - QByteArray savedData() const { - return _saved; - } - - virtual DelayedStorageImage *toDelayedStorageImage() { - return 0; - } - virtual const DelayedStorageImage *toDelayedStorageImage() const { - return 0; - } - - virtual ~Image(); - -protected: - Image(QByteArray format = "PNG") : _format(format) { - } - - void restore() const; - virtual void checkload() const { - } - void invalidateSizeCache() const; - - virtual int32 countWidth() const { - restore(); - return _data.width(); - } - - virtual int32 countHeight() const { - restore(); - return _data.height(); - } - - mutable QByteArray _saved, _format; - mutable bool _forgot = false; - mutable QPixmap _data; + explicit operator bool() const; private: - using Sizes = QMap; - mutable Sizes _sizesCache; + not_null _data; }; @@ -485,291 +337,6 @@ inline StorageKey storageKey(const GeoPointLocation &location) { (uint64(location.width) << 32) | uint64(location.height)); } -class RemoteImage : public Image { -public: - void automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) override; // auto load photo - void automaticLoadSettingsChanged() override; - - bool loaded() const override; - bool loading() const override { - return amLoading(); - } - bool displayLoading() const override; - void cancel() override; - float64 progress() const override; - int32 loadOffset() const override; - - void setImageBytes( - const QByteArray &bytes, - const QByteArray &format = QByteArray()); - - void load( - Data::FileOrigin origin, - bool loadFirst = false, - bool prior = true) override; - void loadEvenCancelled( - Data::FileOrigin origin, - bool loadFirst = false, - bool prior = true) override; - - ~RemoteImage(); - -protected: - // If after loading the image we need to shrink it to fit into a - // specific size, you can return this size here. - virtual QSize shrinkBox() const { - return QSize(); - } - virtual void setInformation(int32 size, int32 width, int32 height) = 0; - virtual FileLoader *createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) = 0; - - void checkload() const override { - doCheckload(); - } - void loadLocal(); - -private: - mutable FileLoader *_loader = nullptr; - bool amLoading() const; - void doCheckload() const; - - void destroyLoaderDelayed(FileLoader *newValue = nullptr) const; - -}; - -class StorageImage : public RemoteImage { -public: - explicit StorageImage(const StorageImageLocation &location, int32 size = 0); - StorageImage(const StorageImageLocation &location, const QByteArray &bytes); - - const StorageImageLocation &location() const override { - return _location; - } - std::optional cacheKey() const override; - void refreshFileReference(const QByteArray &data) { - _location.refreshFileReference(data); - } - -protected: - void setInformation(int32 size, int32 width, int32 height) override; - FileLoader *createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) override; - - int32 countWidth() const override; - int32 countHeight() const override; - - StorageImageLocation _location; - int32 _size; - -}; - -class WebFileImage : public RemoteImage { -public: - WebFileImage(const WebFileLocation &location, QSize box, int size = 0); - WebFileImage( - const WebFileLocation &location, - int width, - int height, - int size = 0); - - std::optional cacheKey() const override; - -protected: - void setInformation(int size, int width, int height) override; - FileLoader *createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) override; - - QSize shrinkBox() const override { - return _box; - } - - int countWidth() const override; - int countHeight() const override; - - WebFileLocation _location; - QSize _box; - int _width = 0; - int _height = 0; - int _size = 0; - -}; - -class GeoPointImage : public RemoteImage { -public: - GeoPointImage(const GeoPointLocation &location); - - std::optional cacheKey() const override; - -protected: - void setInformation(int size, int width, int height) override; - FileLoader *createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) override; - - int countWidth() const override; - int countHeight() const override; - - GeoPointLocation _location; - int _size = 0; - -}; - -class DelayedStorageImage : public StorageImage { -public: - DelayedStorageImage(); - DelayedStorageImage(int32 w, int32 h); - //DelayedStorageImage(QByteArray &bytes); - - void setStorageLocation( - Data::FileOrigin origin, - const StorageImageLocation location); - - virtual DelayedStorageImage *toDelayedStorageImage() override { - return this; - } - virtual const DelayedStorageImage *toDelayedStorageImage() const override { - return this; - } - - void automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) override; // auto load photo - void automaticLoadSettingsChanged() override; - - bool loading() const override { - return _location.isNull() ? _loadRequested : StorageImage::loading(); - } - bool displayLoading() const override; - void cancel() override; - - void load( - Data::FileOrigin origin, - bool loadFirst = false, - bool prior = true) override; - void loadEvenCancelled( - Data::FileOrigin origin, - bool loadFirst = false, - bool prior = true) override; - -private: - bool _loadRequested, _loadCancelled, _loadFromCloud; - -}; - -class WebImage : public RemoteImage { -public: - // If !box.isEmpty() then resize the image to fit in this box. - WebImage(const QString &url, QSize box = QSize()); - WebImage(const QString &url, int width, int height); - - void setSize(int width, int height); - - std::optional cacheKey() const override; - -protected: - QSize shrinkBox() const override { - return _box; - } - void setInformation(int32 size, int32 width, int32 height) override; - FileLoader *createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) override; - - int32 countWidth() const override; - int32 countHeight() const override; - -private: - QString _url; - QSize _box; - int32 _size, _width, _height; - -}; - -namespace internal { - -Image *getImage(const QString &file, QByteArray format); -Image *getImage(const QString &url, QSize box); -Image *getImage(const QString &url, int width, int height); -Image *getImage(const QByteArray &filecontent, QByteArray format); -Image *getImage(const QPixmap &pixmap, QByteArray format); -Image *getImage( - const QByteArray &filecontent, - QByteArray format, - const QPixmap &pixmap); -Image *getImage(int32 width, int32 height); -StorageImage *getImage(const StorageImageLocation &location, int size = 0); -StorageImage *getImage( // photoCachedSize - const StorageImageLocation &location, - const QByteArray &bytes); -Image *getImage(const MTPWebDocument &location); -Image *getImage(const MTPWebDocument &location, QSize box); -WebFileImage *getImage( - const WebFileLocation &location, - int width, - int height, - int size = 0); -WebFileImage *getImage( - const WebFileLocation &location, - QSize box, - int size = 0); -GeoPointImage *getImage( - const GeoPointLocation &location); - -} // namespace internal - -class ImagePtr : public ManagedPtr { -public: - ImagePtr(); - ImagePtr(const QString &file, QByteArray format = QByteArray()) : Parent(internal::getImage(file, format)) { - } - ImagePtr(const QString &url, QSize box) : Parent(internal::getImage(url, box)) { - } - ImagePtr(const QString &url, int width, int height) : Parent(internal::getImage(url, width, height)) { - } - ImagePtr(const QByteArray &filecontent, QByteArray format = QByteArray()) : Parent(internal::getImage(filecontent, format)) { - } - ImagePtr(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap) : Parent(internal::getImage(filecontent, format, pixmap)) { - } - ImagePtr(const QPixmap &pixmap, QByteArray format) : Parent(internal::getImage(pixmap, format)) { - } - ImagePtr(const StorageImageLocation &location, int32 size = 0) : Parent(internal::getImage(location, size)) { - } - ImagePtr(const StorageImageLocation &location, const QByteArray &bytes) : Parent(internal::getImage(location, bytes)) { - } - ImagePtr(const MTPWebDocument &location) : Parent(internal::getImage(location)) { - } - ImagePtr(const MTPWebDocument &location, QSize box) : Parent(internal::getImage(location, box)) { - } - ImagePtr(const WebFileLocation &location, int width, int height, int size = 0) - : Parent(internal::getImage(location, width, height, size)) { - } - ImagePtr(const WebFileLocation &location, QSize box, int size = 0) - : Parent(internal::getImage(location, box, size)) { - } - ImagePtr(const GeoPointLocation &location) - : Parent(internal::getImage(location)) { - } - ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def = ImagePtr()); - ImagePtr(int32 width, int32 height) : Parent(internal::getImage(width, height)) { - } - - explicit operator bool() const { - return (_data != nullptr) && !_data->isNull(); - } - -}; - inline QSize shrinkToKeepAspect(int32 width, int32 height, int32 towidth, int32 toheight) { int32 w = qMax(width, 1), h = qMax(height, 1); if (w * toheight > h * towidth) { @@ -782,10 +349,6 @@ inline QSize shrinkToKeepAspect(int32 width, int32 height, int32 towidth, int32 return QSize(qMax(w, 1), qMax(h, 1)); } -void clearStorageImages(); -void clearAllImages(); -int64 imageCacheSize(); - class PsFileBookmark; class ReadAccessEnabler { public: diff --git a/Telegram/SourceFiles/window/layer_widget.cpp b/Telegram/SourceFiles/window/layer_widget.cpp index d5aa95c1e5..7e86282ac6 100644 --- a/Telegram/SourceFiles/window/layer_widget.cpp +++ b/Telegram/SourceFiles/window/layer_widget.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_chat_helpers.h" #include "ui/widgets/shadow.h" #include "ui/emoji_config.h" +#include "ui/image.h" #include "window/window_main_menu.h" #include "auth_session.h" #include "chat_helpers/stickers.h" diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index cbc79de27a..ec6c961456 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -672,6 +672,8 @@ <(src_loc)/ui/focus_persister.h <(src_loc)/ui/grouped_layout.cpp <(src_loc)/ui/grouped_layout.h +<(src_loc)/ui/image.cpp +<(src_loc)/ui/image.h <(src_loc)/ui/images.cpp <(src_loc)/ui/images.h <(src_loc)/ui/resize_area.h