1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #pragma once
9 
10 #include "ui/effects/animations.h"
11 #include "base/timer.h"
12 #include "base/weak_ptr.h"
13 
14 namespace style {
15 class palette;
16 struct colorizer;
17 } // namespace style
18 
19 namespace Ui {
20 
21 class ChatStyle;
22 struct ChatPaintContext;
23 struct BubblePattern;
24 
25 struct ChatThemeBackground {
26 	QImage prepared;
27 	QImage preparedForTiled;
28 	QImage gradientForFill;
29 	std::optional<QColor> colorForFill;
30 	std::vector<QColor> colors;
31 	float64 patternOpacity = 1.;
32 	int gradientRotation = 0;
33 	bool isPattern = false;
34 	bool tile = false;
35 
waitingForNegativePatternChatThemeBackground36 	[[nodiscard]] bool waitingForNegativePattern() const {
37 		return isPattern && prepared.isNull() && (patternOpacity < 0.);
38 	}
39 };
40 
41 bool operator==(const ChatThemeBackground &a, const ChatThemeBackground &b);
42 bool operator!=(const ChatThemeBackground &a, const ChatThemeBackground &b);
43 
44 struct ChatThemeBackgroundData {
45 	QString path;
46 	QByteArray bytes;
47 	bool gzipSvg = false;
48 	std::vector<QColor> colors;
49 	bool isPattern = false;
50 	float64 patternOpacity = 0.;
51 	bool isBlurred = false;
52 	bool generateGradient = false;
53 	int gradientRotation = 0;
54 };
55 
56 struct ChatThemeBubblesData {
57 	std::vector<QColor> colors;
58 	std::optional<QColor> accent;
59 };
60 
61 struct CacheBackgroundRequest {
62 	ChatThemeBackground background;
63 	QSize area;
64 	int gradientRotationAdd = 0;
65 	float64 gradientProgress = 1.;
66 
67 	explicit operator bool() const {
68 		return !background.prepared.isNull()
69 			|| !background.gradientForFill.isNull();
70 	}
71 };
72 
73 bool operator==(
74 	const CacheBackgroundRequest &a,
75 	const CacheBackgroundRequest &b);
76 bool operator!=(
77 	const CacheBackgroundRequest &a,
78 	const CacheBackgroundRequest &b);
79 
80 struct CacheBackgroundResult {
81 	QImage image;
82 	QImage gradient;
83 	QSize area;
84 	int x = 0;
85 	int y = 0;
86 	bool waitingForNegativePattern = false;
87 };
88 
89 struct CachedBackground {
90 	CachedBackground() = default;
91 	CachedBackground(CacheBackgroundResult &&result);
92 
93 	QPixmap pixmap;
94 	QSize area;
95 	int x = 0;
96 	int y = 0;
97 	bool waitingForNegativePattern = false;
98 };
99 
100 struct BackgroundState {
101 	CachedBackground was;
102 	CachedBackground now;
103 	float64 shown = 1.;
104 };
105 
106 struct ChatThemeKey {
107 	uint64 id = 0;
108 	bool dark = false;
109 
110 	explicit operator bool() const {
111 		return (id != 0);
112 	}
113 };
114 
115 inline bool operator<(ChatThemeKey a, ChatThemeKey b) {
116 	return (a.id < b.id) || ((a.id == b.id) && (a.dark < b.dark));
117 }
118 inline bool operator>(ChatThemeKey a, ChatThemeKey b) {
119 	return (b < a);
120 }
121 inline bool operator<=(ChatThemeKey a, ChatThemeKey b) {
122 	return !(b < a);
123 }
124 inline bool operator>=(ChatThemeKey a, ChatThemeKey b) {
125 	return !(a < b);
126 }
127 inline bool operator==(ChatThemeKey a, ChatThemeKey b) {
128 	return (a.id == b.id) && (a.dark == b.dark);
129 }
130 inline bool operator!=(ChatThemeKey a, ChatThemeKey b) {
131 	return !(a == b);
132 }
133 
134 struct ChatThemeDescriptor {
135 	ChatThemeKey key;
136 	Fn<void(style::palette&)> preparePalette;
137 	ChatThemeBackgroundData backgroundData;
138 	ChatThemeBubblesData bubblesData;
139 	bool basedOnDark = false;
140 };
141 
142 class ChatTheme final : public base::has_weak_ptr {
143 public:
144 	ChatTheme();
145 
146 	// Expected to be invoked on a background thread. Invokes callbacks there.
147 	ChatTheme(ChatThemeDescriptor &&descriptor);
148 
149 	~ChatTheme();
150 
151 	[[nodiscard]] ChatThemeKey key() const;
palette()152 	[[nodiscard]] const style::palette *palette() const {
153 		return _palette.get();
154 	}
155 
156 	void setBackground(ChatThemeBackground &&background);
157 	void updateBackgroundImageFrom(ChatThemeBackground &&background);
background()158 	[[nodiscard]] const ChatThemeBackground &background() const {
159 		return _mutableBackground;
160 	}
161 
162 	void setBubblesBackground(QImage image);
bubblesBackgroundPattern()163 	[[nodiscard]] const BubblePattern *bubblesBackgroundPattern() const {
164 		return _bubblesBackgroundPattern.get();
165 	}
166 
167 	[[nodiscard]] ChatPaintContext preparePaintContext(
168 		not_null<const ChatStyle*> st,
169 		QRect viewport,
170 		QRect clip);
171 	[[nodiscard]] const BackgroundState &backgroundState(QSize area);
172 	void clearBackgroundState();
173 	[[nodiscard]] rpl::producer<> repaintBackgroundRequests() const;
174 	void rotateComplexGradientBackground();
175 
176 private:
177 	void cacheBackground();
178 	void cacheBackgroundNow();
179 	void cacheBackgroundAsync(
180 		const CacheBackgroundRequest &request,
181 		Fn<void(CacheBackgroundResult&&)> done = nullptr);
182 	void setCachedBackground(CacheBackgroundResult &&cached);
183 	[[nodiscard]] CacheBackgroundRequest cacheBackgroundRequest(
184 		QSize area,
185 		int addRotation = 0) const;
186 	[[nodiscard]] bool readyForBackgroundRotation() const;
187 	void generateNextBackgroundRotation();
188 
189 	void cacheBubbles();
190 	void cacheBubblesNow();
191 	void cacheBubblesAsync(
192 		const CacheBackgroundRequest &request);
193 	[[nodiscard]] CacheBackgroundRequest cacheBubblesRequest(
194 		QSize area) const;
195 
196 	[[nodiscard]] style::colorizer bubblesAccentColorizer(
197 		const QColor &accent) const;
198 	void adjustPalette(const ChatThemeDescriptor &descriptor);
199 	void set(const style::color &my, const QColor &color);
200 	void adjust(const style::color &my, const QColor &by);
201 	void adjust(const style::color &my, const style::colorizer &by);
202 
203 	ChatThemeKey _key;
204 	std::unique_ptr<style::palette> _palette;
205 	ChatThemeBackground _mutableBackground;
206 	BackgroundState _backgroundState;
207 	Animations::Simple _backgroundFade;
208 	CacheBackgroundRequest _backgroundCachingRequest;
209 	CacheBackgroundResult _backgroundNext;
210 	QSize _cacheBackgroundArea;
211 	crl::time _lastBackgroundAreaChangeTime = 0;
212 	std::optional<base::Timer> _cacheBackgroundTimer;
213 
214 	CachedBackground _bubblesBackground;
215 	QImage _bubblesBackgroundPrepared;
216 	CacheBackgroundRequest _bubblesCachingRequest;
217 	QSize _cacheBubblesArea;
218 	crl::time _lastBubblesAreaChangeTime = 0;
219 	std::optional<base::Timer> _cacheBubblesTimer;
220 	std::unique_ptr<BubblePattern> _bubblesBackgroundPattern;
221 
222 	rpl::event_stream<> _repaintBackgroundRequests;
223 
224 	rpl::lifetime _lifetime;
225 
226 };
227 
228 struct ChatBackgroundRects {
229 	QRect from;
230 	QRect to;
231 };
232 [[nodiscard]] ChatBackgroundRects ComputeChatBackgroundRects(
233 	QSize fillSize,
234 	QSize imageSize);
235 
236 [[nodiscard]] QColor CountAverageColor(const QImage &image);
237 [[nodiscard]] QColor CountAverageColor(const std::vector<QColor> &colors);
238 [[nodiscard]] bool IsPatternInverted(
239 	const std::vector<QColor> &background,
240 	float64 patternOpacity);
241 [[nodiscard]] QColor ThemeAdjustedColor(QColor original, QColor background);
242 [[nodiscard]] QImage PreprocessBackgroundImage(QImage image);
243 [[nodiscard]] std::optional<QColor> CalculateImageMonoColor(
244 	const QImage &image);
245 [[nodiscard]] QImage PrepareImageForTiled(const QImage &prepared);
246 
247 [[nodiscard]] QImage ReadBackgroundImage(
248 	const QString &path,
249 	const QByteArray &content,
250 	bool gzipSvg);
251 [[nodiscard]] QImage GenerateBackgroundImage(
252 	QSize size,
253 	const std::vector<QColor> &bg,
254 	int gradientRotation,
255 	float64 patternOpacity = 1.,
256 	Fn<void(QPainter&,bool)> drawPattern = nullptr);
257 [[nodiscard]] QImage InvertPatternImage(QImage pattern);
258 [[nodiscard]] QImage PreparePatternImage(
259 	QImage pattern,
260 	const std::vector<QColor> &bg,
261 	int gradientRotation,
262 	float64 patternOpacity);
263 [[nodiscard]] QImage PrepareBlurredBackground(QImage image);
264 [[nodiscard]] QImage GenerateDitheredGradient(
265 	const std::vector<QColor> &colors,
266 	int rotation);
267 [[nodiscard]] ChatThemeBackground PrepareBackgroundImage(
268 	const ChatThemeBackgroundData &data);
269 
270 } // namespace Ui
271