diff --git a/Telegram/SourceFiles/window/section_widget.cpp b/Telegram/SourceFiles/window/section_widget.cpp index 3dcddd795..305fd38eb 100644 --- a/Telegram/SourceFiles/window/section_widget.cpp +++ b/Telegram/SourceFiles/window/section_widget.cpp @@ -91,36 +91,61 @@ void SectionWidget::PaintBackground( Painter p(widget); const auto background = Window::Theme::Background(); - auto fill = QRect(0, 0, widget->width(), controller->content()->height()); + const auto fullHeight = controller->content()->height(); if (const auto color = background->colorForFill()) { - p.fillRect(fill, *color); - return; - } - auto fromy = controller->content()->backgroundFromY(); - auto cached = controller->cachedBackground(fill); - if (!cached.pixmap.isNull()) { - p.drawPixmap(cached.x, fromy + cached.y, cached.pixmap); + p.fillRect(clip, *color); return; } const auto gradient = background->gradientForFill(); + const auto fill = QSize(widget->width(), fullHeight); + auto fromy = controller->content()->backgroundFromY(); + auto cached = controller->cachedBackground(fill); + const auto goodCache = (cached.area == fill); + const auto useCached = goodCache || !gradient.isNull(); + if (!cached.pixmap.isNull()) { + const auto to = QRect( + QPoint(cached.x, fromy + cached.y), + cached.pixmap.size() / cIntRetinaFactor()); + if (goodCache) { + p.drawPixmap(to, cached.pixmap); + } else { + const auto sx = fill.width() / float64(cached.area.width()); + const auto sy = fill.height() / float64(cached.area.height()); + const auto round = [](float64 value) -> int { + return (value >= 0.) + ? int(std::ceil(value)) + : int(std::floor(value)); + }; + const auto sto = QPoint(round(to.x() * sx), round(to.y() * sy)); + p.drawPixmap( + sto.x(), + sto.y(), + round((to.x() + to.width()) * sx) - sto.x(), + round((to.y() + to.height()) * sy) - sto.y(), + cached.pixmap); + } + return; + } 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); + const auto rects = Window::Theme::ComputeBackgroundRects( + fill, + bg.size()); if (!gradient.isNull()) { - p.drawImage(to, gradient); + p.drawImage(rects.to, gradient); p.setCompositionMode(QPainter::CompositionMode_SoftLight); p.setOpacity(patternOpacity); } + auto to = rects.to; to.moveTop(to.top() + fromy); - p.drawPixmap(to, bg, from); + p.drawPixmap(to, bg, rects.from); return; } if (!gradient.isNull()) { auto hq = PainterHighQualityEnabler(p); - p.drawImage(fill, gradient); + p.drawImage(QRect(QPoint(0, fromy), fill), gradient); p.setCompositionMode(QPainter::CompositionMode_SoftLight); p.setOpacity(patternOpacity); } diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 10babdca8..820cb00d1 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -1494,27 +1494,31 @@ QImage PreprocessBackgroundImage(QImage image) { return image; } -void ComputeBackgroundRects(QRect wholeFill, QSize imageSize, QRect &to, QRect &from) { - if (uint64(imageSize.width()) * wholeFill.height() > uint64(imageSize.height()) * wholeFill.width()) { - float64 pxsize = wholeFill.height() / float64(imageSize.height()); - int takewidth = qCeil(wholeFill.width() / pxsize); +BackgroundRects ComputeBackgroundRects(QSize fillSize, QSize imageSize) { + if (uint64(imageSize.width()) * fillSize.height() > uint64(imageSize.height()) * fillSize.width()) { + float64 pxsize = fillSize.height() / float64(imageSize.height()); + int takewidth = qCeil(fillSize.width() / pxsize); if (takewidth > imageSize.width()) { takewidth = imageSize.width(); } else if ((imageSize.width() % 2) != (takewidth % 2)) { ++takewidth; } - to = QRect(int((wholeFill.width() - takewidth * pxsize) / 2.), 0, qCeil(takewidth * pxsize), wholeFill.height()); - from = QRect((imageSize.width() - takewidth) / 2, 0, takewidth, imageSize.height()); + return { + .from = QRect((imageSize.width() - takewidth) / 2, 0, takewidth, imageSize.height()), + .to = QRect(int((fillSize.width() - takewidth * pxsize) / 2.), 0, qCeil(takewidth * pxsize), fillSize.height()), + }; } else { - float64 pxsize = wholeFill.width() / float64(imageSize.width()); - int takeheight = qCeil(wholeFill.height() / pxsize); + float64 pxsize = fillSize.width() / float64(imageSize.width()); + int takeheight = qCeil(fillSize.height() / pxsize); if (takeheight > imageSize.height()) { takeheight = imageSize.height(); } else if ((imageSize.height() % 2) != (takeheight % 2)) { ++takeheight; } - to = QRect(0, int((wholeFill.height() - takeheight * pxsize) / 2.), wholeFill.width(), qCeil(takeheight * pxsize)); - from = QRect(0, (imageSize.height() - takeheight) / 2, imageSize.width(), takeheight); + return { + .from = QRect(0, (imageSize.height() - takeheight) / 2, imageSize.width(), takeheight), + .to = QRect(0, int((fillSize.height() - takeheight * pxsize) / 2.), fillSize.width(), qCeil(takeheight * pxsize)), + }; } } diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index 9758d2fce..30dc7594f 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -270,7 +270,13 @@ private: [[nodiscard]] ChatBackground *Background(); -void ComputeBackgroundRects(QRect wholeFill, QSize imageSize, QRect &to, QRect &from); +struct BackgroundRects { + QRect from; + QRect to; +}; +[[nodiscard]] BackgroundRects ComputeBackgroundRects( + QSize fillSize, + QSize imageSize); bool ReadPaletteValues(const QByteArray &content, Fn callback); diff --git a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp index 6a95f356b..44193beb9 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp @@ -455,12 +455,12 @@ void Generator::paintHistoryBackground() { } else { PainterHighQualityEnabler hq(*_p); - auto fill = QRect(_topBar.x(), _topBar.y(), _topBar.width(), _body.height()); - QRect to, from; - ComputeBackgroundRects(fill, background.size(), to, from); + auto fill = QSize(_topBar.width(), _body.height()); + const auto rects = ComputeBackgroundRects(fill, background.size()); + auto to = rects.to; to.moveTop(to.top() + fromy); to.moveTopLeft(to.topLeft() + _history.topLeft()); - _p->drawImage(to, background, from); + _p->drawImage(to, background, rects.from); } _p->setClipping(false); } diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index e2a8b031e..af30cda65 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -984,24 +984,24 @@ void MainMenu::refreshMenu() { } void MainMenu::refreshBackground() { - const auto fill = QRect(0, 0, st::mainMenuWidth, st::mainMenuCoverHeight); + const auto fill = QSize(st::mainMenuWidth, st::mainMenuCoverHeight); const auto intensityText = IntensityOfColor(st::mainMenuCoverFg->c); const auto background = Window::Theme::Background(); const auto &paper = background->paper(); const auto &pixmap = background->pixmap(); QRect to, from; - Window::Theme::ComputeBackgroundRects(fill, pixmap.size(), to, from); + const auto rects = Window::Theme::ComputeBackgroundRects(fill, pixmap.size()); auto backgroundImage = !paper.backgroundColors().empty() ? Data::GenerateWallPaper( - fill.size() * cIntRetinaFactor(), + fill * cIntRetinaFactor(), paper.backgroundColors(), paper.gradientRotation(), paper.patternOpacity(), [&](QPainter &p) { p.drawPixmap(to, pixmap, from); }) : QImage( - fill.size() * cIntRetinaFactor(), + fill * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); QPainter p(&backgroundImage); @@ -1019,7 +1019,7 @@ void MainMenu::refreshBackground() { // Solid color. if (const auto color = background->colorForFill()) { const auto intensity = IntensityOfColor(*color); - p.fillRect(fill, *color); + p.fillRect(QRect(QPoint(), fill), *color); if (std::abs(intensity - intensityText) < kMinDiffIntensity) { drawShadow(p); } diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index b2abd7a81..2513f8508 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -1318,17 +1318,15 @@ void SessionController::openDocument( session().data().message(contextId)); } -CachedBackground SessionController::cachedBackground(QRect area) { - if (!_cachedBackground.pixmap.isNull() - && area == _cachedBackground.area) { - return _cachedBackground; +CachedBackground SessionController::cachedBackground(QSize area) { + if (_cachedBackground.area != area) { + if (_willCacheForArea != area || !_cacheBackgroundTimer.isActive()) { + _willCacheForArea = area; + _lastAreaChangeTime = crl::now(); + _cacheBackgroundTimer.callOnce(kCacheBackgroundFastTimeout); + } } - if (_willCacheForArea != area || !_cacheBackgroundTimer.isActive()) { - _willCacheForArea = area; - _lastAreaChangeTime = crl::now(); - _cacheBackgroundTimer.callOnce(kCacheBackgroundFastTimeout); - } - return {}; + return _cachedBackground; } void SessionController::cacheBackground() { @@ -1348,10 +1346,10 @@ void SessionController::cacheBackground() { if (background->tile() || bg.isNull()) { auto result = gradient.isNull() ? QImage( - _willCacheForArea.size() * cIntRetinaFactor(), + _willCacheForArea * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied) : gradient.scaled( - _willCacheForArea.size() * cIntRetinaFactor(), + _willCacheForArea * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); result.setDevicePixelRatio(cRetinaFactor()); @@ -1379,15 +1377,12 @@ void SessionController::cacheBackground() { .area = _willCacheForArea, }; } else { - QRect to, from; - Window::Theme::ComputeBackgroundRects( + const auto rects = Window::Theme::ComputeBackgroundRects( _willCacheForArea, - bg.size(), - to, - from); - auto image = bg.toImage().copy(from).scaled( - to.width() * cIntRetinaFactor(), - to.height() * cIntRetinaFactor(), + bg.size()); + auto image = bg.toImage().copy(rects.from).scaled( + rects.to.width() * cIntRetinaFactor(), + rects.to.height() * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); auto result = gradient.isNull() @@ -1401,16 +1396,19 @@ void SessionController::cacheBackground() { QPainter p(&result); p.setCompositionMode(QPainter::CompositionMode_SoftLight); p.setOpacity(patternOpacity); - p.drawImage(QRect(QPoint(), to.size()), image); + p.drawImage(QRect(QPoint(), rects.to.size()), image); } image = QImage(); _cachedBackground = { .pixmap = Ui::PixmapFromImage(std::move(result)), .area = _willCacheForArea, - .x = to.x(), - .y = to.y(), + .x = rects.to.x(), + .y = rects.to.y(), }; } + if (!gradient.isNull()) { + content()->update(); + } } void SessionController::clearCachedBackground() { diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 957877cd4..a758584b0 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -58,7 +58,7 @@ class FiltersMenu; struct CachedBackground { QPixmap pixmap; - QRect area; + QSize area; int x = 0; int y = 0; }; @@ -400,7 +400,7 @@ public: void toggleFiltersMenu(bool enabled); [[nodiscard]] rpl::producer<> filtersMenuChanged() const; - [[nodiscard]] CachedBackground cachedBackground(QRect area); + [[nodiscard]] CachedBackground cachedBackground(QSize area); rpl::lifetime &lifetime() { return _lifetime; @@ -462,7 +462,7 @@ private: rpl::event_stream<> _filtersMenuChanged; CachedBackground _cachedBackground; - QRect _willCacheForArea; + QSize _willCacheForArea; crl::time _lastAreaChangeTime = 0; base::Timer _cacheBackgroundTimer;