From 95565c39edff9b088d50885a7144bcf82c4847d8 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 8 Feb 2019 19:20:08 +0300 Subject: [PATCH] Upload wallpapers to the cloud. --- Telegram/SourceFiles/apiwrap.cpp | 1 - Telegram/SourceFiles/boxes/background_box.cpp | 4 +- Telegram/SourceFiles/boxes/boxes.style | 2 +- Telegram/SourceFiles/data/data_wall_paper.cpp | 598 ++++++++++++++++ Telegram/SourceFiles/data/data_wall_paper.h | 113 +++ Telegram/SourceFiles/storage/file_upload.cpp | 20 +- .../SourceFiles/storage/localimageloader.cpp | 65 ++ .../SourceFiles/storage/localimageloader.h | 2 + .../window/themes/window_theme.cpp | 646 ++---------------- .../SourceFiles/window/themes/window_theme.h | 114 +--- Telegram/gyp/telegram_sources.txt | 2 + 11 files changed, 880 insertions(+), 687 deletions(-) create mode 100644 Telegram/SourceFiles/data/data_wall_paper.cpp create mode 100644 Telegram/SourceFiles/data/data_wall_paper.h diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 6ff21a1ed..8f7b16f22 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -5142,7 +5142,6 @@ void ApiWrap::photoUploadReady( )).done(applier).afterRequest(history->sendRequestId).send(); } } - } void ApiWrap::clearPeerPhoto(not_null<PhotoData*> photo) { diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp index 0aaa2d4ba..3ba8d4229 100644 --- a/Telegram/SourceFiles/boxes/background_box.cpp +++ b/Telegram/SourceFiles/boxes/background_box.cpp @@ -110,7 +110,7 @@ BackgroundBox::Inner::Inner(QWidget *parent) : RpWidget(parent) , _check(std::make_unique<Ui::RoundCheckbox>(st::overviewCheck, [=] { update(); })) { _check->setChecked(true, Ui::RoundCheckbox::SetStyle::Fast); if (Auth().data().wallpapers().empty()) { - resize(kBackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding); + resize(st::boxWideWidth, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding); } else { updatePapers(); } @@ -169,7 +169,7 @@ void BackgroundBox::Inner::updatePapers() { const auto rows = (count / kBackgroundsInRow) + (count % kBackgroundsInRow ? 1 : 0); - resize(kBackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, rows * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding); + resize(st::boxWideWidth, rows * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding); const auto preload = kBackgroundsInRow * 3; for (const auto &paper : _papers | ranges::view::take(preload)) { diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 95ee03d69..e2d5f1faa 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -943,7 +943,7 @@ backgroundCheck: ServiceCheck { diameter: 18px; shift: 2px; thickness: 2px; - tip: point(8px, 13px); + tip: point(7px, 13px); small: 3px; large: 6px; stroke: 2px; diff --git a/Telegram/SourceFiles/data/data_wall_paper.cpp b/Telegram/SourceFiles/data/data_wall_paper.cpp new file mode 100644 index 000000000..798ac52ed --- /dev/null +++ b/Telegram/SourceFiles/data/data_wall_paper.cpp @@ -0,0 +1,598 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "data/data_wall_paper.h" + +#include "data/data_document.h" +#include "data/data_file_origin.h" +#include "data/data_session.h" +#include "storage/serialize_common.h" +#include "core/application.h" +#include "auth_session.h" + +namespace Data { +namespace { + +constexpr auto FromLegacyBackgroundId(int32 legacyId) -> WallPaperId { + return uint64(0xFFFFFFFF00000000ULL) | uint64(uint32(legacyId)); +} + +constexpr auto kUninitializedBackground = FromLegacyBackgroundId(-999); +constexpr auto kTestingThemeBackground = FromLegacyBackgroundId(-666); +constexpr auto kTestingDefaultBackground = FromLegacyBackgroundId(-665); +constexpr auto kTestingEditorBackground = FromLegacyBackgroundId(-664); +constexpr auto kThemeBackground = FromLegacyBackgroundId(-2); +constexpr auto kCustomBackground = FromLegacyBackgroundId(-1); +constexpr auto kLegacy1DefaultBackground = FromLegacyBackgroundId(0); +constexpr auto kDefaultBackground = 5947530738516623361; +constexpr auto kIncorrectDefaultBackground = FromLegacyBackgroundId(105); + +quint32 SerializeMaybeColor(std::optional<QColor> color) { + return color + ? ((quint32(std::clamp(color->red(), 0, 255)) << 16) + | (quint32(std::clamp(color->green(), 0, 255)) << 8) + | quint32(std::clamp(color->blue(), 0, 255))) + : quint32(-1); +} + +std::optional<QColor> MaybeColorFromSerialized(quint32 serialized) { + return (serialized == quint32(-1)) + ? std::nullopt + : std::make_optional(QColor( + int((serialized >> 16) & 0xFFU), + int((serialized >> 8) & 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); +} + +QString StringFromColor(QColor color) { + const auto component = [](int value) { + const auto hex = [](int value) { + value = std::clamp(value, 0, 15); + return (value > 9) + ? ('a' + (value - 10)) + : ('0' + value); + }; + return QString() + hex(value / 16) + hex(value % 16); + }; + return component(color.red()) + + component(color.green()) + + component(color.blue()); +} + +} // namespace + +WallPaper::WallPaper(WallPaperId id) : _id(id) { +} + +void WallPaper::setLocalImageAsThumbnail(std::shared_ptr<Image> image) { + Expects(IsDefaultWallPaper(*this) + || IsLegacy1DefaultWallPaper(*this) + || IsCustomWallPaper(*this)); + Expects(_thumbnail == nullptr); + + _thumbnail = std::move(image); +} + +WallPaperId WallPaper::id() const { + return _id; +} + +std::optional<QColor> WallPaper::backgroundColor() const { + return _backgroundColor; +} + +DocumentData *WallPaper::document() const { + return _document; +} + +Image *WallPaper::thumbnail() const { + return _thumbnail + ? _thumbnail.get() + : _document + ? _document->thumbnail() + : nullptr; +} + +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; +} + +bool WallPaper::isDark() const { + return _flags & MTPDwallPaper::Flag::f_dark; +} + +bool WallPaper::isLocal() const { + return !document() && thumbnail(); +} + +bool WallPaper::isBlurred() const { + return _settings & MTPDwallPaperSettings::Flag::f_blur; +} + +int WallPaper::patternIntensity() const { + return _intensity; +} + +bool WallPaper::hasShareUrl() const { + return !_slug.isEmpty(); +} + +QString WallPaper::shareUrl() const { + if (!hasShareUrl()) { + return QString(); + } + const auto base = Core::App().createInternalLinkFull("bg/" + _slug); + auto params = QStringList(); + if (isPattern()) { + if (_backgroundColor) { + params.push_back("bg_color=" + StringFromColor(*_backgroundColor)); + } + if (_intensity) { + params.push_back("intensity=" + QString::number(_intensity)); + } + } + auto mode = QStringList(); + if (_settings & MTPDwallPaperSettings::Flag::f_blur) { + mode.push_back("blur"); + } + if (_settings & MTPDwallPaperSettings::Flag::f_motion) { + mode.push_back("motion"); + } + if (!mode.isEmpty()) { + params.push_back("mode=" + mode.join('+')); + } + return params.isEmpty() + ? base + : base + '?' + params.join('&'); +} + +void WallPaper::loadThumbnail() const { + if (_thumbnail) { + _thumbnail->load(fileOrigin()); + } + if (_document) { + _document->loadThumbnail(fileOrigin()); + } +} + +void WallPaper::loadDocument() const { + if (_document) { + _document->save(fileOrigin(), QString()); + } +} + +FileOrigin WallPaper::fileOrigin() const { + return FileOriginWallpaper(_id, _accessHash); +} + +MTPWallPaperSettings WallPaper::mtpSettings() const { + return MTP_wallPaperSettings( + MTP_flags(_settings), + (_backgroundColor + ? MTP_int(SerializeMaybeColor(_backgroundColor)) + : MTP_int(0)), + MTP_int(_intensity)); +} + +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._settings |= Flag::f_background_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, 101)) { + result._settings |= Flag::f_intensity; + result._intensity = intensity; + } + } + + return result; +} + +WallPaper WallPaper::withBlurred(bool blurred) const { + using Flag = MTPDwallPaperSettings::Flag; + + auto result = *this; + if (blurred) { + result._settings |= Flag::f_blur; + } else { + result._settings &= ~Flag::f_blur; + } + return result; +} + +WallPaper WallPaper::withPatternIntensity(int intensity) const { + using Flag = MTPDwallPaperSettings::Flag; + + auto result = *this; + result._settings |= Flag::f_intensity; + result._intensity = intensity; + return result; +} + +WallPaper WallPaper::withBackgroundColor(QColor color) const { + using Flag = MTPDwallPaperSettings::Flag; + + auto result = *this; + result._settings |= Flag::f_background_color; + result._backgroundColor = color; + if (ColorFromString(_slug)) { + result._slug = StringFromColor(color); + } + return result; +} + +WallPaper WallPaper::withParamsFrom(const WallPaper &other) const { + auto result = *this; + result._settings = other._settings; + if (other._backgroundColor || !ColorFromString(_slug)) { + result._backgroundColor = other._backgroundColor; + if (ColorFromString(_slug)) { + result._slug = StringFromColor(*result._backgroundColor); + } + } + result._intensity = other._intensity; + return result; +} + +WallPaper WallPaper::withoutImageData() const { + auto result = *this; + result._thumbnail = nullptr; + return result; +} + +std::optional<WallPaper> WallPaper::Create(const MTPWallPaper &data) { + return data.match([](const MTPDwallPaper &data) { + return Create(data); + }); +} + +std::optional<WallPaper> WallPaper::Create(const MTPDwallPaper &data) { + using Flag = MTPDwallPaper::Flag; + + const auto document = Auth().data().processDocument( + data.vdocument); + if (!document->checkWallPaperProperties()) { + return std::nullopt; + } + auto result = WallPaper(data.vid.v); + result._accessHash = data.vaccess_hash.v; + result._flags = data.vflags.v; + result._slug = qs(data.vslug); + result._document = document; + if (data.has_settings()) { + const auto isPattern = ((result._flags & Flag::f_pattern) != 0); + data.vsettings.match([&](const MTPDwallPaperSettings &data) { + using Flag = MTPDwallPaperSettings::Flag; + + result._settings = data.vflags.v; + if (isPattern && data.has_background_color()) { + result._backgroundColor = MaybeColorFromSerialized( + data.vbackground_color.v); + } else { + result._settings &= ~Flag::f_background_color; + } + if (isPattern && data.has_intensity()) { + result._intensity = data.vintensity.v; + } else { + result._settings &= ~Flag::f_intensity; + } + }); + } + return result; +} + +QByteArray WallPaper::serialize() const { + auto size = sizeof(quint64) // _id + + sizeof(quint64) // _accessHash + + sizeof(qint32) // _flags + + Serialize::stringSize(_slug) + + sizeof(qint32) // _settings + + sizeof(quint32) // _backgroundColor + + sizeof(qint32); // _intensity + + auto result = QByteArray(); + result.reserve(size); + { + auto stream = QDataStream(&result, QIODevice::WriteOnly); + stream.setVersion(QDataStream::Qt_5_1); + stream + << quint64(_id) + << quint64(_accessHash) + << qint32(_flags) + << _slug + << qint32(_settings) + << SerializeMaybeColor(_backgroundColor) + << qint32(_intensity); + } + return result; +} + +std::optional<WallPaper> WallPaper::FromSerialized( + const QByteArray &serialized) { + if (serialized.isEmpty()) { + return std::nullopt; + } + + auto id = quint64(); + auto accessHash = quint64(); + auto flags = qint32(); + auto slug = QString(); + auto settings = qint32(); + auto backgroundColor = quint32(); + auto intensity = qint32(); + + auto stream = QDataStream(serialized); + stream.setVersion(QDataStream::Qt_5_1); + stream + >> id + >> accessHash + >> flags + >> slug + >> settings + >> backgroundColor + >> intensity; + if (stream.status() != QDataStream::Ok) { + return std::nullopt; + } else if (intensity < 0 || intensity > 100) { + return std::nullopt; + } + auto result = WallPaper(id); + result._accessHash = accessHash; + result._flags = MTPDwallPaper::Flags::from_raw(flags); + result._slug = slug; + result._settings = MTPDwallPaperSettings::Flags::from_raw(settings); + result._backgroundColor = MaybeColorFromSerialized(backgroundColor); + result._intensity = intensity; + return result; +} + +std::optional<WallPaper> WallPaper::FromLegacySerialized( + quint64 id, + quint64 accessHash, + quint32 flags, + QString slug) { + auto result = WallPaper(id); + result._accessHash = accessHash; + result._flags = MTPDwallPaper::Flags::from_raw(flags); + result._slug = slug; + result._backgroundColor = ColorFromString(slug); + return result; +} + +std::optional<WallPaper> WallPaper::FromLegacyId(qint32 legacyId) { + auto result = WallPaper(FromLegacyBackgroundId(legacyId)); + if (!IsCustomWallPaper(result)) { + result._flags = MTPDwallPaper::Flag::f_default; + } + return result; +} + +std::optional<WallPaper> WallPaper::FromColorSlug(const QString &slug) { + if (const auto color = ColorFromString(slug)) { + auto result = CustomWallPaper(); + result._slug = slug; + result._backgroundColor = color; + return result; + } + return std::nullopt; +} + +WallPaper ThemeWallPaper() { + return WallPaper(kThemeBackground); +} + +bool IsThemeWallPaper(const WallPaper &paper) { + return (paper.id() == kThemeBackground); +} + +WallPaper CustomWallPaper() { + return WallPaper(kCustomBackground); +} + +bool IsCustomWallPaper(const WallPaper &paper) { + return (paper.id() == kCustomBackground); +} + +WallPaper Legacy1DefaultWallPaper() { + return WallPaper(kLegacy1DefaultBackground); +} + +bool IsLegacy1DefaultWallPaper(const WallPaper &paper) { + return (paper.id() == kLegacy1DefaultBackground); +} + +WallPaper DefaultWallPaper() { + return WallPaper(kDefaultBackground); +} + +bool IsDefaultWallPaper(const WallPaper &paper) { + return (paper.id() == kDefaultBackground) + || (paper.id() == kIncorrectDefaultBackground); +} + +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(); +} + +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 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 image; +} + +QImage PrepareBlurredBackground(QImage image) { + constexpr auto kSize = 900; + constexpr auto kRadius = 24; + if (image.width() > kSize || image.height() > kSize) { + image = image.scaled( + kSize, + kSize, + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + } + return Images::BlurLargeImage(image, kRadius); +} + +namespace details { + +WallPaper UninitializedWallPaper() { + return WallPaper(kUninitializedBackground); +} + +bool IsUninitializedWallPaper(const WallPaper &paper) { + return (paper.id() == kUninitializedBackground); +} + +WallPaper TestingThemeWallPaper() { + return WallPaper(kTestingThemeBackground); +} + +bool IsTestingThemeWallPaper(const WallPaper &paper) { + return (paper.id() == kTestingThemeBackground); +} + +WallPaper TestingDefaultWallPaper() { + return WallPaper(kTestingDefaultBackground); +} + +bool IsTestingDefaultWallPaper(const WallPaper &paper) { + return (paper.id() == kTestingDefaultBackground); +} + +WallPaper TestingEditorWallPaper() { + return WallPaper(kTestingEditorBackground); +} + +bool IsTestingEditorWallPaper(const WallPaper &paper) { + return (paper.id() == kTestingEditorBackground); +} + +} // namespace details +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_wall_paper.h b/Telegram/SourceFiles/data/data_wall_paper.h new file mode 100644 index 000000000..dc1445d66 --- /dev/null +++ b/Telegram/SourceFiles/data/data_wall_paper.h @@ -0,0 +1,113 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +class Image; + +namespace Data { + +struct FileOrigin; + +class WallPaper { +public: + explicit WallPaper(WallPaperId id); + + void setLocalImageAsThumbnail(std::shared_ptr<Image> image); + + [[nodiscard]] WallPaperId id() const; + [[nodiscard]] std::optional<QColor> backgroundColor() const; + [[nodiscard]] DocumentData *document() const; + [[nodiscard]] Image *thumbnail() const; + [[nodiscard]] bool isPattern() const; + [[nodiscard]] bool isDefault() const; + [[nodiscard]] bool isCreator() const; + [[nodiscard]] bool isDark() const; + [[nodiscard]] bool isLocal() const; + [[nodiscard]] bool isBlurred() const; + [[nodiscard]] int patternIntensity() const; + [[nodiscard]] bool hasShareUrl() const; + [[nodiscard]] QString shareUrl() const; + + void loadDocument() const; + void loadThumbnail() const; + [[nodiscard]] FileOrigin fileOrigin() const; + [[nodiscard]] MTPWallPaperSettings mtpSettings() const; + + [[nodiscard]] WallPaper withUrlParams( + const QMap<QString, QString> ¶ms) const; + [[nodiscard]] WallPaper withBlurred(bool blurred) const; + [[nodiscard]] WallPaper withPatternIntensity(int intensity) const; + [[nodiscard]] WallPaper withBackgroundColor(QColor color) const; + [[nodiscard]] WallPaper withParamsFrom(const WallPaper &other) const; + [[nodiscard]] WallPaper withoutImageData() const; + + [[nodiscard]] static std::optional<WallPaper> Create( + const MTPWallPaper &data); + [[nodiscard]] static std::optional<WallPaper> Create( + const MTPDwallPaper &data); + + [[nodiscard]] QByteArray serialize() const; + [[nodiscard]] static std::optional<WallPaper> FromSerialized( + const QByteArray &serialized); + [[nodiscard]] static std::optional<WallPaper> FromLegacySerialized( + quint64 id, + quint64 accessHash, + quint32 flags, + QString slug); + [[nodiscard]] static std::optional<WallPaper> FromLegacyId( + qint32 legacyId); + [[nodiscard]] static std::optional<WallPaper> FromColorSlug( + const QString &slug); + +private: + static constexpr auto kDefaultIntensity = 40; + + WallPaperId _id = WallPaperId(); + uint64 _accessHash = 0; + MTPDwallPaper::Flags _flags; + QString _slug; + + MTPDwallPaperSettings::Flags _settings; + std::optional<QColor> _backgroundColor; + int _intensity = kDefaultIntensity; + + DocumentData *_document = nullptr; + std::shared_ptr<Image> _thumbnail; + +}; + +[[nodiscard]] WallPaper ThemeWallPaper(); +[[nodiscard]] bool IsThemeWallPaper(const WallPaper &paper); +[[nodiscard]] WallPaper CustomWallPaper(); +[[nodiscard]] bool IsCustomWallPaper(const WallPaper &paper); +[[nodiscard]] WallPaper Legacy1DefaultWallPaper(); +[[nodiscard]] bool IsLegacy1DefaultWallPaper(const WallPaper &paper); +[[nodiscard]] WallPaper DefaultWallPaper(); +[[nodiscard]] bool IsDefaultWallPaper(const WallPaper &paper); + +QColor PatternColor(QColor background); +QImage PreparePatternImage( + QImage image, + QColor bg, + QColor fg, + int intensity); +QImage PrepareBlurredBackground(QImage image); + +namespace details { + +[[nodiscard]] WallPaper UninitializedWallPaper(); +[[nodiscard]] bool IsUninitializedWallPaper(const WallPaper &paper); +[[nodiscard]] WallPaper TestingThemeWallPaper(); +[[nodiscard]] bool IsTestingThemeWallPaper(const WallPaper &paper); +[[nodiscard]] WallPaper TestingDefaultWallPaper(); +[[nodiscard]] bool IsTestingDefaultWallPaper(const WallPaper &paper); +[[nodiscard]] WallPaper TestingEditorWallPaper(); +[[nodiscard]] bool IsTestingEditorWallPaper(const WallPaper &paper); + +} // namespace details +} // namespace Data diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index 846a51424..522ff6330 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -75,7 +75,9 @@ struct Uploader::File { Uploader::File::File(const SendMediaReady &media) : media(media) { partsCount = media.parts.size(); - if (type() == SendMediaType::File || type() == SendMediaType::Audio) { + if (type() == SendMediaType::File + || type() == SendMediaType::WallPaper + || type() == SendMediaType::Audio) { setDocSize(media.file.isEmpty() ? media.data.size() : media.filesize); @@ -89,7 +91,9 @@ Uploader::File::File(const std::shared_ptr<FileLoadResult> &file) || type() == SendMediaType::Secure) ? file->fileparts.size() : file->thumbparts.size(); - if (type() == SendMediaType::File || type() == SendMediaType::Audio) { + if (type() == SendMediaType::File + || type() == SendMediaType::WallPaper + || type() == SendMediaType::Audio) { setDocSize(file->filesize); } else { docSize = docPartSize = docPartsCount = 0; @@ -149,6 +153,7 @@ void Uploader::uploadMedia( if (media.type == SendMediaType::Photo) { Auth().data().processPhoto(media.photo, media.photoThumbs); } else if (media.type == SendMediaType::File + || media.type == SendMediaType::WallPaper || media.type == SendMediaType::Audio) { const auto document = media.photoThumbs.empty() ? Auth().data().processDocument(media.document) @@ -157,6 +162,9 @@ void Uploader::uploadMedia( base::duplicate(media.photoThumbs.front().second)); if (!media.data.isEmpty()) { document->setData(media.data); + if (media.type == SendMediaType::WallPaper) { + document->checkWallPaperProperties(); + } if (document->saveToCache() && media.data.size() <= Storage::kMaxFileInMemory) { Auth().data().cache().put( @@ -184,6 +192,7 @@ void Uploader::upload( photo->uploadingData = std::make_unique<Data::UploadState>( file->partssize); } else if (file->type == SendMediaType::File + || file->type == SendMediaType::WallPaper || file->type == SendMediaType::Audio) { const auto document = file->thumb.isNull() ? Auth().data().processDocument(file->document) @@ -197,6 +206,9 @@ void Uploader::upload( std::move(file->goodThumbnailBytes)); if (!file->content.isEmpty()) { document->setData(file->content); + if (file->type == SendMediaType::WallPaper) { + document->checkWallPaperProperties(); + } if (document->saveToCache() && file->content.size() <= Storage::kMaxFileInMemory) { Auth().data().cache().put( @@ -220,6 +232,7 @@ void Uploader::currentFailed() { if (j->second.type() == SendMediaType::Photo) { _photoFailed.fire_copy(j->first); } else if (j->second.type() == SendMediaType::File + || j->second.type() == SendMediaType::WallPaper || j->second.type() == SendMediaType::Audio) { const auto document = Auth().data().document(j->second.id()); if (document->uploading()) { @@ -318,6 +331,7 @@ void Uploader::sendNext() { MTP_bytes(md5)); _photoReady.fire({ uploadingId, silent, file }); } else if (uploadingData.type() == SendMediaType::File + || uploadingData.type() == SendMediaType::WallPaper || uploadingData.type() == SendMediaType::Audio) { QByteArray docMd5(32, Qt::Uninitialized); hashMd5Hex(uploadingData.md5Hash.result(), docMd5.data()); @@ -389,6 +403,7 @@ void Uploader::sendNext() { * uploadingData.docPartSize; toSend = content.mid(offset, uploadingData.docPartSize); if ((uploadingData.type() == SendMediaType::File + || uploadingData.type() == SendMediaType::WallPaper || uploadingData.type() == SendMediaType::Audio) && uploadingData.docSentParts <= kUseBigFilesFrom) { uploadingData.md5Hash.feed(toSend.constData(), toSend.size()); @@ -530,6 +545,7 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { } _photoProgress.fire_copy(fullId); } else if (file.type() == SendMediaType::File + || file.type() == SendMediaType::WallPaper || file.type() == SendMediaType::Audio) { const auto document = Auth().data().document(file.id()); if (document->uploading()) { diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index a07e36d55..afb362fc8 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -234,6 +234,71 @@ SendMediaReady PreparePeerPhoto(PeerId peerId, QImage &&image) { 0); } +SendMediaReady PrepareWallPaper(const QImage &image) { + PreparedPhotoThumbs thumbnails; + QVector<MTPPhotoSize> sizes; + + QByteArray jpeg; + QBuffer jpegBuffer(&jpeg); + image.save(&jpegBuffer, "JPG", 87); + + const auto scaled = [&](int size) { + return image.scaled( + size, + size, + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + }; + const auto push = [&](const char *type, QImage &&image) { + sizes.push_back(MTP_photoSize( + MTP_string(type), + MTP_fileLocationUnavailable( + MTP_long(0), + MTP_int(0), + MTP_long(0)), + MTP_int(image.width()), + MTP_int(image.height()), MTP_int(0))); + thumbnails.emplace(type[0], std::move(image)); + }; + push("s", scaled(320)); + + const auto filename = qsl("wallpaper.jpg"); + auto attributes = QVector<MTPDocumentAttribute>( + 1, + MTP_documentAttributeFilename(MTP_string(filename))); + attributes.push_back(MTP_documentAttributeImageSize( + MTP_int(image.width()), + MTP_int(image.height()))); + const auto id = rand_value<DocumentId>(); + const auto document = MTP_document( + MTP_flags(0), + MTP_long(id), + MTP_long(0), + MTP_bytes(QByteArray()), + MTP_int(unixtime()), + MTP_string("image/jpeg"), + MTP_int(jpeg.size()), + MTP_vector<MTPPhotoSize>(sizes), + MTP_int(MTP::maindc()), + MTP_vector<MTPDocumentAttribute>(attributes)); + + return SendMediaReady( + SendMediaType::WallPaper, + QString(), // filepath + filename, + jpeg.size(), + jpeg, + id, + 0, + QString(), + PeerId(), + MTP_photoEmpty(MTP_long(0)), + thumbnails, + document, + QByteArray(), + 0); +} + TaskQueue::TaskQueue(TimeMs stopTimeoutMs) { if (stopTimeoutMs > 0) { _stopTimer = new QTimer(this); diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index 6faff9fde..6e02a7ca3 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -20,6 +20,7 @@ enum class SendMediaType { Photo, Audio, File, + WallPaper, Secure, }; @@ -83,6 +84,7 @@ struct SendMediaReady { }; SendMediaReady PreparePeerPhoto(PeerId peerId, QImage &&image); +SendMediaReady PrepareWallPaper(const QImage &image); using TaskId = void*; // no interface, just id diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 01f8d7680..57d9184a9 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -10,592 +10,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/themes/window_theme_preview.h" #include "mainwidget.h" #include "auth_session.h" -#include "core/application.h" -#include "storage/serialize_common.h" -#include "data/data_document.h" -#include "data/data_session.h" +#include "apiwrap.h" #include "storage/localstorage.h" +#include "storage/localimageloader.h" +#include "storage/file_upload.h" #include "base/parse_helper.h" #include "base/zlib_help.h" +#include "data/data_session.h" #include "ui/image/image.h" #include "boxes/background_box.h" +#include "core/application.h" #include "styles/style_widgets.h" #include "styles/style_history.h" -namespace Data { -namespace { - -constexpr auto FromLegacyBackgroundId(int32 legacyId) -> WallPaperId { - return uint64(0xFFFFFFFF00000000ULL) | uint64(uint32(legacyId)); -} - -constexpr auto kUninitializedBackground = FromLegacyBackgroundId(-999); -constexpr auto kTestingThemeBackground = FromLegacyBackgroundId(-666); -constexpr auto kTestingDefaultBackground = FromLegacyBackgroundId(-665); -constexpr auto kTestingEditorBackground = FromLegacyBackgroundId(-664); -constexpr auto kThemeBackground = FromLegacyBackgroundId(-2); -constexpr auto kCustomBackground = FromLegacyBackgroundId(-1); -constexpr auto kLegacy1DefaultBackground = FromLegacyBackgroundId(0); -constexpr auto kDefaultBackground = 5947530738516623361; -constexpr auto kIncorrectDefaultBackground = FromLegacyBackgroundId(105); - -quint32 SerializeMaybeColor(std::optional<QColor> color) { - return color - ? ((quint32(std::clamp(color->red(), 0, 255)) << 16) - | (quint32(std::clamp(color->green(), 0, 255)) << 8) - | quint32(std::clamp(color->blue(), 0, 255))) - : quint32(-1); -} - -std::optional<QColor> MaybeColorFromSerialized(quint32 serialized) { - return (serialized == quint32(-1)) - ? std::nullopt - : std::make_optional(QColor( - int((serialized >> 16) & 0xFFU), - int((serialized >> 8) & 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); -} - -QString StringFromColor(QColor color) { - const auto component = [](int value) { - const auto hex = [](int value) { - value = std::clamp(value, 0, 15); - return (value > 9) - ? ('a' + (value - 10)) - : ('0' + value); - }; - return QString() + hex(value / 16) + hex(value % 16); - }; - return component(color.red()) - + component(color.green()) - + component(color.blue()); -} - -} // namespace - -WallPaper::WallPaper(WallPaperId id) : _id(id) { -} - -void WallPaper::setLocalImageAsThumbnail(std::shared_ptr<Image> image) { - Expects(IsDefaultWallPaper(*this) - || IsLegacy1DefaultWallPaper(*this) - || IsCustomWallPaper(*this)); - Expects(_thumbnail == nullptr); - - _thumbnail = std::move(image); -} - -WallPaperId WallPaper::id() const { - return _id; -} - -std::optional<QColor> WallPaper::backgroundColor() const { - return _backgroundColor; -} - -DocumentData *WallPaper::document() const { - return _document; -} - -Image *WallPaper::thumbnail() const { - return _thumbnail - ? _thumbnail.get() - : _document - ? _document->thumbnail() - : nullptr; -} - -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; -} - -bool WallPaper::isDark() const { - return _flags & MTPDwallPaper::Flag::f_dark; -} - -bool WallPaper::isLocal() const { - return !document() && thumbnail(); -} - -bool WallPaper::isBlurred() const { - return _settings & MTPDwallPaperSettings::Flag::f_blur; -} - -int WallPaper::patternIntensity() const { - return _intensity; -} - -bool WallPaper::hasShareUrl() const { - return !_slug.isEmpty(); -} - -QString WallPaper::shareUrl() const { - if (!hasShareUrl()) { - return QString(); - } - const auto base = Core::App().createInternalLinkFull("bg/" + _slug); - auto params = QStringList(); - if (isPattern()) { - if (_backgroundColor) { - params.push_back("bg_color=" + StringFromColor(*_backgroundColor)); - } - if (_intensity) { - params.push_back("intensity=" + QString::number(_intensity)); - } - } - auto mode = QStringList(); - if (_settings & MTPDwallPaperSettings::Flag::f_blur) { - mode.push_back("blur"); - } - if (_settings & MTPDwallPaperSettings::Flag::f_motion) { - mode.push_back("motion"); - } - if (!mode.isEmpty()) { - params.push_back("mode=" + mode.join('+')); - } - return params.isEmpty() - ? base - : base + '?' + params.join('&'); -} - -void WallPaper::loadThumbnail() const { - if (_thumbnail) { - _thumbnail->load(fileOrigin()); - } - if (_document) { - _document->loadThumbnail(fileOrigin()); - } -} - -void WallPaper::loadDocument() const { - if (_document) { - _document->save(fileOrigin(), QString()); - } -} - -FileOrigin WallPaper::fileOrigin() const { - 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._settings |= Flag::f_background_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, 101)) { - result._settings |= Flag::f_intensity; - result._intensity = intensity; - } - } - - return result; -} - -WallPaper WallPaper::withBlurred(bool blurred) const { - using Flag = MTPDwallPaperSettings::Flag; - - auto result = *this; - if (blurred) { - result._settings |= Flag::f_blur; - } else { - result._settings &= ~Flag::f_blur; - } - return result; -} - -WallPaper WallPaper::withPatternIntensity(int intensity) const { - using Flag = MTPDwallPaperSettings::Flag; - - auto result = *this; - result._settings |= Flag::f_intensity; - result._intensity = intensity; - return result; -} - -WallPaper WallPaper::withBackgroundColor(QColor color) const { - using Flag = MTPDwallPaperSettings::Flag; - - auto result = *this; - result._settings |= Flag::f_background_color; - result._backgroundColor = color; - if (ColorFromString(_slug)) { - result._slug = StringFromColor(color); - } - return result; -} - -WallPaper WallPaper::withParamsFrom(const WallPaper &other) const { - auto result = *this; - result._settings = other._settings; - if (other._backgroundColor || !ColorFromString(_slug)) { - result._backgroundColor = other._backgroundColor; - if (ColorFromString(_slug)) { - result._slug = StringFromColor(*result._backgroundColor); - } - } - result._intensity = other._intensity; - return result; -} - -WallPaper WallPaper::withoutImageData() const { - auto result = *this; - result._thumbnail = nullptr; - return result; -} - -std::optional<WallPaper> WallPaper::Create(const MTPWallPaper &data) { - return data.match([](const MTPDwallPaper &data) { - return Create(data); - }); -} - -std::optional<WallPaper> WallPaper::Create(const MTPDwallPaper &data) { - using Flag = MTPDwallPaper::Flag; - - const auto document = Auth().data().processDocument( - data.vdocument); - if (!document->checkWallPaperProperties()) { - return std::nullopt; - } - auto result = WallPaper(data.vid.v); - result._accessHash = data.vaccess_hash.v; - result._flags = data.vflags.v; - result._slug = qs(data.vslug); - result._document = document; - if (data.has_settings()) { - const auto isPattern = ((result._flags & Flag::f_pattern) != 0); - data.vsettings.match([&](const MTPDwallPaperSettings &data) { - using Flag = MTPDwallPaperSettings::Flag; - - result._settings = data.vflags.v; - if (isPattern && data.has_background_color()) { - result._backgroundColor = MaybeColorFromSerialized( - data.vbackground_color.v); - } else { - result._settings &= ~Flag::f_background_color; - } - if (isPattern && data.has_intensity()) { - result._intensity = data.vintensity.v; - } else { - result._settings &= ~Flag::f_intensity; - } - }); - } - return result; -} - -QByteArray WallPaper::serialize() const { - auto size = sizeof(quint64) // _id - + sizeof(quint64) // _accessHash - + sizeof(qint32) // _flags - + Serialize::stringSize(_slug) - + sizeof(qint32) // _settings - + sizeof(quint32) // _backgroundColor - + sizeof(qint32); // _intensity - - auto result = QByteArray(); - result.reserve(size); - { - auto stream = QDataStream(&result, QIODevice::WriteOnly); - stream.setVersion(QDataStream::Qt_5_1); - stream - << quint64(_id) - << quint64(_accessHash) - << qint32(_flags) - << _slug - << qint32(_settings) - << SerializeMaybeColor(_backgroundColor) - << qint32(_intensity); - } - return result; -} - -std::optional<WallPaper> WallPaper::FromSerialized( - const QByteArray &serialized) { - if (serialized.isEmpty()) { - return std::nullopt; - } - - auto id = quint64(); - auto accessHash = quint64(); - auto flags = qint32(); - auto slug = QString(); - auto settings = qint32(); - auto backgroundColor = quint32(); - auto intensity = qint32(); - - auto stream = QDataStream(serialized); - stream.setVersion(QDataStream::Qt_5_1); - stream - >> id - >> accessHash - >> flags - >> slug - >> settings - >> backgroundColor - >> intensity; - if (stream.status() != QDataStream::Ok) { - return std::nullopt; - } else if (intensity < 0 || intensity > 100) { - return std::nullopt; - } - auto result = WallPaper(id); - result._accessHash = accessHash; - result._flags = MTPDwallPaper::Flags::from_raw(flags); - result._slug = slug; - result._settings = MTPDwallPaperSettings::Flags::from_raw(settings); - result._backgroundColor = MaybeColorFromSerialized(backgroundColor); - result._intensity = intensity; - return result; -} - -std::optional<WallPaper> WallPaper::FromLegacySerialized( - quint64 id, - quint64 accessHash, - quint32 flags, - QString slug) { - auto result = WallPaper(id); - result._accessHash = accessHash; - result._flags = MTPDwallPaper::Flags::from_raw(flags); - result._slug = slug; - result._backgroundColor = ColorFromString(slug); - return result; -} - -std::optional<WallPaper> WallPaper::FromLegacyId(qint32 legacyId) { - auto result = WallPaper(FromLegacyBackgroundId(legacyId)); - if (!IsCustomWallPaper(result)) { - result._flags = MTPDwallPaper::Flag::f_default; - } - return result; -} - -std::optional<WallPaper> WallPaper::FromColorSlug(const QString &slug) { - if (const auto color = ColorFromString(slug)) { - auto result = CustomWallPaper(); - result._slug = slug; - result._backgroundColor = color; - return result; - } - return std::nullopt; -} - -WallPaper ThemeWallPaper() { - return WallPaper(kThemeBackground); -} - -bool IsThemeWallPaper(const WallPaper &paper) { - return (paper.id() == kThemeBackground); -} - -WallPaper CustomWallPaper() { - return WallPaper(kCustomBackground); -} - -bool IsCustomWallPaper(const WallPaper &paper) { - return (paper.id() == kCustomBackground); -} - -WallPaper Legacy1DefaultWallPaper() { - return WallPaper(kLegacy1DefaultBackground); -} - -bool IsLegacy1DefaultWallPaper(const WallPaper &paper) { - return (paper.id() == kLegacy1DefaultBackground); -} - -WallPaper DefaultWallPaper() { - return WallPaper(kDefaultBackground); -} - -bool IsDefaultWallPaper(const WallPaper &paper) { - return (paper.id() == kDefaultBackground) - || (paper.id() == kIncorrectDefaultBackground); -} - -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(); -} - -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 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 image; -} - -QImage PrepareBlurredBackground(QImage image) { - constexpr auto kSize = 900; - constexpr auto kRadius = 24; - if (image.width() > kSize || image.height() > kSize) { - image = image.scaled( - kSize, - kSize, - Qt::KeepAspectRatio, - Qt::SmoothTransformation); - } - return Images::BlurLargeImage(image, kRadius); -} - -namespace details { - -WallPaper UninitializedWallPaper() { - return WallPaper(kUninitializedBackground); -} - -bool IsUninitializedWallPaper(const WallPaper &paper) { - return (paper.id() == kUninitializedBackground); -} - -WallPaper TestingThemeWallPaper() { - return WallPaper(kTestingThemeBackground); -} - -bool IsTestingThemeWallPaper(const WallPaper &paper) { - return (paper.id() == kTestingThemeBackground); -} - -WallPaper TestingDefaultWallPaper() { - return WallPaper(kTestingDefaultBackground); -} - -bool IsTestingDefaultWallPaper(const WallPaper &paper) { - return (paper.id() == kTestingDefaultBackground); -} - -WallPaper TestingEditorWallPaper() { - return WallPaper(kTestingEditorBackground); -} - -bool IsTestingEditorWallPaper(const WallPaper &paper) { - return (paper.id() == kTestingEditorBackground); -} - -} // namespace details -} // namespace Data - namespace Window { namespace Theme { namespace { @@ -954,9 +381,71 @@ void ChatBackground::start() { if (!Local::readBackground()) { set(Data::ThemeWallPaper()); } + refreshSession(); + subscribe(Core::App().authSessionChanged(), [=] { + refreshSession(); + }); } } +void ChatBackground::refreshSession() { + const auto session = AuthSession::Exists() ? &Auth() : nullptr; + if (_session != session) { + _session = session; + checkUploadWallPaper(); + } +} + +void ChatBackground::checkUploadWallPaper() { + if (!_session) { + _wallPaperUploadLifetime = rpl::lifetime(); + _wallPaperUploadId = FullMsgId(); + _wallPaperRequestId = 0; + return; + } + if (const auto id = base::take(_wallPaperUploadId)) { + _session->uploader().cancel(id); + } + if (const auto id = base::take(_wallPaperRequestId)) { + _session->api().request(id).cancel(); + } + if (!Data::IsCustomWallPaper(_paper) || _original.isNull()) { + return; + } + + const auto ready = PrepareWallPaper(_original); + const auto documentId = ready.id; + _wallPaperUploadId = FullMsgId(0, clientMsgId()); + _session->uploader().uploadMedia(_wallPaperUploadId, ready); + if (_wallPaperUploadLifetime) { + return; + } + _wallPaperUploadLifetime = _session->uploader().documentReady( + ) | rpl::start_with_next([=](const Storage::UploadedDocument &data) { + if (data.fullId != _wallPaperUploadId) { + return; + } + _wallPaperUploadId = FullMsgId(); + _wallPaperRequestId = _session->api().request( + MTPaccount_UploadWallPaper( + data.file, + MTP_string("image/jpeg"), + _paper.mtpSettings() + ) + ).done([=](const MTPWallPaper &result) { + result.match([&](const MTPDwallPaper &data) { + _session->data().documentConvert( + _session->data().document(documentId), + data.vdocument); + }); + if (const auto paper = Data::WallPaper::Create(result)) { + _paper = *paper; + writeNewBackgroundSettings(); + } + }).send(); + }); +} + void ChatBackground::set(const Data::WallPaper &paper, QImage image) { image = ProcessBackgroundImage(std::move(image)); @@ -1041,6 +530,7 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) { notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true); notify(BackgroundUpdate(BackgroundUpdate::Type::ApplyingTheme, tile()), true); } + checkUploadWallPaper(); } void ChatBackground::setPreparedImage(QImage original, QImage prepared) { diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index 9fb49b7ce..fe41be72e 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -7,109 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -class Image; +#include "data/data_wall_paper.h" -namespace Data { - -struct FileOrigin; - -class WallPaper { -public: - explicit WallPaper(WallPaperId id); - - void setLocalImageAsThumbnail(std::shared_ptr<Image> image); - - [[nodiscard]] WallPaperId id() const; - [[nodiscard]] std::optional<QColor> backgroundColor() const; - [[nodiscard]] DocumentData *document() const; - [[nodiscard]] Image *thumbnail() const; - [[nodiscard]] bool isPattern() const; - [[nodiscard]] bool isDefault() const; - [[nodiscard]] bool isCreator() const; - [[nodiscard]] bool isDark() const; - [[nodiscard]] bool isLocal() const; - [[nodiscard]] bool isBlurred() const; - [[nodiscard]] int patternIntensity() const; - [[nodiscard]] bool hasShareUrl() const; - [[nodiscard]] QString shareUrl() const; - - void loadDocument() const; - void loadThumbnail() const; - [[nodiscard]] FileOrigin fileOrigin() const; - - [[nodiscard]] WallPaper withUrlParams( - const QMap<QString, QString> ¶ms) const; - [[nodiscard]] WallPaper withBlurred(bool blurred) const; - [[nodiscard]] WallPaper withPatternIntensity(int intensity) const; - [[nodiscard]] WallPaper withBackgroundColor(QColor color) const; - [[nodiscard]] WallPaper withParamsFrom(const WallPaper &other) const; - [[nodiscard]] WallPaper withoutImageData() const; - - [[nodiscard]] static std::optional<WallPaper> Create( - const MTPWallPaper &data); - [[nodiscard]] static std::optional<WallPaper> Create( - const MTPDwallPaper &data); - - [[nodiscard]] QByteArray serialize() const; - [[nodiscard]] static std::optional<WallPaper> FromSerialized( - const QByteArray &serialized); - [[nodiscard]] static std::optional<WallPaper> FromLegacySerialized( - quint64 id, - quint64 accessHash, - quint32 flags, - QString slug); - [[nodiscard]] static std::optional<WallPaper> FromLegacyId( - qint32 legacyId); - [[nodiscard]] static std::optional<WallPaper> FromColorSlug( - const QString &slug); - -private: - static constexpr auto kDefaultIntensity = 40; - - WallPaperId _id = WallPaperId(); - uint64 _accessHash = 0; - MTPDwallPaper::Flags _flags; - QString _slug; - - MTPDwallPaperSettings::Flags _settings; - std::optional<QColor> _backgroundColor; - int _intensity = kDefaultIntensity; - - DocumentData *_document = nullptr; - std::shared_ptr<Image> _thumbnail; - -}; - -[[nodiscard]] WallPaper ThemeWallPaper(); -[[nodiscard]] bool IsThemeWallPaper(const WallPaper &paper); -[[nodiscard]] WallPaper CustomWallPaper(); -[[nodiscard]] bool IsCustomWallPaper(const WallPaper &paper); -[[nodiscard]] WallPaper Legacy1DefaultWallPaper(); -[[nodiscard]] bool IsLegacy1DefaultWallPaper(const WallPaper &paper); -[[nodiscard]] WallPaper DefaultWallPaper(); -[[nodiscard]] bool IsDefaultWallPaper(const WallPaper &paper); - -QColor PatternColor(QColor background); -QImage PreparePatternImage( - QImage image, - QColor bg, - QColor fg, - int intensity); -QImage PrepareBlurredBackground(QImage image); - -namespace details { - -[[nodiscard]] WallPaper UninitializedWallPaper(); -[[nodiscard]] bool IsUninitializedWallPaper(const WallPaper &paper); -[[nodiscard]] WallPaper TestingThemeWallPaper(); -[[nodiscard]] bool IsTestingThemeWallPaper(const WallPaper &paper); -[[nodiscard]] WallPaper TestingDefaultWallPaper(); -[[nodiscard]] bool IsTestingDefaultWallPaper(const WallPaper &paper); -[[nodiscard]] WallPaper TestingEditorWallPaper(); -[[nodiscard]] bool IsTestingEditorWallPaper(const WallPaper &paper); - -} // namespace details -} // namespace Data +class AuthSession; namespace Window { namespace Theme { @@ -185,7 +85,9 @@ struct BackgroundUpdate { bool tiled; }; -class ChatBackground : public base::Observable<BackgroundUpdate> { +class ChatBackground + : public base::Observable<BackgroundUpdate> + , private base::Subscriber { public: ChatBackground(); @@ -251,6 +153,8 @@ private: void keepApplied(const QString &path, bool write); [[nodiscard]] bool isNonDefaultThemeOrBackground(); [[nodiscard]] bool isNonDefaultBackground(); + void refreshSession(); + void checkUploadWallPaper(); friend bool IsNightMode(); friend void SetNightModeValue(bool nightMode); @@ -259,6 +163,7 @@ private: friend void KeepApplied(); friend bool IsNonDefaultBackground(); + AuthSession *_session = nullptr; Data::WallPaper _paper = Data::details::UninitializedWallPaper(); std::optional<QColor> _paperColor; QImage _original; @@ -278,6 +183,9 @@ private: bool _tileForRevert = false; std::vector<AdjustableColor> _adjustableColors; + FullMsgId _wallPaperUploadId; + mtpRequestId _wallPaperRequestId = 0; + rpl::lifetime _wallPaperUploadLifetime; }; diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 7a95b0947..09e216394 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -207,6 +207,8 @@ <(src_loc)/data/data_user.h <(src_loc)/data/data_user_photos.cpp <(src_loc)/data/data_user_photos.h +<(src_loc)/data/data_wall_paper.cpp +<(src_loc)/data/data_wall_paper.h <(src_loc)/data/data_web_page.cpp <(src_loc)/data/data_web_page.h <(src_loc)/dialogs/dialogs_entry.cpp