Add support for linear gradients without patterns.

This commit is contained in:
John Preston 2021-08-13 15:58:38 +03:00
parent ba7e976fe2
commit 436d7b9d82
8 changed files with 87 additions and 121 deletions

View file

@ -326,8 +326,18 @@ void BackgroundBox::Inner::validatePaperThumbnail(
paper.dataMedia = document->createMediaView(); paper.dataMedia = document->createMediaView();
paper.dataMedia->thumbnailWanted(paper.data.fileOrigin()); paper.dataMedia->thumbnailWanted(paper.data.fileOrigin());
} }
} if (!paper.dataMedia->thumbnail()) {
if (!paper.dataMedia || !paper.dataMedia->thumbnail()) { return;
}
} else if (!paper.data.backgroundColors().empty()) {
paper.thumbnail = Ui::PixmapFromImage(
Data::GenerateWallPaper(
st::backgroundSize * cIntRetinaFactor(),
paper.data.backgroundColors(),
paper.data.gradientRotation()));
paper.thumbnail.setDevicePixelRatio(cRetinaFactor());
return;
} else {
return; return;
} }
} }

View file

@ -419,12 +419,24 @@ BackgroundPreviewBox::BackgroundPreviewBox(
if (_media) { if (_media) {
_media->thumbnailWanted(_paper.fileOrigin()); _media->thumbnailWanted(_paper.fileOrigin());
} }
generateBackground();
_controller->session().downloaderTaskFinished( _controller->session().downloaderTaskFinished(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
update(); update();
}, lifetime()); }, lifetime());
} }
void BackgroundPreviewBox::generateBackground() {
if (_paper.backgroundColors().empty()) {
return;
}
_generated = Ui::PixmapFromImage(Data::GenerateWallPaper(
QSize(st::boxWideWidth, st::boxWideWidth) * cIntRetinaFactor(),
_paper.backgroundColors(),
_paper.gradientRotation()));
_generated.setDevicePixelRatio(cRetinaFactor());
}
not_null<HistoryView::ElementDelegate*> BackgroundPreviewBox::delegate() { not_null<HistoryView::ElementDelegate*> BackgroundPreviewBox::delegate() {
return static_cast<HistoryView::ElementDelegate*>(this); return static_cast<HistoryView::ElementDelegate*>(this);
} }
@ -525,21 +537,23 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
const auto ms = crl::now(); const auto ms = crl::now();
const auto color = _paper.backgroundColor(); if (_scaled.isNull()) {
if (color) { setScaledFromThumb();
p.fillRect(e->rect(), *color);
} }
if (!color || _paper.isPattern()) { if (!_generated.isNull()
if (!_scaled.isNull() || setScaledFromThumb()) { && (_scaled.isNull()
paintImage(p); || (_fadeOutThumbnail.isNull() && _fadeIn.animating()))) {
paintRadial(p); p.drawPixmap(0, 0, _generated);
} else if (!color) { }
p.fillRect(e->rect(), st::boxBg); if (!_scaled.isNull()) {
return; paintImage(p);
} else { paintRadial(p);
// Progress of pattern loading. } else if (_generated.isNull()) {
paintRadial(p); p.fillRect(e->rect(), st::boxBg);
} return;
} else {
// Progress of pattern loading.
paintRadial(p);
} }
paintTexts(p, ms); paintTexts(p, ms);
} }
@ -655,7 +669,10 @@ void BackgroundPreviewBox::radialAnimationCallback(crl::time now) {
checkLoadedDocument(); checkLoadedDocument();
} }
bool BackgroundPreviewBox::setScaledFromThumb() { void BackgroundPreviewBox::setScaledFromThumb() {
if (!_scaled.isNull()) {
return;
}
const auto localThumbnail = _paper.localThumbnail(); const auto localThumbnail = _paper.localThumbnail();
const auto thumbnail = localThumbnail const auto thumbnail = localThumbnail
? localThumbnail ? localThumbnail
@ -663,9 +680,9 @@ bool BackgroundPreviewBox::setScaledFromThumb() {
? _media->thumbnail() ? _media->thumbnail()
: nullptr; : nullptr;
if (!thumbnail) { if (!thumbnail) {
return false; return;
} else if (_paper.isPattern() && _paper.document() != nullptr) { } else if (_paper.isPattern() && _paper.document() != nullptr) {
return false; return;
} }
auto scaled = PrepareScaledFromFull( auto scaled = PrepareScaledFromFull(
thumbnail->original(), thumbnail->original(),
@ -679,7 +696,6 @@ bool BackgroundPreviewBox::setScaledFromThumb() {
Data::PrepareBlurredBackground(thumbnail->original()), Data::PrepareBlurredBackground(thumbnail->original()),
Images::Option(0)); Images::Option(0));
setScaledFromImage(std::move(scaled), std::move(blurred)); setScaledFromImage(std::move(scaled), std::move(blurred));
return true;
} }
void BackgroundPreviewBox::setScaledFromImage( void BackgroundPreviewBox::setScaledFromImage(

View file

@ -56,8 +56,9 @@ private:
void radialAnimationCallback(crl::time now); void radialAnimationCallback(crl::time now);
QRect radialRect() const; QRect radialRect() const;
void generateBackground();
void checkLoadedDocument(); void checkLoadedDocument();
bool setScaledFromThumb(); void setScaledFromThumb();
void setScaledFromImage(QImage &&image, QImage &&blurred); void setScaledFromImage(QImage &&image, QImage &&blurred);
void updateServiceBg(const std::vector<QColor> &bg); void updateServiceBg(const std::vector<QColor> &bg);
std::vector<QColor> patternBackgroundColors() const; std::vector<QColor> patternBackgroundColors() const;
@ -76,7 +77,7 @@ private:
Data::WallPaper _paper; Data::WallPaper _paper;
std::shared_ptr<Data::DocumentMedia> _media; std::shared_ptr<Data::DocumentMedia> _media;
QImage _full; QImage _full;
QPixmap _scaled, _blurred, _fadeOutThumbnail; QPixmap _generated, _scaled, _blurred, _fadeOutThumbnail;
Ui::Animations::Simple _fadeIn; Ui::Animations::Simple _fadeIn;
Ui::RadialAnimation _radial; Ui::RadialAnimation _radial;
base::binary_guard _generating; base::binary_guard _generating;

View file

@ -755,26 +755,31 @@ bool IsCloudWallPaper(const WallPaper &paper) {
p.fillRect(0, 0, width, height, QBrush(std::move(gradient))); p.fillRect(0, 0, width, height, QBrush(std::move(gradient)));
p.end(); p.end();
return image; return Images::DitherImage(std::move(image));
} }
QImage PreparePatternImage( QImage GenerateWallPaper(
QSize size, QSize size,
Fn<void(QPainter&)> drawPattern,
const std::vector<QColor> &bg, const std::vector<QColor> &bg,
int rotation, int gradientRotation,
float64 opacity) { float64 patternOpacity,
Fn<void(QPainter&)> drawPattern) {
auto result = QImage(size, QImage::Format_ARGB32_Premultiplied); auto result = QImage(size, QImage::Format_ARGB32_Premultiplied);
if (bg.size() < 2) { if (bg.size() < 2) {
result.fill(bg.empty() ? DefaultBackgroundColor() : bg.front()); result.fill(bg.empty() ? DefaultBackgroundColor() : bg.front());
} else { } else {
result = FillDitheredGradient(std::move(result), bg, rotation); result = FillDitheredGradient(
std::move(result),
bg,
gradientRotation);
}
if (drawPattern) {
auto p = QPainter(&result);
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(patternOpacity);
drawPattern(p);
p.end();
} }
auto p = QPainter(&result);
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(opacity);
drawPattern(p);
p.end();
return result; return result;
} }
@ -782,11 +787,16 @@ QImage PreparePatternImage(
QImage PreparePatternImage( QImage PreparePatternImage(
QImage pattern, QImage pattern,
const std::vector<QColor> &bg, const std::vector<QColor> &bg,
int rotation, int gradientRotation,
float64 opacity) { float64 patternOpacity) {
auto result = PreparePatternImage(pattern.size(), [&](QPainter &p) { auto result = GenerateWallPaper(
p.drawImage(QRect(QPoint(), pattern.size()), pattern); pattern.size(),
}, bg, rotation, opacity); bg,
gradientRotation,
patternOpacity,
[&](QPainter &p) {
p.drawImage(QRect(QPoint(), pattern.size()), pattern);
});
pattern = QImage(); pattern = QImage();
return result; return result;

View file

@ -122,17 +122,17 @@ private:
[[nodiscard]] bool IsDefaultWallPaper(const WallPaper &paper); [[nodiscard]] bool IsDefaultWallPaper(const WallPaper &paper);
[[nodiscard]] bool IsCloudWallPaper(const WallPaper &paper); [[nodiscard]] bool IsCloudWallPaper(const WallPaper &paper);
[[nodiscard]] QImage PreparePatternImage( [[nodiscard]] QImage GenerateWallPaper(
QSize size, QSize size,
Fn<void(QPainter&)> drawPattern,
const std::vector<QColor> &bg, const std::vector<QColor> &bg,
int rotation, int gradientRotation,
float64 opacity); float64 patternOpacity = 1.,
Fn<void(QPainter&)> drawPattern = nullptr);
[[nodiscard]] QImage PreparePatternImage( [[nodiscard]] QImage PreparePatternImage(
QImage pattern, QImage pattern,
const std::vector<QColor> &bg, const std::vector<QColor> &bg,
int rotation, int gradientRotation,
float64 opacity); float64 patternOpacity);
[[nodiscard]] QImage PrepareBlurredBackground(QImage image); [[nodiscard]] QImage PrepareBlurredBackground(QImage image);
[[nodiscard]] QImage GenerateDitheredGradient( [[nodiscard]] QImage GenerateDitheredGradient(
const std::vector<QColor> &colors, const std::vector<QColor> &colors,

View file

@ -436,77 +436,6 @@ bool InitializeFromSaved(Saved &&saved) {
return true; return true;
} }
[[nodiscard]] QImage DitherImage(QImage image) {
Expects(image.bytesPerLine() == image.width() * 4);
const auto width = image.width();
const auto height = image.height();
if (width < 16 || height < 16) {
return image;
}
const auto area = width * height;
const auto shifts = std::make_unique<uchar[]>(area);
memset_rand(shifts.get(), area);
// shiftx = int(shift & 0x0F) - 8; shifty = int(shift >> 4) - 8;
// Clamp shifts close to edges.
for (auto y = 0; y != 8; ++y) {
const auto min = 8 - y;
const auto shifted = (min << 4);
auto shift = shifts.get() + y * width;
for (const auto till = shift + width; shift != till; ++shift) {
if ((*shift >> 4) < min) {
*shift = shifted | (*shift & 0x0F);
}
}
}
for (auto y = height - 7; y != height; ++y) {
const auto max = 8 + (height - y - 1);
const auto shifted = (max << 4);
auto shift = shifts.get() + y * width;
for (const auto till = shift + width; shift != till; ++shift) {
if ((*shift >> 4) > max) {
*shift = shifted | (*shift & 0x0F);
}
}
}
for (auto shift = shifts.get(), ytill = shift + area
; shift != ytill
; shift += width - 8) {
for (const auto till = shift + 8; shift != till; ++shift) {
const auto min = (till - shift);
if ((*shift & 0x0F) < min) {
*shift = (*shift & 0xF0) | min;
}
}
}
for (auto shift = shifts.get(), ytill = shift + area; shift != ytill;) {
shift += width - 7;
for (const auto till = shift + 7; shift != till; ++shift) {
const auto max = 8 + (till - shift - 1);
if ((*shift & 0x0F) > max) {
*shift = (*shift & 0xF0) | max;
}
}
}
auto result = image;
result.detach();
const auto src = reinterpret_cast<const uint32*>(image.constBits());
const auto dst = reinterpret_cast<uint32*>(result.bits());
for (auto index = 0; index != area; ++index) {
const auto shift = shifts[index];
const auto shiftx = int(shift & 0x0F) - 8;
const auto shifty = int(shift >> 4) - 8;
dst[index] = src[index + (shifty * width) + shiftx];
}
return result;
}
[[nodiscard]] QImage PostprocessBackgroundImage( [[nodiscard]] QImage PostprocessBackgroundImage(
QImage image, QImage image,
const Data::WallPaper &paper) { const Data::WallPaper &paper) {
@ -517,7 +446,7 @@ bool InitializeFromSaved(Saved &&saved) {
image.setDevicePixelRatio(cRetinaFactor()); image.setDevicePixelRatio(cRetinaFactor());
if (Data::IsDefaultWallPaper(paper) if (Data::IsDefaultWallPaper(paper)
|| Data::details::IsTestingDefaultWallPaper(paper)) { || Data::details::IsTestingDefaultWallPaper(paper)) {
return DitherImage(std::move(image)); return Images::DitherImage(std::move(image));
} }
return image; return image;
} }

View file

@ -993,13 +993,13 @@ void MainMenu::refreshBackground() {
QRect to, from; QRect to, from;
Window::Theme::ComputeBackgroundRects(fill, pixmap.size(), to, from); Window::Theme::ComputeBackgroundRects(fill, pixmap.size(), to, from);
auto backgroundImage = paper.isPattern() auto backgroundImage = !paper.backgroundColors().empty()
? Data::PreparePatternImage( ? Data::GenerateWallPaper(
fill.size() * cIntRetinaFactor(), fill.size() * cIntRetinaFactor(),
[&](QPainter &p) { p.drawPixmap(to, pixmap, from); },
paper.backgroundColors(), paper.backgroundColors(),
paper.gradientRotation(), paper.gradientRotation(),
paper.patternOpacity()) paper.patternOpacity(),
[&](QPainter &p) { p.drawPixmap(to, pixmap, from); })
: QImage( : QImage(
fill.size() * cIntRetinaFactor(), fill.size() * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied); QImage::Format_ARGB32_Premultiplied);

@ -1 +1 @@
Subproject commit 40f0e3c6d43a02c20a5cb189ead8559c97bbbd9b Subproject commit 3d5fcdb7ddab336ee2fadc3ceeced420e2c1f504