Dither default background to avoid color banding.

This commit is contained in:
John Preston 2021-07-23 15:16:22 +03:00
parent 8f478b86ee
commit 565877630f
6 changed files with 100 additions and 14 deletions

View File

@ -239,6 +239,7 @@ void BackgroundBox::Inner::sortPapers() {
return std::make_tuple(
data.id() == current,
night ? data.isDark() : !data.isDark(),
Data::IsDefaultWallPaper(data),
!data.isDefault() && !Data::IsLegacy1DefaultWallPaper(data),
Data::IsLegacy2DefaultWallPaper(data),
Data::IsLegacy1DefaultWallPaper(data));

View File

@ -622,6 +622,7 @@ void BackgroundPreviewBox::paintDate(Painter &p) {
if (!date || !_serviceBg) {
return;
}
auto hq = PainterHighQualityEnabler(p);
const auto text = date->text;
const auto bubbleHeight = st::msgServicePadding.top() + st::msgServiceFont->height + st::msgServicePadding.bottom();
const auto bubbleTop = st::msgServiceMargin.top();
@ -758,7 +759,7 @@ void BackgroundPreviewBox::checkLoadedDocument() {
};
_generating = Data::ReadImageAsync(
_media.get(),
Window::Theme::ProcessBackgroundImage,
Window::Theme::PreprocessBackgroundImage,
generateCallback);
}

View File

@ -1265,7 +1265,7 @@ void MainWidget::checkChatBackground() {
};
_background->generating = Data::ReadImageAsync(
media.get(),
Window::Theme::ProcessBackgroundImage,
Window::Theme::PreprocessBackgroundImage,
generateCallback);
}

View File

@ -431,12 +431,89 @@ bool InitializeFromSaved(Saved &&saved) {
return true;
}
QImage validateBackgroundImage(QImage image) {
[[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) {
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
image = std::move(image).convertToFormat(
QImage::Format_ARGB32_Premultiplied);
}
image.setDevicePixelRatio(cRetinaFactor());
if (Data::IsDefaultWallPaper(paper)
|| Data::details::IsTestingDefaultWallPaper(paper)) {
return DitherImage(std::move(image));
}
return image;
}
@ -530,7 +607,9 @@ ChatBackground::ChatBackground() : _adjustableColors({
}
void ChatBackground::setThemeData(QImage &&themeImage, bool themeTile) {
_themeImage = validateBackgroundImage(std::move(themeImage));
_themeImage = PostprocessBackgroundImage(
std::move(themeImage),
Data::ThemeWallPaper());
_themeTile = themeTile;
}
@ -627,8 +706,12 @@ void ChatBackground::checkUploadWallPaper() {
});
}
QImage ChatBackground::postprocessBackgroundImage(QImage image) {
return PostprocessBackgroundImage(std::move(image), _paper);
}
void ChatBackground::set(const Data::WallPaper &paper, QImage image) {
image = ProcessBackgroundImage(std::move(image));
image = PreprocessBackgroundImage(std::move(image));
const auto needResetAdjustable = Data::IsDefaultWallPaper(paper)
&& !Data::IsDefaultWallPaper(_paper)
@ -657,7 +740,7 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) {
image.load(qsl(":/gui/art/background.jpg"));
setPaper(Data::details::TestingDefaultWallPaper());
}
image = validateBackgroundImage(std::move(image));
image = postprocessBackgroundImage(std::move(image));
setPreparedImage(image, image);
} else {
if (Data::IsLegacy1DefaultWallPaper(_paper)) {
@ -681,7 +764,7 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) {
: image));
if (const auto fill = _paper.backgroundColor()) {
if (_paper.isPattern() && !image.isNull()) {
auto prepared = validateBackgroundImage(
auto prepared = postprocessBackgroundImage(
Data::PreparePatternImage(
image,
*fill,
@ -697,7 +780,7 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) {
}
}
} else {
image = validateBackgroundImage(std::move(image));
image = postprocessBackgroundImage(std::move(image));
setPreparedImage(image, image);
}
}
@ -1015,7 +1098,7 @@ void ChatBackground::keepApplied(const Object &object, bool write) {
}
} else if (Data::details::IsTestingThemeWallPaper(_paper)) {
setPaper(Data::ThemeWallPaper());
_themeImage = validateBackgroundImage(base::duplicate(_original));
_themeImage = postprocessBackgroundImage(base::duplicate(_original));
_themeTile = tile();
if (write) {
writeNewBackgroundSettings();
@ -1403,7 +1486,7 @@ QColor AdjustedColor(QColor original, QColor background) {
).toRgb();
}
QImage ProcessBackgroundImage(QImage image) {
QImage PreprocessBackgroundImage(QImage image) {
constexpr auto kMaxSize = 2960;
if (image.format() != QImage::Format_ARGB32_Premultiplied) {

View File

@ -98,9 +98,9 @@ bool LoadFromContent(
const QByteArray &content,
not_null<Instance*> out,
Cached *outCache);
QColor CountAverageColor(const QImage &image);
QColor AdjustedColor(QColor original, QColor background);
QImage ProcessBackgroundImage(QImage image);
[[nodiscard]] QColor CountAverageColor(const QImage &image);
[[nodiscard]] QColor AdjustedColor(QColor original, QColor background);
[[nodiscard]] QImage PreprocessBackgroundImage(QImage image);
struct BackgroundUpdate {
enum class Type {
@ -215,6 +215,7 @@ private:
[[nodiscard]] bool isNonDefaultThemeOrBackground();
[[nodiscard]] bool isNonDefaultBackground();
void checkUploadWallPaper();
[[nodiscard]] QImage postprocessBackgroundImage(QImage image);
friend bool IsNightMode();
friend void SetNightModeValue(bool nightMode);

@ -1 +1 @@
Subproject commit 12429c198d9176e109af282d28198ef1bd143971
Subproject commit 1ed242718ee2dd6363ff738acb9151e649500ed7