Support patterns with negative intensity.

This commit is contained in:
John Preston 2021-08-17 17:35:10 +03:00
parent 5383ae3d96
commit 662966ba31
7 changed files with 109 additions and 42 deletions

View file

@ -279,7 +279,7 @@ bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
}); });
} }
AdminLog::OwnedItem GenerateTextItem( [[nodiscard]] AdminLog::OwnedItem GenerateTextItem(
not_null<HistoryView::ElementDelegate*> delegate, not_null<HistoryView::ElementDelegate*> delegate,
not_null<History*> history, not_null<History*> history,
const QString &text, const QString &text,
@ -308,7 +308,7 @@ AdminLog::OwnedItem GenerateTextItem(
return AdminLog::OwnedItem(delegate, item); return AdminLog::OwnedItem(delegate, item);
} }
QImage PrepareScaledNonPattern( [[nodiscard]] QImage PrepareScaledNonPattern(
const QImage &image, const QImage &image,
Images::Option blur) { Images::Option blur) {
const auto size = st::boxWideWidth; const auto size = st::boxWideWidth;
@ -331,7 +331,7 @@ QImage PrepareScaledNonPattern(
size); size);
} }
QImage PrepareScaledFromFull( [[nodiscard]] QImage PrepareScaledFromFull(
const QImage &image, const QImage &image,
bool isPattern, bool isPattern,
const std::vector<QColor> &background, const std::vector<QColor> &background,
@ -350,6 +350,12 @@ QImage PrepareScaledFromFull(
QImage::Format_ARGB32_Premultiplied); QImage::Format_ARGB32_Premultiplied);
} }
[[nodiscard]] QImage BlackImage(QSize size) {
auto result = QImage(size, QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::black);
return result;
}
} // namespace } // namespace
BackgroundPreviewBox::BackgroundPreviewBox( BackgroundPreviewBox::BackgroundPreviewBox(
@ -385,10 +391,14 @@ void BackgroundPreviewBox::generateBackground() {
if (_paper.backgroundColors().empty()) { if (_paper.backgroundColors().empty()) {
return; return;
} }
_generated = Ui::PixmapFromImage(Data::GenerateWallPaper( const auto size = QSize(st::boxWideWidth, st::boxWideWidth)
QSize(st::boxWideWidth, st::boxWideWidth) * cIntRetinaFactor(), * cIntRetinaFactor();
_paper.backgroundColors(), _generated = Ui::PixmapFromImage((_paper.patternOpacity() >= 0.)
_paper.gradientRotation())); ? Data::GenerateWallPaper(
size,
_paper.backgroundColors(),
_paper.gradientRotation())
: BlackImage(size));
_generated.setDevicePixelRatio(cRetinaFactor()); _generated.setDevicePixelRatio(cRetinaFactor());
} }

View file

@ -369,7 +369,7 @@ WallPaper WallPaper::withUrlParams(
if (const auto string = params.value("intensity"); !string.isEmpty()) { if (const auto string = params.value("intensity"); !string.isEmpty()) {
auto ok = false; auto ok = false;
const auto intensity = string.toInt(&ok); 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; result._intensity = intensity;
} }
} }
@ -604,7 +604,7 @@ std::optional<WallPaper> WallPaper::FromSerialized(
} }
if (stream.status() != QDataStream::Ok) { if (stream.status() != QDataStream::Ok) {
return std::nullopt; return std::nullopt;
} else if (intensity < 0 || intensity > 100) { } else if (intensity < -100 || intensity > 100) {
return std::nullopt; return std::nullopt;
} }
auto result = WallPaper(id); auto result = WallPaper(id);
@ -710,18 +710,27 @@ QImage GenerateWallPaper(
auto result = bg.empty() auto result = bg.empty()
? Images::GenerateGradient(size, { DefaultBackgroundColor() }) ? Images::GenerateGradient(size, { DefaultBackgroundColor() })
: Images::GenerateGradient(size, bg, gradientRotation); : Images::GenerateGradient(size, bg, gradientRotation);
if (bg.size() > 1) { if (bg.size() > 1 && (!drawPattern || patternOpacity >= 0.)) {
result = Images::DitherImage(std::move(result)); result = Images::DitherImage(std::move(result));
} }
if (drawPattern) { if (drawPattern) {
auto p = QPainter(&result); auto p = QPainter(&result);
p.setCompositionMode(QPainter::CompositionMode_SoftLight); if (patternOpacity >= 0.) {
p.setOpacity(patternOpacity); p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(patternOpacity);
} else {
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
}
drawPattern(p); 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( QImage PreparePatternImage(

View file

@ -553,7 +553,10 @@ void BackgroundRow::radialAnimationCallback(crl::time now) {
void BackgroundRow::updateImage() { void BackgroundRow::updateImage() {
const auto size = st::settingsBackgroundThumb; const auto size = st::settingsBackgroundThumb;
const auto fullsize = size * cIntRetinaFactor(); 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()); back.setDevicePixelRatio(cRetinaFactor());
{ {
Painter p(&back); Painter p(&back);
@ -568,8 +571,13 @@ void BackgroundRow::updateImage() {
if (!gradient.isNull()) { if (!gradient.isNull()) {
auto hq = PainterHighQualityEnabler(p); auto hq = PainterHighQualityEnabler(p);
p.drawImage(QRect(0, 0, size, size), gradient); p.drawImage(QRect(0, 0, size, size), gradient);
p.setCompositionMode(QPainter::CompositionMode_SoftLight); if (patternOpacity >= 0.) {
p.setOpacity(patternOpacity); p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(patternOpacity);
} else {
p.setCompositionMode(
QPainter::CompositionMode_DestinationIn);
}
} }
const auto &prepared = background->prepared(); const auto &prepared = background->prepared();
if (!prepared.isNull()) { if (!prepared.isNull()) {
@ -587,8 +595,18 @@ void BackgroundRow::updateImage() {
prepared, prepared,
QRect(sx, sy, s, s)); 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); Images::prepareRound(back, ImageRoundRadius::Small);
_background = Ui::PixmapFromImage(std::move(back)); _background = Ui::PixmapFromImage(std::move(back));
_background.setDevicePixelRatio(cRetinaFactor()); _background.setDevicePixelRatio(cRetinaFactor());

View file

@ -140,7 +140,7 @@ void SectionWidget::PaintBackground(
}; };
const auto hasNow = !state.now.pixmap.isNull(); const auto hasNow = !state.now.pixmap.isNull();
const auto goodNow = hasNow && (state.now.area == fill); const auto goodNow = hasNow && (state.now.area == fill);
const auto useCache = goodNow || (hasNow && !gradient.isNull()); const auto useCache = goodNow || !gradient.isNull();
if (useCache) { if (useCache) {
if (state.shown < 1. && !gradient.isNull()) { if (state.shown < 1. && !gradient.isNull()) {
paintCache(state.was); paintCache(state.was);
@ -156,22 +156,11 @@ void SectionWidget::PaintBackground(
const auto rects = Window::Theme::ComputeBackgroundRects( const auto rects = Window::Theme::ComputeBackgroundRects(
fill, fill,
prepared.size()); prepared.size());
if (!gradient.isNull()) {
p.drawImage(rects.to, gradient);
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(patternOpacity);
}
auto to = rects.to; auto to = rects.to;
to.moveTop(to.top() + fromy); to.moveTop(to.top() + fromy);
p.drawImage(to, prepared, rects.from); p.drawImage(to, prepared, rects.from);
return; 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()) { if (!prepared.isNull()) {
const auto &tiled = background->preparedForTiled(); const auto &tiled = background->preparedForTiled();
auto left = clip.left(); auto left = clip.left();

View file

@ -73,8 +73,9 @@ std::optional<QColor> CalculateImageMonoColor(const QImage &image) {
} }
[[nodiscard]] bool GoodImageFormatAndSize(const QImage &image) { [[nodiscard]] bool GoodImageFormatAndSize(const QImage &image) {
return (image.format() == QImage::Format_ARGB32_Premultiplied) return !image.size().isEmpty()
&& !image.size().isEmpty(); && (image.format() == QImage::Format_ARGB32_Premultiplied
|| image.format() == QImage::Format_RGB32);
} }
QByteArray readThemeContent(const QString &path) { QByteArray readThemeContent(const QString &path) {
@ -901,9 +902,19 @@ QImage ChatBackground::createCurrentImage() const {
result.setDevicePixelRatio(1.); result.setDevicePixelRatio(1.);
{ {
auto p = QPainter(&result); auto p = QPainter(&result);
p.setCompositionMode(QPainter::CompositionMode_SoftLight); const auto patternOpacity = paper().patternOpacity();
p.setOpacity(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); 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; return result;
} }
@ -1439,7 +1450,8 @@ QString EditingPalettePath() {
} }
QColor CountAverageColor(const QImage &image) { 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 }; uint64 components[3] = { 0 };
const auto w = image.width(); const auto w = image.width();

View file

@ -97,8 +97,13 @@ constexpr auto kBackgroundFadeDuration = crl::time(200);
if (!request.prepared.isNull()) { if (!request.prepared.isNull()) {
QPainter p(&result); QPainter p(&result);
if (!gradient.isNull()) { if (!gradient.isNull()) {
p.setCompositionMode(QPainter::CompositionMode_SoftLight); if (request.patternOpacity >= 0.) {
p.setOpacity(request.patternOpacity); p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(request.patternOpacity);
} else {
p.setCompositionMode(
QPainter::CompositionMode_DestinationIn);
}
} }
const auto &tiled = request.preparedForTiled; const auto &tiled = request.preparedForTiled;
const auto w = tiled.width() / cRetinaFactor(); const auto w = tiled.width() / cRetinaFactor();
@ -112,9 +117,17 @@ constexpr auto kBackgroundFadeDuration = crl::time(200);
p.drawImage(QPointF(i * w, j * h), tiled); 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 { return {
.image = std::move(result), .image = std::move(result).convertToFormat(
QImage::Format_ARGB32_Premultiplied),
.gradient = gradient, .gradient = gradient,
.area = request.area, .area = request.area,
}; };
@ -136,13 +149,23 @@ constexpr auto kBackgroundFadeDuration = crl::time(200);
result.setDevicePixelRatio(cRetinaFactor()); result.setDevicePixelRatio(cRetinaFactor());
if (!gradient.isNull()) { if (!gradient.isNull()) {
QPainter p(&result); QPainter p(&result);
p.setCompositionMode(QPainter::CompositionMode_SoftLight); if (request.patternOpacity >= 0.) {
p.setOpacity(request.patternOpacity); p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(request.patternOpacity);
} else {
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
}
p.drawImage(QRect(QPoint(), rects.to.size()), image); 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(); image = QImage();
return { return {
.image = std::move(result), .image = std::move(result).convertToFormat(
QImage::Format_ARGB32_Premultiplied),
.gradient = gradient, .gradient = gradient,
.area = request.area, .area = request.area,
.x = rects.to.x(), .x = rects.to.x(),
@ -1424,7 +1447,13 @@ void SessionController::openDocument(
const BackgroundState &SessionController::backgroundState(QSize area) { const BackgroundState &SessionController::backgroundState(QSize area) {
_backgroundState.shown = _backgroundFade.value(1.); _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 if (_willCacheForArea != area
|| (!_cacheBackgroundTimer.isActive() || (!_cacheBackgroundTimer.isActive()
&& !_backgroundCachingRequest)) { && !_backgroundCachingRequest)) {

@ -1 +1 @@
Subproject commit 669767855a197976183684d25b2753899ee95e5c Subproject commit 6b320a99da1d1a4430c8168998f62e1e5ec919ab