diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp index 808f3d7b9d..93e291f615 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp @@ -279,7 +279,7 @@ bool ServiceCheck::checkRippleStartPosition(QPoint position) const { }); } -AdminLog::OwnedItem GenerateTextItem( +[[nodiscard]] AdminLog::OwnedItem GenerateTextItem( not_null delegate, not_null history, const QString &text, @@ -308,7 +308,7 @@ AdminLog::OwnedItem GenerateTextItem( return AdminLog::OwnedItem(delegate, item); } -QImage PrepareScaledNonPattern( +[[nodiscard]] QImage PrepareScaledNonPattern( const QImage &image, Images::Option blur) { const auto size = st::boxWideWidth; @@ -331,7 +331,7 @@ QImage PrepareScaledNonPattern( size); } -QImage PrepareScaledFromFull( +[[nodiscard]] QImage PrepareScaledFromFull( const QImage &image, bool isPattern, const std::vector &background, @@ -350,6 +350,12 @@ QImage PrepareScaledFromFull( QImage::Format_ARGB32_Premultiplied); } +[[nodiscard]] QImage BlackImage(QSize size) { + auto result = QImage(size, QImage::Format_ARGB32_Premultiplied); + result.fill(Qt::black); + return result; +} + } // namespace BackgroundPreviewBox::BackgroundPreviewBox( @@ -385,10 +391,14 @@ void BackgroundPreviewBox::generateBackground() { if (_paper.backgroundColors().empty()) { return; } - _generated = Ui::PixmapFromImage(Data::GenerateWallPaper( - QSize(st::boxWideWidth, st::boxWideWidth) * cIntRetinaFactor(), - _paper.backgroundColors(), - _paper.gradientRotation())); + const auto size = QSize(st::boxWideWidth, st::boxWideWidth) + * cIntRetinaFactor(); + _generated = Ui::PixmapFromImage((_paper.patternOpacity() >= 0.) + ? Data::GenerateWallPaper( + size, + _paper.backgroundColors(), + _paper.gradientRotation()) + : BlackImage(size)); _generated.setDevicePixelRatio(cRetinaFactor()); } diff --git a/Telegram/SourceFiles/data/data_wall_paper.cpp b/Telegram/SourceFiles/data/data_wall_paper.cpp index 02b1defab3..b37a01fe9c 100644 --- a/Telegram/SourceFiles/data/data_wall_paper.cpp +++ b/Telegram/SourceFiles/data/data_wall_paper.cpp @@ -369,7 +369,7 @@ WallPaper WallPaper::withUrlParams( if (const auto string = params.value("intensity"); !string.isEmpty()) { auto ok = false; const auto intensity = string.toInt(&ok); - if (ok && base::in_range(intensity, 0, 101)) { + if (ok && base::in_range(intensity, -100, 101)) { result._intensity = intensity; } } @@ -604,7 +604,7 @@ std::optional WallPaper::FromSerialized( } if (stream.status() != QDataStream::Ok) { return std::nullopt; - } else if (intensity < 0 || intensity > 100) { + } else if (intensity < -100 || intensity > 100) { return std::nullopt; } auto result = WallPaper(id); @@ -710,18 +710,27 @@ QImage GenerateWallPaper( auto result = bg.empty() ? Images::GenerateGradient(size, { DefaultBackgroundColor() }) : Images::GenerateGradient(size, bg, gradientRotation); - if (bg.size() > 1) { + if (bg.size() > 1 && (!drawPattern || patternOpacity >= 0.)) { result = Images::DitherImage(std::move(result)); } if (drawPattern) { auto p = QPainter(&result); - p.setCompositionMode(QPainter::CompositionMode_SoftLight); - p.setOpacity(patternOpacity); + if (patternOpacity >= 0.) { + p.setCompositionMode(QPainter::CompositionMode_SoftLight); + p.setOpacity(patternOpacity); + } else { + p.setCompositionMode(QPainter::CompositionMode_DestinationIn); + } drawPattern(p); - p.end(); + if (patternOpacity < 0. && patternOpacity > -1.) { + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + p.setOpacity(1. + patternOpacity); + p.fillRect(QRect{ QPoint(), size }, Qt::black); + } } - return result; + return std::move(result).convertToFormat( + QImage::Format_ARGB32_Premultiplied); } QImage PreparePatternImage( diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 4195a3265a..7c60a09667 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -553,7 +553,10 @@ void BackgroundRow::radialAnimationCallback(crl::time now) { void BackgroundRow::updateImage() { const auto size = st::settingsBackgroundThumb; const auto fullsize = size * cIntRetinaFactor(); - QImage back(fullsize, fullsize, QImage::Format_ARGB32_Premultiplied); + + // We use Format_RGB32 so that DestinationIn shows black, not transparent. + // Then we'll convert to Format_ARGB32_Premultiplied for round corners. + auto back = QImage(fullsize, fullsize, QImage::Format_RGB32); back.setDevicePixelRatio(cRetinaFactor()); { Painter p(&back); @@ -568,8 +571,13 @@ void BackgroundRow::updateImage() { if (!gradient.isNull()) { auto hq = PainterHighQualityEnabler(p); p.drawImage(QRect(0, 0, size, size), gradient); - p.setCompositionMode(QPainter::CompositionMode_SoftLight); - p.setOpacity(patternOpacity); + if (patternOpacity >= 0.) { + p.setCompositionMode(QPainter::CompositionMode_SoftLight); + p.setOpacity(patternOpacity); + } else { + p.setCompositionMode( + QPainter::CompositionMode_DestinationIn); + } } const auto &prepared = background->prepared(); if (!prepared.isNull()) { @@ -587,8 +595,18 @@ void BackgroundRow::updateImage() { prepared, QRect(sx, sy, s, s)); } + if (!gradient.isNull() + && !prepared.isNull() + && patternOpacity < 0. + && patternOpacity > -1.) { + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + p.setOpacity(patternOpacity); + p.fillRect(QRect(0, 0, size, size), Qt::black); + } } } + back = std::move(back).convertToFormat( + QImage::Format_ARGB32_Premultiplied); Images::prepareRound(back, ImageRoundRadius::Small); _background = Ui::PixmapFromImage(std::move(back)); _background.setDevicePixelRatio(cRetinaFactor()); diff --git a/Telegram/SourceFiles/window/section_widget.cpp b/Telegram/SourceFiles/window/section_widget.cpp index 0d577fd4b8..6ad9781b5b 100644 --- a/Telegram/SourceFiles/window/section_widget.cpp +++ b/Telegram/SourceFiles/window/section_widget.cpp @@ -140,7 +140,7 @@ void SectionWidget::PaintBackground( }; const auto hasNow = !state.now.pixmap.isNull(); const auto goodNow = hasNow && (state.now.area == fill); - const auto useCache = goodNow || (hasNow && !gradient.isNull()); + const auto useCache = goodNow || !gradient.isNull(); if (useCache) { if (state.shown < 1. && !gradient.isNull()) { paintCache(state.was); @@ -156,22 +156,11 @@ void SectionWidget::PaintBackground( const auto rects = Window::Theme::ComputeBackgroundRects( fill, prepared.size()); - if (!gradient.isNull()) { - p.drawImage(rects.to, gradient); - p.setCompositionMode(QPainter::CompositionMode_SoftLight); - p.setOpacity(patternOpacity); - } auto to = rects.to; to.moveTop(to.top() + fromy); p.drawImage(to, prepared, rects.from); return; } - if (!gradient.isNull()) { - const auto hq = PainterHighQualityEnabler(p); - p.drawImage(QRect(QPoint(0, fromy), fill), gradient); - p.setCompositionMode(QPainter::CompositionMode_SoftLight); - p.setOpacity(patternOpacity); - } if (!prepared.isNull()) { const auto &tiled = background->preparedForTiled(); auto left = clip.left(); diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index bad64a8c82..b144bf337d 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -73,8 +73,9 @@ std::optional CalculateImageMonoColor(const QImage &image) { } [[nodiscard]] bool GoodImageFormatAndSize(const QImage &image) { - return (image.format() == QImage::Format_ARGB32_Premultiplied) - && !image.size().isEmpty(); + return !image.size().isEmpty() + && (image.format() == QImage::Format_ARGB32_Premultiplied + || image.format() == QImage::Format_RGB32); } QByteArray readThemeContent(const QString &path) { @@ -901,9 +902,19 @@ QImage ChatBackground::createCurrentImage() const { result.setDevicePixelRatio(1.); { auto p = QPainter(&result); - p.setCompositionMode(QPainter::CompositionMode_SoftLight); - p.setOpacity(paper().patternOpacity()); + const auto patternOpacity = paper().patternOpacity(); + if (patternOpacity >= 0.) { + p.setCompositionMode(QPainter::CompositionMode_SoftLight); + p.setOpacity(patternOpacity); + } else { + p.setCompositionMode(QPainter::CompositionMode_DestinationIn); + } p.drawImage(QRect(QPoint(), _prepared.size()), _prepared); + if (patternOpacity < 0. && patternOpacity > -1.) { + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + p.setOpacity(1. + patternOpacity); + p.fillRect(QRect(QPoint(), _prepared.size()), Qt::black); + } } return result; } @@ -1439,7 +1450,8 @@ QString EditingPalettePath() { } QColor CountAverageColor(const QImage &image) { - Expects(image.format() == QImage::Format_ARGB32_Premultiplied); + Expects(image.format() == QImage::Format_ARGB32_Premultiplied + || image.format() == QImage::Format_RGB32); uint64 components[3] = { 0 }; const auto w = image.width(); diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 6c076d9693..678350a5a9 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -97,8 +97,13 @@ constexpr auto kBackgroundFadeDuration = crl::time(200); if (!request.prepared.isNull()) { QPainter p(&result); if (!gradient.isNull()) { - p.setCompositionMode(QPainter::CompositionMode_SoftLight); - p.setOpacity(request.patternOpacity); + if (request.patternOpacity >= 0.) { + p.setCompositionMode(QPainter::CompositionMode_SoftLight); + p.setOpacity(request.patternOpacity); + } else { + p.setCompositionMode( + QPainter::CompositionMode_DestinationIn); + } } const auto &tiled = request.preparedForTiled; const auto w = tiled.width() / cRetinaFactor(); @@ -112,9 +117,17 @@ constexpr auto kBackgroundFadeDuration = crl::time(200); p.drawImage(QPointF(i * w, j * h), tiled); } } + if (!gradient.isNull() + && request.patternOpacity < 0. + && request.patternOpacity > -1.) { + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + p.setOpacity(1. + request.patternOpacity); + p.fillRect(QRect(QPoint(), request.area), Qt::black); + } } return { - .image = std::move(result), + .image = std::move(result).convertToFormat( + QImage::Format_ARGB32_Premultiplied), .gradient = gradient, .area = request.area, }; @@ -136,13 +149,23 @@ constexpr auto kBackgroundFadeDuration = crl::time(200); result.setDevicePixelRatio(cRetinaFactor()); if (!gradient.isNull()) { QPainter p(&result); - p.setCompositionMode(QPainter::CompositionMode_SoftLight); - p.setOpacity(request.patternOpacity); + if (request.patternOpacity >= 0.) { + p.setCompositionMode(QPainter::CompositionMode_SoftLight); + p.setOpacity(request.patternOpacity); + } else { + p.setCompositionMode(QPainter::CompositionMode_DestinationIn); + } p.drawImage(QRect(QPoint(), rects.to.size()), image); + if (request.patternOpacity < 0. && request.patternOpacity > -1.) { + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + p.setOpacity(1. + request.patternOpacity); + p.fillRect(QRect(QPoint(), rects.to.size()), Qt::black); + } } image = QImage(); return { - .image = std::move(result), + .image = std::move(result).convertToFormat( + QImage::Format_ARGB32_Premultiplied), .gradient = gradient, .area = request.area, .x = rects.to.x(), @@ -1424,7 +1447,13 @@ void SessionController::openDocument( const BackgroundState &SessionController::backgroundState(QSize area) { _backgroundState.shown = _backgroundFade.value(1.); - if (_backgroundState.now.area != area) { + if (_backgroundState.now.pixmap.isNull() + && !Window::Theme::Background()->gradientForFill().isNull()) { + // We don't support direct painting of patterned gradients. + // So we need to sync-generate cache image here. + setCachedBackground(CacheBackground(currentCacheRequest(area))); + _cacheBackgroundTimer.cancel(); + } else if (_backgroundState.now.area != area) { if (_willCacheForArea != area || (!_cacheBackgroundTimer.isActive() && !_backgroundCachingRequest)) { diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 669767855a..6b320a99da 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 669767855a197976183684d25b2753899ee95e5c +Subproject commit 6b320a99da1d1a4430c8168998f62e1e5ec919ab