From 565877630f728c0be6cc5e5ba71dc4b2bf6f9757 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 23 Jul 2021 15:16:22 +0300 Subject: [PATCH] Dither default background to avoid color banding. --- Telegram/SourceFiles/boxes/background_box.cpp | 1 + .../boxes/background_preview_box.cpp | 3 +- Telegram/SourceFiles/mainwidget.cpp | 2 +- .../window/themes/window_theme.cpp | 99 +++++++++++++++++-- .../SourceFiles/window/themes/window_theme.h | 7 +- Telegram/lib_ui | 2 +- 6 files changed, 100 insertions(+), 14 deletions(-) diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp index 2ea498f5fe..dbd2a1822a 100644 --- a/Telegram/SourceFiles/boxes/background_box.cpp +++ b/Telegram/SourceFiles/boxes/background_box.cpp @@ -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)); diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp index a85eb58e8a..b7c5b3e9d3 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp @@ -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); } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index cce67eb09e..5f46adcf19 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1265,7 +1265,7 @@ void MainWidget::checkChatBackground() { }; _background->generating = Data::ReadImageAsync( media.get(), - Window::Theme::ProcessBackgroundImage, + Window::Theme::PreprocessBackgroundImage, generateCallback); } diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 38faedffe6..6dd1201ad3 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -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(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(image.constBits()); + const auto dst = reinterpret_cast(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) { diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index 598b53f1e3..c7b8ec572d 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -98,9 +98,9 @@ bool LoadFromContent( const QByteArray &content, not_null 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); diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 12429c198d..1ed242718e 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 12429c198d9176e109af282d28198ef1bd143971 +Subproject commit 1ed242718ee2dd6363ff738acb9151e649500ed7