color_utils_unittest.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <stdlib.h>
  5. #include "skia/ext/platform_canvas.h"
  6. #include "testing/gtest/include/gtest/gtest.h"
  7. #include "third_party/skia/include/core/SkBitmap.h"
  8. #include "third_party/skia/include/core/SkColorPriv.h"
  9. #include "ui/gfx/canvas.h"
  10. #include "ui/gfx/color_palette.h"
  11. #include "ui/gfx/color_utils.h"
  12. #include "ui/gfx/geometry/rect.h"
  13. namespace color_utils {
  14. TEST(ColorUtils, SkColorToHSLRed) {
  15. HSL hsl = {0, 0, 0};
  16. SkColorToHSL(SK_ColorRED, &hsl);
  17. EXPECT_DOUBLE_EQ(hsl.h, 0);
  18. EXPECT_DOUBLE_EQ(hsl.s, 1);
  19. EXPECT_DOUBLE_EQ(hsl.l, 0.5);
  20. }
  21. TEST(ColorUtils, SkColorToHSLGrey) {
  22. HSL hsl = {0, 0, 0};
  23. SkColorToHSL(SkColorSetARGB(255, 128, 128, 128), &hsl);
  24. EXPECT_DOUBLE_EQ(hsl.h, 0);
  25. EXPECT_DOUBLE_EQ(hsl.s, 0);
  26. EXPECT_EQ(static_cast<int>(hsl.l * 100),
  27. static_cast<int>(0.5 * 100)); // Accurate to two decimal places.
  28. }
  29. TEST(ColorUtils, HSLToSkColorWithAlpha) {
  30. SkColor red = SkColorSetARGB(128, 255, 0, 0);
  31. HSL hsl = {0, 1, 0.5};
  32. SkColor result = HSLToSkColor(hsl, 128);
  33. EXPECT_EQ(SkColorGetA(red), SkColorGetA(result));
  34. EXPECT_EQ(SkColorGetR(red), SkColorGetR(result));
  35. EXPECT_EQ(SkColorGetG(red), SkColorGetG(result));
  36. EXPECT_EQ(SkColorGetB(red), SkColorGetB(result));
  37. }
  38. TEST(ColorUtils, RGBtoHSLRoundTrip) {
  39. // Just spot check values near the edges.
  40. for (int r = 0; r < 10; ++r) {
  41. for (int g = 0; g < 10; ++g) {
  42. for (int b = 0; b < 10; ++b) {
  43. SkColor rgb = SkColorSetARGB(255, r, g, b);
  44. HSL hsl = {0, 0, 0};
  45. SkColorToHSL(rgb, &hsl);
  46. SkColor out = HSLToSkColor(hsl, 255);
  47. EXPECT_EQ(SkColorGetR(out), SkColorGetR(rgb));
  48. EXPECT_EQ(SkColorGetG(out), SkColorGetG(rgb));
  49. EXPECT_EQ(SkColorGetB(out), SkColorGetB(rgb));
  50. }
  51. }
  52. }
  53. for (int r = 240; r < 256; ++r) {
  54. for (int g = 240; g < 256; ++g) {
  55. for (int b = 240; b < 256; ++b) {
  56. SkColor rgb = SkColorSetARGB(255, r, g, b);
  57. HSL hsl = {0, 0, 0};
  58. SkColorToHSL(rgb, &hsl);
  59. SkColor out = HSLToSkColor(hsl, 255);
  60. EXPECT_EQ(SkColorGetR(out), SkColorGetR(rgb));
  61. EXPECT_EQ(SkColorGetG(out), SkColorGetG(rgb));
  62. EXPECT_EQ(SkColorGetB(out), SkColorGetB(rgb));
  63. }
  64. }
  65. }
  66. }
  67. TEST(ColorUtils, IsWithinHSLRange) {
  68. HSL hsl = {0.3, 0.4, 0.5};
  69. HSL lower = {0.2, 0.3, 0.4};
  70. HSL upper = {0.4, 0.5, 0.6};
  71. EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper));
  72. // Bounds are inclusive.
  73. hsl.h = 0.2;
  74. EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper));
  75. hsl.h = 0.4;
  76. EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper));
  77. hsl.s = 0.3;
  78. EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper));
  79. hsl.s = 0.5;
  80. EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper));
  81. hsl.l = 0.4;
  82. EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper));
  83. hsl.l = 0.6;
  84. EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper));
  85. }
  86. TEST(ColorUtils, IsWithinHSLRangeHueWrapAround) {
  87. HSL hsl = {0.3, 0.4, 0.5};
  88. HSL lower = {0.8, -1, -1};
  89. HSL upper = {1.2, -1, -1};
  90. hsl.h = 0.1;
  91. EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper));
  92. hsl.h = 0.9;
  93. EXPECT_TRUE(IsWithinHSLRange(hsl, lower, upper));
  94. hsl.h = 0.3;
  95. EXPECT_FALSE(IsWithinHSLRange(hsl, lower, upper));
  96. }
  97. TEST(ColorUtils, IsHSLShiftMeaningful) {
  98. HSL noop_all_neg_one{-1.0, -1.0, -1.0};
  99. HSL noop_s_point_five{-1.0, 0.5, -1.0};
  100. HSL noop_l_point_five{-1.0, -1.0, 0.5};
  101. HSL only_h{0.1, -1.0, -1.0};
  102. HSL only_s{-1.0, 0.1, -1.0};
  103. HSL only_l{-1.0, -1.0, 0.1};
  104. HSL only_hs{0.1, 0.1, -1.0};
  105. HSL only_hl{0.1, -1.0, 0.1};
  106. HSL only_sl{-1.0, 0.1, 0.1};
  107. HSL all_set{0.1, 0.2, 0.3};
  108. EXPECT_FALSE(IsHSLShiftMeaningful(noop_all_neg_one));
  109. EXPECT_FALSE(IsHSLShiftMeaningful(noop_s_point_five));
  110. EXPECT_FALSE(IsHSLShiftMeaningful(noop_l_point_five));
  111. EXPECT_TRUE(IsHSLShiftMeaningful(only_h));
  112. EXPECT_TRUE(IsHSLShiftMeaningful(only_s));
  113. EXPECT_TRUE(IsHSLShiftMeaningful(only_l));
  114. EXPECT_TRUE(IsHSLShiftMeaningful(only_hs));
  115. EXPECT_TRUE(IsHSLShiftMeaningful(only_hl));
  116. EXPECT_TRUE(IsHSLShiftMeaningful(only_sl));
  117. EXPECT_TRUE(IsHSLShiftMeaningful(all_set));
  118. }
  119. TEST(ColorUtils, ColorToHSLRegisterSpill) {
  120. // In a opt build on Linux, this was causing a register spill on my laptop
  121. // (Pentium M) when converting from SkColor to HSL.
  122. SkColor input = SkColorSetARGB(255, 206, 154, 89);
  123. HSL hsl = {-1, -1, -1};
  124. SkColor result = HSLShift(input, hsl);
  125. // |result| should be the same as |input| since we passed in a value meaning
  126. // no color shift.
  127. EXPECT_EQ(SkColorGetA(input), SkColorGetA(result));
  128. EXPECT_EQ(SkColorGetR(input), SkColorGetR(result));
  129. EXPECT_EQ(SkColorGetG(input), SkColorGetG(result));
  130. EXPECT_EQ(SkColorGetB(input), SkColorGetB(result));
  131. }
  132. TEST(ColorUtils, AlphaBlend) {
  133. SkColor fore = SkColorSetARGB(255, 200, 200, 200);
  134. SkColor back = SkColorSetARGB(255, 100, 100, 100);
  135. EXPECT_TRUE(AlphaBlend(fore, back, 1.0f) == fore);
  136. EXPECT_TRUE(AlphaBlend(fore, back, 0.0f) == back);
  137. // One is fully transparent, result is partially transparent.
  138. back = SkColorSetA(back, 0);
  139. EXPECT_EQ(136U, SkColorGetA(AlphaBlend(fore, back, SkAlpha{136})));
  140. // Both are fully transparent, result is fully transparent.
  141. fore = SkColorSetA(fore, 0);
  142. EXPECT_EQ(0U, SkColorGetA(AlphaBlend(fore, back, 1.0f)));
  143. }
  144. TEST(ColorUtils, SkColorToRgbaString) {
  145. SkColor color = SkColorSetARGB(153, 100, 150, 200);
  146. std::string color_string = SkColorToRgbaString(color);
  147. EXPECT_EQ(color_string, "rgba(100,150,200,0.6)");
  148. }
  149. TEST(ColorUtils, SkColorToRgbString) {
  150. SkColor color = SkColorSetARGB(200, 50, 100, 150);
  151. std::string color_string = SkColorToRgbString(color);
  152. EXPECT_EQ(color_string, "50,100,150");
  153. }
  154. TEST(ColorUtils, IsDarkDarkestColorChange) {
  155. ASSERT_FALSE(IsDark(SK_ColorLTGRAY));
  156. const SkColor old_darkest_color = SetDarkestColorForTesting(SK_ColorLTGRAY);
  157. EXPECT_TRUE(IsDark(SK_ColorLTGRAY));
  158. SetDarkestColorForTesting(old_darkest_color);
  159. }
  160. TEST(ColorUtils, MidpointLuminanceMatches) {
  161. const SkColor old_darkest_color = SetDarkestColorForTesting(SK_ColorBLACK);
  162. auto [darkest, midpoint, lightest] = GetLuminancesForTesting();
  163. EXPECT_FLOAT_EQ(GetContrastRatio(darkest, midpoint),
  164. GetContrastRatio(midpoint, lightest));
  165. SetDarkestColorForTesting(old_darkest_color);
  166. std::tie(darkest, midpoint, lightest) = GetLuminancesForTesting();
  167. EXPECT_FLOAT_EQ(GetContrastRatio(darkest, midpoint),
  168. GetContrastRatio(midpoint, lightest));
  169. }
  170. TEST(ColorUtils, GetColorWithMaxContrast) {
  171. const SkColor old_darkest_color = SetDarkestColorForTesting(SK_ColorBLACK);
  172. EXPECT_EQ(SK_ColorWHITE, GetColorWithMaxContrast(SK_ColorBLACK));
  173. EXPECT_EQ(SK_ColorWHITE,
  174. GetColorWithMaxContrast(SkColorSetRGB(0x75, 0x75, 0x75)));
  175. EXPECT_EQ(SK_ColorBLACK, GetColorWithMaxContrast(SK_ColorWHITE));
  176. EXPECT_EQ(SK_ColorBLACK,
  177. GetColorWithMaxContrast(SkColorSetRGB(0x76, 0x76, 0x76)));
  178. SetDarkestColorForTesting(old_darkest_color);
  179. EXPECT_EQ(old_darkest_color, GetColorWithMaxContrast(SK_ColorWHITE));
  180. }
  181. TEST(ColorUtils, GetEndpointColorWithMinContrast) {
  182. const SkColor old_darkest_color = SetDarkestColorForTesting(SK_ColorBLACK);
  183. EXPECT_EQ(SK_ColorBLACK, GetEndpointColorWithMinContrast(SK_ColorBLACK));
  184. EXPECT_EQ(SK_ColorBLACK,
  185. GetEndpointColorWithMinContrast(SkColorSetRGB(0x75, 0x75, 0x75)));
  186. EXPECT_EQ(SK_ColorWHITE, GetEndpointColorWithMinContrast(SK_ColorWHITE));
  187. EXPECT_EQ(SK_ColorWHITE,
  188. GetEndpointColorWithMinContrast(SkColorSetRGB(0x76, 0x76, 0x76)));
  189. SetDarkestColorForTesting(old_darkest_color);
  190. EXPECT_EQ(old_darkest_color,
  191. GetEndpointColorWithMinContrast(old_darkest_color));
  192. }
  193. TEST(ColorUtils, BlendForMinContrast_ForegroundAlreadyMeetsMinimum) {
  194. const auto result = BlendForMinContrast(SK_ColorBLACK, SK_ColorWHITE);
  195. EXPECT_EQ(SK_AlphaTRANSPARENT, result.alpha);
  196. EXPECT_EQ(SK_ColorBLACK, result.color);
  197. }
  198. TEST(ColorUtils, BlendForMinContrast_BlendDarker) {
  199. const SkColor foreground = SkColorSetRGB(0xAA, 0xAA, 0xAA);
  200. const auto result = BlendForMinContrast(foreground, SK_ColorWHITE);
  201. EXPECT_NE(SK_AlphaTRANSPARENT, result.alpha);
  202. EXPECT_NE(foreground, result.color);
  203. EXPECT_GE(GetContrastRatio(result.color, SK_ColorWHITE),
  204. kMinimumReadableContrastRatio);
  205. }
  206. TEST(ColorUtils, BlendForMinContrast_BlendLighter) {
  207. const SkColor foreground = SkColorSetRGB(0x33, 0x33, 0x33);
  208. const auto result = BlendForMinContrast(foreground, SK_ColorBLACK);
  209. EXPECT_NE(SK_AlphaTRANSPARENT, result.alpha);
  210. EXPECT_NE(foreground, result.color);
  211. EXPECT_GE(GetContrastRatio(result.color, SK_ColorBLACK),
  212. kMinimumReadableContrastRatio);
  213. }
  214. TEST(ColorUtils, BlendForMinContrast_StopsAtDarkestColor) {
  215. const SkColor darkest_color = SkColorSetRGB(0x44, 0x44, 0x44);
  216. const SkColor old_darkest_color = SetDarkestColorForTesting(darkest_color);
  217. EXPECT_EQ(darkest_color, BlendForMinContrast(SkColorSetRGB(0x55, 0x55, 0x55),
  218. SkColorSetRGB(0xAA, 0xAA, 0xAA))
  219. .color);
  220. SetDarkestColorForTesting(old_darkest_color);
  221. }
  222. TEST(ColorUtils, BlendForMinContrast_ComputesExpectedOpacities) {
  223. const SkColor source = SkColorSetRGB(0xDE, 0xE1, 0xE6);
  224. const SkColor target = SkColorSetRGB(0xFF, 0xFF, 0xFF);
  225. const SkColor base = source;
  226. SkAlpha alpha = BlendForMinContrast(source, base, target, 1.11f).alpha;
  227. EXPECT_NEAR(alpha / 255.0f, 0.4f, 0.03f);
  228. alpha = BlendForMinContrast(source, base, target, 1.19f).alpha;
  229. EXPECT_NEAR(alpha / 255.0f, 0.65f, 0.03f);
  230. alpha = BlendForMinContrast(source, base, target, 1.13728f).alpha;
  231. EXPECT_NEAR(alpha / 255.0f, 0.45f, 0.03f);
  232. }
  233. TEST(ColorUtils, BlendTowardMaxContrast_PreservesAlpha) {
  234. SkColor test_colors[] = {SK_ColorBLACK, SK_ColorWHITE,
  235. SK_ColorRED, SK_ColorYELLOW,
  236. SK_ColorMAGENTA, gfx::kGoogleGreen500,
  237. gfx::kGoogleRed050, gfx::kGoogleBlue800};
  238. SkAlpha test_alphas[] = {SK_AlphaTRANSPARENT, 0x0F,
  239. gfx::kDisabledControlAlpha, 0xDD};
  240. SkAlpha blend_alpha = 0x7F;
  241. for (const SkColor color : test_colors) {
  242. SkColor opaque_result =
  243. color_utils::BlendTowardMaxContrast(color, blend_alpha);
  244. for (const SkAlpha alpha : test_alphas) {
  245. SkColor input = SkColorSetA(color, alpha);
  246. SkColor result = color_utils::BlendTowardMaxContrast(input, blend_alpha);
  247. // Alpha was preserved.
  248. EXPECT_EQ(SkColorGetA(result), alpha);
  249. // RGB channels unaffected by alpha of input.
  250. EXPECT_EQ(SkColorSetA(result, SK_AlphaOPAQUE), opaque_result);
  251. }
  252. }
  253. }
  254. TEST(ColorUtils, BlendForMinContrast_MatchesNaiveImplementation) {
  255. constexpr SkColor default_foreground = SkColorSetRGB(0xDE, 0xE1, 0xE6);
  256. constexpr SkColor high_contrast_foreground = SK_ColorWHITE;
  257. constexpr SkColor background = default_foreground;
  258. constexpr float kContrastRatio = 1.11f;
  259. const auto result = BlendForMinContrast(
  260. default_foreground, background, high_contrast_foreground, kContrastRatio);
  261. // Naive implementation is direct translation of function description.
  262. SkAlpha alpha = SK_AlphaTRANSPARENT;
  263. SkColor color = default_foreground;
  264. for (int i = SK_AlphaTRANSPARENT; i <= SK_AlphaOPAQUE; ++i) {
  265. alpha = static_cast<SkAlpha>(i);
  266. color = AlphaBlend(high_contrast_foreground, default_foreground, alpha);
  267. if (GetContrastRatio(color, background) >= kContrastRatio)
  268. break;
  269. }
  270. EXPECT_EQ(alpha, result.alpha);
  271. EXPECT_EQ(color, result.color);
  272. }
  273. } // namespace color_utils