From 7de28fc4bdd6b4deec6edcec3975e45a19a65f22 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 22 Aug 2019 15:10:27 +0300
Subject: [PATCH] Apply saturation and value in colorizer.

---
 .../SourceFiles/settings/settings_chat.cpp    | 64 ++++++++++++++++++-
 .../window/themes/window_theme.cpp            | 62 +++++++++++++++---
 .../SourceFiles/window/themes/window_theme.h  |  6 +-
 3 files changed, 119 insertions(+), 13 deletions(-)

diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp
index e00198726..39bc72618 100644
--- a/Telegram/SourceFiles/settings/settings_chat.cpp
+++ b/Telegram/SourceFiles/settings/settings_chat.cpp
@@ -43,6 +43,58 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "styles/style_boxes.h"
 
 namespace Settings {
+namespace {
+
+const auto kColorizeIgnoredKeys = base::flat_set<QLatin1String>{ {
+	qstr("boxTextFgGood"),
+	qstr("boxTextFgError"),
+	qstr("historyPeer1NameFg"),
+	qstr("historyPeer1NameFgSelected"),
+	qstr("historyPeer1UserpicBg"),
+	qstr("historyPeer2NameFg"),
+	qstr("historyPeer2NameFgSelected"),
+	qstr("historyPeer2UserpicBg"),
+	qstr("historyPeer3NameFg"),
+	qstr("historyPeer3NameFgSelected"),
+	qstr("historyPeer3UserpicBg"),
+	qstr("historyPeer4NameFg"),
+	qstr("historyPeer4NameFgSelected"),
+	qstr("historyPeer4UserpicBg"),
+	qstr("historyPeer5NameFg"),
+	qstr("historyPeer5NameFgSelected"),
+	qstr("historyPeer5UserpicBg"),
+	qstr("historyPeer6NameFg"),
+	qstr("historyPeer6NameFgSelected"),
+	qstr("historyPeer6UserpicBg"),
+	qstr("historyPeer7NameFg"),
+	qstr("historyPeer7NameFgSelected"),
+	qstr("historyPeer7UserpicBg"),
+	qstr("historyPeer8NameFg"),
+	qstr("historyPeer8NameFgSelected"),
+	qstr("historyPeer8UserpicBg"),
+	qstr("msgFile1Bg"),
+	qstr("msgFile1BgDark"),
+	qstr("msgFile1BgOver"),
+	qstr("msgFile1BgSelected"),
+	qstr("msgFile2Bg"),
+	qstr("msgFile2BgDark"),
+	qstr("msgFile2BgOver"),
+	qstr("msgFile2BgSelected"),
+	qstr("msgFile3Bg"),
+	qstr("msgFile3BgDark"),
+	qstr("msgFile3BgOver"),
+	qstr("msgFile3BgSelected"),
+	qstr("msgFile4Bg"),
+	qstr("msgFile4BgDark"),
+	qstr("msgFile4BgOver"),
+	qstr("msgFile4BgSelected"),
+	qstr("mediaviewFileRedCornerFg"),
+	qstr("mediaviewFileYellowCornerFg"),
+	qstr("mediaviewFileGreenCornerFg"),
+	qstr("mediaviewFileBlueCornerFg"),
+} };
+
+} // namespace
 
 class BackgroundRow : public Ui::RpWidget {
 public:
@@ -876,10 +928,16 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
 			scheme.accentColor));
 		box->setSaveCallback([=](QColor result) {
 			auto colorizer = Window::Theme::Colorizer();
+			colorizer.ignoreKeys = kColorizeIgnoredKeys;
 			colorizer.hueThreshold = 10;
-			colorizer.saturationThreshold = 10;
-			colorizer.wasHue = scheme.accentColor.hue();
-			colorizer.nowHue = result.hue();
+			scheme.accentColor.getHsv(
+				&colorizer.wasHue,
+				&colorizer.wasSaturation,
+				&colorizer.wasValue);
+			result.getHsv(
+				&colorizer.nowHue,
+				&colorizer.nowSaturation,
+				&colorizer.nowValue);
 			apply(scheme, &colorizer);
 		});
 	};
diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp
index 36bf523b6..89fad322f 100644
--- a/Telegram/SourceFiles/window/themes/window_theme.cpp
+++ b/Telegram/SourceFiles/window/themes/window_theme.cpp
@@ -153,24 +153,65 @@ void Colorize(
 	auto saturation = 0;
 	auto value = 0;
 	color.getHsv(&hue, &saturation, &value);
-	if ((saturation < colorizer->saturationThreshold)
-		|| (std::abs(hue - colorizer->wasHue) > colorizer->hueThreshold)) {
-		return;
-	}
-	const auto changed = hue + (colorizer->nowHue - colorizer->wasHue);
+	const auto changeColor = std::abs(hue - colorizer->wasHue)
+		<= colorizer->hueThreshold;
+	const auto nowHue = hue + (colorizer->nowHue - colorizer->wasHue);
+	const auto nowSaturation = ((saturation > colorizer->wasSaturation)
+		&& (colorizer->nowSaturation > colorizer->wasSaturation))
+		? (((colorizer->nowSaturation * (255 - colorizer->wasSaturation))
+			+ ((saturation - colorizer->wasSaturation)
+				* (255 - colorizer->nowSaturation)))
+			/ (255 - colorizer->wasSaturation))
+		: ((saturation != colorizer->wasSaturation)
+			&& (colorizer->wasSaturation != 0))
+		? ((saturation * colorizer->nowSaturation)
+			/ colorizer->wasSaturation)
+		: colorizer->nowSaturation;
+	const auto nowValue = (value > colorizer->wasValue)
+		? (((colorizer->nowValue * (255 - colorizer->wasValue))
+			+ ((value - colorizer->wasValue)
+				* (255 - colorizer->nowValue)))
+			/ (255 - colorizer->wasValue))
+		: (value < colorizer->wasValue)
+		? ((value * colorizer->nowValue)
+			/ colorizer->wasValue)
+		: colorizer->nowValue;
 	auto nowR = 0;
 	auto nowG = 0;
 	auto nowB = 0;
 	QColor::fromHsv(
-		(changed + 360) % 360,
-		saturation,
-		value
+		changeColor ? ((nowHue + 360) % 360) : hue,
+		changeColor ? nowSaturation : saturation,
+		nowValue
 	).getRgb(&nowR, &nowG, &nowB);
 	r = uchar(nowR);
 	g = uchar(nowG);
 	b = uchar(nowB);
 }
 
+void Colorize(uint32 &pixel, not_null<const Colorizer*> colorizer) {
+	const auto chars = reinterpret_cast<uchar*>(&pixel);
+	Colorize(
+		chars[2],
+		chars[1],
+		chars[0],
+		colorizer);
+}
+
+void Colorize(QImage &image, not_null<const Colorizer*> colorizer) {
+	image = std::move(image).convertToFormat(QImage::Format_ARGB32);
+	const auto bytes = image.bits();
+	const auto bytesPerLine = image.bytesPerLine();
+	for (auto line = 0; line != image.height(); ++line) {
+		const auto ints = reinterpret_cast<uint32*>(
+			bytes + line * bytesPerLine);
+		const auto end = ints + image.width();
+		for (auto p = ints; p != end; ++p) {
+			Colorize(*p, colorizer);
+		}
+	}
+}
+
 enum class SetResult {
 	Ok,
 	Bad,
@@ -190,7 +231,7 @@ SetResult setColorSchemeValue(
 		auto g = readHexUchar(data[3], data[4], error);
 		auto b = readHexUchar(data[5], data[6], error);
 		auto a = (size == 9) ? readHexUchar(data[7], data[8], error) : uchar(255);
-		if (colorizer) {
+		if (colorizer && !colorizer->ignoreKeys.contains(name)) {
 			Colorize(r, g, b, colorizer);
 		}
 		if (error) {
@@ -362,6 +403,9 @@ bool loadTheme(
 				LOG(("Theme Error: could not read background image in the theme file."));
 				return false;
 			}
+			if (colorizer) {
+				Colorize(background, colorizer);
+			}
 			auto buffer = QBuffer(&cache.background);
 			if (!background.save(&buffer, "BMP")) {
 				LOG(("Theme Error: could not write background image as a BMP to cache."));
diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h
index 61a6e662c..ee3e3a8c3 100644
--- a/Telegram/SourceFiles/window/themes/window_theme.h
+++ b/Telegram/SourceFiles/window/themes/window_theme.h
@@ -51,9 +51,13 @@ struct Preview {
 
 struct Colorizer {
 	int wasHue = 0;
+	int wasSaturation = 0;
+	int wasValue = 0;
 	int nowHue = 0;
+	int nowSaturation = 0;
+	int nowValue = 0;
 	int hueThreshold = 0;
-	int saturationThreshold = 0;
+	base::flat_set<QLatin1String> ignoreKeys;
 };
 
 bool Apply(const QString &filepath);