mirror of https://github.com/procxx/kepka.git
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 * 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) {
|
||||
|
@ -621,9 +624,12 @@ bool BackgroundPreviewBox::setScaledFromThumb() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::setScaledFromImage(QImage &&image) {
|
||||
void BackgroundPreviewBox::setScaledFromImage(
|
||||
QImage &&image,
|
||||
QImage &&blurred) {
|
||||
updateServiceBg(Window::Theme::CountAverageColor(image));
|
||||
_scaled = App::pixmapFromImageInPlace(std::move(image));
|
||||
_blurred = App::pixmapFromImageInPlace(std::move(blurred));
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::updateServiceBg(std::optional<QColor> background) {
|
||||
|
@ -655,16 +661,23 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
|||
guard = std::move(right)
|
||||
]() mutable {
|
||||
auto scaled = PrepareScaledFromFull(image, patternBackground);
|
||||
const auto ms = getms();
|
||||
auto blurred = patternBackground
|
||||
? QImage()
|
||||
: PrepareScaledNonPattern(
|
||||
Data::PrepareBlurredBackground(image),
|
||||
Images::Option(0));
|
||||
crl::on_main([
|
||||
this,
|
||||
image = std::move(image),
|
||||
scaled = std::move(scaled),
|
||||
blurred = std::move(blurred),
|
||||
guard = std::move(guard)
|
||||
]() mutable {
|
||||
if (!guard) {
|
||||
return;
|
||||
}
|
||||
setScaledFromImage(std::move(scaled));
|
||||
setScaledFromImage(std::move(scaled), std::move(blurred));
|
||||
_full = std::move(image);
|
||||
update();
|
||||
});
|
||||
|
|
|
@ -67,7 +67,7 @@ private:
|
|||
|
||||
void checkLoadedDocument();
|
||||
bool setScaledFromThumb();
|
||||
void setScaledFromImage(QImage &&image);
|
||||
void setScaledFromImage(QImage &&image, QImage &&blurred = QImage());
|
||||
void updateServiceBg(std::optional<QColor> background);
|
||||
std::optional<QColor> patternBackgroundColor() const;
|
||||
void paintImage(Painter &p);
|
||||
|
@ -79,7 +79,7 @@ private:
|
|||
AdminLog::OwnedItem _text2;
|
||||
Data::WallPaper _paper;
|
||||
QImage _full;
|
||||
QPixmap _scaled;
|
||||
QPixmap _scaled, _blurred;
|
||||
Ui::RadialAnimation _radial;
|
||||
base::binary_guard _generating;
|
||||
std::optional<QColor> _serviceBg;
|
||||
|
|
|
@ -52,12 +52,11 @@ QImage prepareBlur(QImage img) {
|
|||
if (img.isNull()) {
|
||||
return img;
|
||||
}
|
||||
auto ratio = img.devicePixelRatio();
|
||||
auto fmt = img.format();
|
||||
const auto ratio = img.devicePixelRatio();
|
||||
const auto fmt = img.format();
|
||||
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);
|
||||
Assert(!img.isNull());
|
||||
}
|
||||
|
||||
uchar *pix = img.bits();
|
||||
|
@ -170,6 +169,238 @@ yi += stride;
|
|||
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) {
|
||||
Assert(!img.isNull());
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ namespace Images {
|
|||
|
||||
QPixmap PixmapFast(QImage &&image);
|
||||
|
||||
QImage BlurLargeImage(QImage image, int radius);
|
||||
|
||||
QImage prepareBlur(QImage image);
|
||||
void prepareRound(
|
||||
QImage &image,
|
||||
|
|
|
@ -150,6 +150,10 @@ bool WallPaper::isLocal() const {
|
|||
return !document() && thumbnail();
|
||||
}
|
||||
|
||||
bool WallPaper::isBlurred() const {
|
||||
return _settings & MTPDwallPaperSettings::Flag::f_blur;
|
||||
}
|
||||
|
||||
int WallPaper::patternIntensity() const {
|
||||
return _intensity;
|
||||
}
|
||||
|
@ -482,6 +486,19 @@ QImage PreparePatternImage(
|
|||
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 {
|
||||
|
||||
WallPaper UninitializedWallPaper() {
|
||||
|
@ -973,6 +990,9 @@ void ChatBackground::setPreparedImage(QImage original, QImage prepared) {
|
|||
Expects(prepared.width() > 0 && prepared.height() > 0);
|
||||
|
||||
_original = std::move(original);
|
||||
if (!_paper.isPattern() && _paper.isBlurred()) {
|
||||
prepared = Data::PrepareBlurredBackground(std::move(prepared));
|
||||
}
|
||||
if (adjustPaletteRequired()) {
|
||||
adjustPaletteUsingBackground(prepared);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
[[nodiscard]] bool isCreator() const;
|
||||
[[nodiscard]] bool isDark() const;
|
||||
[[nodiscard]] bool isLocal() const;
|
||||
[[nodiscard]] bool isBlurred() const;
|
||||
[[nodiscard]] int patternIntensity() const;
|
||||
[[nodiscard]] bool hasShareUrl() const;
|
||||
[[nodiscard]] QString shareUrl() const;
|
||||
|
@ -89,6 +90,7 @@ QImage PreparePatternImage(
|
|||
QColor bg,
|
||||
QColor fg,
|
||||
int intensity);
|
||||
QImage PrepareBlurredBackground(QImage image);
|
||||
|
||||
namespace details {
|
||||
|
||||
|
|
Loading…
Reference in New Issue