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->thumbnailWanted(paper.data.fileOrigin());
}
}
if (!paper.dataMedia || !paper.dataMedia->thumbnail()) {
if (!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;
}
}

View file

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

View file

@ -56,8 +56,9 @@ private:
void radialAnimationCallback(crl::time now);
QRect radialRect() const;
void generateBackground();
void checkLoadedDocument();
bool setScaledFromThumb();
void setScaledFromThumb();
void setScaledFromImage(QImage &&image, QImage &&blurred);
void updateServiceBg(const std::vector<QColor> &bg);
std::vector<QColor> patternBackgroundColors() const;
@ -76,7 +77,7 @@ private:
Data::WallPaper _paper;
std::shared_ptr<Data::DocumentMedia> _media;
QImage _full;
QPixmap _scaled, _blurred, _fadeOutThumbnail;
QPixmap _generated, _scaled, _blurred, _fadeOutThumbnail;
Ui::Animations::Simple _fadeIn;
Ui::RadialAnimation _radial;
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.end();
return image;
return Images::DitherImage(std::move(image));
}
QImage PreparePatternImage(
QImage GenerateWallPaper(
QSize size,
Fn<void(QPainter&)> drawPattern,
const std::vector<QColor> &bg,
int rotation,
float64 opacity) {
int gradientRotation,
float64 patternOpacity,
Fn<void(QPainter&)> drawPattern) {
auto result = QImage(size, QImage::Format_ARGB32_Premultiplied);
if (bg.size() < 2) {
result.fill(bg.empty() ? DefaultBackgroundColor() : bg.front());
} 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;
}
@ -782,11 +787,16 @@ QImage PreparePatternImage(
QImage PreparePatternImage(
QImage pattern,
const std::vector<QColor> &bg,
int rotation,
float64 opacity) {
auto result = PreparePatternImage(pattern.size(), [&](QPainter &p) {
p.drawImage(QRect(QPoint(), pattern.size()), pattern);
}, bg, rotation, opacity);
int gradientRotation,
float64 patternOpacity) {
auto result = GenerateWallPaper(
pattern.size(),
bg,
gradientRotation,
patternOpacity,
[&](QPainter &p) {
p.drawImage(QRect(QPoint(), pattern.size()), pattern);
});
pattern = QImage();
return result;

View file

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

View file

@ -436,77 +436,6 @@ bool InitializeFromSaved(Saved &&saved) {
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(
QImage image,
const Data::WallPaper &paper) {
@ -517,7 +446,7 @@ bool InitializeFromSaved(Saved &&saved) {
image.setDevicePixelRatio(cRetinaFactor());
if (Data::IsDefaultWallPaper(paper)
|| Data::details::IsTestingDefaultWallPaper(paper)) {
return DitherImage(std::move(image));
return Images::DitherImage(std::move(image));
}
return image;
}

View file

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

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