mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Start support of linear gradient wallpapers.
This commit is contained in:
parent
1fd28d5cfb
commit
c2b1187948
14 changed files with 379 additions and 227 deletions
|
@ -336,13 +336,11 @@ void BackgroundBox::Inner::validatePaperThumbnail(
|
|||
: paper.dataMedia->thumbnail();
|
||||
auto original = thumbnail->original();
|
||||
if (paper.data.isPattern()) {
|
||||
// #TODO themes gradients
|
||||
const auto color = *paper.data.backgroundColor();
|
||||
original = Data::PreparePatternImage(
|
||||
std::move(original),
|
||||
color,
|
||||
Data::PatternColor(color),
|
||||
paper.data.patternIntensity());
|
||||
paper.data.backgroundColors(),
|
||||
paper.data.gradientRotation(),
|
||||
paper.data.patternOpacity());
|
||||
}
|
||||
paper.thumbnail = Ui::PixmapFromImage(TakeMiddleSample(
|
||||
original,
|
||||
|
|
|
@ -379,13 +379,17 @@ QImage ColorizePattern(QImage image, QColor color) {
|
|||
|
||||
QImage PrepareScaledFromFull(
|
||||
const QImage &image,
|
||||
std::optional<QColor> patternBackground,
|
||||
const std::vector<QColor> &patternBackground,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity,
|
||||
Images::Option blur = Images::Option(0)) {
|
||||
auto result = PrepareScaledNonPattern(image, blur);
|
||||
if (patternBackground) {
|
||||
result = ColorizePattern(
|
||||
if (!patternBackground.empty()) {
|
||||
result = Data::PreparePatternImage(
|
||||
std::move(result),
|
||||
Data::PatternColor(*patternBackground));
|
||||
patternBackground,
|
||||
gradientRotation,
|
||||
patternOpacity);
|
||||
}
|
||||
return std::move(result).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
|
@ -433,7 +437,7 @@ void BackgroundPreviewBox::prepare() {
|
|||
if (_paper.hasShareUrl()) {
|
||||
addLeftButton(tr::lng_background_share(), [=] { share(); });
|
||||
}
|
||||
updateServiceBg(_paper.backgroundColor());
|
||||
updateServiceBg(_paper.backgroundColors());
|
||||
|
||||
_paper.loadDocument();
|
||||
const auto document = _paper.document();
|
||||
|
@ -669,7 +673,9 @@ bool BackgroundPreviewBox::setScaledFromThumb() {
|
|||
}
|
||||
auto scaled = PrepareScaledFromFull(
|
||||
thumbnail->original(),
|
||||
patternBackgroundColor(),
|
||||
patternBackgroundColors(),
|
||||
_paper.gradientRotation(),
|
||||
_paper.patternOpacity(),
|
||||
_paper.document() ? Images::Option::Blurred : Images::Option(0));
|
||||
auto blurred = (_paper.document() || _paper.isPattern())
|
||||
? QImage()
|
||||
|
@ -683,7 +689,7 @@ bool BackgroundPreviewBox::setScaledFromThumb() {
|
|||
void BackgroundPreviewBox::setScaledFromImage(
|
||||
QImage &&image,
|
||||
QImage &&blurred) {
|
||||
updateServiceBg(Window::Theme::CountAverageColor(image));
|
||||
updateServiceBg({ Window::Theme::CountAverageColor(image) });
|
||||
if (!_full.isNull()) {
|
||||
startFadeInFrom(std::move(_scaled));
|
||||
}
|
||||
|
@ -710,16 +716,26 @@ void BackgroundPreviewBox::checkBlurAnimationStart() {
|
|||
startFadeInFrom(_paper.isBlurred() ? _scaled : _blurred);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::updateServiceBg(std::optional<QColor> background) {
|
||||
if (background) {
|
||||
_serviceBg = Window::Theme::AdjustedColor(
|
||||
st::msgServiceBg->c,
|
||||
*background);
|
||||
void BackgroundPreviewBox::updateServiceBg(const std::vector<QColor> &bg) {
|
||||
const auto count = int(bg.size());
|
||||
if (!count) {
|
||||
return;
|
||||
}
|
||||
auto red = 0, green = 0, blue = 0;
|
||||
for (const auto &color : bg) {
|
||||
red += color.red();
|
||||
green += color.green();
|
||||
blue += color.blue();
|
||||
}
|
||||
_serviceBg = Window::Theme::AdjustedColor(
|
||||
st::msgServiceBg->c,
|
||||
QColor(red / count, green / count, blue / count));
|
||||
}
|
||||
|
||||
std::optional<QColor> BackgroundPreviewBox::patternBackgroundColor() const {
|
||||
return _paper.isPattern() ? _paper.backgroundColor() : std::nullopt;
|
||||
std::vector<QColor> BackgroundPreviewBox::patternBackgroundColors() const {
|
||||
return _paper.isPattern()
|
||||
? _paper.backgroundColors()
|
||||
: std::vector<QColor>();
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::checkLoadedDocument() {
|
||||
|
@ -737,15 +753,21 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
|||
crl::async([
|
||||
this,
|
||||
image = std::move(image),
|
||||
patternBackground = patternBackgroundColor(),
|
||||
patternBackground = patternBackgroundColors(),
|
||||
gradientRotation = _paper.gradientRotation(),
|
||||
patternOpacity = _paper.patternOpacity(),
|
||||
guard = _generating.make_guard()
|
||||
]() mutable {
|
||||
auto scaled = PrepareScaledFromFull(image, patternBackground);
|
||||
auto blurred = patternBackground
|
||||
? QImage()
|
||||
: PrepareScaledNonPattern(
|
||||
auto scaled = PrepareScaledFromFull(
|
||||
image,
|
||||
patternBackground,
|
||||
gradientRotation,
|
||||
patternOpacity);
|
||||
auto blurred = patternBackground.empty()
|
||||
? PrepareScaledNonPattern(
|
||||
Data::PrepareBlurredBackground(image),
|
||||
Images::Option(0));
|
||||
Images::Option(0))
|
||||
: QImage();
|
||||
crl::on_main(std::move(guard), [
|
||||
this,
|
||||
image = std::move(image),
|
||||
|
|
|
@ -59,8 +59,8 @@ private:
|
|||
void checkLoadedDocument();
|
||||
bool setScaledFromThumb();
|
||||
void setScaledFromImage(QImage &&image, QImage &&blurred);
|
||||
void updateServiceBg(std::optional<QColor> background);
|
||||
std::optional<QColor> patternBackgroundColor() const;
|
||||
void updateServiceBg(const std::vector<QColor> &bg);
|
||||
std::vector<QColor> patternBackgroundColors() const;
|
||||
void paintImage(Painter &p);
|
||||
void paintRadial(Painter &p);
|
||||
void paintTexts(Painter &p, crl::time ms);
|
||||
|
|
|
@ -232,17 +232,20 @@ bool ShowWallPaper(
|
|||
match->captured(1),
|
||||
qthelp::UrlParamNameTransform::ToLower);
|
||||
const auto bg = params.value("bg_color");
|
||||
if (!params.value("gradient").isEmpty()
|
||||
|| bg.contains('~')
|
||||
|| bg.contains('-')) {
|
||||
const auto color = params.value("color");
|
||||
const auto gradient = params.value("gradient");
|
||||
if (gradient.contains('~') || bg.contains('~')) {
|
||||
Ui::show(Box<InformBox>(
|
||||
tr::lng_background_gradient_unsupported(tr::now)));
|
||||
return false;
|
||||
}
|
||||
const auto color = params.value("color");
|
||||
return BackgroundPreviewBox::Start(
|
||||
controller,
|
||||
(color.isEmpty() ? params.value(qsl("slug")) : color),
|
||||
(!color.isEmpty()
|
||||
? color
|
||||
: !gradient.isEmpty()
|
||||
? gradient
|
||||
: params.value(qsl("slug"))),
|
||||
params);
|
||||
}
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ constexpr auto kVersion = 1;
|
|||
}
|
||||
|
||||
[[nodiscard]] std::vector<QColor> ColorsFromString(const QString &string) {
|
||||
constexpr auto kMaxColors = 1; // #TODO themes gradients replace to 4
|
||||
constexpr auto kMaxColors = 2; // #TODO themes gradients replace to 4
|
||||
const auto view = QStringView(string);
|
||||
const auto count = int(view.size() / 6);
|
||||
if (!count || count > kMaxColors || view.size() != count * 7 - 1) {
|
||||
|
@ -237,6 +237,14 @@ int WallPaper::patternIntensity() const {
|
|||
return _intensity;
|
||||
}
|
||||
|
||||
float64 WallPaper::patternOpacity() const {
|
||||
return _intensity / 100.;
|
||||
}
|
||||
|
||||
int WallPaper::gradientRotation() const {
|
||||
return _rotation;
|
||||
}
|
||||
|
||||
bool WallPaper::hasShareUrl() const {
|
||||
return !_slug.isEmpty();
|
||||
}
|
||||
|
@ -350,6 +358,8 @@ WallPaper WallPaper::withUrlParams(
|
|||
result._intensity = intensity;
|
||||
}
|
||||
}
|
||||
result._rotation = params.value("rotation").toInt();
|
||||
result._rotation = (std::clamp(result._rotation, 0, 315) / 45) * 45;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -420,8 +430,7 @@ std::optional<WallPaper> WallPaper::Create(
|
|||
}
|
||||
const auto unsupported = data.vsettings()
|
||||
&& data.vsettings()->match([&](const MTPDwallPaperSettings &data) {
|
||||
return data.vsecond_background_color()
|
||||
|| data.vthird_background_color()
|
||||
return data.vthird_background_color()
|
||||
|| data.vfourth_background_color(); // #TODO themes gradients
|
||||
});
|
||||
if (unsupported) {
|
||||
|
@ -450,14 +459,16 @@ std::optional<WallPaper> WallPaper::Create(
|
|||
}
|
||||
});
|
||||
}
|
||||
if (result.isPattern() && result.backgroundColors().empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<WallPaper> WallPaper::Create(const MTPDwallPaperNoFile &data) {
|
||||
const auto unsupported = data.vsettings()
|
||||
&& data.vsettings()->match([&](const MTPDwallPaperSettings &data) {
|
||||
return data.vsecond_background_color()
|
||||
|| data.vthird_background_color()
|
||||
return data.vthird_background_color()
|
||||
|| data.vfourth_background_color(); // #TODO themes gradients
|
||||
});
|
||||
if (unsupported) {
|
||||
|
@ -477,6 +488,9 @@ std::optional<WallPaper> WallPaper::Create(const MTPDwallPaperNoFile &data) {
|
|||
}
|
||||
});
|
||||
}
|
||||
if (result.backgroundColors().empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -554,7 +568,7 @@ std::optional<WallPaper> WallPaper::FromSerialized(
|
|||
>> blurred
|
||||
>> backgroundColorsCount;
|
||||
// #TODO themes gradients replace with 4
|
||||
if (backgroundColorsCount < 0 || backgroundColorsCount > 1) {
|
||||
if (backgroundColorsCount < 0 || backgroundColorsCount > 2) {
|
||||
return std::nullopt;
|
||||
}
|
||||
backgroundColors.reserve(backgroundColorsCount);
|
||||
|
@ -610,6 +624,9 @@ std::optional<WallPaper> WallPaper::FromSerialized(
|
|||
result._backgroundColors = std::move(backgroundColors);
|
||||
result._intensity = intensity;
|
||||
result._rotation = rotation;
|
||||
if (result.isPattern() && result.backgroundColors().empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -625,6 +642,9 @@ std::optional<WallPaper> WallPaper::FromLegacySerialized(
|
|||
if (const auto color = ColorFromString(slug)) {
|
||||
result._backgroundColors.push_back(*color);
|
||||
}
|
||||
if (result.isPattern() && result.backgroundColors().empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -695,78 +715,69 @@ bool IsCloudWallPaper(const WallPaper &paper) {
|
|||
&& !details::IsTestingEditorWallPaper(paper);
|
||||
}
|
||||
|
||||
QColor PatternColor(QColor background) {
|
||||
const auto hue = background.hueF();
|
||||
const auto saturation = background.saturationF();
|
||||
const auto value = background.valueF();
|
||||
return QColor::fromHsvF(
|
||||
hue,
|
||||
std::min(1.0, saturation + 0.05 + 0.1 * (1. - saturation)),
|
||||
(value > 0.5
|
||||
? std::max(0., value * 0.65)
|
||||
: std::max(0., std::min(1., 1. - value * 0.65))),
|
||||
0.4
|
||||
).toRgb();
|
||||
[[nodiscard]] QImage FillDitheredGradient(
|
||||
QImage image,
|
||||
const std::vector<QColor> &colors,
|
||||
int rotation) {
|
||||
Expects(colors.size() > 1);
|
||||
|
||||
image.setDevicePixelRatio(1.);
|
||||
const auto width = image.width();
|
||||
const auto height = image.height();
|
||||
if (!width || !height) {
|
||||
return image;
|
||||
}
|
||||
|
||||
auto p = QPainter(&image);
|
||||
const auto [start, finalStop] = [&]() -> std::pair<QPoint, QPoint> {
|
||||
const auto type = std::clamp(rotation, 0, 315) / 45;
|
||||
switch (type) {
|
||||
case 0: return { { 0, 0 }, { 0, height } };
|
||||
case 1: return { { width, 0 }, { 0, height } };
|
||||
case 2: return { { width, 0 }, { 0, 0 } };
|
||||
case 3: return { { width, height }, { 0, 0 } };
|
||||
case 4: return { { 0, height }, { 0, 0 } };
|
||||
case 5: return { { 0, height }, { width, 0 } };
|
||||
case 6: return { { 0, 0 }, { width, 0 } };
|
||||
case 7: return { { 0, 0 }, { width, height } };
|
||||
}
|
||||
Unexpected("Rotation value in GenerateDitheredGradient.");
|
||||
}();
|
||||
auto gradient = QLinearGradient(start, finalStop);
|
||||
gradient.setStops(QGradientStops{
|
||||
{ 0.0, colors[0] },
|
||||
{ 1.0, colors[1] }
|
||||
});
|
||||
p.fillRect(0, 0, width, height, QBrush(std::move(gradient)));
|
||||
p.end();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
QImage PreparePatternImage(
|
||||
QImage image,
|
||||
QColor bg,
|
||||
QColor fg,
|
||||
int intensity) {
|
||||
const std::vector<QColor> &bg,
|
||||
int rotation,
|
||||
float64 opacity) {
|
||||
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||
image = std::move(image).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
// Similar to ColorizePattern.
|
||||
// But here we set bg to all 'alpha=0' pixels and fg to opaque ones.
|
||||
|
||||
const auto width = image.width();
|
||||
const auto height = image.height();
|
||||
const auto alpha = anim::interpolate(
|
||||
0,
|
||||
255,
|
||||
fg.alphaF() * std::clamp(intensity / 100., 0., 1.));
|
||||
if (!alpha) {
|
||||
image.fill(bg);
|
||||
return image;
|
||||
auto result = QImage(image.size(), QImage::Format_ARGB32_Premultiplied);
|
||||
if (bg.size() < 2) {
|
||||
result.fill(bg.empty() ? QColor(213, 223, 233) : bg.front());
|
||||
} else {
|
||||
result = FillDitheredGradient(std::move(result), bg, rotation);
|
||||
}
|
||||
fg.setAlpha(255);
|
||||
const auto patternBg = anim::shifted(bg);
|
||||
const auto patternFg = anim::shifted(fg);
|
||||
auto p = QPainter(&result);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||
p.setOpacity(opacity);
|
||||
p.drawImage(QRect(QPoint(), image.size()), image);
|
||||
p.end();
|
||||
|
||||
constexpr auto resultIntsPerPixel = 1;
|
||||
const auto resultIntsPerLine = (image.bytesPerLine() >> 2);
|
||||
const auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel;
|
||||
auto resultInts = reinterpret_cast<uint32*>(image.bits());
|
||||
Assert(resultIntsAdded >= 0);
|
||||
Assert(image.depth() == static_cast<int>((resultIntsPerPixel * sizeof(uint32)) << 3));
|
||||
Assert(image.bytesPerLine() == (resultIntsPerLine << 2));
|
||||
|
||||
const auto maskBytesPerPixel = (image.depth() >> 3);
|
||||
const auto maskBytesPerLine = image.bytesPerLine();
|
||||
const auto maskBytesAdded = maskBytesPerLine - width * maskBytesPerPixel;
|
||||
|
||||
// We want to read the last byte of four available.
|
||||
// This is the difference with style::colorizeImage.
|
||||
auto maskBytes = image.constBits() + (maskBytesPerPixel - 1);
|
||||
Assert(maskBytesAdded >= 0);
|
||||
Assert(image.depth() == (maskBytesPerPixel << 3));
|
||||
for (auto y = 0; y != height; ++y) {
|
||||
for (auto x = 0; x != width; ++x) {
|
||||
const auto maskOpacity = static_cast<anim::ShiftedMultiplier>(
|
||||
*maskBytes) + 1;
|
||||
const auto fgOpacity = (maskOpacity * alpha) >> 8;
|
||||
const auto bgOpacity = 256 - fgOpacity;
|
||||
*resultInts = anim::unshifted(
|
||||
patternBg * bgOpacity + patternFg * fgOpacity);
|
||||
maskBytes += maskBytesPerPixel;
|
||||
resultInts += resultIntsPerPixel;
|
||||
}
|
||||
maskBytes += maskBytesAdded;
|
||||
resultInts += resultIntsAdded;
|
||||
}
|
||||
return image;
|
||||
image = QImage();
|
||||
return result;
|
||||
}
|
||||
|
||||
QImage PrepareBlurredBackground(QImage image) {
|
||||
|
@ -782,6 +793,22 @@ QImage PrepareBlurredBackground(QImage image) {
|
|||
return Images::BlurLargeImage(image, kRadius);
|
||||
}
|
||||
|
||||
QImage GenerateDitheredGradient(
|
||||
const std::vector<QColor> &colors,
|
||||
int rotation) {
|
||||
constexpr auto kSize = 512;
|
||||
return FillDitheredGradient(
|
||||
QImage(kSize, kSize, QImage::Format_ARGB32_Premultiplied),
|
||||
colors,
|
||||
rotation);
|
||||
}
|
||||
|
||||
QImage GenerateDitheredGradient(const Data::WallPaper &paper) {
|
||||
return GenerateDitheredGradient(
|
||||
paper.backgroundColors(),
|
||||
paper.gradientRotation());
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
WallPaper UninitializedWallPaper() {
|
||||
|
|
|
@ -46,6 +46,8 @@ public:
|
|||
[[nodiscard]] bool isLocal() const;
|
||||
[[nodiscard]] bool isBlurred() const;
|
||||
[[nodiscard]] int patternIntensity() const;
|
||||
[[nodiscard]] float64 patternOpacity() const;
|
||||
[[nodiscard]] int gradientRotation() const;
|
||||
[[nodiscard]] bool hasShareUrl() const;
|
||||
[[nodiscard]] QString shareUrl(not_null<Main::Session*> session) const;
|
||||
|
||||
|
@ -120,13 +122,16 @@ private:
|
|||
[[nodiscard]] bool IsDefaultWallPaper(const WallPaper &paper);
|
||||
[[nodiscard]] bool IsCloudWallPaper(const WallPaper &paper);
|
||||
|
||||
QColor PatternColor(QColor background);
|
||||
QImage PreparePatternImage(
|
||||
[[nodiscard]] QImage PreparePatternImage(
|
||||
QImage image,
|
||||
QColor bg,
|
||||
QColor fg,
|
||||
int intensity);
|
||||
QImage PrepareBlurredBackground(QImage image);
|
||||
const std::vector<QColor> &bg,
|
||||
int rotation,
|
||||
float64 opacity);
|
||||
[[nodiscard]] QImage PrepareBlurredBackground(QImage image);
|
||||
[[nodiscard]] QImage GenerateDitheredGradient(
|
||||
const std::vector<QColor> &colors,
|
||||
int rotation);
|
||||
[[nodiscard]] QImage GenerateDitheredGradient(const WallPaper &paper);
|
||||
|
||||
namespace details {
|
||||
|
||||
|
|
|
@ -59,10 +59,10 @@ void ThemeDocument::fillPatternFieldsFrom(const QString &url) {
|
|||
const auto params = qthelp::url_parse_params(
|
||||
paramsString,
|
||||
qthelp::UrlParamNameTransform::ToLower);
|
||||
const auto kDefaultBackground = QColor(213, 223, 233);
|
||||
const auto paper = Data::DefaultWallPaper().withUrlParams(params);
|
||||
_intensity = paper.patternIntensity();
|
||||
_background = paper.backgroundColor().value_or(kDefaultBackground);
|
||||
_background = paper.backgroundColors();
|
||||
_patternOpacity = paper.patternOpacity();
|
||||
_gradientRotation = paper.gradientRotation();
|
||||
}
|
||||
|
||||
QSize ThemeDocument::countOptimalSize() {
|
||||
|
@ -262,8 +262,8 @@ void ThemeDocument::prepareThumbnailFrom(
|
|||
original = Data::PreparePatternImage(
|
||||
std::move(original),
|
||||
_background,
|
||||
Data::PatternColor(_background),
|
||||
_intensity);
|
||||
_gradientRotation,
|
||||
_patternOpacity);
|
||||
}
|
||||
_thumbnail = Ui::PixmapFromImage(std::move(original));
|
||||
_thumbnailGood = good;
|
||||
|
|
|
@ -74,8 +74,9 @@ private:
|
|||
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
|
||||
|
||||
// For wallpaper documents.
|
||||
QColor _background;
|
||||
int _intensity = 0;
|
||||
std::vector<QColor> _background;
|
||||
float64 _patternOpacity = 0.;
|
||||
int _gradientRotation = 0;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -781,24 +781,39 @@ bool MainWidget::selectingPeer() const {
|
|||
}
|
||||
|
||||
void MainWidget::cacheBackground() {
|
||||
if (Window::Theme::Background()->colorForFill()) {
|
||||
const auto background = Window::Theme::Background();
|
||||
if (background->colorForFill()) {
|
||||
return;
|
||||
} else if (Window::Theme::Background()->tile()) {
|
||||
auto &bg = Window::Theme::Background()->pixmapForTiled();
|
||||
|
||||
auto result = QImage(_willCacheFor.width() * cIntRetinaFactor(), _willCacheFor.height() * cIntRetinaFactor(), QImage::Format_RGB32);
|
||||
}
|
||||
const auto gradient = background->gradientForFill();
|
||||
const auto patternOpacity = background->paper().patternOpacity();
|
||||
const auto &bg = background->pixmap();
|
||||
if (background->tile() || bg.isNull()) {
|
||||
auto result = gradient.isNull()
|
||||
? QImage(
|
||||
_willCacheFor.size() * cIntRetinaFactor(),
|
||||
QImage::Format_ARGB32_Premultiplied)
|
||||
: gradient.scaled(
|
||||
_willCacheFor.size() * cIntRetinaFactor(),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
{
|
||||
if (!bg.isNull()) {
|
||||
QPainter p(&result);
|
||||
auto w = bg.width() / cRetinaFactor();
|
||||
auto h = bg.height() / cRetinaFactor();
|
||||
if (!gradient.isNull()) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||
p.setOpacity(patternOpacity);
|
||||
}
|
||||
const auto &tiled = background->pixmapForTiled();
|
||||
auto w = tiled.width() / cRetinaFactor();
|
||||
auto h = tiled.height() / cRetinaFactor();
|
||||
auto sx = 0;
|
||||
auto sy = 0;
|
||||
auto cx = qCeil(_willCacheFor.width() / w);
|
||||
auto cy = qCeil(_willCacheFor.height() / h);
|
||||
for (int i = sx; i < cx; ++i) {
|
||||
for (int j = sy; j < cy; ++j) {
|
||||
p.drawPixmap(QPointF(i * w, j * h), bg);
|
||||
p.drawPixmap(QPointF(i * w, j * h), tiled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -806,18 +821,30 @@ void MainWidget::cacheBackground() {
|
|||
_cachedY = 0;
|
||||
_cachedBackground = Ui::PixmapFromImage(std::move(result));
|
||||
} else {
|
||||
auto &bg = Window::Theme::Background()->pixmap();
|
||||
|
||||
QRect to, from;
|
||||
Window::Theme::ComputeBackgroundRects(_willCacheFor, bg.size(), to, from);
|
||||
auto image = bg.toImage().copy(from).scaled(
|
||||
to.width() * cIntRetinaFactor(),
|
||||
to.height() * cIntRetinaFactor(),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
auto result = gradient.isNull()
|
||||
? std::move(image)
|
||||
: gradient.scaled(
|
||||
image.size(),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
if (!gradient.isNull()) {
|
||||
QPainter p(&result);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||
p.setOpacity(patternOpacity);
|
||||
p.drawImage(QRect(QPoint(), to.size()), image);
|
||||
}
|
||||
image = QImage();
|
||||
_cachedX = to.x();
|
||||
_cachedY = to.y();
|
||||
_cachedBackground = Ui::PixmapFromImage(
|
||||
bg.toImage().copy(from).scaled(
|
||||
to.width() * cIntRetinaFactor(),
|
||||
to.height() * cIntRetinaFactor(),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation));
|
||||
_cachedBackground = Ui::PixmapFromImage(std::move(result));
|
||||
_cachedBackground.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
_cachedFor = _willCacheFor;
|
||||
|
|
|
@ -551,41 +551,39 @@ void BackgroundRow::radialAnimationCallback(crl::time now) {
|
|||
}
|
||||
|
||||
void BackgroundRow::updateImage() {
|
||||
int32 size = st::settingsBackgroundThumb * cIntRetinaFactor();
|
||||
QImage back(size, size, QImage::Format_ARGB32_Premultiplied);
|
||||
const auto size = st::settingsBackgroundThumb;
|
||||
const auto fullsize = size * cIntRetinaFactor();
|
||||
QImage back(fullsize, fullsize, QImage::Format_ARGB32_Premultiplied);
|
||||
back.setDevicePixelRatio(cRetinaFactor());
|
||||
{
|
||||
Painter p(&back);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
if (const auto color = Window::Theme::Background()->colorForFill()) {
|
||||
p.fillRect(
|
||||
0,
|
||||
0,
|
||||
st::settingsBackgroundThumb,
|
||||
st::settingsBackgroundThumb,
|
||||
*color);
|
||||
const auto background = Window::Theme::Background();
|
||||
if (const auto color = background->colorForFill()) {
|
||||
p.fillRect(0, 0, size, size, *color);
|
||||
} else {
|
||||
const auto &pix = Window::Theme::Background()->pixmap();
|
||||
const auto sx = (pix.width() > pix.height())
|
||||
? ((pix.width() - pix.height()) / 2)
|
||||
: 0;
|
||||
const auto sy = (pix.height() > pix.width())
|
||||
? ((pix.height() - pix.width()) / 2)
|
||||
: 0;
|
||||
const auto s = (pix.width() > pix.height())
|
||||
? pix.height()
|
||||
: pix.width();
|
||||
p.drawPixmap(
|
||||
0,
|
||||
0,
|
||||
st::settingsBackgroundThumb,
|
||||
st::settingsBackgroundThumb,
|
||||
pix,
|
||||
sx,
|
||||
sy,
|
||||
s,
|
||||
s);
|
||||
const auto gradient = background->gradientForFill();
|
||||
const auto patternOpacity = background->paper().patternOpacity();
|
||||
if (!gradient.isNull()) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.drawImage(QRect(0, 0, size, size), gradient);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||
p.setOpacity(patternOpacity);
|
||||
}
|
||||
const auto &pix = background->pixmap();
|
||||
if (!pix.isNull()) {
|
||||
const auto sx = (pix.width() > pix.height())
|
||||
? ((pix.width() - pix.height()) / 2)
|
||||
: 0;
|
||||
const auto sy = (pix.height() > pix.width())
|
||||
? ((pix.height() - pix.width()) / 2)
|
||||
: 0;
|
||||
const auto s = (pix.width() > pix.height())
|
||||
? pix.height()
|
||||
: pix.width();
|
||||
p.drawPixmap(0, 0, size, size, pix, sx, sy, s, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
Images::prepareRound(back, ImageRoundRadius::Small);
|
||||
|
|
|
@ -90,8 +90,9 @@ void SectionWidget::PaintBackground(
|
|||
QRect clip) {
|
||||
Painter p(widget);
|
||||
|
||||
const auto background = Window::Theme::Background();
|
||||
auto fill = QRect(0, 0, widget->width(), controller->content()->height());
|
||||
if (const auto color = Window::Theme::Background()->colorForFill()) {
|
||||
if (const auto color = background->colorForFill()) {
|
||||
p.fillRect(fill, *color);
|
||||
return;
|
||||
}
|
||||
|
@ -99,31 +100,45 @@ void SectionWidget::PaintBackground(
|
|||
auto x = 0, y = 0;
|
||||
auto cached = controller->content()->cachedBackground(fill, x, y);
|
||||
if (cached.isNull()) {
|
||||
if (Window::Theme::Background()->tile()) {
|
||||
auto &pix = Window::Theme::Background()->pixmapForTiled();
|
||||
auto left = clip.left();
|
||||
auto top = clip.top();
|
||||
auto right = clip.left() + clip.width();
|
||||
auto bottom = clip.top() + clip.height();
|
||||
auto w = pix.width() / cRetinaFactor();
|
||||
auto h = pix.height() / cRetinaFactor();
|
||||
auto sx = qFloor(left / w);
|
||||
auto sy = qFloor((top - fromy) / h);
|
||||
auto cx = qCeil(right / w);
|
||||
auto cy = qCeil((bottom - fromy) / h);
|
||||
for (auto i = sx; i < cx; ++i) {
|
||||
for (auto j = sy; j < cy; ++j) {
|
||||
p.drawPixmap(QPointF(i * w, fromy + j * h), pix);
|
||||
const auto gradient = background->gradientForFill();
|
||||
const auto patternOpacity = background->paper().patternOpacity();
|
||||
const auto &bg = background->pixmap();
|
||||
if (background->tile() || bg.isNull()) {
|
||||
if (!gradient.isNull()) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.drawImage(fill, gradient);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||
p.setOpacity(patternOpacity);
|
||||
}
|
||||
if (!bg.isNull()) {
|
||||
auto &tiled = background->pixmapForTiled();
|
||||
auto left = clip.left();
|
||||
auto top = clip.top();
|
||||
auto right = clip.left() + clip.width();
|
||||
auto bottom = clip.top() + clip.height();
|
||||
auto w = tiled.width() / cRetinaFactor();
|
||||
auto h = tiled.height() / cRetinaFactor();
|
||||
auto sx = qFloor(left / w);
|
||||
auto sy = qFloor((top - fromy) / h);
|
||||
auto cx = qCeil(right / w);
|
||||
auto cy = qCeil((bottom - fromy) / h);
|
||||
for (auto i = sx; i < cx; ++i) {
|
||||
for (auto j = sy; j < cy; ++j) {
|
||||
p.drawPixmap(QPointF(i * w, fromy + j * h), tiled);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
auto &pix = Window::Theme::Background()->pixmap();
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
QRect to, from;
|
||||
Window::Theme::ComputeBackgroundRects(fill, pix.size(), to, from);
|
||||
Window::Theme::ComputeBackgroundRects(fill, bg.size(), to, from);
|
||||
if (!gradient.isNull()) {
|
||||
p.drawImage(to, gradient);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||
p.setOpacity(patternOpacity);
|
||||
}
|
||||
to.moveTop(to.top() + fromy);
|
||||
p.drawPixmap(to, pix, from);
|
||||
p.drawPixmap(to, bg, from);
|
||||
}
|
||||
} else {
|
||||
p.drawPixmap(x, fromy + y, cached);
|
||||
|
|
|
@ -58,18 +58,23 @@ inline bool AreTestingTheme() {
|
|||
return !GlobalApplying.paletteForRevert.isEmpty();
|
||||
}
|
||||
|
||||
bool CalculateIsMonoColorImage(const QImage &image) {
|
||||
if (!image.isNull()) {
|
||||
const auto bits = reinterpret_cast<const uint32*>(image.constBits());
|
||||
const auto first = bits[0];
|
||||
for (auto i = 0; i < image.width() * image.height(); i++) {
|
||||
if (first != bits[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
std::optional<QColor> CalculateImageMonoColor(const QImage &image) {
|
||||
if (image.isNull()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return false;
|
||||
const auto bits = reinterpret_cast<const uint32*>(image.constBits());
|
||||
const auto first = bits[0];
|
||||
for (auto i = 0; i < image.width() * image.height(); i++) {
|
||||
if (first != bits[i]) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return image.pixelColor(QPoint());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool GoodImageFormatAndSize(const QImage &image) {
|
||||
return (image.format() == QImage::Format_ARGB32_Premultiplied)
|
||||
&& !image.size().isEmpty();
|
||||
}
|
||||
|
||||
QByteArray readThemeContent(const QString &path) {
|
||||
|
@ -731,7 +736,7 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) {
|
|||
}
|
||||
if (Data::IsThemeWallPaper(_paper)) {
|
||||
(nightMode() ? _tileNightValue : _tileDayValue) = _themeTile;
|
||||
setPreparedImage(_themeImage, _themeImage);
|
||||
setPrepared(_themeImage, _themeImage, QImage());
|
||||
} else if (Data::details::IsTestingThemeWallPaper(_paper)
|
||||
|| Data::details::IsTestingDefaultWallPaper(_paper)
|
||||
|| Data::details::IsTestingEditorWallPaper(_paper)) {
|
||||
|
@ -741,8 +746,9 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) {
|
|||
setPaper(Data::details::TestingDefaultWallPaper());
|
||||
}
|
||||
image = postprocessBackgroundImage(std::move(image));
|
||||
setPreparedImage(image, image);
|
||||
setPrepared(image, image, QImage());
|
||||
} else {
|
||||
const auto &bgColors = _paper.backgroundColors();
|
||||
if (Data::IsLegacy1DefaultWallPaper(_paper)) {
|
||||
image.load(qsl(":/gui/art/bg_initial.jpg"));
|
||||
const auto scale = cScale() * cIntRetinaFactor();
|
||||
|
@ -752,7 +758,7 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) {
|
|||
Qt::SmoothTransformation);
|
||||
}
|
||||
} else if (Data::IsDefaultWallPaper(_paper)
|
||||
|| (_paper.backgroundColors().empty() && image.isNull())) {
|
||||
|| (bgColors.empty() && image.isNull())) {
|
||||
setPaper(Data::DefaultWallPaper().withParamsFrom(_paper));
|
||||
image.load(qsl(":/gui/art/background.jpg"));
|
||||
}
|
||||
|
@ -762,29 +768,38 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) {
|
|||
|| Data::IsLegacy1DefaultWallPaper(_paper))
|
||||
? QImage()
|
||||
: image));
|
||||
if (const auto fill = _paper.backgroundColor()) {
|
||||
if (_paper.isPattern() && !image.isNull()) {
|
||||
if (_paper.isPattern() && !image.isNull()) {
|
||||
if (bgColors.size() < 2) {
|
||||
auto prepared = postprocessBackgroundImage(
|
||||
Data::PreparePatternImage(
|
||||
image,
|
||||
*fill,
|
||||
Data::PatternColor(*fill),
|
||||
_paper.patternIntensity()));
|
||||
setPreparedImage(std::move(image), std::move(prepared));
|
||||
bgColors,
|
||||
_paper.gradientRotation(),
|
||||
_paper.patternOpacity()));
|
||||
setPrepared(
|
||||
std::move(image),
|
||||
std::move(prepared),
|
||||
QImage());
|
||||
} else {
|
||||
_original = QImage();
|
||||
_pixmap = QPixmap();
|
||||
_pixmapForTiled = QPixmap();
|
||||
if (adjustPaletteRequired()) {
|
||||
adjustPaletteUsingColor(*fill);
|
||||
}
|
||||
setPrepared(
|
||||
image,
|
||||
image,
|
||||
Data::GenerateDitheredGradient(_paper));
|
||||
}
|
||||
} else if (bgColors.size() == 1) {
|
||||
setPrepared(QImage(), QImage(), QImage());
|
||||
} else if (!bgColors.empty()) {
|
||||
setPrepared(
|
||||
QImage(),
|
||||
QImage(),
|
||||
Data::GenerateDitheredGradient(_paper));
|
||||
} else {
|
||||
image = postprocessBackgroundImage(std::move(image));
|
||||
setPreparedImage(image, image);
|
||||
setPrepared(image, image, QImage());
|
||||
}
|
||||
}
|
||||
Assert(colorForFill()
|
||||
|| !_gradient.isNull()
|
||||
|| (!_original.isNull()
|
||||
&& !_pixmap.isNull()
|
||||
&& !_pixmapForTiled.isNull()));
|
||||
|
@ -797,27 +812,37 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) {
|
|||
checkUploadWallPaper();
|
||||
}
|
||||
|
||||
void ChatBackground::setPreparedImage(QImage original, QImage prepared) {
|
||||
Expects(original.format() == QImage::Format_ARGB32_Premultiplied);
|
||||
Expects(original.width() > 0 && original.height() > 0);
|
||||
Expects(prepared.format() == QImage::Format_ARGB32_Premultiplied);
|
||||
Expects(prepared.width() > 0 && prepared.height() > 0);
|
||||
void ChatBackground::setPrepared(
|
||||
QImage original,
|
||||
QImage prepared,
|
||||
QImage gradient) {
|
||||
Expects(original.isNull() || GoodImageFormatAndSize(original));
|
||||
Expects(prepared.isNull() || GoodImageFormatAndSize(prepared));
|
||||
Expects(gradient.isNull() || GoodImageFormatAndSize(gradient));
|
||||
|
||||
_original = std::move(original);
|
||||
if (!_paper.isPattern() && _paper.isBlurred()) {
|
||||
if (!prepared.isNull() && !_paper.isPattern() && _paper.isBlurred()) {
|
||||
prepared = Data::PrepareBlurredBackground(std::move(prepared));
|
||||
}
|
||||
if (adjustPaletteRequired()) {
|
||||
adjustPaletteUsingBackground(prepared);
|
||||
if (!gradient.isNull()) {
|
||||
adjustPaletteUsingBackground(gradient);
|
||||
} else if (!prepared.isNull()) {
|
||||
adjustPaletteUsingBackground(prepared);
|
||||
} else if (!_paper.backgroundColors().empty()) {
|
||||
adjustPaletteUsingColor(_paper.backgroundColors().front());
|
||||
}
|
||||
}
|
||||
|
||||
_original = std::move(original);
|
||||
_gradient = std::move(gradient);
|
||||
preparePixmaps(std::move(prepared));
|
||||
}
|
||||
|
||||
void ChatBackground::preparePixmaps(QImage image) {
|
||||
const auto width = image.width();
|
||||
const auto height = image.height();
|
||||
const auto isSmallForTiled = (width < kMinimumTiledSize)
|
||||
|| (height < kMinimumTiledSize);
|
||||
const auto isSmallForTiled = (width > 0 && height > 0)
|
||||
&& (width < kMinimumTiledSize || height < kMinimumTiledSize);
|
||||
if (isSmallForTiled) {
|
||||
const auto repeatTimesX = qCeil(kMinimumTiledSize / (1. * width));
|
||||
const auto repeatTimesY = qCeil(kMinimumTiledSize / (1. * height));
|
||||
|
@ -841,8 +866,12 @@ void ChatBackground::preparePixmaps(QImage image) {
|
|||
}
|
||||
_pixmapForTiled = Ui::PixmapFromImage(std::move(imageForTiled));
|
||||
}
|
||||
_isMonoColorImage = CalculateIsMonoColorImage(image);
|
||||
_pixmap = Ui::PixmapFromImage(std::move(image));
|
||||
_imageMonoColor = _gradient.isNull()
|
||||
? CalculateImageMonoColor(image)
|
||||
: std::nullopt;
|
||||
_pixmap = image.isNull()
|
||||
? QPixmap()
|
||||
: Ui::PixmapFromImage(std::move(image));
|
||||
if (!isSmallForTiled) {
|
||||
_pixmapForTiled = _pixmap;
|
||||
}
|
||||
|
@ -910,7 +939,15 @@ void ChatBackground::adjustPaletteUsingColor(QColor color) {
|
|||
}
|
||||
|
||||
std::optional<QColor> ChatBackground::colorForFill() const {
|
||||
return _pixmap.isNull() ? _paper.backgroundColor() : std::nullopt;
|
||||
return !_pixmap.isNull()
|
||||
? imageMonoColor()
|
||||
: !_gradient.isNull()
|
||||
? std::nullopt
|
||||
: _paper.backgroundColor();
|
||||
}
|
||||
|
||||
QImage ChatBackground::gradientForFill() const {
|
||||
return _gradient;
|
||||
}
|
||||
|
||||
QImage ChatBackground::createCurrentImage() const {
|
||||
|
@ -921,8 +958,24 @@ QImage ChatBackground::createCurrentImage() const {
|
|||
QImage::Format_ARGB32_Premultiplied);
|
||||
result.fill(*fill);
|
||||
return result;
|
||||
} else if (_gradient.isNull()) {
|
||||
return pixmap().toImage();
|
||||
} else if (pixmap().isNull()) {
|
||||
return _gradient;
|
||||
}
|
||||
return pixmap().toImage();
|
||||
const auto &pattern = pixmap();
|
||||
auto result = _gradient.scaled(
|
||||
pattern.size(),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
result.setDevicePixelRatio(1.);
|
||||
{
|
||||
auto p = QPainter(&result);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||
p.setOpacity(paper().patternOpacity());
|
||||
p.drawPixmap(QRect(QPoint(), pattern.size()), pattern);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ChatBackground::tile() const {
|
||||
|
@ -949,8 +1002,8 @@ bool ChatBackground::tileNight() const {
|
|||
return _tileNightValue;
|
||||
}
|
||||
|
||||
bool ChatBackground::isMonoColorImage() const {
|
||||
return _isMonoColorImage;
|
||||
std::optional<QColor> ChatBackground::imageMonoColor() const {
|
||||
return _imageMonoColor;
|
||||
}
|
||||
|
||||
void ChatBackground::setTile(bool tile) {
|
||||
|
|
|
@ -164,7 +164,7 @@ public:
|
|||
void appliedEditedPalette();
|
||||
void downloadingStarted(bool tile);
|
||||
|
||||
[[nodiscard]] Data::WallPaper paper() const {
|
||||
[[nodiscard]] const Data::WallPaper &paper() const {
|
||||
return _paper;
|
||||
}
|
||||
[[nodiscard]] WallPaperId id() const {
|
||||
|
@ -177,11 +177,12 @@ public:
|
|||
return _pixmapForTiled;
|
||||
}
|
||||
[[nodiscard]] std::optional<QColor> colorForFill() const;
|
||||
[[nodiscard]] QImage gradientForFill() const;
|
||||
[[nodiscard]] QImage createCurrentImage() const;
|
||||
[[nodiscard]] bool tile() const;
|
||||
[[nodiscard]] bool tileDay() const;
|
||||
[[nodiscard]] bool tileNight() const;
|
||||
[[nodiscard]] bool isMonoColorImage() const;
|
||||
[[nodiscard]] std::optional<QColor> imageMonoColor() const;
|
||||
[[nodiscard]] bool nightModeChangeAllowed() const;
|
||||
|
||||
private:
|
||||
|
@ -195,7 +196,7 @@ private:
|
|||
[[nodiscard]] bool started() const;
|
||||
void initialRead();
|
||||
void saveForRevert();
|
||||
void setPreparedImage(QImage original, QImage prepared);
|
||||
void setPrepared(QImage original, QImage prepared, QImage gradient);
|
||||
void preparePixmaps(QImage image);
|
||||
void writeNewBackgroundSettings();
|
||||
void setPaper(const Data::WallPaper &paper);
|
||||
|
@ -236,6 +237,7 @@ private:
|
|||
rpl::event_stream<BackgroundUpdate> _updates;
|
||||
Data::WallPaper _paper = Data::details::UninitializedWallPaper();
|
||||
std::optional<QColor> _paperColor;
|
||||
QImage _gradient;
|
||||
QImage _original;
|
||||
QPixmap _pixmap;
|
||||
QPixmap _pixmapForTiled;
|
||||
|
@ -245,7 +247,7 @@ private:
|
|||
std::optional<bool> _localStoredTileDayValue;
|
||||
std::optional<bool> _localStoredTileNightValue;
|
||||
|
||||
bool _isMonoColorImage = false;
|
||||
std::optional<QColor> _imageMonoColor;
|
||||
|
||||
Object _themeObject;
|
||||
QImage _themeImage;
|
||||
|
|
|
@ -87,9 +87,10 @@ constexpr auto kMinDiffIntensity = 0.25;
|
|||
|
||||
[[nodiscard]] bool IsFilledCover() {
|
||||
const auto background = Window::Theme::Background();
|
||||
return false; AssertIsDebug();
|
||||
return background->tile()
|
||||
|| background->colorForFill().has_value()
|
||||
|| background->isMonoColorImage()
|
||||
|| !background->gradientForFill().isNull()
|
||||
|| background->paper().isPattern()
|
||||
|| Data::IsLegacy1DefaultWallPaper(background->paper());
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue