diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index d581900b5..699544acd 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -7,14 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "mainwidget.h" -#include -#include -#include #include "api/api_updates.h" #include "data/data_photo.h" #include "data/data_document.h" #include "data/data_document_media.h" #include "data/data_document_resolver.h" +#include "data/data_wall_paper.h" #include "data/data_web_page.h" #include "data/data_game.h" #include "data/data_peer_values.h" @@ -122,9 +120,6 @@ namespace { // Send channel views each second. constexpr auto kSendViewsTimeout = crl::time(1000); -// Cache background scaled image after 3s. -constexpr auto kCacheBackgroundTimeout = 3000; - } // namespace enum StackItemType { @@ -239,7 +234,6 @@ MainWidget::MainWidget( , _dialogs(this, _controller) , _history(this, _controller) , _playerPlaylist(this, _controller) -, _cacheBackgroundTimer([=] { cacheBackground(); }) , _viewsIncrementTimer([=] { viewsIncrement(); }) , _changelogs(Core::Changelogs::Create(&controller->session())) { setupConnectingWidget(); @@ -345,15 +339,6 @@ MainWidget::MainWidget( QCoreApplication::instance()->installEventFilter(this); - using Update = Window::Theme::BackgroundUpdate; - Window::Theme::Background()->updates( - ) | rpl::start_with_next([=](const Update &update) { - if (update.type == Update::Type::New - || update.type == Update::Type::Changed) { - clearCachedBackground(); - } - }, lifetime()); - subscribe(Media::Player::instance()->playerWidgetOver(), [this](bool over) { if (over) { if (_playerPlaylist->isHidden()) { @@ -780,76 +765,6 @@ bool MainWidget::selectingPeer() const { return _hider ? true : false; } -void MainWidget::cacheBackground() { - const auto background = Window::Theme::Background(); - if (background->colorForFill()) { - return; - } - const auto gradient = background->gradientForFill(); - const auto patternOpacity = background->paper().patternOpacity(); - const auto &bg = background->pixmap(); - if (background->tile() || bg.isNull()) { - auto result = gradient.isNull() - ? QImage( - _willCacheFor.size() * cIntRetinaFactor(), - QImage::Format_ARGB32_Premultiplied) - : gradient.scaled( - _willCacheFor.size() * cIntRetinaFactor(), - Qt::IgnoreAspectRatio, - Qt::SmoothTransformation); - result.setDevicePixelRatio(cRetinaFactor()); - if (!bg.isNull()) { - QPainter p(&result); - if (!gradient.isNull()) { - p.setCompositionMode(QPainter::CompositionMode_SoftLight); - p.setOpacity(patternOpacity); - } - const auto &tiled = background->pixmapForTiled(); - auto w = tiled.width() / cRetinaFactor(); - auto h = tiled.height() / cRetinaFactor(); - auto sx = 0; - auto sy = 0; - auto cx = qCeil(_willCacheFor.width() / w); - auto cy = qCeil(_willCacheFor.height() / h); - for (int i = sx; i < cx; ++i) { - for (int j = sy; j < cy; ++j) { - p.drawPixmap(QPointF(i * w, j * h), tiled); - } - } - } - _cachedX = 0; - _cachedY = 0; - _cachedBackground = Ui::PixmapFromImage(std::move(result)); - } else { - QRect to, from; - Window::Theme::ComputeBackgroundRects(_willCacheFor, bg.size(), to, from); - auto image = bg.toImage().copy(from).scaled( - to.width() * cIntRetinaFactor(), - to.height() * cIntRetinaFactor(), - Qt::IgnoreAspectRatio, - Qt::SmoothTransformation); - auto result = gradient.isNull() - ? std::move(image) - : gradient.scaled( - image.size(), - Qt::IgnoreAspectRatio, - Qt::SmoothTransformation); - result.setDevicePixelRatio(cRetinaFactor()); - if (!gradient.isNull()) { - QPainter p(&result); - p.setCompositionMode(QPainter::CompositionMode_SoftLight); - p.setOpacity(patternOpacity); - p.drawImage(QRect(QPoint(), to.size()), image); - } - image = QImage(); - _cachedX = to.x(); - _cachedY = to.y(); - _cachedBackground = Ui::PixmapFromImage(std::move(result)); - _cachedBackground.setDevicePixelRatio(cRetinaFactor()); - } - _cachedFor = _willCacheFor; -} - crl::time MainWidget::highlightStartTime(not_null item) const { return _history->highlightStartTime(item); } @@ -1179,25 +1094,6 @@ void MainWidget::dialogsCancelled() { _history->activate(); } -void MainWidget::clearCachedBackground() { - _cachedBackground = QPixmap(); - _cacheBackgroundTimer.cancel(); - update(); -} - -QPixmap MainWidget::cachedBackground(const QRect &forRect, int &x, int &y) { - if (!_cachedBackground.isNull() && forRect == _cachedFor) { - x = _cachedX; - y = _cachedY; - return _cachedBackground; - } - if (_willCacheFor != forRect || !_cacheBackgroundTimer.isActive()) { - _willCacheFor = forRect; - _cacheBackgroundTimer.callOnce(kCacheBackgroundTimeout); - } - return QPixmap(); -} - void MainWidget::setChatBackground( const Data::WallPaper &background, QImage &&image) { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 3f18b3263..3fd19aed5 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -185,8 +185,6 @@ public: void searchMessages(const QString &query, Dialogs::Key inChat); - QPixmap cachedBackground(const QRect &forRect, int &x, int &y); - void setChatBackground( const Data::WallPaper &background, QImage &&image = QImage()); @@ -299,9 +297,6 @@ private: void showAll(); void clearHider(not_null instance); - void cacheBackground(); - void clearCachedBackground(); - [[nodiscard]] auto floatPlayerDelegate() -> not_null; not_null floatPlayerWidget() override; @@ -389,12 +384,6 @@ private: int _exportTopBarHeight = 0; int _contentScrollAddToY = 0; - QPixmap _cachedBackground; - QRect _cachedFor, _willCacheFor; - int _cachedX = 0; - int _cachedY = 0; - base::Timer _cacheBackgroundTimer; - PhotoData *_deletingPhoto = nullptr; base::flat_map, base::flat_set> _viewsIncremented; diff --git a/Telegram/SourceFiles/window/section_widget.cpp b/Telegram/SourceFiles/window/section_widget.cpp index 41a7cc9f4..3dcddd795 100644 --- a/Telegram/SourceFiles/window/section_widget.cpp +++ b/Telegram/SourceFiles/window/section_widget.cpp @@ -97,51 +97,50 @@ void SectionWidget::PaintBackground( return; } auto fromy = controller->content()->backgroundFromY(); - auto x = 0, y = 0; - auto cached = controller->content()->cachedBackground(fill, x, y); - if (cached.isNull()) { - const auto gradient = background->gradientForFill(); - const auto patternOpacity = background->paper().patternOpacity(); - const auto &bg = background->pixmap(); - if (background->tile() || bg.isNull()) { - if (!gradient.isNull()) { - auto hq = PainterHighQualityEnabler(p); - p.drawImage(fill, gradient); - p.setCompositionMode(QPainter::CompositionMode_SoftLight); - p.setOpacity(patternOpacity); - } - if (!bg.isNull()) { - auto &tiled = background->pixmapForTiled(); - auto left = clip.left(); - auto top = clip.top(); - auto right = clip.left() + clip.width(); - auto bottom = clip.top() + clip.height(); - auto w = tiled.width() / cRetinaFactor(); - auto h = tiled.height() / cRetinaFactor(); - auto sx = qFloor(left / w); - auto sy = qFloor((top - fromy) / h); - auto cx = qCeil(right / w); - auto cy = qCeil((bottom - fromy) / h); - for (auto i = sx; i < cx; ++i) { - for (auto j = sy; j < cy; ++j) { - p.drawPixmap(QPointF(i * w, fromy + j * h), tiled); - } - } - } - } else { - auto hq = PainterHighQualityEnabler(p); - QRect to, from; - Window::Theme::ComputeBackgroundRects(fill, bg.size(), to, from); - if (!gradient.isNull()) { - p.drawImage(to, gradient); - p.setCompositionMode(QPainter::CompositionMode_SoftLight); - p.setOpacity(patternOpacity); - } - to.moveTop(to.top() + fromy); - p.drawPixmap(to, bg, from); + auto cached = controller->cachedBackground(fill); + if (!cached.pixmap.isNull()) { + p.drawPixmap(cached.x, fromy + cached.y, cached.pixmap); + return; + } + const auto gradient = background->gradientForFill(); + const auto patternOpacity = background->paper().patternOpacity(); + const auto &bg = background->pixmap(); + if (!bg.isNull() && !background->tile()) { + auto hq = PainterHighQualityEnabler(p); + QRect to, from; + Window::Theme::ComputeBackgroundRects(fill, bg.size(), to, from); + if (!gradient.isNull()) { + p.drawImage(to, gradient); + p.setCompositionMode(QPainter::CompositionMode_SoftLight); + p.setOpacity(patternOpacity); + } + to.moveTop(to.top() + fromy); + p.drawPixmap(to, bg, from); + return; + } + if (!gradient.isNull()) { + auto hq = PainterHighQualityEnabler(p); + p.drawImage(fill, gradient); + p.setCompositionMode(QPainter::CompositionMode_SoftLight); + p.setOpacity(patternOpacity); + } + if (!bg.isNull()) { + auto &tiled = background->pixmapForTiled(); + auto left = clip.left(); + auto top = clip.top(); + auto right = clip.left() + clip.width(); + auto bottom = clip.top() + clip.height(); + auto w = tiled.width() / cRetinaFactor(); + auto h = tiled.height() / cRetinaFactor(); + auto sx = qFloor(left / w); + auto sy = qFloor((top - fromy) / h); + auto cx = qCeil(right / w); + auto cy = qCeil((bottom - fromy) / h); + for (auto i = sx; i < cx; ++i) { + for (auto j = sy; j < cy; ++j) { + p.drawPixmap(QPointF(i * w, fromy + j * h), tiled); + } } - } else { - p.drawPixmap(x, fromy + y, cached); } } diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 6f256c8b4..d51c20db6 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -59,6 +59,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "support/support_helper.h" #include "storage/file_upload.h" #include "facades.h" +#include "window/themes/window_theme.h" #include "styles/style_window.h" #include "styles/style_dialogs.h" #include "styles/style_layers.h" // st::boxLabel @@ -68,6 +69,9 @@ namespace { constexpr auto kMaxChatEntryHistorySize = 50; +// Cache background scaled image after 3s. +constexpr auto kCacheBackgroundTimeout = 3000; + } // namespace void ActivateWindow(not_null controller) { @@ -463,7 +467,8 @@ SessionController::SessionController( std::make_unique( _window->widget(), this)) -, _invitePeekTimer([=] { checkInvitePeek(); }) { +, _invitePeekTimer([=] { checkInvitePeek(); }) +, _cacheBackgroundTimer([=] { cacheBackground(); }) { init(); if (Media::Player::instance()->pauseGifByRoundVideo()) { @@ -506,6 +511,15 @@ SessionController::SessionController( })); }, _lifetime); + using Update = Window::Theme::BackgroundUpdate; + Window::Theme::Background()->updates( + ) | rpl::start_with_next([=](const Update &update) { + if (update.type == Update::Type::New + || update.type == Update::Type::Changed) { + clearCachedBackground(); + } + }, lifetime()); + session->addWindow(this); } @@ -1303,6 +1317,99 @@ void SessionController::openDocument( session().data().message(contextId)); } +CachedBackground SessionController::cachedBackground(QRect area) { + if (!_cachedBackground.pixmap.isNull() + && area == _cachedBackground.area) { + return _cachedBackground; + } + if (_willCacheForArea != area || !_cacheBackgroundTimer.isActive()) { + _willCacheForArea = area; + _cacheBackgroundTimer.callOnce(kCacheBackgroundTimeout); + } + return {}; +} + +void SessionController::cacheBackground() { + const auto background = Window::Theme::Background(); + if (background->colorForFill()) { + return; + } + const auto gradient = background->gradientForFill(); + const auto patternOpacity = background->paper().patternOpacity(); + const auto &bg = background->pixmap(); + if (background->tile() || bg.isNull()) { + auto result = gradient.isNull() + ? QImage( + _willCacheForArea.size() * cIntRetinaFactor(), + QImage::Format_ARGB32_Premultiplied) + : gradient.scaled( + _willCacheForArea.size() * cIntRetinaFactor(), + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + result.setDevicePixelRatio(cRetinaFactor()); + if (!bg.isNull()) { + QPainter p(&result); + if (!gradient.isNull()) { + p.setCompositionMode(QPainter::CompositionMode_SoftLight); + p.setOpacity(patternOpacity); + } + const auto &tiled = background->pixmapForTiled(); + auto w = tiled.width() / cRetinaFactor(); + auto h = tiled.height() / cRetinaFactor(); + auto sx = 0; + auto sy = 0; + auto cx = qCeil(_willCacheForArea.width() / w); + auto cy = qCeil(_willCacheForArea.height() / h); + for (int i = sx; i < cx; ++i) { + for (int j = sy; j < cy; ++j) { + p.drawPixmap(QPointF(i * w, j * h), tiled); + } + } + } + _cachedBackground = CachedBackground{ + .pixmap = Ui::PixmapFromImage(std::move(result)), + .area = _willCacheForArea, + }; + } else { + QRect to, from; + Window::Theme::ComputeBackgroundRects( + _willCacheForArea, + bg.size(), + to, + from); + auto image = bg.toImage().copy(from).scaled( + to.width() * cIntRetinaFactor(), + to.height() * cIntRetinaFactor(), + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + auto result = gradient.isNull() + ? std::move(image) + : gradient.scaled( + image.size(), + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + result.setDevicePixelRatio(cRetinaFactor()); + if (!gradient.isNull()) { + QPainter p(&result); + p.setCompositionMode(QPainter::CompositionMode_SoftLight); + p.setOpacity(patternOpacity); + p.drawImage(QRect(QPoint(), to.size()), image); + } + image = QImage(); + _cachedBackground = { + .pixmap = Ui::PixmapFromImage(std::move(result)), + .area = _willCacheForArea, + .x = to.x(), + .y = to.y(), + }; + } +} + +void SessionController::clearCachedBackground() { + _cachedBackground = {}; + _cacheBackgroundTimer.cancel(); +} + SessionController::~SessionController() { resetFakeUnreadWhileOpened(); } diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index a187c0c9f..a9acc15dd 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -56,6 +56,13 @@ class SectionMemento; class Controller; class FiltersMenu; +struct CachedBackground { + QPixmap pixmap; + QRect area; + int x = 0; + int y = 0; +}; + enum class GifPauseReason { Any = 0, InlineResults = (1 << 0), @@ -393,6 +400,8 @@ public: void toggleFiltersMenu(bool enabled); [[nodiscard]] rpl::producer<> filtersMenuChanged() const; + [[nodiscard]] CachedBackground cachedBackground(QRect area); + rpl::lifetime &lifetime() { return _lifetime; } @@ -422,6 +431,9 @@ private: void checkInvitePeek(); + void cacheBackground(); + void clearCachedBackground(); + const not_null _window; std::unique_ptr _passportForm; @@ -449,6 +461,10 @@ private: rpl::event_stream<> _filtersMenuChanged; + CachedBackground _cachedBackground; + QRect _willCacheForArea; + base::Timer _cacheBackgroundTimer; + rpl::lifetime _lifetime; };