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<History*> 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<QColor> &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());
}

View File

@ -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> 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(

View File

@ -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());

View File

@ -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();

View File

@ -73,8 +73,9 @@ std::optional<QColor> 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();

View File

@ -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)) {

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