mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 10:11:41 -05:00
Add support for pattern wallpapers.
This commit is contained in:
parent
c2744700c0
commit
b6edf4561d
13 changed files with 401 additions and 107 deletions
|
@ -398,8 +398,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_theme_keep_changes" = "Keep changes";
|
"lng_theme_keep_changes" = "Keep changes";
|
||||||
"lng_theme_revert" = "Revert";
|
"lng_theme_revert" = "Revert";
|
||||||
"lng_background_header" = "Background preview";
|
"lng_background_header" = "Background preview";
|
||||||
"lng_background_text1" = "You can't swipe left or right to preview anything - this is tdesktop, sorry.";
|
"lng_background_text1" = "Ah, you kids today with techno music! You should enjoy the classics, like Hasselhoff!";
|
||||||
"lng_background_text2" = "Sounds awful.";
|
"lng_background_text2" = "I can't even take you seriously right now.";
|
||||||
"lng_background_bad_link" = "This background link appears to be invalid.";
|
"lng_background_bad_link" = "This background link appears to be invalid.";
|
||||||
"lng_background_apply" = "Apply";
|
"lng_background_apply" = "Apply";
|
||||||
"lng_background_share" = "Share";
|
"lng_background_share" = "Share";
|
||||||
|
|
|
@ -74,9 +74,9 @@ AdminLog::OwnedItem GenerateTextItem(
|
||||||
return AdminLog::OwnedItem(delegate, item);
|
return AdminLog::OwnedItem(delegate, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage PrepareScaledFromFull(
|
QImage PrepareScaledNonPattern(
|
||||||
const QImage &image,
|
const QImage &image,
|
||||||
Images::Option blur = Images::Option(0)) {
|
Images::Option blur) {
|
||||||
const auto size = st::boxWideWidth;
|
const auto size = st::boxWideWidth;
|
||||||
const auto width = std::max(image.width(), 1);
|
const auto width = std::max(image.width(), 1);
|
||||||
const auto height = std::max(image.height(), 1);
|
const auto height = std::max(image.height(), 1);
|
||||||
|
@ -90,17 +90,71 @@ QImage PrepareScaledFromFull(
|
||||||
image,
|
image,
|
||||||
takeWidth,
|
takeWidth,
|
||||||
takeHeight,
|
takeHeight,
|
||||||
Images::Option::Smooth | blur,
|
Images::Option::Smooth
|
||||||
|
| Images::Option::TransparentBackground
|
||||||
|
| blur,
|
||||||
size,
|
size,
|
||||||
size);
|
size);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap PrepareScaledFromThumb(not_null<Image*> thumb, bool good) {
|
QImage ColorizePattern(QImage image, QColor color) {
|
||||||
return thumb->loaded()
|
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||||
? App::pixmapFromImageInPlace(PrepareScaledFromFull(
|
image = std::move(image).convertToFormat(
|
||||||
thumb->original(),
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
good ? Images::Option(0) : Images::Option::Blurred))
|
}
|
||||||
: QPixmap();
|
// Similar to style::colorizeImage.
|
||||||
|
// But style::colorizeImage takes pattern with all pixels having the
|
||||||
|
// same components value, from (0, 0, 0, 0) to (255, 255, 255, 255).
|
||||||
|
//
|
||||||
|
// While in patterns we have different value ranges, usually they are
|
||||||
|
// from (0, 0, 0, 0) to (0, 0, 0, 255), so we should use only 'alpha'.
|
||||||
|
|
||||||
|
const auto width = image.width();
|
||||||
|
const auto height = image.height();
|
||||||
|
const auto pattern = anim::shifted(color);
|
||||||
|
|
||||||
|
const auto resultBytesPerPixel = (image.depth() >> 3);
|
||||||
|
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) {
|
||||||
|
auto maskOpacity = static_cast<anim::ShiftedMultiplier>(*maskBytes) + 1;
|
||||||
|
*resultInts = anim::unshifted(pattern * maskOpacity);
|
||||||
|
maskBytes += maskBytesPerPixel;
|
||||||
|
resultInts += resultIntsPerPixel;
|
||||||
|
}
|
||||||
|
maskBytes += maskBytesAdded;
|
||||||
|
resultInts += resultIntsAdded;
|
||||||
|
}
|
||||||
|
return std::move(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage PrepareScaledFromFull(
|
||||||
|
const QImage &image,
|
||||||
|
std::optional<QColor> patternBackground,
|
||||||
|
Images::Option blur = Images::Option(0)) {
|
||||||
|
auto result = PrepareScaledNonPattern(image, blur);
|
||||||
|
if (patternBackground) {
|
||||||
|
result = ColorizePattern(
|
||||||
|
std::move(result),
|
||||||
|
Window::Theme::PatternColor(*patternBackground));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -358,10 +412,11 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
|
||||||
Painter p(this);
|
Painter p(this);
|
||||||
|
|
||||||
const auto ms = getms();
|
const auto ms = getms();
|
||||||
|
const auto color = _paper.backgroundColor();
|
||||||
if (const auto color = _paper.backgroundColor()) {
|
if (color) {
|
||||||
p.fillRect(e->rect(), *color);
|
p.fillRect(e->rect(), *color);
|
||||||
} else {
|
}
|
||||||
|
if (!color || _paper.isPattern()) {
|
||||||
if (_scaled.isNull() && !setScaledFromThumb()) {
|
if (_scaled.isNull() && !setScaledFromThumb()) {
|
||||||
p.fillRect(e->rect(), st::boxBg);
|
p.fillRect(e->rect(), st::boxBg);
|
||||||
return;
|
return;
|
||||||
|
@ -375,6 +430,11 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
|
||||||
void BackgroundPreviewBox::paintImage(Painter &p) {
|
void BackgroundPreviewBox::paintImage(Painter &p) {
|
||||||
Expects(!_scaled.isNull());
|
Expects(!_scaled.isNull());
|
||||||
|
|
||||||
|
p.setOpacity(_paper.isPattern()
|
||||||
|
? std::clamp(_paper.patternIntensity() / 100., 0., 1.)
|
||||||
|
: 1.);
|
||||||
|
const auto guard = gsl::finally([&] { p.setOpacity(1.); });
|
||||||
|
|
||||||
const auto factor = cIntRetinaFactor();
|
const auto factor = cIntRetinaFactor();
|
||||||
const auto size = st::boxWideWidth;
|
const auto size = st::boxWideWidth;
|
||||||
const auto from = QRect(
|
const auto from = QRect(
|
||||||
|
@ -457,10 +517,25 @@ void BackgroundPreviewBox::step_radial(TimeMs ms, bool timer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BackgroundPreviewBox::setScaledFromThumb() {
|
bool BackgroundPreviewBox::setScaledFromThumb() {
|
||||||
_scaled = PrepareScaledFromThumb(
|
Expects(_paper.thumbnail() != nullptr);
|
||||||
_paper.thumbnail(),
|
|
||||||
!_paper.document());
|
const auto thumbnail = _paper.thumbnail();
|
||||||
return !_scaled.isNull();
|
if (!thumbnail->loaded()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
setScaledFromImage(PrepareScaledFromFull(
|
||||||
|
thumbnail->original(),
|
||||||
|
patternBackgroundColor(),
|
||||||
|
_paper.document() ? Images::Option::Blurred : Images::Option(0)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundPreviewBox::setScaledFromImage(QImage &&image) {
|
||||||
|
_scaled = App::pixmapFromImageInPlace(std::move(image));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<QColor> BackgroundPreviewBox::patternBackgroundColor() const {
|
||||||
|
return _paper.isPattern() ? _paper.backgroundColor() : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackgroundPreviewBox::checkLoadedDocument() {
|
void BackgroundPreviewBox::checkLoadedDocument() {
|
||||||
|
@ -477,9 +552,10 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
||||||
crl::async([
|
crl::async([
|
||||||
this,
|
this,
|
||||||
image = std::move(image),
|
image = std::move(image),
|
||||||
|
patternBackground = patternBackgroundColor(),
|
||||||
guard = std::move(right)
|
guard = std::move(right)
|
||||||
]() mutable {
|
]() mutable {
|
||||||
auto scaled = PrepareScaledFromFull(image);
|
auto scaled = PrepareScaledFromFull(image, patternBackground);
|
||||||
crl::on_main([
|
crl::on_main([
|
||||||
this,
|
this,
|
||||||
image = std::move(image),
|
image = std::move(image),
|
||||||
|
@ -489,7 +565,7 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
||||||
if (!guard) {
|
if (!guard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_scaled = App::pixmapFromImageInPlace(std::move(scaled));
|
setScaledFromImage(std::move(scaled));
|
||||||
_full = std::move(image);
|
_full = std::move(image);
|
||||||
update();
|
update();
|
||||||
});
|
});
|
||||||
|
@ -497,17 +573,19 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BackgroundPreviewBox::Start(const QString &slug, const QString &mode) {
|
bool BackgroundPreviewBox::Start(
|
||||||
|
const QString &slug,
|
||||||
|
const QMap<QString, QString> ¶ms) {
|
||||||
if (const auto paper = Data::WallPaper::FromColorSlug(slug)) {
|
if (const auto paper = Data::WallPaper::FromColorSlug(slug)) {
|
||||||
Ui::show(Box<BackgroundPreviewBox>(*paper));
|
Ui::show(Box<BackgroundPreviewBox>(paper->withUrlParams(params)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!IsValidWallPaperSlug(slug)) {
|
if (!IsValidWallPaperSlug(slug)) {
|
||||||
Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
|
Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Auth().api().requestWallPaper(slug, [](const Data::WallPaper &result) {
|
Auth().api().requestWallPaper(slug, [=](const Data::WallPaper &result) {
|
||||||
Ui::show(Box<BackgroundPreviewBox>(result));
|
Ui::show(Box<BackgroundPreviewBox>(result.withUrlParams(params)));
|
||||||
}, [](const RPCError &error) {
|
}, [](const RPCError &error) {
|
||||||
Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
|
Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,7 +39,9 @@ class BackgroundPreviewBox
|
||||||
public:
|
public:
|
||||||
BackgroundPreviewBox(QWidget*, const Data::WallPaper &paper);
|
BackgroundPreviewBox(QWidget*, const Data::WallPaper &paper);
|
||||||
|
|
||||||
static bool Start(const QString &slug, const QString &mode);
|
static bool Start(
|
||||||
|
const QString &slug,
|
||||||
|
const QMap<QString, QString> ¶ms);
|
||||||
|
|
||||||
using Element = HistoryView::Element;
|
using Element = HistoryView::Element;
|
||||||
HistoryView::Context elementContext() override;
|
HistoryView::Context elementContext() override;
|
||||||
|
@ -67,6 +69,8 @@ private:
|
||||||
|
|
||||||
void checkLoadedDocument();
|
void checkLoadedDocument();
|
||||||
bool setScaledFromThumb();
|
bool setScaledFromThumb();
|
||||||
|
void setScaledFromImage(QImage &&image);
|
||||||
|
std::optional<QColor> patternBackgroundColor() const;
|
||||||
void paintImage(Painter &p);
|
void paintImage(Painter &p);
|
||||||
void paintRadial(Painter &p, TimeMs ms);
|
void paintRadial(Painter &p, TimeMs ms);
|
||||||
void paintTexts(Painter &p, TimeMs ms);
|
void paintTexts(Painter &p, TimeMs ms);
|
||||||
|
|
|
@ -69,7 +69,7 @@ boxTitle: FlatLabel(defaultFlatLabel) {
|
||||||
linkFontOver: font(17px semibold underline);
|
linkFontOver: font(17px semibold underline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boxTitlePosition: point(23px, 20px);
|
boxTitlePosition: point(23px, 16px);
|
||||||
boxTitleHeight: 56px;
|
boxTitleHeight: 56px;
|
||||||
boxLayerTitlePosition: point(23px, 16px);
|
boxLayerTitlePosition: point(23px, 16px);
|
||||||
boxLayerTitleHeight: 56px;
|
boxLayerTitleHeight: 56px;
|
||||||
|
|
|
@ -173,7 +173,7 @@ bool ShowWallPaper(const Match &match, const QVariant &context) {
|
||||||
qthelp::UrlParamNameTransform::ToLower);
|
qthelp::UrlParamNameTransform::ToLower);
|
||||||
return BackgroundPreviewBox::Start(
|
return BackgroundPreviewBox::Start(
|
||||||
params.value(qsl("slug")),
|
params.value(qsl("slug")),
|
||||||
params.value(qsl("mode")));
|
params);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ResolveUsername(const Match &match, const QVariant &context) {
|
bool ResolveUsername(const Match &match, const QVariant &context) {
|
||||||
|
|
|
@ -1057,7 +1057,7 @@ bool MainWidget::sendMessageFail(const RPCError &error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::cacheBackground() {
|
void MainWidget::cacheBackground() {
|
||||||
if (Window::Theme::Background()->color()) {
|
if (Window::Theme::Background()->colorForFill()) {
|
||||||
return;
|
return;
|
||||||
} else if (Window::Theme::Background()->tile()) {
|
} else if (Window::Theme::Background()->tile()) {
|
||||||
auto &bg = Window::Theme::Background()->pixmapForTiled();
|
auto &bg = Window::Theme::Background()->pixmapForTiled();
|
||||||
|
|
|
@ -265,7 +265,7 @@ void BackgroundRow::updateImage() {
|
||||||
Painter p(&back);
|
Painter p(&back);
|
||||||
PainterHighQualityEnabler hq(p);
|
PainterHighQualityEnabler hq(p);
|
||||||
|
|
||||||
if (const auto color = Window::Theme::Background()->color()) {
|
if (const auto color = Window::Theme::Background()->colorForFill()) {
|
||||||
p.fillRect(
|
p.fillRect(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -48,9 +48,11 @@ constexpr auto kFileLoaderQueueStopTimeout = TimeMs(5000);
|
||||||
constexpr auto kDefaultStickerInstallDate = TimeId(1);
|
constexpr auto kDefaultStickerInstallDate = TimeId(1);
|
||||||
constexpr auto kProxyTypeShift = 1024;
|
constexpr auto kProxyTypeShift = 1024;
|
||||||
constexpr auto kWriteMapTimeout = TimeMs(1000);
|
constexpr auto kWriteMapTimeout = TimeMs(1000);
|
||||||
|
constexpr auto kSavedBackgroundFormat = QImage::Format_ARGB32_Premultiplied;
|
||||||
|
|
||||||
constexpr auto kWallPaperLegacySerializeTagId = int32(-111);
|
constexpr auto kWallPaperLegacySerializeTagId = int32(-111);
|
||||||
constexpr auto kWallPaperSerializeTagId = int32(-112);
|
constexpr auto kWallPaperSerializeTagId = int32(-112);
|
||||||
|
constexpr auto kWallPaperSidesLimit = 10000;
|
||||||
|
|
||||||
constexpr auto kSinglePeerTypeUser = qint32(1);
|
constexpr auto kSinglePeerTypeUser = qint32(1);
|
||||||
constexpr auto kSinglePeerTypeChat = qint32(2);
|
constexpr auto kSinglePeerTypeChat = qint32(2);
|
||||||
|
@ -3960,7 +3962,7 @@ void readSavedGifs() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeBackground(const Data::WallPaper &paper, const QImage &img) {
|
void writeBackground(const Data::WallPaper &paper, const QImage &image) {
|
||||||
if (!_working() || !_backgroundCanWrite) {
|
if (!_working() || !_backgroundCanWrite) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3973,11 +3975,33 @@ void writeBackground(const Data::WallPaper &paper, const QImage &img) {
|
||||||
auto &backgroundKey = Window::Theme::IsNightMode()
|
auto &backgroundKey = Window::Theme::IsNightMode()
|
||||||
? _backgroundKeyNight
|
? _backgroundKeyNight
|
||||||
: _backgroundKeyDay;
|
: _backgroundKeyDay;
|
||||||
QByteArray bmp;
|
auto imageData = QByteArray();
|
||||||
if (!img.isNull()) {
|
if (!image.isNull()) {
|
||||||
QBuffer buf(&bmp);
|
const auto width = qint32(image.width());
|
||||||
if (!img.save(&buf, "BMP")) {
|
const auto height = qint32(image.height());
|
||||||
return;
|
const auto perpixel = (image.depth() >> 3);
|
||||||
|
const auto srcperline = image.bytesPerLine();
|
||||||
|
const auto srcsize = srcperline * height;
|
||||||
|
const auto dstperline = width * perpixel;
|
||||||
|
const auto dstsize = dstperline * height;
|
||||||
|
const auto copy = (image.format() != kSavedBackgroundFormat)
|
||||||
|
? image.convertToFormat(kSavedBackgroundFormat)
|
||||||
|
: image;
|
||||||
|
imageData.resize(2 * sizeof(qint32) + dstsize);
|
||||||
|
|
||||||
|
auto dst = bytes::make_detached_span(imageData);
|
||||||
|
bytes::copy(dst, bytes::object_as_span(&width));
|
||||||
|
dst = dst.subspan(sizeof(qint32));
|
||||||
|
bytes::copy(dst, bytes::object_as_span(&height));
|
||||||
|
dst = dst.subspan(sizeof(qint32));
|
||||||
|
const auto src = bytes::make_span(image.constBits(), srcsize);
|
||||||
|
if (srcsize == dstsize) {
|
||||||
|
bytes::copy(dst, src);
|
||||||
|
} else {
|
||||||
|
for (auto y = 0; y != height; ++y) {
|
||||||
|
bytes::copy(dst, src.subspan(y * srcperline, dstperline));
|
||||||
|
dst = dst.subspan(dstperline);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!backgroundKey) {
|
if (!backgroundKey) {
|
||||||
|
@ -3988,20 +4012,12 @@ void writeBackground(const Data::WallPaper &paper, const QImage &img) {
|
||||||
const auto serialized = paper.serialize();
|
const auto serialized = paper.serialize();
|
||||||
quint32 size = sizeof(qint32)
|
quint32 size = sizeof(qint32)
|
||||||
+ Serialize::bytearraySize(serialized)
|
+ Serialize::bytearraySize(serialized)
|
||||||
+ Serialize::bytearraySize(bmp);
|
+ Serialize::bytearraySize(imageData);
|
||||||
EncryptedDescriptor data(size);
|
EncryptedDescriptor data(size);
|
||||||
data.stream
|
data.stream
|
||||||
<< qint32(kWallPaperSerializeTagId)
|
<< qint32(kWallPaperSerializeTagId)
|
||||||
<< serialized
|
<< serialized
|
||||||
<< bmp;
|
<< imageData;
|
||||||
//+2 * sizeof(quint64)
|
|
||||||
// + sizeof(quint32)
|
|
||||||
// + Serialize::stringSize(paper.slug)
|
|
||||||
|
|
||||||
// << quint64(paper.id)
|
|
||||||
// << quint64(paper.accessHash)
|
|
||||||
// << quint32(paper.flags.value())
|
|
||||||
// << paper.slug
|
|
||||||
|
|
||||||
FileWriteDescriptor file(backgroundKey);
|
FileWriteDescriptor file(backgroundKey);
|
||||||
file.writeEncrypted(data);
|
file.writeEncrypted(data);
|
||||||
|
@ -4052,8 +4068,8 @@ bool readBackground() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray bmp;
|
QByteArray imageData;
|
||||||
bg.stream >> bmp;
|
bg.stream >> imageData;
|
||||||
const auto isOldEmptyImage = (bg.stream.status() != QDataStream::Ok);
|
const auto isOldEmptyImage = (bg.stream.status() != QDataStream::Ok);
|
||||||
if (isOldEmptyImage
|
if (isOldEmptyImage
|
||||||
|| Data::IsLegacy1DefaultWallPaper(*paper)
|
|| Data::IsLegacy1DefaultWallPaper(*paper)
|
||||||
|
@ -4067,20 +4083,56 @@ bool readBackground() {
|
||||||
}
|
}
|
||||||
_backgroundCanWrite = true;
|
_backgroundCanWrite = true;
|
||||||
return true;
|
return true;
|
||||||
} else if (Data::IsThemeWallPaper(*paper) && bmp.isEmpty()) {
|
} else if (Data::IsThemeWallPaper(*paper) && imageData.isEmpty()) {
|
||||||
_backgroundCanWrite = false;
|
_backgroundCanWrite = false;
|
||||||
Window::Theme::Background()->setImage(*paper);
|
Window::Theme::Background()->setImage(*paper);
|
||||||
_backgroundCanWrite = true;
|
_backgroundCanWrite = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto image = QImage();
|
auto image = QImage();
|
||||||
auto buffer = QBuffer(&bmp);
|
if (legacyId == kWallPaperSerializeTagId) {
|
||||||
auto reader = QImageReader(&buffer);
|
const auto perpixel = 4;
|
||||||
|
auto src = bytes::make_span(imageData);
|
||||||
|
auto width = qint32();
|
||||||
|
auto height = qint32();
|
||||||
|
if (src.size() > 2 * sizeof(qint32)) {
|
||||||
|
bytes::copy(
|
||||||
|
bytes::object_as_span(&width),
|
||||||
|
src.subspan(0, sizeof(qint32)));
|
||||||
|
src = src.subspan(sizeof(qint32));
|
||||||
|
bytes::copy(
|
||||||
|
bytes::object_as_span(&height),
|
||||||
|
src.subspan(0, sizeof(qint32)));
|
||||||
|
src = src.subspan(sizeof(qint32));
|
||||||
|
if (width + height <= kWallPaperSidesLimit
|
||||||
|
&& src.size() == width * height * perpixel) {
|
||||||
|
image = QImage(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
if (!image.isNull()) {
|
||||||
|
const auto srcperline = width * perpixel;
|
||||||
|
const auto srcsize = srcperline * height;
|
||||||
|
const auto dstperline = image.bytesPerLine();
|
||||||
|
const auto dstsize = dstperline * height;
|
||||||
|
Assert(srcsize == dstsize);
|
||||||
|
bytes::copy(
|
||||||
|
bytes::make_span(image.bits(), dstsize),
|
||||||
|
src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto buffer = QBuffer(&imageData);
|
||||||
|
auto reader = QImageReader(&buffer);
|
||||||
#ifndef OS_MAC_OLD
|
#ifndef OS_MAC_OLD
|
||||||
reader.setAutoTransform(true);
|
reader.setAutoTransform(true);
|
||||||
#endif // OS_MAC_OLD
|
#endif // OS_MAC_OLD
|
||||||
if (reader.read(&image) || paper->backgroundColor()) {
|
if (!reader.read(&image)) {
|
||||||
|
image = QImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!image.isNull()|| paper->backgroundColor()) {
|
||||||
_backgroundCanWrite = false;
|
_backgroundCanWrite = false;
|
||||||
Window::Theme::Background()->setImage(*paper, std::move(image));
|
Window::Theme::Background()->setImage(*paper, std::move(image));
|
||||||
_backgroundCanWrite = true;
|
_backgroundCanWrite = true;
|
||||||
|
|
|
@ -140,7 +140,7 @@ void writeSavedGifs();
|
||||||
void readSavedGifs();
|
void readSavedGifs();
|
||||||
int32 countSavedGifsHash();
|
int32 countSavedGifsHash();
|
||||||
|
|
||||||
void writeBackground(const Data::WallPaper &paper, const QImage &img);
|
void writeBackground(const Data::WallPaper &paper, const QImage &image);
|
||||||
bool readBackground();
|
bool readBackground();
|
||||||
|
|
||||||
void writeTheme(const Window::Theme::Saved &saved);
|
void writeTheme(const Window::Theme::Saved &saved);
|
||||||
|
|
|
@ -62,6 +62,10 @@ void stopManager() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect, QPoint dstPoint) {
|
void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect, QPoint dstPoint) {
|
||||||
|
// In background_box ColorizePattern we use the fact that
|
||||||
|
// colorizeImage takes only first byte of the mask, so it
|
||||||
|
// could be used for wallpaper patterns, which have values
|
||||||
|
// in ranges (0, 0, 0, 0) to (0, 0, 0, 255) (only 'alpha').
|
||||||
if (srcRect.isNull()) {
|
if (srcRect.isNull()) {
|
||||||
srcRect = src.rect();
|
srcRect = src.rect();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -77,7 +77,7 @@ void SectionWidget::PaintBackground(QWidget *widget, QPaintEvent *event) {
|
||||||
|
|
||||||
auto clip = event->rect();
|
auto clip = event->rect();
|
||||||
auto fill = QRect(0, 0, widget->width(), App::main()->height());
|
auto fill = QRect(0, 0, widget->width(), App::main()->height());
|
||||||
if (const auto color = Window::Theme::Background()->color()) {
|
if (const auto color = Window::Theme::Background()->colorForFill()) {
|
||||||
p.fillRect(fill, *color);
|
p.fillRect(fill, *color);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,92 @@ std::optional<QColor> MaybeColorFromSerialized(quint32 serialized) {
|
||||||
int(serialized & 0xFFU)));
|
int(serialized & 0xFFU)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<QColor> ColorFromString(const QString &string) {
|
||||||
|
if (string.size() != 6) {
|
||||||
|
return {};
|
||||||
|
} else if (ranges::find_if(string, [](QChar ch) {
|
||||||
|
return (ch < 'a' || ch > 'f')
|
||||||
|
&& (ch < 'A' || ch > 'F')
|
||||||
|
&& (ch < '0' || ch > '9');
|
||||||
|
}) != string.end()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const auto component = [](const QString &text, int index) {
|
||||||
|
const auto decimal = [](QChar hex) {
|
||||||
|
const auto code = hex.unicode();
|
||||||
|
return (code >= '0' && code <= '9')
|
||||||
|
? int(code - '0')
|
||||||
|
: (code >= 'a' && code <= 'f')
|
||||||
|
? int(code - 'a' + 0x0a)
|
||||||
|
: int(code - 'A' + 0x0a);
|
||||||
|
};
|
||||||
|
index *= 2;
|
||||||
|
return decimal(text[index]) * 0x10 + decimal(text[index + 1]);
|
||||||
|
};
|
||||||
|
return QColor(
|
||||||
|
component(string, 0),
|
||||||
|
component(string, 1),
|
||||||
|
component(string, 2),
|
||||||
|
255);
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage PreparePatternImage(QImage image, QColor bg, QColor fg, int intensity) {
|
||||||
|
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 std::move(image);
|
||||||
|
}
|
||||||
|
fg.setAlpha(255);
|
||||||
|
const auto patternBg = anim::shifted(bg);
|
||||||
|
const auto patternFg = anim::shifted(fg);
|
||||||
|
|
||||||
|
const auto resultBytesPerPixel = (image.depth() >> 3);
|
||||||
|
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 std::move(image);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
WallPaper::WallPaper(WallPaperId id) : _id(id) {
|
WallPaper::WallPaper(WallPaperId id) : _id(id) {
|
||||||
|
@ -105,6 +191,22 @@ Image *WallPaper::thumbnail() const {
|
||||||
return _thumbnail;
|
return _thumbnail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WallPaper::isPattern() const {
|
||||||
|
return _flags & MTPDwallPaper::Flag::f_pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WallPaper::isDefault() const {
|
||||||
|
return _flags & MTPDwallPaper::Flag::f_default;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WallPaper::isCreator() const {
|
||||||
|
return _flags & MTPDwallPaper::Flag::f_creator;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WallPaper::patternIntensity() const {
|
||||||
|
return _intensity;
|
||||||
|
}
|
||||||
|
|
||||||
bool WallPaper::hasShareUrl() const {
|
bool WallPaper::hasShareUrl() const {
|
||||||
return !_slug.isEmpty();
|
return !_slug.isEmpty();
|
||||||
}
|
}
|
||||||
|
@ -131,6 +233,39 @@ FileOrigin WallPaper::fileOrigin() const {
|
||||||
return FileOriginWallpaper(_id, _accessHash);
|
return FileOriginWallpaper(_id, _accessHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WallPaper WallPaper::withUrlParams(
|
||||||
|
const QMap<QString, QString> ¶ms) const {
|
||||||
|
using Flag = MTPDwallPaperSettings::Flag;
|
||||||
|
|
||||||
|
auto result = *this;
|
||||||
|
result._settings = Flag(0);
|
||||||
|
result._backgroundColor = ColorFromString(_slug);
|
||||||
|
result._intensity = kDefaultIntensity;
|
||||||
|
|
||||||
|
if (auto mode = params.value("mode"); !mode.isEmpty()) {
|
||||||
|
const auto list = mode.replace('+', ' ').split(' ');
|
||||||
|
for (const auto &change : list) {
|
||||||
|
if (change == qstr("blur")) {
|
||||||
|
result._settings |= Flag::f_blur;
|
||||||
|
} else if (change == qstr("motion")) {
|
||||||
|
result._settings |= Flag::f_motion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (const auto color = ColorFromString(params.value("bg_color"))) {
|
||||||
|
result._backgroundColor = color;
|
||||||
|
}
|
||||||
|
if (const auto string = params.value("intensity"); !string.isEmpty()) {
|
||||||
|
auto ok = false;
|
||||||
|
const auto intensity = string.toInt(&ok);
|
||||||
|
if (ok && base::in_range(intensity, 0, 100)) {
|
||||||
|
result._intensity = intensity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<WallPaper> WallPaper::Create(const MTPWallPaper &data) {
|
std::optional<WallPaper> WallPaper::Create(const MTPWallPaper &data) {
|
||||||
return data.match([](const MTPDwallPaper &data) {
|
return data.match([](const MTPDwallPaper &data) {
|
||||||
return Create(data);
|
return Create(data);
|
||||||
|
@ -138,6 +273,8 @@ std::optional<WallPaper> WallPaper::Create(const MTPWallPaper &data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<WallPaper> WallPaper::Create(const MTPDwallPaper &data) {
|
std::optional<WallPaper> WallPaper::Create(const MTPDwallPaper &data) {
|
||||||
|
using Flag = MTPDwallPaper::Flag;
|
||||||
|
|
||||||
const auto document = Auth().data().processDocument(
|
const auto document = Auth().data().processDocument(
|
||||||
data.vdocument);
|
data.vdocument);
|
||||||
if (!document->checkWallPaperProperties()) {
|
if (!document->checkWallPaperProperties()) {
|
||||||
|
@ -150,14 +287,21 @@ std::optional<WallPaper> WallPaper::Create(const MTPDwallPaper &data) {
|
||||||
result._document = document;
|
result._document = document;
|
||||||
result._thumbnail = document->thumbnail();
|
result._thumbnail = document->thumbnail();
|
||||||
if (data.has_settings()) {
|
if (data.has_settings()) {
|
||||||
|
const auto isPattern = ((result._flags & Flag::f_pattern) != 0);
|
||||||
data.vsettings.match([&](const MTPDwallPaperSettings &data) {
|
data.vsettings.match([&](const MTPDwallPaperSettings &data) {
|
||||||
|
using Flag = MTPDwallPaperSettings::Flag;
|
||||||
|
|
||||||
result._settings = data.vflags.v;
|
result._settings = data.vflags.v;
|
||||||
if (data.has_background_color()) {
|
if (isPattern && data.has_background_color()) {
|
||||||
result._backgroundColor = MaybeColorFromSerialized(
|
result._backgroundColor = MaybeColorFromSerialized(
|
||||||
data.vbackground_color.v);
|
data.vbackground_color.v);
|
||||||
|
} else {
|
||||||
|
result._settings &= ~Flag::f_background_color;
|
||||||
}
|
}
|
||||||
if (data.has_intensity()) {
|
if (isPattern && data.has_intensity()) {
|
||||||
result._intensity = data.vintensity.v;
|
result._intensity = data.vintensity.v;
|
||||||
|
} else {
|
||||||
|
result._settings &= ~Flag::f_intensity;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -203,7 +347,6 @@ std::optional<WallPaper> WallPaper::FromSerialized(
|
||||||
auto settings = qint32();
|
auto settings = qint32();
|
||||||
auto backgroundColor = quint32();
|
auto backgroundColor = quint32();
|
||||||
auto intensity = qint32();
|
auto intensity = qint32();
|
||||||
auto documentId = quint64();
|
|
||||||
|
|
||||||
auto stream = QDataStream(serialized);
|
auto stream = QDataStream(serialized);
|
||||||
stream.setVersion(QDataStream::Qt_5_1);
|
stream.setVersion(QDataStream::Qt_5_1);
|
||||||
|
@ -214,8 +357,7 @@ std::optional<WallPaper> WallPaper::FromSerialized(
|
||||||
>> slug
|
>> slug
|
||||||
>> settings
|
>> settings
|
||||||
>> backgroundColor
|
>> backgroundColor
|
||||||
>> intensity
|
>> intensity;
|
||||||
>> documentId;
|
|
||||||
if (stream.status() != QDataStream::Ok) {
|
if (stream.status() != QDataStream::Ok) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
} else if (intensity < 0 || intensity > 100) {
|
} else if (intensity < 0 || intensity > 100) {
|
||||||
|
@ -243,7 +385,7 @@ std::optional<WallPaper> WallPaper::FromLegacySerialized(
|
||||||
result._accessHash = accessHash;
|
result._accessHash = accessHash;
|
||||||
result._flags = MTPDwallPaper::Flags::from_raw(flags);
|
result._flags = MTPDwallPaper::Flags::from_raw(flags);
|
||||||
result._slug = slug;
|
result._slug = slug;
|
||||||
result._backgroundColor = Window::Theme::GetWallPaperColor(slug);
|
result._backgroundColor = ColorFromString(slug);
|
||||||
if (!ValidateFlags(result._flags)) {
|
if (!ValidateFlags(result._flags)) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -259,7 +401,7 @@ std::optional<WallPaper> WallPaper::FromLegacyId(qint32 legacyId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<WallPaper> WallPaper::FromColorSlug(const QString &slug) {
|
std::optional<WallPaper> WallPaper::FromColorSlug(const QString &slug) {
|
||||||
if (const auto color = Window::Theme::GetWallPaperColor(slug)) {
|
if (const auto color = ColorFromString(slug)) {
|
||||||
auto result = CustomWallPaper();
|
auto result = CustomWallPaper();
|
||||||
result._slug = slug;
|
result._slug = slug;
|
||||||
result._backgroundColor = color;
|
result._backgroundColor = color;
|
||||||
|
@ -707,6 +849,11 @@ void ChatBackground::start() {
|
||||||
void ChatBackground::setImage(
|
void ChatBackground::setImage(
|
||||||
const Data::WallPaper &paper,
|
const Data::WallPaper &paper,
|
||||||
QImage &&image) {
|
QImage &&image) {
|
||||||
|
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||||
|
image = std::move(image).convertToFormat(
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
}
|
||||||
|
|
||||||
const auto needResetAdjustable = Data::IsDefaultWallPaper(paper)
|
const auto needResetAdjustable = Data::IsDefaultWallPaper(paper)
|
||||||
&& !Data::IsDefaultWallPaper(_paper)
|
&& !Data::IsDefaultWallPaper(_paper)
|
||||||
&& !nightMode()
|
&& !nightMode()
|
||||||
|
@ -744,7 +891,7 @@ void ChatBackground::setImage(
|
||||||
Qt::SmoothTransformation);
|
Qt::SmoothTransformation);
|
||||||
}
|
}
|
||||||
} else if (Data::IsDefaultWallPaper(_paper)
|
} else if (Data::IsDefaultWallPaper(_paper)
|
||||||
|| (!color() && image.isNull())) {
|
|| (!_paper.backgroundColor() && image.isNull())) {
|
||||||
setPaper(Data::DefaultWallPaper());
|
setPaper(Data::DefaultWallPaper());
|
||||||
image.load(qsl(":/gui/art/bg.jpg"));
|
image.load(qsl(":/gui/art/bg.jpg"));
|
||||||
}
|
}
|
||||||
|
@ -754,15 +901,26 @@ void ChatBackground::setImage(
|
||||||
|| Data::IsLegacy1DefaultWallPaper(_paper))
|
|| Data::IsLegacy1DefaultWallPaper(_paper))
|
||||||
? QImage()
|
? QImage()
|
||||||
: image));
|
: image));
|
||||||
if (const auto fill = color()) {
|
if (const auto fill = _paper.backgroundColor()) {
|
||||||
if (adjustPaletteRequired()) {
|
if (_paper.isPattern() && !image.isNull()) {
|
||||||
adjustPaletteUsingColor(*fill);
|
setPreparedImage(Data::PreparePatternImage(
|
||||||
|
std::move(image),
|
||||||
|
*fill,
|
||||||
|
PatternColor(*fill),
|
||||||
|
_paper.patternIntensity()));
|
||||||
|
} else {
|
||||||
|
_pixmap = QPixmap();
|
||||||
|
_pixmapForTiled = QPixmap();
|
||||||
|
if (adjustPaletteRequired()) {
|
||||||
|
adjustPaletteUsingColor(*fill);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setPreparedImage(prepareBackgroundImage(std::move(image)));
|
setPreparedImage(prepareBackgroundImage(std::move(image)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Assert((!_pixmap.isNull() && !_pixmapForTiled.isNull()) || color());
|
Assert((!_pixmap.isNull() && !_pixmapForTiled.isNull())
|
||||||
|
|| colorForFill());
|
||||||
|
|
||||||
notify(BackgroundUpdate(BackgroundUpdate::Type::New, tile()));
|
notify(BackgroundUpdate(BackgroundUpdate::Type::New, tile()));
|
||||||
if (needResetAdjustable) {
|
if (needResetAdjustable) {
|
||||||
|
@ -865,15 +1023,19 @@ void ChatBackground::adjustPaletteUsingBackground(const QImage &img) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatBackground::adjustPaletteUsingColor(QColor color) {
|
void ChatBackground::adjustPaletteUsingColor(QColor color) {
|
||||||
auto hue = color.hslHueF();
|
const auto hue = color.hslHueF();
|
||||||
auto saturation = color.hslSaturationF();
|
const auto saturation = color.hslSaturationF();
|
||||||
for (const auto &color : _adjustableColors) {
|
for (const auto &color : _adjustableColors) {
|
||||||
adjustColor(color.item, hue, saturation);
|
adjustColor(color.item, hue, saturation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<QColor> ChatBackground::colorForFill() const {
|
||||||
|
return _pixmap.isNull() ? _paper.backgroundColor() : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
QImage ChatBackground::createCurrentImage() const {
|
QImage ChatBackground::createCurrentImage() const {
|
||||||
if (const auto fill = color()) {
|
if (const auto fill = colorForFill()) {
|
||||||
auto result = QImage(
|
auto result = QImage(
|
||||||
kMinimumTiledSize,
|
kMinimumTiledSize,
|
||||||
kMinimumTiledSize,
|
kMinimumTiledSize,
|
||||||
|
@ -881,7 +1043,7 @@ QImage ChatBackground::createCurrentImage() const {
|
||||||
result.fill(*fill);
|
result.fill(*fill);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return pixmap().toImage();
|
return pixmap().toImage(); // #TODO patterns
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatBackground::tile() const {
|
bool ChatBackground::tile() const {
|
||||||
|
@ -909,7 +1071,7 @@ bool ChatBackground::tileNight() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatBackground::ensureStarted() {
|
void ChatBackground::ensureStarted() {
|
||||||
if (_pixmap.isNull()) {
|
if (_pixmap.isNull() && !_paper.backgroundColor()) {
|
||||||
// We should start first, otherwise the default call
|
// We should start first, otherwise the default call
|
||||||
// to start() will reset this value to _themeTile.
|
// to start() will reset this value to _themeTile.
|
||||||
start();
|
start();
|
||||||
|
@ -1019,6 +1181,7 @@ void ChatBackground::setTestingTheme(Instance &&theme) {
|
||||||
setTile(theme.tiled);
|
setTile(theme.tiled);
|
||||||
} else {
|
} else {
|
||||||
// Apply current background image so that service bg colors are recounted.
|
// Apply current background image so that service bg colors are recounted.
|
||||||
|
// #TODO patterns
|
||||||
setImage(_paper, std::move(_pixmap).toImage());
|
setImage(_paper, std::move(_pixmap).toImage());
|
||||||
}
|
}
|
||||||
notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true);
|
notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true);
|
||||||
|
@ -1086,7 +1249,7 @@ void ChatBackground::writeNewBackgroundSettings() {
|
||||||
((Data::IsThemeWallPaper(_paper)
|
((Data::IsThemeWallPaper(_paper)
|
||||||
|| Data::IsDefaultWallPaper(_paper))
|
|| Data::IsDefaultWallPaper(_paper))
|
||||||
? QImage()
|
? QImage()
|
||||||
: _pixmap.toImage()));
|
: _pixmap.toImage())); // #TODO patterns
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatBackground::revert() {
|
void ChatBackground::revert() {
|
||||||
|
@ -1097,6 +1260,7 @@ void ChatBackground::revert() {
|
||||||
setImage(_paperForRevert, std::move(_imageForRevert));
|
setImage(_paperForRevert, std::move(_imageForRevert));
|
||||||
} else {
|
} else {
|
||||||
// Apply current background image so that service bg colors are recounted.
|
// Apply current background image so that service bg colors are recounted.
|
||||||
|
// #TODO patterns
|
||||||
setImage(_paper, std::move(_pixmap).toImage());
|
setImage(_paper, std::move(_pixmap).toImage());
|
||||||
}
|
}
|
||||||
notify(BackgroundUpdate(BackgroundUpdate::Type::RevertingTheme, tile()), true);
|
notify(BackgroundUpdate(BackgroundUpdate::Type::RevertingTheme, tile()), true);
|
||||||
|
@ -1335,35 +1499,6 @@ bool IsPaletteTestingPath(const QString &path) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<QColor> GetWallPaperColor(const QString &slug) {
|
|
||||||
if (slug.size() != 6) {
|
|
||||||
return {};
|
|
||||||
} else if (ranges::find_if(slug, [](QChar ch) {
|
|
||||||
return (ch < 'a' || ch > 'f')
|
|
||||||
&& (ch < 'A' || ch > 'F')
|
|
||||||
&& (ch < '0' || ch > '9');
|
|
||||||
}) != slug.end()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const auto component = [](const QString &text, int index) {
|
|
||||||
const auto decimal = [](QChar hex) {
|
|
||||||
const auto code = hex.unicode();
|
|
||||||
return (code >= '0' && code <= '9')
|
|
||||||
? int(code - '0')
|
|
||||||
: (code >= 'a' && code <= 'f')
|
|
||||||
? int(code - 'a' + 0x0a)
|
|
||||||
: int(code - 'A' + 0x0a);
|
|
||||||
};
|
|
||||||
index *= 2;
|
|
||||||
return decimal(text[index]) * 0x10 + decimal(text[index + 1]);
|
|
||||||
};
|
|
||||||
return QColor(
|
|
||||||
component(slug, 0),
|
|
||||||
component(slug, 1),
|
|
||||||
component(slug, 2),
|
|
||||||
255);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComputeBackgroundRects(QRect wholeFill, QSize imageSize, QRect &to, QRect &from) {
|
void ComputeBackgroundRects(QRect wholeFill, QSize imageSize, QRect &to, QRect &from) {
|
||||||
if (uint64(imageSize.width()) * wholeFill.height() > uint64(imageSize.height()) * wholeFill.width()) {
|
if (uint64(imageSize.width()) * wholeFill.height() > uint64(imageSize.height()) * wholeFill.width()) {
|
||||||
float64 pxsize = wholeFill.height() / float64(imageSize.height());
|
float64 pxsize = wholeFill.height() / float64(imageSize.height());
|
||||||
|
@ -1444,5 +1579,19 @@ bool ReadPaletteValues(const QByteArray &content, Fn<bool(QLatin1String name, QL
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Theme
|
} // namespace Theme
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
|
@ -23,6 +23,10 @@ public:
|
||||||
[[nodiscard]] std::optional<QColor> backgroundColor() const;
|
[[nodiscard]] std::optional<QColor> backgroundColor() const;
|
||||||
[[nodiscard]] DocumentData *document() const;
|
[[nodiscard]] DocumentData *document() const;
|
||||||
[[nodiscard]] Image *thumbnail() const;
|
[[nodiscard]] Image *thumbnail() const;
|
||||||
|
[[nodiscard]] bool isPattern() const;
|
||||||
|
[[nodiscard]] bool isDefault() const;
|
||||||
|
[[nodiscard]] bool isCreator() const;
|
||||||
|
[[nodiscard]] int patternIntensity() const;
|
||||||
[[nodiscard]] bool hasShareUrl() const;
|
[[nodiscard]] bool hasShareUrl() const;
|
||||||
[[nodiscard]] QString shareUrl() const;
|
[[nodiscard]] QString shareUrl() const;
|
||||||
|
|
||||||
|
@ -30,6 +34,9 @@ public:
|
||||||
void loadThumbnail() const;
|
void loadThumbnail() const;
|
||||||
[[nodiscard]] FileOrigin fileOrigin() const;
|
[[nodiscard]] FileOrigin fileOrigin() const;
|
||||||
|
|
||||||
|
[[nodiscard]] WallPaper withUrlParams(
|
||||||
|
const QMap<QString, QString> ¶ms) const;
|
||||||
|
|
||||||
[[nodiscard]] static std::optional<WallPaper> Create(
|
[[nodiscard]] static std::optional<WallPaper> Create(
|
||||||
const MTPWallPaper &data);
|
const MTPWallPaper &data);
|
||||||
[[nodiscard]] static std::optional<WallPaper> Create(
|
[[nodiscard]] static std::optional<WallPaper> Create(
|
||||||
|
@ -49,6 +56,8 @@ public:
|
||||||
const QString &slug);
|
const QString &slug);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr auto kDefaultIntensity = 40;
|
||||||
|
|
||||||
WallPaperId _id = WallPaperId();
|
WallPaperId _id = WallPaperId();
|
||||||
uint64 _accessHash = 0;
|
uint64 _accessHash = 0;
|
||||||
MTPDwallPaper::Flags _flags;
|
MTPDwallPaper::Flags _flags;
|
||||||
|
@ -56,7 +65,7 @@ private:
|
||||||
|
|
||||||
MTPDwallPaperSettings::Flags _settings;
|
MTPDwallPaperSettings::Flags _settings;
|
||||||
std::optional<QColor> _backgroundColor;
|
std::optional<QColor> _backgroundColor;
|
||||||
int _intensity = 40;
|
int _intensity = kDefaultIntensity;
|
||||||
|
|
||||||
DocumentData *_document = nullptr;
|
DocumentData *_document = nullptr;
|
||||||
Image *_thumbnail = nullptr;
|
Image *_thumbnail = nullptr;
|
||||||
|
@ -138,7 +147,7 @@ void Revert();
|
||||||
bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent);
|
bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent);
|
||||||
bool IsPaletteTestingPath(const QString &path);
|
bool IsPaletteTestingPath(const QString &path);
|
||||||
|
|
||||||
[[nodiscard]] std::optional<QColor> GetWallPaperColor(const QString &slug);
|
QColor PatternColor(QColor background);
|
||||||
|
|
||||||
struct BackgroundUpdate {
|
struct BackgroundUpdate {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
|
@ -190,9 +199,7 @@ public:
|
||||||
[[nodiscard]] const QPixmap &pixmapForTiled() const {
|
[[nodiscard]] const QPixmap &pixmapForTiled() const {
|
||||||
return _pixmapForTiled;
|
return _pixmapForTiled;
|
||||||
}
|
}
|
||||||
[[nodiscard]] std::optional<QColor> color() const {
|
[[nodiscard]] std::optional<QColor> colorForFill() const;
|
||||||
return _paper.backgroundColor();
|
|
||||||
}
|
|
||||||
[[nodiscard]] QImage createCurrentImage() const;
|
[[nodiscard]] QImage createCurrentImage() const;
|
||||||
[[nodiscard]] bool tile() const;
|
[[nodiscard]] bool tile() const;
|
||||||
[[nodiscard]] bool tileDay() const;
|
[[nodiscard]] bool tileDay() const;
|
||||||
|
|
Loading…
Add table
Reference in a new issue