mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-04-07 10:03:02 +00:00
Support blurred backgrounds.
This commit is contained in:
parent
e7043c4d63
commit
fe21b5a502
@ -514,7 +514,10 @@ void BackgroundPreviewBox::paintImage(Painter &p) {
|
|||||||
(size - height()) / 2 * factor,
|
(size - height()) / 2 * factor,
|
||||||
size * factor,
|
size * factor,
|
||||||
height() * factor);
|
height() * factor);
|
||||||
p.drawPixmap(rect(), _scaled, from);
|
p.drawPixmap(
|
||||||
|
rect(),
|
||||||
|
(!_blurred.isNull() && _paper.isBlurred()) ? _blurred : _scaled,
|
||||||
|
from);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackgroundPreviewBox::paintRadial(Painter &p, TimeMs ms) {
|
void BackgroundPreviewBox::paintRadial(Painter &p, TimeMs ms) {
|
||||||
@ -621,9 +624,12 @@ bool BackgroundPreviewBox::setScaledFromThumb() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackgroundPreviewBox::setScaledFromImage(QImage &&image) {
|
void BackgroundPreviewBox::setScaledFromImage(
|
||||||
|
QImage &&image,
|
||||||
|
QImage &&blurred) {
|
||||||
updateServiceBg(Window::Theme::CountAverageColor(image));
|
updateServiceBg(Window::Theme::CountAverageColor(image));
|
||||||
_scaled = App::pixmapFromImageInPlace(std::move(image));
|
_scaled = App::pixmapFromImageInPlace(std::move(image));
|
||||||
|
_blurred = App::pixmapFromImageInPlace(std::move(blurred));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackgroundPreviewBox::updateServiceBg(std::optional<QColor> background) {
|
void BackgroundPreviewBox::updateServiceBg(std::optional<QColor> background) {
|
||||||
@ -655,16 +661,23 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
|||||||
guard = std::move(right)
|
guard = std::move(right)
|
||||||
]() mutable {
|
]() mutable {
|
||||||
auto scaled = PrepareScaledFromFull(image, patternBackground);
|
auto scaled = PrepareScaledFromFull(image, patternBackground);
|
||||||
|
const auto ms = getms();
|
||||||
|
auto blurred = patternBackground
|
||||||
|
? QImage()
|
||||||
|
: PrepareScaledNonPattern(
|
||||||
|
Data::PrepareBlurredBackground(image),
|
||||||
|
Images::Option(0));
|
||||||
crl::on_main([
|
crl::on_main([
|
||||||
this,
|
this,
|
||||||
image = std::move(image),
|
image = std::move(image),
|
||||||
scaled = std::move(scaled),
|
scaled = std::move(scaled),
|
||||||
|
blurred = std::move(blurred),
|
||||||
guard = std::move(guard)
|
guard = std::move(guard)
|
||||||
]() mutable {
|
]() mutable {
|
||||||
if (!guard) {
|
if (!guard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setScaledFromImage(std::move(scaled));
|
setScaledFromImage(std::move(scaled), std::move(blurred));
|
||||||
_full = std::move(image);
|
_full = std::move(image);
|
||||||
update();
|
update();
|
||||||
});
|
});
|
||||||
|
@ -67,7 +67,7 @@ private:
|
|||||||
|
|
||||||
void checkLoadedDocument();
|
void checkLoadedDocument();
|
||||||
bool setScaledFromThumb();
|
bool setScaledFromThumb();
|
||||||
void setScaledFromImage(QImage &&image);
|
void setScaledFromImage(QImage &&image, QImage &&blurred = QImage());
|
||||||
void updateServiceBg(std::optional<QColor> background);
|
void updateServiceBg(std::optional<QColor> background);
|
||||||
std::optional<QColor> patternBackgroundColor() const;
|
std::optional<QColor> patternBackgroundColor() const;
|
||||||
void paintImage(Painter &p);
|
void paintImage(Painter &p);
|
||||||
@ -79,7 +79,7 @@ private:
|
|||||||
AdminLog::OwnedItem _text2;
|
AdminLog::OwnedItem _text2;
|
||||||
Data::WallPaper _paper;
|
Data::WallPaper _paper;
|
||||||
QImage _full;
|
QImage _full;
|
||||||
QPixmap _scaled;
|
QPixmap _scaled, _blurred;
|
||||||
Ui::RadialAnimation _radial;
|
Ui::RadialAnimation _radial;
|
||||||
base::binary_guard _generating;
|
base::binary_guard _generating;
|
||||||
std::optional<QColor> _serviceBg;
|
std::optional<QColor> _serviceBg;
|
||||||
|
@ -52,12 +52,11 @@ QImage prepareBlur(QImage img) {
|
|||||||
if (img.isNull()) {
|
if (img.isNull()) {
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
auto ratio = img.devicePixelRatio();
|
const auto ratio = img.devicePixelRatio();
|
||||||
auto fmt = img.format();
|
const auto fmt = img.format();
|
||||||
if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32_Premultiplied) {
|
if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32_Premultiplied) {
|
||||||
img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
img = std::move(img).convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||||
img.setDevicePixelRatio(ratio);
|
img.setDevicePixelRatio(ratio);
|
||||||
Assert(!img.isNull());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uchar *pix = img.bits();
|
uchar *pix = img.bits();
|
||||||
@ -170,6 +169,238 @@ yi += stride;
|
|||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QImage BlurLargeImage(QImage image, int radius) {
|
||||||
|
const auto width = image.width();
|
||||||
|
const auto height = image.height();
|
||||||
|
if (width <= radius || height <= radius || radius < 1) {
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image.format() != QImage::Format_RGB32
|
||||||
|
&& image.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||||
|
image = std::move(image).convertToFormat(
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
}
|
||||||
|
const auto pixels = image.bits();
|
||||||
|
|
||||||
|
const auto widthm = width - 1;
|
||||||
|
const auto heightm = height - 1;
|
||||||
|
const auto area = width * height;
|
||||||
|
const auto div = 2 * radius + 1;
|
||||||
|
const auto radius1 = radius + 1;
|
||||||
|
const auto divsum = radius1 * radius1;
|
||||||
|
|
||||||
|
const auto dvcount = 256 * divsum;
|
||||||
|
const auto buffers = (div * 3) // stack
|
||||||
|
+ std::max(width, height) // vmin
|
||||||
|
+ area * 3 // rgb
|
||||||
|
+ dvcount; // dv
|
||||||
|
auto storage = std::vector<int>(buffers);
|
||||||
|
auto taken = 0;
|
||||||
|
const auto take = [&](int size) {
|
||||||
|
const auto result = gsl::make_span(storage).subspan(taken, size);
|
||||||
|
taken += size;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Small buffers
|
||||||
|
const auto stack = take(div * 3).data();
|
||||||
|
const auto vmin = take(std::max(width, height)).data();
|
||||||
|
|
||||||
|
// Large buffers
|
||||||
|
const auto rgb = take(area * 3).data();
|
||||||
|
const auto dvs = take(dvcount);
|
||||||
|
|
||||||
|
auto &&ints = ranges::view::ints(0);
|
||||||
|
for (auto &&[value, index] : ranges::view::zip(dvs, ints)) {
|
||||||
|
value = (index / divsum);
|
||||||
|
}
|
||||||
|
const auto dv = dvs.data();
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
auto yp = 0;
|
||||||
|
auto stackpointer = 0;
|
||||||
|
auto stackstart = 0;
|
||||||
|
auto rbs = 0;
|
||||||
|
auto yw = 0;
|
||||||
|
auto yi = 0;
|
||||||
|
auto yi3 = 0;
|
||||||
|
for (const auto y : ranges::view::ints(0, height)) {
|
||||||
|
const auto yw = y * width;
|
||||||
|
auto rinsum = 0;
|
||||||
|
auto ginsum = 0;
|
||||||
|
auto binsum = 0;
|
||||||
|
auto routsum = 0;
|
||||||
|
auto goutsum = 0;
|
||||||
|
auto boutsum = 0;
|
||||||
|
auto rsum = 0;
|
||||||
|
auto gsum = 0;
|
||||||
|
auto bsum = 0;
|
||||||
|
|
||||||
|
for (const auto i : ranges::view::ints(-radius, radius + 1)) {
|
||||||
|
const auto sir = &stack[(i + radius) * 3];
|
||||||
|
const auto offset = (yi + std::min(area, std::max(i, 0))) * 4;
|
||||||
|
sir[0] = pixels[offset];
|
||||||
|
sir[1] = pixels[offset + 1];
|
||||||
|
sir[2] = pixels[offset + 2];
|
||||||
|
|
||||||
|
rbs = radius1 - abs(i);
|
||||||
|
rsum += sir[0] * rbs;
|
||||||
|
gsum += sir[1] * rbs;
|
||||||
|
bsum += sir[2] * rbs;
|
||||||
|
if (i > 0) {
|
||||||
|
rinsum += sir[0];
|
||||||
|
ginsum += sir[1];
|
||||||
|
binsum += sir[2];
|
||||||
|
} else {
|
||||||
|
routsum += sir[0];
|
||||||
|
goutsum += sir[1];
|
||||||
|
boutsum += sir[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stackpointer = radius;
|
||||||
|
|
||||||
|
for (const auto x : ranges::view::ints(0, width)) {
|
||||||
|
rgb[yi3] = dv[rsum];
|
||||||
|
rgb[yi3 + 1] = dv[gsum];
|
||||||
|
rgb[yi3 + 2] = dv[bsum];
|
||||||
|
|
||||||
|
rsum -= routsum;
|
||||||
|
gsum -= goutsum;
|
||||||
|
bsum -= boutsum;
|
||||||
|
|
||||||
|
stackstart = stackpointer - radius + div;
|
||||||
|
const auto sir = &stack[(stackstart % div) * 3];
|
||||||
|
|
||||||
|
routsum -= sir[0];
|
||||||
|
goutsum -= sir[1];
|
||||||
|
boutsum -= sir[2];
|
||||||
|
|
||||||
|
if (y == 0) {
|
||||||
|
vmin[x] = std::min(x + radius + 1, area);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto offset = (yw + vmin[x]) * 4;
|
||||||
|
sir[0] = pixels[offset];
|
||||||
|
sir[1] = pixels[offset + 1];
|
||||||
|
sir[2] = pixels[offset + 2];
|
||||||
|
rinsum += sir[0];
|
||||||
|
ginsum += sir[1];
|
||||||
|
binsum += sir[2];
|
||||||
|
|
||||||
|
rsum += rinsum;
|
||||||
|
gsum += ginsum;
|
||||||
|
bsum += binsum;
|
||||||
|
{
|
||||||
|
stackpointer = (stackpointer + 1) % div;
|
||||||
|
const auto sir = &stack[(stackpointer % div) * 3];
|
||||||
|
|
||||||
|
routsum += sir[0];
|
||||||
|
goutsum += sir[1];
|
||||||
|
boutsum += sir[2];
|
||||||
|
|
||||||
|
rinsum -= sir[0];
|
||||||
|
ginsum -= sir[1];
|
||||||
|
binsum -= sir[2];
|
||||||
|
}
|
||||||
|
yi++;
|
||||||
|
yi3 = yi * 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto x : ranges::view::ints(0, width)) {
|
||||||
|
auto rinsum = 0;
|
||||||
|
auto ginsum = 0;
|
||||||
|
auto binsum = 0;
|
||||||
|
auto routsum = 0;
|
||||||
|
auto goutsum = 0;
|
||||||
|
auto boutsum = 0;
|
||||||
|
auto rsum = 0;
|
||||||
|
auto gsum = 0;
|
||||||
|
auto bsum = 0;
|
||||||
|
yp = -radius * width;
|
||||||
|
for (const auto i : ranges::view::ints(-radius, radius + 1)) {
|
||||||
|
yi = std::max(0, yp) + x;
|
||||||
|
yi3 = yi * 3;
|
||||||
|
|
||||||
|
const auto sir = &stack[(i + radius) * 3];
|
||||||
|
|
||||||
|
sir[0] = rgb[yi3];
|
||||||
|
sir[1] = rgb[yi3 + 1];
|
||||||
|
sir[2] = rgb[yi3 + 2];
|
||||||
|
|
||||||
|
rbs = radius1 - std::abs(i);
|
||||||
|
|
||||||
|
rsum += rgb[yi3] * rbs;
|
||||||
|
gsum += rgb[yi3 + 1] * rbs;
|
||||||
|
bsum += rgb[yi3 + 2] * rbs;
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
rinsum += sir[0];
|
||||||
|
ginsum += sir[1];
|
||||||
|
binsum += sir[2];
|
||||||
|
} else {
|
||||||
|
routsum += sir[0];
|
||||||
|
goutsum += sir[1];
|
||||||
|
boutsum += sir[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < heightm) {
|
||||||
|
yp += width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yi = x;
|
||||||
|
stackpointer = radius;
|
||||||
|
for (const auto y : ranges::view::ints(0, height)) {
|
||||||
|
const auto offset = yi * 4;
|
||||||
|
pixels[offset] = dv[rsum];
|
||||||
|
pixels[offset + 1] = dv[gsum];
|
||||||
|
pixels[offset + 2] = dv[bsum];
|
||||||
|
rsum -= routsum;
|
||||||
|
gsum -= goutsum;
|
||||||
|
bsum -= boutsum;
|
||||||
|
|
||||||
|
stackstart = stackpointer - radius + div;
|
||||||
|
const auto sir = &stack[(stackstart % div) * 3];
|
||||||
|
|
||||||
|
routsum -= sir[0];
|
||||||
|
goutsum -= sir[1];
|
||||||
|
boutsum -= sir[2];
|
||||||
|
|
||||||
|
if (x == 0) {
|
||||||
|
vmin[y] = std::min(y + radius1, heightm) * width;
|
||||||
|
}
|
||||||
|
const auto p = (x + vmin[y]) * 3;
|
||||||
|
|
||||||
|
sir[0] = rgb[p];
|
||||||
|
sir[1] = rgb[p + 1];
|
||||||
|
sir[2] = rgb[p + 2];
|
||||||
|
|
||||||
|
rinsum += sir[0];
|
||||||
|
ginsum += sir[1];
|
||||||
|
binsum += sir[2];
|
||||||
|
|
||||||
|
rsum += rinsum;
|
||||||
|
gsum += ginsum;
|
||||||
|
bsum += binsum;
|
||||||
|
{
|
||||||
|
stackpointer = (stackpointer + 1) % div;
|
||||||
|
const auto sir = &stack[stackpointer * 3];
|
||||||
|
|
||||||
|
routsum += sir[0];
|
||||||
|
goutsum += sir[1];
|
||||||
|
boutsum += sir[2];
|
||||||
|
|
||||||
|
rinsum -= sir[0];
|
||||||
|
ginsum -= sir[1];
|
||||||
|
binsum -= sir[2];
|
||||||
|
}
|
||||||
|
yi += width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
void prepareCircle(QImage &img) {
|
void prepareCircle(QImage &img) {
|
||||||
Assert(!img.isNull());
|
Assert(!img.isNull());
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@ namespace Images {
|
|||||||
|
|
||||||
QPixmap PixmapFast(QImage &&image);
|
QPixmap PixmapFast(QImage &&image);
|
||||||
|
|
||||||
|
QImage BlurLargeImage(QImage image, int radius);
|
||||||
|
|
||||||
QImage prepareBlur(QImage image);
|
QImage prepareBlur(QImage image);
|
||||||
void prepareRound(
|
void prepareRound(
|
||||||
QImage &image,
|
QImage &image,
|
||||||
|
@ -150,6 +150,10 @@ bool WallPaper::isLocal() const {
|
|||||||
return !document() && thumbnail();
|
return !document() && thumbnail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WallPaper::isBlurred() const {
|
||||||
|
return _settings & MTPDwallPaperSettings::Flag::f_blur;
|
||||||
|
}
|
||||||
|
|
||||||
int WallPaper::patternIntensity() const {
|
int WallPaper::patternIntensity() const {
|
||||||
return _intensity;
|
return _intensity;
|
||||||
}
|
}
|
||||||
@ -482,6 +486,19 @@ QImage PreparePatternImage(
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QImage PrepareBlurredBackground(QImage image) {
|
||||||
|
constexpr auto kSize = 900;
|
||||||
|
constexpr auto kRadius = 24;
|
||||||
|
if (image.width() > kSize || image.height() > kSize) {
|
||||||
|
image = image.scaled(
|
||||||
|
kSize,
|
||||||
|
kSize,
|
||||||
|
Qt::KeepAspectRatio,
|
||||||
|
Qt::SmoothTransformation);
|
||||||
|
}
|
||||||
|
return Images::BlurLargeImage(image, kRadius);
|
||||||
|
}
|
||||||
|
|
||||||
namespace details {
|
namespace details {
|
||||||
|
|
||||||
WallPaper UninitializedWallPaper() {
|
WallPaper UninitializedWallPaper() {
|
||||||
@ -973,6 +990,9 @@ void ChatBackground::setPreparedImage(QImage original, QImage prepared) {
|
|||||||
Expects(prepared.width() > 0 && prepared.height() > 0);
|
Expects(prepared.width() > 0 && prepared.height() > 0);
|
||||||
|
|
||||||
_original = std::move(original);
|
_original = std::move(original);
|
||||||
|
if (!_paper.isPattern() && _paper.isBlurred()) {
|
||||||
|
prepared = Data::PrepareBlurredBackground(std::move(prepared));
|
||||||
|
}
|
||||||
if (adjustPaletteRequired()) {
|
if (adjustPaletteRequired()) {
|
||||||
adjustPaletteUsingBackground(prepared);
|
adjustPaletteUsingBackground(prepared);
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ public:
|
|||||||
[[nodiscard]] bool isCreator() const;
|
[[nodiscard]] bool isCreator() const;
|
||||||
[[nodiscard]] bool isDark() const;
|
[[nodiscard]] bool isDark() const;
|
||||||
[[nodiscard]] bool isLocal() const;
|
[[nodiscard]] bool isLocal() const;
|
||||||
|
[[nodiscard]] bool isBlurred() const;
|
||||||
[[nodiscard]] int patternIntensity() const;
|
[[nodiscard]] int patternIntensity() const;
|
||||||
[[nodiscard]] bool hasShareUrl() const;
|
[[nodiscard]] bool hasShareUrl() const;
|
||||||
[[nodiscard]] QString shareUrl() const;
|
[[nodiscard]] QString shareUrl() const;
|
||||||
@ -89,6 +90,7 @@ QImage PreparePatternImage(
|
|||||||
QColor bg,
|
QColor bg,
|
||||||
QColor fg,
|
QColor fg,
|
||||||
int intensity);
|
int intensity);
|
||||||
|
QImage PrepareBlurredBackground(QImage image);
|
||||||
|
|
||||||
namespace details {
|
namespace details {
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user