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( return std::make_tuple(
data.id() == current, data.id() == current,
night ? data.isDark() : !data.isDark(), night ? data.isDark() : !data.isDark(),
Data::IsDefaultWallPaper(data),
!data.isDefault() && !Data::IsLegacy1DefaultWallPaper(data), !data.isDefault() && !Data::IsLegacy1DefaultWallPaper(data),
Data::IsLegacy2DefaultWallPaper(data), Data::IsLegacy2DefaultWallPaper(data),
Data::IsLegacy1DefaultWallPaper(data)); Data::IsLegacy1DefaultWallPaper(data));

View File

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

View File

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

View File

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

View File

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

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