mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Move all background helper methods to chat_theme module.
This commit is contained in:
parent
0a1e84ddb2
commit
f5a323e40a
16 changed files with 491 additions and 299 deletions
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "lang/lang_keys.h"
|
||||
#include "ui/effects/round_checkbox.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
|
@ -331,7 +332,7 @@ void BackgroundBox::Inner::validatePaperThumbnail(
|
|||
}
|
||||
} else if (!paper.data.backgroundColors().empty()) {
|
||||
paper.thumbnail = Ui::PixmapFromImage(
|
||||
Data::GenerateWallPaper(
|
||||
Ui::GenerateBackgroundImage(
|
||||
st::backgroundSize * cIntRetinaFactor(),
|
||||
paper.data.backgroundColors(),
|
||||
paper.data.gradientRotation()));
|
||||
|
@ -346,7 +347,7 @@ void BackgroundBox::Inner::validatePaperThumbnail(
|
|||
: paper.dataMedia->thumbnail();
|
||||
auto original = thumbnail->original();
|
||||
if (paper.data.isPattern()) {
|
||||
original = Data::PreparePatternImage(
|
||||
original = Ui::PreparePatternImage(
|
||||
std::move(original),
|
||||
paper.data.backgroundColors(),
|
||||
paper.data.gradientRotation(),
|
||||
|
|
|
@ -341,7 +341,7 @@ bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
|
|||
Images::Option blur = Images::Option(0)) {
|
||||
auto result = PrepareScaledNonPattern(image, blur);
|
||||
if (isPattern) {
|
||||
result = Data::PreparePatternImage(
|
||||
result = Ui::PreparePatternImage(
|
||||
std::move(result),
|
||||
background,
|
||||
gradientRotation,
|
||||
|
@ -395,7 +395,7 @@ void BackgroundPreviewBox::generateBackground() {
|
|||
const auto size = QSize(st::boxWideWidth, st::boxWideWidth)
|
||||
* cIntRetinaFactor();
|
||||
_generated = Ui::PixmapFromImage((_paper.patternOpacity() >= 0.)
|
||||
? Data::GenerateWallPaper(
|
||||
? Ui::GenerateBackgroundImage(
|
||||
size,
|
||||
_paper.backgroundColors(),
|
||||
_paper.gradientRotation())
|
||||
|
@ -668,7 +668,7 @@ void BackgroundPreviewBox::setScaledFromThumb() {
|
|||
auto blurred = (_paper.document() || _paper.isPattern())
|
||||
? QImage()
|
||||
: PrepareScaledNonPattern(
|
||||
Data::PrepareBlurredBackground(thumbnail->original()),
|
||||
Ui::PrepareBlurredBackground(thumbnail->original()),
|
||||
Images::Option(0));
|
||||
setScaledFromImage(std::move(scaled), std::move(blurred));
|
||||
}
|
||||
|
@ -676,7 +676,7 @@ void BackgroundPreviewBox::setScaledFromThumb() {
|
|||
void BackgroundPreviewBox::setScaledFromImage(
|
||||
QImage &&image,
|
||||
QImage &&blurred) {
|
||||
updateServiceBg({ Window::Theme::CountAverageColor(image) });
|
||||
updateServiceBg({ Ui::CountAverageColor(image) });
|
||||
if (!_full.isNull()) {
|
||||
startFadeInFrom(std::move(_scaled));
|
||||
}
|
||||
|
@ -714,7 +714,7 @@ void BackgroundPreviewBox::updateServiceBg(const std::vector<QColor> &bg) {
|
|||
green += color.green();
|
||||
blue += color.blue();
|
||||
}
|
||||
_serviceBg = Window::Theme::AdjustedColor(
|
||||
_serviceBg = Ui::ThemeAdjustedColor(
|
||||
st::msgServiceBg->c,
|
||||
QColor(red / count, green / count, blue / count));
|
||||
}
|
||||
|
@ -748,7 +748,7 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
|||
patternOpacity);
|
||||
auto blurred = !isPattern
|
||||
? PrepareScaledNonPattern(
|
||||
Data::PrepareBlurredBackground(image),
|
||||
Ui::PrepareBlurredBackground(image),
|
||||
Images::Option(0))
|
||||
: QImage();
|
||||
crl::on_main(std::move(guard), [
|
||||
|
@ -763,9 +763,9 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
|||
});
|
||||
});
|
||||
};
|
||||
_generating = Data::ReadImageAsync(
|
||||
_generating = Data::ReadBackgroundImageAsync(
|
||||
_media.get(),
|
||||
Window::Theme::PreprocessBackgroundImage,
|
||||
Ui::PreprocessBackgroundImage,
|
||||
generateCallback);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history_item.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "platform/platform_file_utilities.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "window/window_session_controller.h"
|
||||
|
||||
|
@ -33,8 +34,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Data {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxWallpaperSize = 3072;
|
||||
|
||||
void LaunchWithWarning(
|
||||
// not_null<Window::Controller*> controller,
|
||||
const QString &name,
|
||||
|
@ -175,19 +174,7 @@ bool IsIpRevealingName(const QString &filepath) {
|
|||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage ReadImage(
|
||||
const QString &path,
|
||||
const QByteArray &content,
|
||||
bool gzipSvg) {
|
||||
return Images::Read({
|
||||
.path = path,
|
||||
.content = content,
|
||||
.maxSize = QSize(kMaxWallpaperSize, kMaxWallpaperSize),
|
||||
.gzipSvg = gzipSvg,
|
||||
}).image;
|
||||
}
|
||||
|
||||
base::binary_guard ReadImageAsync(
|
||||
base::binary_guard ReadBackgroundImageAsync(
|
||||
not_null<Data::DocumentMedia*> media,
|
||||
FnMut<QImage(QImage)> postprocess,
|
||||
FnMut<void(QImage&&)> done) {
|
||||
|
@ -201,7 +188,7 @@ base::binary_guard ReadImageAsync(
|
|||
guard = result.make_guard(),
|
||||
callback = std::move(done)
|
||||
]() mutable {
|
||||
auto image = ReadImage(path, bytes, gzipSvg);
|
||||
auto image = Ui::ReadBackgroundImage(path, bytes, gzipSvg);
|
||||
if (postprocess) {
|
||||
image = postprocess(std::move(image));
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class DocumentMedia;
|
|||
// [[nodiscard]] bool IsValidMediaFile(const QString &filepath);
|
||||
[[nodiscard]] bool IsExecutableName(const QString &filepath);
|
||||
[[nodiscard]] bool IsIpRevealingName(const QString &filepath);
|
||||
base::binary_guard ReadImageAsync(
|
||||
base::binary_guard ReadBackgroundImageAsync(
|
||||
not_null<Data::DocumentMedia*> media,
|
||||
FnMut<QImage(QImage)> postprocess,
|
||||
FnMut<void(QImage&&)> done);
|
||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_file_origin.h"
|
||||
#include "data/data_session.h"
|
||||
#include "storage/serialize_common.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "core/application.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
|
@ -45,10 +46,6 @@ constexpr auto kVersion = 1;
|
|||
return color ? SerializeColor(*color) : quint32(-1);
|
||||
}
|
||||
|
||||
[[nodiscard]] QColor DefaultBackgroundColor() {
|
||||
return QColor(213, 223, 233);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<QColor> ColorsFromMTP(
|
||||
const MTPDwallPaperSettings &data) {
|
||||
auto result = std::vector<QColor>();
|
||||
|
@ -683,89 +680,8 @@ bool IsCloudWallPaper(const WallPaper &paper) {
|
|||
&& !details::IsTestingEditorWallPaper(paper);
|
||||
}
|
||||
|
||||
QImage GenerateWallPaper(
|
||||
QSize size,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity,
|
||||
Fn<void(QPainter&)> drawPattern) {
|
||||
auto result = bg.empty()
|
||||
? Images::GenerateGradient(size, { DefaultBackgroundColor() })
|
||||
: Images::GenerateGradient(size, bg, gradientRotation);
|
||||
if (bg.size() > 1 && (!drawPattern || patternOpacity >= 0.)) {
|
||||
result = Images::DitherImage(std::move(result));
|
||||
}
|
||||
if (drawPattern) {
|
||||
auto p = QPainter(&result);
|
||||
if (patternOpacity >= 0.) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||
p.setOpacity(patternOpacity);
|
||||
} else {
|
||||
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
||||
}
|
||||
drawPattern(p);
|
||||
if (patternOpacity < 0. && patternOpacity > -1.) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
p.setOpacity(1. + patternOpacity);
|
||||
p.fillRect(QRect{ QPoint(), size }, Qt::black);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(result).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
|
||||
QImage PreparePatternImage(
|
||||
QImage pattern,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity) {
|
||||
auto result = GenerateWallPaper(
|
||||
pattern.size(),
|
||||
bg,
|
||||
gradientRotation,
|
||||
patternOpacity,
|
||||
[&](QPainter &p) {
|
||||
p.drawImage(QRect(QPoint(), pattern.size()), pattern);
|
||||
});
|
||||
|
||||
pattern = QImage();
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
QImage GenerateDitheredGradient(
|
||||
const std::vector<QColor> &colors,
|
||||
int rotation) {
|
||||
constexpr auto kSize = 512;
|
||||
const auto size = QSize(kSize, kSize);
|
||||
if (colors.empty()) {
|
||||
return Images::GenerateGradient(size, { DefaultBackgroundColor() });
|
||||
}
|
||||
auto result = Images::GenerateGradient(size, colors, rotation);
|
||||
if (colors.size() > 1) {
|
||||
result = Images::DitherImage(std::move(result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QImage GenerateDitheredGradient(const Data::WallPaper &paper) {
|
||||
if (paper.backgroundColors().empty()) {
|
||||
return GenerateDitheredGradient({ DefaultBackgroundColor() }, 0);
|
||||
}
|
||||
return GenerateDitheredGradient(
|
||||
return Ui::GenerateDitheredGradient(
|
||||
paper.backgroundColors(),
|
||||
paper.gradientRotation());
|
||||
}
|
||||
|
|
|
@ -121,21 +121,6 @@ private:
|
|||
[[nodiscard]] bool IsDefaultWallPaper(const WallPaper &paper);
|
||||
[[nodiscard]] bool IsCloudWallPaper(const WallPaper &paper);
|
||||
|
||||
[[nodiscard]] QImage GenerateWallPaper(
|
||||
QSize size,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity = 1.,
|
||||
Fn<void(QPainter&)> drawPattern = nullptr);
|
||||
[[nodiscard]] QImage PreparePatternImage(
|
||||
QImage pattern,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity);
|
||||
[[nodiscard]] QImage PrepareBlurredBackground(QImage image);
|
||||
[[nodiscard]] QImage GenerateDitheredGradient(
|
||||
const std::vector<QColor> &colors,
|
||||
int rotation);
|
||||
[[nodiscard]] QImage GenerateDitheredGradient(const WallPaper &paper);
|
||||
|
||||
[[nodiscard]] QColor ColorFromSerialized(quint32 serialized);
|
||||
|
|
|
@ -279,7 +279,7 @@ void ThemeDocument::validateThumbnail() const {
|
|||
}
|
||||
|
||||
void ThemeDocument::generateThumbnail() const {
|
||||
_thumbnail = Ui::PixmapFromImage(Data::GenerateWallPaper(
|
||||
_thumbnail = Ui::PixmapFromImage(Ui::GenerateBackgroundImage(
|
||||
QSize(_pixw, _pixh) * cIntRetinaFactor(),
|
||||
_background,
|
||||
_gradientRotation,
|
||||
|
@ -316,7 +316,7 @@ void ThemeDocument::prepareThumbnailFrom(
|
|||
_pixw,
|
||||
_pixh);
|
||||
if (isPattern) {
|
||||
original = Data::PreparePatternImage(
|
||||
original = Ui::PreparePatternImage(
|
||||
std::move(original),
|
||||
_background,
|
||||
_gradientRotation,
|
||||
|
|
|
@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_histories.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "api/api_text_entities.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
|
@ -1183,9 +1184,9 @@ void MainWidget::checkChatBackground() {
|
|||
: background->data;
|
||||
setReadyChatBackground(ready, std::move(image));
|
||||
};
|
||||
_background->generating = Data::ReadImageAsync(
|
||||
_background->generating = Data::ReadBackgroundImageAsync(
|
||||
media.get(),
|
||||
Window::Theme::PreprocessBackgroundImage,
|
||||
Ui::PreprocessBackgroundImage,
|
||||
generateCallback);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,12 @@ constexpr auto kMaxChatEntryHistorySize = 50;
|
|||
constexpr auto kCacheBackgroundTimeout = 3 * crl::time(1000);
|
||||
constexpr auto kCacheBackgroundFastTimeout = crl::time(200);
|
||||
constexpr auto kBackgroundFadeDuration = crl::time(200);
|
||||
constexpr auto kMinimumTiledSize = 512;
|
||||
constexpr auto kMaxSize = 2960;
|
||||
|
||||
[[nodiscard]] QColor DefaultBackgroundColor() {
|
||||
return QColor(213, 223, 233);
|
||||
}
|
||||
|
||||
[[nodiscard]] CacheBackgroundResult CacheBackground(
|
||||
const CacheBackgroundRequest &request) {
|
||||
|
@ -141,7 +147,7 @@ bool operator!=(
|
|||
}
|
||||
|
||||
CachedBackground::CachedBackground(CacheBackgroundResult &&result)
|
||||
: pixmap(Ui::PixmapFromImage(std::move(result.image)))
|
||||
: pixmap(PixmapFromImage(std::move(result.image)))
|
||||
, area(result.area)
|
||||
, x(result.x)
|
||||
, y(result.y) {
|
||||
|
@ -169,6 +175,18 @@ void ChatTheme::setBackground(ChatThemeBackground &&background) {
|
|||
_repaintBackgroundRequests.fire({});
|
||||
}
|
||||
|
||||
void ChatTheme::updateBackgroundImageFrom(ChatThemeBackground &&background) {
|
||||
_mutableBackground.prepared = std::move(background.prepared);
|
||||
_mutableBackground.preparedForTiled = std::move(
|
||||
background.preparedForTiled);
|
||||
if (!_backgroundState.now.pixmap.isNull()) {
|
||||
if (_cacheBackgroundTimer) {
|
||||
_cacheBackgroundTimer->cancel();
|
||||
}
|
||||
cacheBackgroundNow();
|
||||
}
|
||||
}
|
||||
|
||||
uint64 ChatTheme::key() const {
|
||||
return _id;
|
||||
}
|
||||
|
@ -184,7 +202,7 @@ void ChatTheme::setBubblesBackground(QImage image) {
|
|||
});
|
||||
}
|
||||
if (!_bubblesBackgroundPattern) {
|
||||
_bubblesBackgroundPattern = Ui::PrepareBubblePattern();
|
||||
_bubblesBackgroundPattern = PrepareBubblePattern();
|
||||
}
|
||||
_bubblesBackgroundPattern->pixmap = _bubblesBackground.pixmap;
|
||||
_repaintBackgroundRequests.fire({});
|
||||
|
@ -427,4 +445,244 @@ ChatBackgroundRects ComputeChatBackgroundRects(
|
|||
}
|
||||
}
|
||||
|
||||
QColor CountAverageColor(const QImage &image) {
|
||||
Expects(image.format() == QImage::Format_ARGB32_Premultiplied
|
||||
|| image.format() == QImage::Format_RGB32);
|
||||
|
||||
uint64 components[3] = { 0 };
|
||||
const auto w = image.width();
|
||||
const auto h = image.height();
|
||||
const auto size = w * h;
|
||||
if (const auto pix = image.constBits()) {
|
||||
for (auto i = 0, l = size * 4; i != l; i += 4) {
|
||||
components[2] += pix[i + 0];
|
||||
components[1] += pix[i + 1];
|
||||
components[0] += pix[i + 2];
|
||||
}
|
||||
}
|
||||
if (size) {
|
||||
for (auto i = 0; i != 3; ++i) {
|
||||
components[i] /= size;
|
||||
}
|
||||
}
|
||||
return QColor(components[0], components[1], components[2]);
|
||||
}
|
||||
|
||||
QColor ThemeAdjustedColor(QColor original, QColor background) {
|
||||
return QColor::fromHslF(
|
||||
background.hslHueF(),
|
||||
background.hslSaturationF(),
|
||||
original.lightnessF(),
|
||||
original.alphaF()
|
||||
).toRgb();
|
||||
}
|
||||
|
||||
QImage PreprocessBackgroundImage(QImage image) {
|
||||
if (image.isNull()) {
|
||||
return image;
|
||||
}
|
||||
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||
image = std::move(image).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
if (image.width() > 40 * image.height()) {
|
||||
const auto width = 40 * image.height();
|
||||
const auto height = image.height();
|
||||
image = image.copy((image.width() - width) / 2, 0, width, height);
|
||||
} else if (image.height() > 40 * image.width()) {
|
||||
const auto width = image.width();
|
||||
const auto height = 40 * image.width();
|
||||
image = image.copy(0, (image.height() - height) / 2, width, height);
|
||||
}
|
||||
if (image.width() > kMaxSize || image.height() > kMaxSize) {
|
||||
image = image.scaled(
|
||||
kMaxSize,
|
||||
kMaxSize,
|
||||
Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
std::optional<QColor> CalculateImageMonoColor(const QImage &image) {
|
||||
Expects(image.bytesPerLine() == 4 * image.width());
|
||||
|
||||
if (image.isNull()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
QImage PrepareImageForTiled(const QImage &prepared) {
|
||||
const auto width = prepared.width();
|
||||
const auto height = prepared.height();
|
||||
const auto isSmallForTiled = (width > 0 && height > 0)
|
||||
&& (width < kMinimumTiledSize || height < kMinimumTiledSize);
|
||||
if (!isSmallForTiled) {
|
||||
return prepared;
|
||||
}
|
||||
const auto repeatTimesX = (kMinimumTiledSize + width - 1) / width;
|
||||
const auto repeatTimesY = (kMinimumTiledSize + height - 1) / height;
|
||||
auto result = QImage(
|
||||
width * repeatTimesX,
|
||||
height * repeatTimesY,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(prepared.devicePixelRatio());
|
||||
auto imageForTiledBytes = result.bits();
|
||||
auto bytesInLine = width * sizeof(uint32);
|
||||
for (auto timesY = 0; timesY != repeatTimesY; ++timesY) {
|
||||
auto imageBytes = prepared.constBits();
|
||||
for (auto y = 0; y != height; ++y) {
|
||||
for (auto timesX = 0; timesX != repeatTimesX; ++timesX) {
|
||||
memcpy(imageForTiledBytes, imageBytes, bytesInLine);
|
||||
imageForTiledBytes += bytesInLine;
|
||||
}
|
||||
imageBytes += prepared.bytesPerLine();
|
||||
imageForTiledBytes += result.bytesPerLine() - (repeatTimesX * bytesInLine);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage ReadBackgroundImage(
|
||||
const QString &path,
|
||||
const QByteArray &content,
|
||||
bool gzipSvg) {
|
||||
return Images::Read({
|
||||
.path = path,
|
||||
.content = content,
|
||||
.maxSize = QSize(kMaxSize, kMaxSize),
|
||||
.gzipSvg = gzipSvg,
|
||||
}).image;
|
||||
}
|
||||
|
||||
QImage GenerateBackgroundImage(
|
||||
QSize size,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity,
|
||||
Fn<void(QPainter&)> drawPattern) {
|
||||
auto result = bg.empty()
|
||||
? Images::GenerateGradient(size, { DefaultBackgroundColor() })
|
||||
: Images::GenerateGradient(size, bg, gradientRotation);
|
||||
if (bg.size() > 1 && (!drawPattern || patternOpacity >= 0.)) {
|
||||
result = Images::DitherImage(std::move(result));
|
||||
}
|
||||
if (drawPattern) {
|
||||
auto p = QPainter(&result);
|
||||
if (patternOpacity >= 0.) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||
p.setOpacity(patternOpacity);
|
||||
} else {
|
||||
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
||||
}
|
||||
drawPattern(p);
|
||||
if (patternOpacity < 0. && patternOpacity > -1.) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
p.setOpacity(1. + patternOpacity);
|
||||
p.fillRect(QRect{ QPoint(), size }, Qt::black);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(result).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
|
||||
QImage PreparePatternImage(
|
||||
QImage pattern,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity) {
|
||||
auto result = GenerateBackgroundImage(
|
||||
pattern.size(),
|
||||
bg,
|
||||
gradientRotation,
|
||||
patternOpacity,
|
||||
[&](QPainter &p) {
|
||||
p.drawImage(QRect(QPoint(), pattern.size()), pattern);
|
||||
});
|
||||
|
||||
pattern = QImage();
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
QImage GenerateDitheredGradient(
|
||||
const std::vector<QColor> &colors,
|
||||
int rotation) {
|
||||
constexpr auto kSize = 512;
|
||||
const auto size = QSize(kSize, kSize);
|
||||
if (colors.empty()) {
|
||||
return Images::GenerateGradient(size, { DefaultBackgroundColor() });
|
||||
}
|
||||
auto result = Images::GenerateGradient(size, colors, rotation);
|
||||
if (colors.size() > 1) {
|
||||
result = Images::DitherImage(std::move(result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ChatThemeBackground PrepareBackgroundImage(
|
||||
const QString &path,
|
||||
const QByteArray &bytes,
|
||||
bool gzipSvg,
|
||||
const std::vector<QColor> &colors,
|
||||
bool isPattern,
|
||||
float64 patternOpacity,
|
||||
bool isBlurred) {
|
||||
auto prepared = (isPattern || colors.empty())
|
||||
? PreprocessBackgroundImage(ReadBackgroundImage(path, bytes, gzipSvg))
|
||||
: QImage();
|
||||
if (isPattern && !prepared.isNull()) {
|
||||
if (colors.size() < 2) {
|
||||
const auto gradientRotation = 0; // No gradient here.
|
||||
prepared = PreparePatternImage(
|
||||
std::move(prepared),
|
||||
colors,
|
||||
gradientRotation,
|
||||
patternOpacity);
|
||||
}
|
||||
prepared.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
} else if (colors.empty()) {
|
||||
prepared.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
}
|
||||
const auto imageMonoColor = (colors.size() < 2)
|
||||
? CalculateImageMonoColor(prepared)
|
||||
: std::nullopt;
|
||||
if (!prepared.isNull() && !isPattern && isBlurred) {
|
||||
prepared = PrepareBlurredBackground(std::move(prepared));
|
||||
}
|
||||
return ChatThemeBackground{
|
||||
.prepared = prepared,
|
||||
.preparedForTiled = PrepareImageForTiled(prepared),
|
||||
.colorForFill = (!prepared.isNull()
|
||||
? imageMonoColor
|
||||
: (colors.size() > 1 || colors.empty())
|
||||
? std::nullopt
|
||||
: std::make_optional(colors.front())),
|
||||
.colors = colors,
|
||||
.patternOpacity = patternOpacity,
|
||||
.isPattern = isPattern,
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Window::Theme
|
||||
|
|
|
@ -117,6 +117,7 @@ public:
|
|||
}
|
||||
|
||||
void setBackground(ChatThemeBackground &&background);
|
||||
void updateBackgroundImageFrom(ChatThemeBackground &&background);
|
||||
const ChatThemeBackground &background() const {
|
||||
return _mutableBackground;
|
||||
}
|
||||
|
@ -175,4 +176,40 @@ struct ChatBackgroundRects {
|
|||
QSize fillSize,
|
||||
QSize imageSize);
|
||||
|
||||
[[nodiscard]] QColor CountAverageColor(const QImage &image);
|
||||
[[nodiscard]] QColor ThemeAdjustedColor(QColor original, QColor background);
|
||||
[[nodiscard]] QImage PreprocessBackgroundImage(QImage image);
|
||||
[[nodiscard]] std::optional<QColor> CalculateImageMonoColor(
|
||||
const QImage &image);
|
||||
[[nodiscard]] QImage PrepareImageForTiled(const QImage &prepared);
|
||||
|
||||
[[nodiscard]] QImage ReadBackgroundImage(
|
||||
const QString &path,
|
||||
const QByteArray &content,
|
||||
bool gzipSvg);
|
||||
[[nodiscard]] QImage GenerateBackgroundImage(
|
||||
QSize size,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity = 1.,
|
||||
Fn<void(QPainter&)> drawPattern = nullptr);
|
||||
[[nodiscard]] QImage PreparePatternImage(
|
||||
QImage pattern,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity);
|
||||
[[nodiscard]] QImage PrepareBlurredBackground(QImage image);
|
||||
[[nodiscard]] QImage GenerateDitheredGradient(
|
||||
const std::vector<QColor> &colors,
|
||||
int rotation);
|
||||
|
||||
[[nodiscard]] ChatThemeBackground PrepareBackgroundImage(
|
||||
const QString &path,
|
||||
const QByteArray &bytes,
|
||||
bool gzipSvg,
|
||||
const std::vector<QColor> &colors,
|
||||
bool isPattern,
|
||||
float64 patternOpacity,
|
||||
bool isBlurred);
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -24,8 +24,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/unixtime.h"
|
||||
#include "base/crc32hash.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_document_resolver.h"
|
||||
#include "main/main_account.h" // Account::local.
|
||||
#include "main/main_domain.h" // Domain::activeSessionValue.
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
|
@ -43,7 +45,6 @@ namespace {
|
|||
constexpr auto kThemeFileSizeLimit = 5 * 1024 * 1024;
|
||||
constexpr auto kBackgroundSizeLimit = 25 * 1024 * 1024;
|
||||
constexpr auto kNightThemeFile = ":/gui/night.tdesktop-theme"_cs;
|
||||
constexpr auto kMinimumTiledSize = 512;
|
||||
|
||||
struct Applying {
|
||||
Saved data;
|
||||
|
@ -58,20 +59,6 @@ inline bool AreTestingTheme() {
|
|||
return !GlobalApplying.paletteForRevert.isEmpty();
|
||||
}
|
||||
|
||||
std::optional<QColor> CalculateImageMonoColor(const QImage &image) {
|
||||
if (image.isNull()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
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.size().isEmpty()
|
||||
&& (image.format() == QImage::Format_ARGB32_Premultiplied
|
||||
|
@ -646,7 +633,7 @@ QImage ChatBackground::postprocessBackgroundImage(QImage image) {
|
|||
}
|
||||
|
||||
void ChatBackground::set(const Data::WallPaper &paper, QImage image) {
|
||||
image = PreprocessBackgroundImage(std::move(image));
|
||||
image = Ui::PreprocessBackgroundImage(std::move(image));
|
||||
|
||||
const auto needResetAdjustable = Data::IsDefaultWallPaper(paper)
|
||||
&& !Data::IsDefaultWallPaper(_paper)
|
||||
|
@ -701,7 +688,7 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) {
|
|||
if (_paper.isPattern() && !image.isNull()) {
|
||||
if (bgColors.size() < 2) {
|
||||
auto prepared = postprocessBackgroundImage(
|
||||
Data::PreparePatternImage(
|
||||
Ui::PreparePatternImage(
|
||||
image,
|
||||
bgColors,
|
||||
_paper.gradientRotation(),
|
||||
|
@ -752,7 +739,7 @@ void ChatBackground::setPrepared(
|
|||
Expects(gradient.isNull() || GoodImageFormatAndSize(gradient));
|
||||
|
||||
if (!prepared.isNull() && !_paper.isPattern() && _paper.isBlurred()) {
|
||||
prepared = Data::PrepareBlurredBackground(std::move(prepared));
|
||||
prepared = Ui::PrepareBlurredBackground(std::move(prepared));
|
||||
}
|
||||
if (adjustPaletteRequired()) {
|
||||
if (!gradient.isNull()) {
|
||||
|
@ -768,40 +755,9 @@ void ChatBackground::setPrepared(
|
|||
_prepared = std::move(prepared);
|
||||
_gradient = std::move(gradient);
|
||||
_imageMonoColor = _gradient.isNull()
|
||||
? CalculateImageMonoColor(_prepared)
|
||||
? Ui::CalculateImageMonoColor(_prepared)
|
||||
: std::nullopt;
|
||||
prepareImageForTiled();
|
||||
}
|
||||
|
||||
void ChatBackground::prepareImageForTiled() {
|
||||
const auto width = _prepared.width();
|
||||
const auto height = _prepared.height();
|
||||
const auto isSmallForTiled = (width > 0 && height > 0)
|
||||
&& (width < kMinimumTiledSize || height < kMinimumTiledSize);
|
||||
if (!isSmallForTiled) {
|
||||
_preparedForTiled = _prepared;
|
||||
return;
|
||||
}
|
||||
const auto repeatTimesX = qCeil(kMinimumTiledSize / (1. * width));
|
||||
const auto repeatTimesY = qCeil(kMinimumTiledSize / (1. * height));
|
||||
_preparedForTiled = QImage(
|
||||
width * repeatTimesX,
|
||||
height * repeatTimesY,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
_preparedForTiled.setDevicePixelRatio(_prepared.devicePixelRatio());
|
||||
auto imageForTiledBytes = _preparedForTiled.bits();
|
||||
auto bytesInLine = width * sizeof(uint32);
|
||||
for (auto timesY = 0; timesY != repeatTimesY; ++timesY) {
|
||||
auto imageBytes = _prepared.constBits();
|
||||
for (auto y = 0; y != height; ++y) {
|
||||
for (auto timesX = 0; timesX != repeatTimesX; ++timesX) {
|
||||
memcpy(imageForTiledBytes, imageBytes, bytesInLine);
|
||||
imageForTiledBytes += bytesInLine;
|
||||
}
|
||||
imageBytes += _prepared.bytesPerLine();
|
||||
imageForTiledBytes += _preparedForTiled.bytesPerLine() - (repeatTimesX * bytesInLine);
|
||||
}
|
||||
}
|
||||
_preparedForTiled = Ui::PrepareImageForTiled(_prepared);
|
||||
}
|
||||
|
||||
void ChatBackground::setPaper(const Data::WallPaper &paper) {
|
||||
|
@ -850,13 +806,13 @@ void ChatBackground::clearEditingTheme(ClearEditing clear) {
|
|||
}
|
||||
|
||||
void ChatBackground::adjustPaletteUsingBackground(const QImage &image) {
|
||||
adjustPaletteUsingColor(CountAverageColor(image));
|
||||
adjustPaletteUsingColor(Ui::CountAverageColor(image));
|
||||
}
|
||||
|
||||
void ChatBackground::adjustPaletteUsingColor(QColor color) {
|
||||
const auto prepared = color.toHsl();
|
||||
for (const auto &adjustable : _adjustableColors) {
|
||||
const auto adjusted = AdjustedColor(adjustable.item->c, prepared);
|
||||
const auto adjusted = Ui::ThemeAdjustedColor(adjustable.item->c, prepared);
|
||||
adjustable.item.set(
|
||||
adjusted.red(),
|
||||
adjusted.green(),
|
||||
|
@ -885,10 +841,7 @@ void ChatBackground::recacheGradientForFill(QImage gradient) {
|
|||
|
||||
QImage ChatBackground::createCurrentImage() const {
|
||||
if (const auto fill = colorForFill()) {
|
||||
auto result = QImage(
|
||||
kMinimumTiledSize,
|
||||
kMinimumTiledSize,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
auto result = QImage(512, 512, QImage::Format_ARGB32_Premultiplied);
|
||||
result.fill(*fill);
|
||||
return result;
|
||||
} else if (_gradient.isNull()) {
|
||||
|
@ -1477,64 +1430,6 @@ QString EditingPalettePath() {
|
|||
return cWorkingDir() + "tdata/editing-theme.tdesktop-palette";
|
||||
}
|
||||
|
||||
QColor CountAverageColor(const QImage &image) {
|
||||
Expects(image.format() == QImage::Format_ARGB32_Premultiplied
|
||||
|| image.format() == QImage::Format_RGB32);
|
||||
|
||||
uint64 components[3] = { 0 };
|
||||
const auto w = image.width();
|
||||
const auto h = image.height();
|
||||
const auto size = w * h;
|
||||
if (const auto pix = image.constBits()) {
|
||||
for (auto i = 0, l = size * 4; i != l; i += 4) {
|
||||
components[2] += pix[i + 0];
|
||||
components[1] += pix[i + 1];
|
||||
components[0] += pix[i + 2];
|
||||
}
|
||||
}
|
||||
if (size) {
|
||||
for (auto i = 0; i != 3; ++i) {
|
||||
components[i] /= size;
|
||||
}
|
||||
}
|
||||
return QColor(components[0], components[1], components[2]);
|
||||
}
|
||||
|
||||
QColor AdjustedColor(QColor original, QColor background) {
|
||||
return QColor::fromHslF(
|
||||
background.hslHueF(),
|
||||
background.hslSaturationF(),
|
||||
original.lightnessF(),
|
||||
original.alphaF()
|
||||
).toRgb();
|
||||
}
|
||||
|
||||
QImage PreprocessBackgroundImage(QImage image) {
|
||||
constexpr auto kMaxSize = 2960;
|
||||
|
||||
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||
image = std::move(image).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
if (image.width() > 40 * image.height()) {
|
||||
const auto width = 40 * image.height();
|
||||
const auto height = image.height();
|
||||
image = image.copy((image.width() - width) / 2, 0, width, height);
|
||||
} else if (image.height() > 40 * image.width()) {
|
||||
const auto width = image.width();
|
||||
const auto height = 40 * image.width();
|
||||
image = image.copy(0, (image.height() - height) / 2, width, height);
|
||||
}
|
||||
if (image.width() > kMaxSize || image.height() > kMaxSize) {
|
||||
image = image.scaled(
|
||||
kMaxSize,
|
||||
kMaxSize,
|
||||
Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
bool ReadPaletteValues(const QByteArray &content, Fn<bool(QLatin1String name, QLatin1String value)> callback) {
|
||||
if (content.size() > kThemeSchemeSizeLimit) {
|
||||
LOG(("Theme Error: color scheme file too large (should be less than 1 MB, got %2)").arg(content.size()));
|
||||
|
|
|
@ -18,6 +18,10 @@ namespace Window {
|
|||
class Controller;
|
||||
} // namespace Window
|
||||
|
||||
namespace Ui {
|
||||
struct ChatThemeBackground;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
namespace Theme {
|
||||
|
||||
|
@ -107,9 +111,6 @@ bool LoadFromContent(
|
|||
const QByteArray &content,
|
||||
not_null<Instance*> out,
|
||||
Cached *outCache);
|
||||
[[nodiscard]] QColor CountAverageColor(const QImage &image);
|
||||
[[nodiscard]] QColor AdjustedColor(QColor original, QColor background);
|
||||
[[nodiscard]] QImage PreprocessBackgroundImage(QImage image);
|
||||
|
||||
struct BackgroundUpdate {
|
||||
enum class Type {
|
||||
|
@ -280,7 +281,9 @@ private:
|
|||
|
||||
[[nodiscard]] ChatBackground *Background();
|
||||
|
||||
bool ReadPaletteValues(const QByteArray &content, Fn<bool(QLatin1String name, QLatin1String value)> callback);
|
||||
bool ReadPaletteValues(
|
||||
const QByteArray &content,
|
||||
Fn<bool(QLatin1String name, QLatin1String value)> callback);
|
||||
|
||||
} // namespace Theme
|
||||
} // namespace Window
|
||||
|
|
|
@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_document.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/data_session.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/image/image_prepare.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/toast/toast.h"
|
||||
|
@ -185,7 +186,7 @@ void CloudListCheck::ensureContrast() {
|
|||
QImage::Format_ARGB32_Premultiplied);
|
||||
const auto active = style::internal::EnsureContrast(
|
||||
_colors->radiobuttonActive,
|
||||
CountAverageColor(image));
|
||||
Ui::CountAverageColor(image));
|
||||
_colors->radiobuttonInactive = _colors->radiobuttonActive = QColor(
|
||||
active.red(),
|
||||
active.green(),
|
||||
|
|
|
@ -996,7 +996,7 @@ void MainMenu::refreshBackground() {
|
|||
prepared.size());
|
||||
|
||||
auto backgroundImage = paper.isPattern()
|
||||
? Data::GenerateWallPaper(
|
||||
? Ui::GenerateBackgroundImage(
|
||||
fill * cIntRetinaFactor(),
|
||||
paper.backgroundColors(),
|
||||
paper.gradientRotation(),
|
||||
|
|
|
@ -28,6 +28,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/data_document_resolver.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_group_call.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
|
@ -71,6 +74,34 @@ namespace {
|
|||
|
||||
constexpr auto kMaxChatEntryHistorySize = 50;
|
||||
|
||||
[[nodiscard]] Fn<void(style::palette&)> PreparePaletteCallback(
|
||||
bool dark,
|
||||
std::optional<QColor> accent) {
|
||||
return [=](style::palette &palette) {
|
||||
using namespace Theme;
|
||||
if (dark) {
|
||||
const auto &embedded = EmbeddedThemes();
|
||||
const auto i = ranges::find(
|
||||
embedded,
|
||||
EmbeddedType::Night,
|
||||
&EmbeddedScheme::type);
|
||||
Assert(i != end(embedded));
|
||||
|
||||
auto instance = Instance();
|
||||
const auto loaded = LoadFromFile(
|
||||
i->path,
|
||||
&instance,
|
||||
nullptr,
|
||||
nullptr,
|
||||
accent ? ColorizerFrom(*i, *accent) : Colorizer());
|
||||
Assert(loaded);
|
||||
palette = instance.palette;
|
||||
} else {
|
||||
// #TODO themes apply accent color to classic theme
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ActivateWindow(not_null<SessionController*> controller) {
|
||||
|
@ -458,6 +489,13 @@ void SessionNavigation::showPollResults(
|
|||
showSection(std::make_shared<Info::Memento>(poll, contextId), params);
|
||||
}
|
||||
|
||||
struct SessionController::CachedTheme {
|
||||
std::shared_ptr<Ui::ChatTheme> theme;
|
||||
std::shared_ptr<Data::DocumentMedia> media;
|
||||
Data::WallPaper paper;
|
||||
rpl::lifetime lifetime;
|
||||
};
|
||||
|
||||
SessionController::SessionController(
|
||||
not_null<Main::Session*> session,
|
||||
not_null<Controller*> window)
|
||||
|
@ -1325,8 +1363,8 @@ auto SessionController::cachedChatThemeValue(
|
|||
return rpl::single(_defaultChatTheme);
|
||||
}
|
||||
const auto i = _customChatThemes.find(key);
|
||||
if (i != end(_customChatThemes) && i->second) {
|
||||
return rpl::single(i->second);
|
||||
if (i != end(_customChatThemes) && i->second.theme) {
|
||||
return rpl::single(i->second.theme);
|
||||
}
|
||||
if (i == end(_customChatThemes)) {
|
||||
cacheChatTheme(data);
|
||||
|
@ -1358,57 +1396,119 @@ void SessionController::pushDefaultChatBackground() {
|
|||
|
||||
void SessionController::cacheChatTheme(const Data::CloudTheme &data) {
|
||||
Expects(data.id != 0);
|
||||
|
||||
using namespace Theme;
|
||||
Expects(data.paper.has_value());
|
||||
Expects(!data.paper->backgroundColors().empty());
|
||||
|
||||
const auto key = data.id;
|
||||
const auto dark = data.basedOnDark;
|
||||
const auto accent = data.accentColor;
|
||||
if (data.paper) {
|
||||
const auto document = data.paper->document();
|
||||
|
||||
}
|
||||
const auto preparePalette = [=](style::palette &palette) {
|
||||
if (dark) {
|
||||
const auto &embedded = Theme::EmbeddedThemes();
|
||||
const auto i = ranges::find(
|
||||
embedded,
|
||||
EmbeddedType::Night,
|
||||
&EmbeddedScheme::type);
|
||||
Assert(i != end(embedded));
|
||||
|
||||
auto instance = Instance();
|
||||
const auto loaded = LoadFromFile(
|
||||
i->path,
|
||||
&instance,
|
||||
nullptr,
|
||||
nullptr,
|
||||
accent ? ColorizerFrom(*i, *accent) : Colorizer());
|
||||
Assert(loaded);
|
||||
palette = instance.palette;
|
||||
} else {
|
||||
// #TODO themes apply accent color to classic theme
|
||||
}
|
||||
};
|
||||
const auto prepareBackground = [] {
|
||||
return Ui::ChatThemeBackground{
|
||||
|
||||
};
|
||||
};
|
||||
const auto document = data.paper->document();
|
||||
const auto media = document ? document->createMediaView() : nullptr;
|
||||
data.paper->loadDocument();
|
||||
auto &theme = _customChatThemes.emplace(
|
||||
key,
|
||||
CachedTheme{ .media = media, .paper = *data.paper }).first->second;
|
||||
auto descriptor = Ui::ChatThemeDescriptor{
|
||||
.id = key,
|
||||
.preparePalette = preparePalette,
|
||||
.prepareBackground = prepareBackground,
|
||||
.preparePalette = PreparePaletteCallback(
|
||||
data.basedOnDark,
|
||||
data.accentColor),
|
||||
.prepareBackground = backgroundGenerator(theme),
|
||||
};
|
||||
crl::async([this, descriptor = std::move(descriptor), weak = base::make_weak(this)] {
|
||||
crl::on_main(weak, [
|
||||
crl::async([
|
||||
this,
|
||||
descriptor = std::move(descriptor),
|
||||
weak = base::make_weak(this)
|
||||
]{
|
||||
crl::on_main(weak,[
|
||||
this,
|
||||
result = std::make_shared<Ui::ChatTheme>(std::move(descriptor))
|
||||
]() mutable {
|
||||
_customChatThemes.emplace(result->key(), result);
|
||||
_cachedThemesStream.fire(std::move(result));
|
||||
cacheChatThemeDone(std::move(result));
|
||||
});
|
||||
});
|
||||
if (media && media->loaded(true)) {
|
||||
theme.media = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SessionController::cacheChatThemeDone(
|
||||
std::shared_ptr<Ui::ChatTheme> result) {
|
||||
Expects(result != nullptr);
|
||||
|
||||
const auto key = result->key();
|
||||
const auto i = _customChatThemes.find(key);
|
||||
if (i == end(_customChatThemes)) {
|
||||
return;
|
||||
}
|
||||
i->second.theme = result;
|
||||
if (i->second.media) {
|
||||
if (i->second.media->loaded(true)) {
|
||||
updateCustomThemeBackground(i->second);
|
||||
} else {
|
||||
session().downloaderTaskFinished(
|
||||
) | rpl::filter([=] {
|
||||
const auto i = _customChatThemes.find(key);
|
||||
Assert(i != end(_customChatThemes));
|
||||
return !i->second.media || i->second.media->loaded(true);
|
||||
}) | rpl::start_with_next([=] {
|
||||
const auto i = _customChatThemes.find(key);
|
||||
Assert(i != end(_customChatThemes));
|
||||
updateCustomThemeBackground(i->second);
|
||||
}, i->second.lifetime);
|
||||
}
|
||||
}
|
||||
_cachedThemesStream.fire(std::move(result));
|
||||
}
|
||||
|
||||
void SessionController::updateCustomThemeBackground(CachedTheme &theme) {
|
||||
const auto guard = gsl::finally([&] {
|
||||
theme.lifetime.destroy();
|
||||
theme.media = nullptr;
|
||||
});
|
||||
if (!theme.media || !theme.theme || !theme.media->loaded(true)) {
|
||||
return;
|
||||
}
|
||||
const auto key = theme.theme->key();
|
||||
const auto weak = base::make_weak(this);
|
||||
crl::async([=, generator = backgroundGenerator(theme, false)] {
|
||||
crl::on_main(weak, [=, result = generator()]() mutable {
|
||||
const auto i = _customChatThemes.find(key);
|
||||
if (i != end(_customChatThemes)) {
|
||||
i->second.theme->updateBackgroundImageFrom(std::move(result));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Fn<Ui::ChatThemeBackground()> SessionController::backgroundGenerator(
|
||||
CachedTheme &theme,
|
||||
bool generateGradient) {
|
||||
const auto &paper = theme.paper;
|
||||
const auto &media = theme.media;
|
||||
const auto paperPath = media ? media->owner()->filepath() : QString();
|
||||
const auto paperBytes = media ? media->bytes() : QByteArray();
|
||||
const auto gzipSvg = media && media->owner()->isPatternWallPaperSVG();
|
||||
const auto &colors = paper.backgroundColors();
|
||||
const auto isPattern = paper.isPattern();
|
||||
const auto patternOpacity = paper.patternOpacity();
|
||||
const auto isBlurred = paper.isBlurred();
|
||||
const auto gradientRotation = paper.gradientRotation();
|
||||
return [=] {
|
||||
auto result = Ui::PrepareBackgroundImage(
|
||||
paperPath,
|
||||
paperBytes,
|
||||
gzipSvg,
|
||||
colors,
|
||||
isPattern,
|
||||
patternOpacity,
|
||||
isBlurred);
|
||||
if (generateGradient) {
|
||||
result.gradientForFill = (colors.size() > 1)
|
||||
? Ui::GenerateDitheredGradient(colors, gradientRotation)
|
||||
: QImage();
|
||||
result.gradientRotation = gradientRotation;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
HistoryView::PaintContext SessionController::preparePaintContext(
|
||||
|
|
|
@ -47,6 +47,7 @@ class LayerWidget;
|
|||
enum class ReportReason;
|
||||
class ChatTheme;
|
||||
struct ChatPaintContext;
|
||||
struct ChatThemeBackground;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Data {
|
||||
|
@ -419,6 +420,8 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
struct CachedTheme;
|
||||
|
||||
void init();
|
||||
void initSupportMode();
|
||||
void refreshFiltersMenu();
|
||||
|
@ -445,6 +448,11 @@ private:
|
|||
|
||||
void pushDefaultChatBackground();
|
||||
void cacheChatTheme(const Data::CloudTheme &data);
|
||||
void cacheChatThemeDone(std::shared_ptr<Ui::ChatTheme> result);
|
||||
void updateCustomThemeBackground(CachedTheme &theme);
|
||||
[[nodiscard]] Fn<Ui::ChatThemeBackground()> backgroundGenerator(
|
||||
CachedTheme &theme,
|
||||
bool generateGradient = true);
|
||||
|
||||
const not_null<Controller*> _window;
|
||||
|
||||
|
@ -474,7 +482,7 @@ private:
|
|||
rpl::event_stream<> _filtersMenuChanged;
|
||||
|
||||
std::shared_ptr<Ui::ChatTheme> _defaultChatTheme;
|
||||
base::flat_map<uint64, std::shared_ptr<Ui::ChatTheme>> _customChatThemes;
|
||||
base::flat_map<uint64, CachedTheme> _customChatThemes;
|
||||
rpl::event_stream<std::shared_ptr<Ui::ChatTheme>> _cachedThemesStream;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
|
Loading…
Add table
Reference in a new issue