Start support of linear gradient wallpapers.

This commit is contained in:
John Preston
2021-08-12 17:51:44 +03:00
parent 1fd28d5cfb
commit c2b1187948
14 changed files with 379 additions and 227 deletions
+36 -21
View File
@@ -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());
}