Don't blend SoftLight patterns in realtime.

This commit is contained in:
John Preston 2021-08-13 20:19:06 +03:00
parent 2b46f87d7b
commit b9a9520ef5
7 changed files with 92 additions and 59 deletions

View file

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

View file

@ -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)),
};
}
}

View file

@ -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<bool(QLatin1String name, QLatin1String value)> callback);

View file

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

View file

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

View file

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

View file

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