Skip to content

Commit e792088

Browse files
committed
Insert gift pattern symbols in background.
1 parent 1dc70d5 commit e792088

File tree

5 files changed

+113
-17
lines changed

5 files changed

+113
-17
lines changed

Telegram/SourceFiles/ui/chat/chat_theme.cpp

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ For license and copyright information please follow this link:
77
*/
88
#include "ui/chat/chat_theme.h"
99

10-
#include "ui/image/image_prepare.h"
10+
#include "ui/color_contrast.h"
11+
#include "ui/emoji_config.h"
12+
#include "ui/painter.h"
1113
#include "ui/power_saving.h"
1214
#include "ui/ui_utility.h"
1315
#include "ui/chat/message_bubble.h"
1416
#include "ui/chat/chat_style.h"
15-
#include "ui/color_contrast.h"
17+
#include "ui/image/image_prepare.h"
18+
#include "ui/text/text_custom_emoji.h"
1619
#include "ui/style/style_core_palette.h"
1720
#include "ui/style/style_palette_colorizer.h"
1821

@@ -53,6 +56,14 @@ constexpr auto kMinAcceptableContrast = 1.14;// 4.5;
5356
return (doubled % 2) ? 0.5 : 1.;
5457
}
5558

59+
[[nodiscard]] int RotationForSymbol(const QRectF &symbol) {
60+
const auto a = int(base::SafeRound(symbol.x() * 255));
61+
const auto b = int(base::SafeRound(symbol.y() * 255));
62+
const auto value = uint16((uint16(a) << 8) | uint16(b));
63+
const auto shuffled = uint16(value << 5) | uint16(value >> 3);
64+
return (shuffled % 90) - 45;
65+
}
66+
5667
[[nodiscard]] CacheBackgroundResult CacheBackgroundByRequest(
5768
const CacheBackgroundRequest &request) {
5869
Expects(!request.area.isEmpty());
@@ -83,14 +94,17 @@ constexpr auto kMinAcceptableContrast = 1.14;// 4.5;
8394
QPainter p(&result);
8495
if (!gradient.isNull()) {
8596
if (request.background.patternOpacity >= 0.) {
86-
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
97+
p.setCompositionMode(
98+
QPainter::CompositionMode_SoftLight);
8799
p.setOpacity(request.background.patternOpacity);
88100
} else {
89101
p.setCompositionMode(
90102
QPainter::CompositionMode_DestinationIn);
91103
}
92104
}
93-
const auto tiled = request.background.isPattern
105+
const auto hasGiftSymbols = request.background.isPattern
106+
&& !request.background.giftSymbols.empty();
107+
auto tiled = request.background.isPattern
94108
? request.background.prepared.scaled(
95109
request.area.height() * ratio,
96110
request.area.height() * ratio,
@@ -99,6 +113,31 @@ constexpr auto kMinAcceptableContrast = 1.14;// 4.5;
99113
: request.background.preparedForTiled;
100114
const auto w = tiled.width() / float(ratio);
101115
const auto h = tiled.height() / float(ratio);
116+
117+
const auto &giftSymbols = request.background.giftSymbols;
118+
const auto &giftSymbolFrame = request.background.giftSymbolFrame;
119+
const auto giftSymbolSize = giftSymbolFrame.size() / ratio;
120+
if (hasGiftSymbols) {
121+
auto q = QPainter(&tiled);
122+
auto hq = PainterHighQualityEnabler(q);
123+
q.setOpacity(0.8);
124+
for (const auto &symbol : giftSymbols) {
125+
const auto rect = QRectF(
126+
symbol.x() * w,
127+
symbol.y() * h,
128+
symbol.width() * w,
129+
symbol.height() * h);
130+
q.save();
131+
q.translate(rect.center());
132+
q.scale(
133+
rect.width() / giftSymbolSize.width(),
134+
rect.height() / giftSymbolSize.height());
135+
q.rotate(RotationForSymbol(symbol));
136+
q.translate(-rect.center());
137+
q.drawImage(rect.topLeft(), giftSymbolFrame);
138+
q.restore();
139+
}
140+
}
102141
const auto cx = int(std::ceil(request.area.width() / w));
103142
const auto cy = int(std::ceil(request.area.height() / h));
104143
const auto rows = cy;
@@ -111,7 +150,8 @@ constexpr auto kMinAcceptableContrast = 1.14;// 4.5;
111150
const auto useshift = xshift / float(ratio);
112151
for (auto y = 0; y != rows; ++y) {
113152
for (auto x = 0; x != cols; ++x) {
114-
p.drawImage(QPointF(useshift + x * w, y * h), tiled);
153+
const auto position = QPointF(useshift + x * w, y * h);
154+
p.drawImage(position, tiled);
115155
}
116156
}
117157
if (!gradient.isNull()
@@ -1093,7 +1133,7 @@ ChatThemeBackground PrepareBackgroundImage(
10931133
data.path,
10941134
data.bytes,
10951135
data.gzipSvg,
1096-
data.findGiftSymbols)
1136+
!data.giftSymbolFrame.isNull())
10971137
: BackgroundImageFields();
10981138
auto prepared = needBackground
10991139
? PreprocessBackgroundImage(std::move(read.image))
@@ -1143,10 +1183,32 @@ ChatThemeBackground PrepareBackgroundImage(
11431183
: std::make_optional(data.colors.front())),
11441184
.colors = data.colors,
11451185
.giftSymbols = std::move(read.giftSymbols),
1186+
.giftSymbolFrame = data.giftSymbolFrame,
11461187
.patternOpacity = data.patternOpacity,
11471188
.gradientRotation = data.generateGradient ? data.gradientRotation : 0,
11481189
.isPattern = data.isPattern,
11491190
};
11501191
}
11511192

1193+
[[nodiscard]] QImage PrepareGiftSymbol(
1194+
const std::unique_ptr<Text::CustomEmoji> &emoji) {
1195+
if (!emoji || !emoji->ready()) {
1196+
return QImage();
1197+
}
1198+
const auto ratio = style::DevicePixelRatio();
1199+
const auto size = Ui::Emoji::GetSizeNormal() / ratio;
1200+
auto result = QImage(
1201+
2 * QSize(size, size) * ratio,
1202+
QImage::Format_ARGB32_Premultiplied);
1203+
result.setDevicePixelRatio(ratio);
1204+
result.fill(Qt::transparent);
1205+
auto p = QPainter(&result);
1206+
const auto shift = (2 * size - (Ui::Emoji::GetSizeLarge() / ratio)) / 2;
1207+
emoji->paint(p, {
1208+
.textColor = QColor(0, 0, 0),
1209+
.position = QPoint(shift, shift),
1210+
});
1211+
return result;
1212+
}
1213+
11521214
} // namespace Window::Theme

Telegram/SourceFiles/ui/chat/chat_theme.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ class palette;
1616
struct colorizer;
1717
} // namespace style
1818

19+
namespace Ui::Text {
20+
class CustomEmoji;
21+
} // namespace Ui::Text
22+
1923
namespace Ui {
2024

2125
class ChatStyle;
@@ -30,6 +34,7 @@ struct ChatThemeBackground {
3034
std::optional<QColor> colorForFill;
3135
std::vector<QColor> colors;
3236
std::vector<QRectF> giftSymbols;
37+
QImage giftSymbolFrame;
3338
float64 patternOpacity = 1.;
3439
int gradientRotation = 0;
3540
bool isPattern = false;
@@ -47,6 +52,7 @@ struct ChatThemeBackgroundData {
4752
QString key;
4853
QString path;
4954
QByteArray bytes;
55+
QImage giftSymbolFrame;
5056
bool gzipSvg = false;
5157
std::vector<QColor> colors;
5258
bool isPattern = false;
@@ -55,7 +61,6 @@ struct ChatThemeBackgroundData {
5561
bool isBlurred = false;
5662
bool forDarkMode = false;
5763
bool generateGradient = false;
58-
bool findGiftSymbols = false;
5964
int gradientRotation = 0;
6065
};
6166

@@ -269,5 +274,7 @@ struct BackgroundImageFields {
269274
int rotation);
270275
[[nodiscard]] ChatThemeBackground PrepareBackgroundImage(
271276
const ChatThemeBackgroundData &data);
277+
[[nodiscard]] QImage PrepareGiftSymbol(
278+
const std::unique_ptr<Text::CustomEmoji> &emoji);
272279

273280
} // namespace Ui

Telegram/SourceFiles/window/window_session_controller.cpp

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,8 +1505,8 @@ struct SessionController::CachedThemeKey {
15051505
struct SessionController::CachedTheme {
15061506
std::weak_ptr<Ui::ChatTheme> theme;
15071507
std::shared_ptr<Data::DocumentMedia> media;
1508+
std::unique_ptr<Ui::Text::CustomEmoji> giftSymbol;
15081509
Data::WallPaper paper;
1509-
bool findGiftSymbols = false;
15101510
bool basedOnDark = false;
15111511
bool caching = false;
15121512
rpl::lifetime lifetime;
@@ -3296,13 +3296,20 @@ void SessionController::cacheChatTheme(
32963296
const auto document = use.document();
32973297
const auto media = document ? document->createMediaView() : nullptr;
32983298
const auto findGiftSymbols = (data.unique != nullptr);
3299+
auto giftSymbol = findGiftSymbols
3300+
? session().data().customEmojiManager().create(
3301+
data.unique->pattern.document,
3302+
crl::guard(this, [=] { _giftSymbolLoaded.fire({}); }),
3303+
Data::CustomEmojiSizeTag::Large)
3304+
: nullptr;
3305+
const auto giftSymbolReady = !giftSymbol || giftSymbol->ready();
32993306
use.loadDocument();
33003307
auto &theme = [&]() -> CachedTheme& {
33013308
const auto i = _customChatThemes.find(key);
33023309
if (i != end(_customChatThemes)) {
33033310
i->second.media = media;
3311+
i->second.giftSymbol = std::move(giftSymbol);
33043312
i->second.paper = use;
3305-
i->second.findGiftSymbols = findGiftSymbols;
33063313
i->second.basedOnDark = dark;
33073314
i->second.caching = true;
33083315
return i->second;
@@ -3311,8 +3318,8 @@ void SessionController::cacheChatTheme(
33113318
key,
33123319
CachedTheme{
33133320
.media = media,
3321+
.giftSymbol = std::move(giftSymbol),
33143322
.paper = use,
3315-
.findGiftSymbols = findGiftSymbols,
33163323
.basedOnDark = dark,
33173324
.caching = true,
33183325
}).first->second;
@@ -3341,6 +3348,9 @@ void SessionController::cacheChatTheme(
33413348
});
33423349
if (media && media->loaded(true)) {
33433350
theme.media = nullptr;
3351+
if (giftSymbolReady) {
3352+
theme.giftSymbol = nullptr;
3353+
}
33443354
}
33453355
}
33463356

@@ -3358,15 +3368,25 @@ void SessionController::cacheChatThemeDone(
33583368
}
33593369
i->second.caching = false;
33603370
i->second.theme = result;
3361-
if (i->second.media) {
3362-
if (i->second.media->loaded(true)) {
3371+
const auto media = i->second.media.get();
3372+
const auto giftSymbol = i->second.giftSymbol.get();
3373+
if (media || giftSymbol) {
3374+
if ((!media || media->loaded(true))
3375+
&& (!giftSymbol || giftSymbol->ready())) {
33633376
updateCustomThemeBackground(i->second);
33643377
} else {
3365-
session().downloaderTaskFinished(
3378+
rpl::merge(
3379+
session().downloaderTaskFinished(),
3380+
((giftSymbol && !giftSymbol->ready())
3381+
? (_giftSymbolLoaded.events() | rpl::type_erased())
3382+
: rpl::never<rpl::empty_value>())
33663383
) | rpl::filter([=] {
33673384
const auto i = _customChatThemes.find(key);
33683385
Assert(i != end(_customChatThemes));
3369-
return !i->second.media || i->second.media->loaded(true);
3386+
const auto media = i->second.media.get();
3387+
const auto giftSymbol = i->second.giftSymbol.get();
3388+
return (!media || media->loaded(true))
3389+
&& (!giftSymbol || giftSymbol->ready());
33703390
}) | rpl::start_with_next([=] {
33713391
const auto i = _customChatThemes.find(key);
33723392
Assert(i != end(_customChatThemes));
@@ -3381,9 +3401,15 @@ void SessionController::updateCustomThemeBackground(CachedTheme &theme) {
33813401
const auto guard = gsl::finally([&] {
33823402
theme.lifetime.destroy();
33833403
theme.media = nullptr;
3404+
theme.giftSymbol = nullptr;
33843405
});
33853406
const auto strong = theme.theme.lock();
3386-
if (!theme.media || !strong || !theme.media->loaded(true)) {
3407+
const auto media = theme.media.get();
3408+
const auto giftSymbol = theme.giftSymbol.get();
3409+
if (!strong
3410+
|| (!media && !giftSymbol)
3411+
|| (media && !media->loaded(true))
3412+
|| (giftSymbol && !giftSymbol->ready())) {
33873413
return;
33883414
}
33893415
const auto key = strong->key();
@@ -3424,6 +3450,7 @@ Ui::ChatThemeBackgroundData SessionController::backgroundData(
34243450
.key = paper.key(),
34253451
.path = paperPath,
34263452
.bytes = paperBytes,
3453+
.giftSymbolFrame = Ui::PrepareGiftSymbol(theme.giftSymbol),
34273454
.gzipSvg = gzipSvg,
34283455
.colors = colors,
34293456
.isPattern = isPattern,
@@ -3432,7 +3459,6 @@ Ui::ChatThemeBackgroundData SessionController::backgroundData(
34323459
.isBlurred = isBlurred,
34333460
.forDarkMode = theme.basedOnDark,
34343461
.generateGradient = generateGradient,
3435-
.findGiftSymbols = theme.findGiftSymbols,
34363462
.gradientRotation = gradientRotation,
34373463
};
34383464
}

Telegram/SourceFiles/window/window_session_controller.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,7 @@ class SessionController : public SessionNavigation {
799799
const std::shared_ptr<Ui::ChatTheme> _defaultChatTheme;
800800
base::flat_map<CachedThemeKey, CachedTheme> _customChatThemes;
801801
rpl::event_stream<std::shared_ptr<Ui::ChatTheme>> _cachedThemesStream;
802+
rpl::event_stream<> _giftSymbolLoaded;
802803
const std::unique_ptr<Ui::ChatStyle> _chatStyle;
803804
std::weak_ptr<Ui::ChatTheme> _chatStyleTheme;
804805
std::deque<std::shared_ptr<Ui::ChatTheme>> _lastUsedCustomChatThemes;

Telegram/lib_ui

0 commit comments

Comments
 (0)