color_transform_unittest.cc 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. // Copyright (c) 2016 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 <tuple>
  5. #include <vector>
  6. #include "base/logging.h"
  7. #include "testing/gtest/include/gtest/gtest.h"
  8. #include "third_party/skia/include/effects/SkRuntimeEffect.h"
  9. #include "ui/gfx/color_space.h"
  10. #include "ui/gfx/color_transform.h"
  11. #include "ui/gfx/geometry/transform.h"
  12. #include "ui/gfx/gfx_export.h"
  13. #include "ui/gfx/icc_profile.h"
  14. #include "ui/gfx/skia_color_space_util.h"
  15. #include "ui/gfx/test/icc_profiles.h"
  16. namespace gfx {
  17. // Allowed math error.
  18. const float kMathEpsilon = 0.001f;
  19. // Internal functions, exposted for testing.
  20. GFX_EXPORT Transform GetTransferMatrix(ColorSpace::MatrixID id);
  21. ColorSpace::PrimaryID all_primaries[] = {
  22. ColorSpace::PrimaryID::BT709, ColorSpace::PrimaryID::BT470M,
  23. ColorSpace::PrimaryID::BT470BG, ColorSpace::PrimaryID::SMPTE170M,
  24. ColorSpace::PrimaryID::SMPTE240M, ColorSpace::PrimaryID::FILM,
  25. ColorSpace::PrimaryID::BT2020, ColorSpace::PrimaryID::SMPTEST428_1,
  26. ColorSpace::PrimaryID::SMPTEST431_2, ColorSpace::PrimaryID::P3,
  27. };
  28. ColorSpace::TransferID simple_transfers[] = {
  29. ColorSpace::TransferID::BT709, ColorSpace::TransferID::GAMMA22,
  30. ColorSpace::TransferID::GAMMA28, ColorSpace::TransferID::SMPTE170M,
  31. ColorSpace::TransferID::SMPTE240M, ColorSpace::TransferID::SMPTEST428_1,
  32. ColorSpace::TransferID::LINEAR, ColorSpace::TransferID::LOG,
  33. ColorSpace::TransferID::LOG_SQRT, ColorSpace::TransferID::IEC61966_2_4,
  34. ColorSpace::TransferID::BT1361_ECG, ColorSpace::TransferID::SRGB,
  35. ColorSpace::TransferID::BT2020_10, ColorSpace::TransferID::BT2020_12,
  36. ColorSpace::TransferID::SRGB_HDR,
  37. };
  38. ColorSpace::TransferID extended_transfers[] = {
  39. ColorSpace::TransferID::LINEAR_HDR,
  40. ColorSpace::TransferID::SRGB_HDR,
  41. };
  42. ColorSpace::MatrixID all_matrices[] = {
  43. ColorSpace::MatrixID::RGB, ColorSpace::MatrixID::BT709,
  44. ColorSpace::MatrixID::FCC, ColorSpace::MatrixID::BT470BG,
  45. ColorSpace::MatrixID::SMPTE170M, ColorSpace::MatrixID::SMPTE240M,
  46. ColorSpace::MatrixID::YCOCG, ColorSpace::MatrixID::BT2020_NCL,
  47. ColorSpace::MatrixID::YDZDX,
  48. };
  49. ColorSpace::RangeID all_ranges[] = {ColorSpace::RangeID::FULL,
  50. ColorSpace::RangeID::LIMITED,
  51. ColorSpace::RangeID::DERIVED};
  52. bool optimizations[] = {true, false};
  53. TEST(SimpleColorSpace, BT709toSRGB) {
  54. ColorSpace bt709 = ColorSpace::CreateREC709();
  55. ColorSpace sRGB = ColorSpace::CreateSRGB();
  56. std::unique_ptr<ColorTransform> t(
  57. ColorTransform::NewColorTransform(bt709, sRGB));
  58. ColorTransform::TriStim tmp(16.0f / 255.0f, 0.5f, 0.5f);
  59. t->Transform(&tmp, 1);
  60. EXPECT_NEAR(tmp.x(), 0.0f, kMathEpsilon);
  61. EXPECT_NEAR(tmp.y(), 0.0f, kMathEpsilon);
  62. EXPECT_NEAR(tmp.z(), 0.0f, kMathEpsilon);
  63. tmp = ColorTransform::TriStim(235.0f / 255.0f, 0.5f, 0.5f);
  64. t->Transform(&tmp, 1);
  65. EXPECT_NEAR(tmp.x(), 1.0f, kMathEpsilon);
  66. EXPECT_NEAR(tmp.y(), 1.0f, kMathEpsilon);
  67. EXPECT_NEAR(tmp.z(), 1.0f, kMathEpsilon);
  68. // Test a blue color
  69. tmp = ColorTransform::TriStim(128.0f / 255.0f, 240.0f / 255.0f, 0.5f);
  70. t->Transform(&tmp, 1);
  71. EXPECT_GT(tmp.z(), tmp.x());
  72. EXPECT_GT(tmp.z(), tmp.y());
  73. }
  74. TEST(SimpleColorSpace, BT2020CLtoBT2020RGB) {
  75. ColorSpace bt2020cl(
  76. ColorSpace::PrimaryID::BT2020, ColorSpace::TransferID::BT2020_10,
  77. ColorSpace::MatrixID::BT2020_CL, ColorSpace::RangeID::LIMITED);
  78. ColorSpace bt2020rgb(ColorSpace::PrimaryID::BT2020,
  79. ColorSpace::TransferID::BT2020_10,
  80. ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
  81. std::unique_ptr<ColorTransform> t(
  82. ColorTransform::NewColorTransform(bt2020cl, bt2020rgb));
  83. ColorTransform::TriStim tmp(16.0f / 255.0f, 0.5f, 0.5f);
  84. t->Transform(&tmp, 1);
  85. EXPECT_NEAR(tmp.x(), 0.0f, kMathEpsilon);
  86. EXPECT_NEAR(tmp.y(), 0.0f, kMathEpsilon);
  87. EXPECT_NEAR(tmp.z(), 0.0f, kMathEpsilon);
  88. tmp = ColorTransform::TriStim(235.0f / 255.0f, 0.5f, 0.5f);
  89. t->Transform(&tmp, 1);
  90. EXPECT_NEAR(tmp.x(), 1.0f, kMathEpsilon);
  91. EXPECT_NEAR(tmp.y(), 1.0f, kMathEpsilon);
  92. EXPECT_NEAR(tmp.z(), 1.0f, kMathEpsilon);
  93. // Test a blue color
  94. tmp = ColorTransform::TriStim(128.0f / 255.0f, 240.0f / 255.0f, 0.5f);
  95. t->Transform(&tmp, 1);
  96. EXPECT_GT(tmp.z(), tmp.x());
  97. EXPECT_GT(tmp.z(), tmp.y());
  98. }
  99. TEST(SimpleColorSpace, YCOCGLimitedToSRGB) {
  100. ColorSpace ycocg(ColorSpace::PrimaryID::BT709, ColorSpace::TransferID::SRGB,
  101. ColorSpace::MatrixID::YCOCG, ColorSpace::RangeID::LIMITED);
  102. ColorSpace sRGB = ColorSpace::CreateSRGB();
  103. std::unique_ptr<ColorTransform> t(
  104. ColorTransform::NewColorTransform(ycocg, sRGB));
  105. ColorTransform::TriStim tmp(16.0f / 255.0f, 128.0f / 255.0f, 128.0f / 255.0f);
  106. t->Transform(&tmp, 1);
  107. EXPECT_NEAR(tmp.x(), 0.0f, kMathEpsilon);
  108. EXPECT_NEAR(tmp.y(), 0.0f, kMathEpsilon);
  109. EXPECT_NEAR(tmp.z(), 0.0f, kMathEpsilon);
  110. tmp = ColorTransform::TriStim(235.0f / 255.0f, 128.0f / 255.0f,
  111. 128.0f / 255.0f);
  112. t->Transform(&tmp, 1);
  113. EXPECT_NEAR(tmp.x(), 1.0f, kMathEpsilon);
  114. EXPECT_NEAR(tmp.y(), 1.0f, kMathEpsilon);
  115. EXPECT_NEAR(tmp.z(), 1.0f, kMathEpsilon);
  116. // Test a blue color
  117. // Use the equations for MatrixCoefficients 8 and VideoFullRangeFlag 0 in
  118. // ITU-T H.273:
  119. // Equations 11-13: E'_R = 0.0, E'_G = 0.0, E'_B = 1.0
  120. // Equations 20-22: R = 16, G = 16, B = 219 + 16 = 235
  121. // Equations 44-46:
  122. // Y = Round(0.5 * 16 + 0.25 * (16 + 235)) = Round(70.75) = 71
  123. // Cb = Round(0.5 * 16 - 0.25 * (16 + 235)) + 128 = Round(-54.75) + 128 = 73
  124. // Cr = Round(0.5 * (16 - 235)) + 128 = Round(-109.5) + 128 = 18
  125. // In this test we omit the Round() calls to avoid rounding errors.
  126. // Y = 0.5 * 16 + 0.25 * (16 + 235) = 70.75
  127. // Cb = 0.5 * 16 - 0.25 * (16 + 235) + 128 = -54.75 + 128 = 73.25
  128. // Cr = 0.5 * (16 - 235) + 128 = -109.5 + 128 = 18.5
  129. tmp =
  130. ColorTransform::TriStim(70.75f / 255.0f, 73.25f / 255.0f, 18.5f / 255.0f);
  131. t->Transform(&tmp, 1);
  132. EXPECT_NEAR(tmp.x(), 0.0f, kMathEpsilon);
  133. EXPECT_NEAR(tmp.y(), 0.0f, kMathEpsilon);
  134. EXPECT_NEAR(tmp.z(), 1.0f, kMathEpsilon);
  135. }
  136. TEST(SimpleColorSpace, TransferFnCancel) {
  137. ColorSpace::PrimaryID primary = ColorSpace::PrimaryID::BT709;
  138. ColorSpace::MatrixID matrix = ColorSpace::MatrixID::RGB;
  139. ColorSpace::RangeID range = ColorSpace::RangeID::FULL;
  140. // BT709 has a gamma of 2.2222 (with some adjustments)
  141. ColorSpace bt709(primary, ColorSpace::TransferID::BT709, matrix, range);
  142. // IEC61966_2_1 has the sRGB gamma of 2.4 (with some adjustments)
  143. ColorSpace srgb(primary, ColorSpace::TransferID::SRGB, matrix, range);
  144. // gamma28 is a simple exponential
  145. ColorSpace gamma28(primary, ColorSpace::TransferID::GAMMA28, matrix, range);
  146. // gamma24 is a simple exponential
  147. ColorSpace gamma24(primary, ColorSpace::TransferID::GAMMA24, matrix, range);
  148. // BT709 source is common for video and sRGB destination is common for
  149. // monitors. The two transfer functions are very close, and should cancel
  150. // out (so the transfer between them should be the identity). This particular
  151. // case is important for power reasons.
  152. std::unique_ptr<ColorTransform> bt709_to_srgb(
  153. ColorTransform::NewColorTransform(bt709, srgb));
  154. EXPECT_EQ(bt709_to_srgb->NumberOfStepsForTesting(), 0u);
  155. // Gamma 2.8 isn't even close to BT709 and won't cancel out (so we will have
  156. // two steps in the transform -- to-linear and from-linear).
  157. std::unique_ptr<ColorTransform> bt709_to_gamma28(
  158. ColorTransform::NewColorTransform(bt709, gamma28));
  159. EXPECT_EQ(bt709_to_gamma28->NumberOfStepsForTesting(), 2u);
  160. // Gamma 2.4 is closer to BT709, but not close enough to actually cancel out.
  161. std::unique_ptr<ColorTransform> bt709_to_gamma24(
  162. ColorTransform::NewColorTransform(bt709, gamma24));
  163. EXPECT_EQ(bt709_to_gamma24->NumberOfStepsForTesting(), 2u);
  164. // Rec 601 YUV to RGB conversion should have a single step.
  165. gfx::ColorSpace rec601 = gfx::ColorSpace::CreateREC601();
  166. std::unique_ptr<ColorTransform> rec601_yuv_to_rgb(
  167. ColorTransform::NewColorTransform(rec601, rec601.GetAsFullRangeRGB()));
  168. EXPECT_EQ(rec601_yuv_to_rgb->NumberOfStepsForTesting(), 1u);
  169. }
  170. TEST(SimpleColorSpace, SRGBFromICCAndNotICC) {
  171. float kPixelEpsilon = kMathEpsilon;
  172. ColorTransform::TriStim value_fromicc;
  173. ColorTransform::TriStim value_default;
  174. ICCProfile srgb_icc_profile = ICCProfileForTestingSRGB();
  175. ColorSpace srgb_fromicc = srgb_icc_profile.GetColorSpace();
  176. ColorSpace srgb_default = gfx::ColorSpace::CreateSRGB();
  177. ColorSpace xyzd50 = gfx::ColorSpace::CreateXYZD50();
  178. value_fromicc = value_default = ColorTransform::TriStim(0.1f, 0.5f, 0.9f);
  179. std::unique_ptr<ColorTransform> toxyzd50_fromicc(
  180. ColorTransform::NewColorTransform(srgb_fromicc, xyzd50));
  181. // This will be converted to a transfer function and then linear transform.
  182. EXPECT_EQ(toxyzd50_fromicc->NumberOfStepsForTesting(), 2u);
  183. toxyzd50_fromicc->Transform(&value_fromicc, 1);
  184. std::unique_ptr<ColorTransform> toxyzd50_default(
  185. ColorTransform::NewColorTransform(srgb_default, xyzd50));
  186. // This will have a transfer function and then linear transform.
  187. EXPECT_EQ(toxyzd50_default->NumberOfStepsForTesting(), 2u);
  188. toxyzd50_default->Transform(&value_default, 1);
  189. EXPECT_NEAR(value_fromicc.x(), value_default.x(), kPixelEpsilon);
  190. EXPECT_NEAR(value_fromicc.y(), value_default.y(), kPixelEpsilon);
  191. EXPECT_NEAR(value_fromicc.z(), value_default.z(), kPixelEpsilon);
  192. value_fromicc = value_default = ColorTransform::TriStim(0.1f, 0.5f, 0.9f);
  193. std::unique_ptr<ColorTransform> fromxyzd50_fromicc(
  194. ColorTransform::NewColorTransform(xyzd50, srgb_fromicc));
  195. fromxyzd50_fromicc->Transform(&value_fromicc, 1);
  196. std::unique_ptr<ColorTransform> fromxyzd50_default(
  197. ColorTransform::NewColorTransform(xyzd50, srgb_default));
  198. fromxyzd50_default->Transform(&value_default, 1);
  199. EXPECT_NEAR(value_fromicc.x(), value_default.x(), kPixelEpsilon);
  200. EXPECT_NEAR(value_fromicc.y(), value_default.y(), kPixelEpsilon);
  201. EXPECT_NEAR(value_fromicc.z(), value_default.z(), kPixelEpsilon);
  202. }
  203. TEST(SimpleColorSpace, BT709toSRGBICC) {
  204. ICCProfile srgb_icc = ICCProfileForTestingSRGB();
  205. ColorSpace bt709 = ColorSpace::CreateREC709();
  206. ColorSpace sRGB = srgb_icc.GetColorSpace();
  207. std::unique_ptr<ColorTransform> t(
  208. ColorTransform::NewColorTransform(bt709, sRGB));
  209. ColorTransform::TriStim tmp(16.0f / 255.0f, 0.5f, 0.5f);
  210. t->Transform(&tmp, 1);
  211. EXPECT_NEAR(tmp.x(), 0.0f, kMathEpsilon);
  212. EXPECT_NEAR(tmp.y(), 0.0f, kMathEpsilon);
  213. EXPECT_NEAR(tmp.z(), 0.0f, kMathEpsilon);
  214. tmp = ColorTransform::TriStim(235.0f / 255.0f, 0.5f, 0.5f);
  215. t->Transform(&tmp, 1);
  216. EXPECT_NEAR(tmp.x(), 1.0f, kMathEpsilon);
  217. EXPECT_NEAR(tmp.y(), 1.0f, kMathEpsilon);
  218. EXPECT_NEAR(tmp.z(), 1.0f, kMathEpsilon);
  219. // Test a blue color
  220. tmp = ColorTransform::TriStim(128.0f / 255.0f, 240.0f / 255.0f, 0.5f);
  221. t->Transform(&tmp, 1);
  222. EXPECT_GT(tmp.z(), tmp.x());
  223. EXPECT_GT(tmp.z(), tmp.y());
  224. }
  225. TEST(SimpleColorSpace, ICCProfileOnlyXYZ) {
  226. const float kPixelEpsilon = 2.5f / 255.f;
  227. ICCProfile icc_profile = ICCProfileForTestingNoAnalyticTrFn();
  228. ColorSpace icc_space = icc_profile.GetColorSpace();
  229. ColorSpace xyzd50 = ColorSpace::CreateXYZD50();
  230. ColorTransform::TriStim input_value(127.f / 255, 187.f / 255, 157.f / 255);
  231. ColorTransform::TriStim transformed_value = input_value;
  232. ColorTransform::TriStim expected_transformed_value(
  233. 0.34090986847877502f, 0.42633286118507385f, 0.3408740758895874f);
  234. // Two steps should be needed, transfer fn and matrix.
  235. std::unique_ptr<ColorTransform> icc_to_xyzd50(
  236. ColorTransform::NewColorTransform(icc_space, xyzd50));
  237. EXPECT_EQ(icc_to_xyzd50->NumberOfStepsForTesting(), 2u);
  238. icc_to_xyzd50->Transform(&transformed_value, 1);
  239. EXPECT_NEAR(transformed_value.x(), expected_transformed_value.x(),
  240. kPixelEpsilon);
  241. EXPECT_NEAR(transformed_value.y(), expected_transformed_value.y(),
  242. kPixelEpsilon);
  243. EXPECT_NEAR(transformed_value.z(), expected_transformed_value.z(),
  244. kPixelEpsilon);
  245. // Two steps should be needed, matrix and transfer fn.
  246. std::unique_ptr<ColorTransform> xyzd50_to_icc(
  247. ColorTransform::NewColorTransform(xyzd50, icc_space));
  248. EXPECT_EQ(xyzd50_to_icc->NumberOfStepsForTesting(), 2u);
  249. xyzd50_to_icc->Transform(&transformed_value, 1);
  250. EXPECT_NEAR(input_value.x(), transformed_value.x(), kPixelEpsilon);
  251. EXPECT_NEAR(input_value.y(), transformed_value.y(), kPixelEpsilon);
  252. EXPECT_NEAR(input_value.z(), transformed_value.z(), kPixelEpsilon);
  253. }
  254. TEST(SimpleColorSpace, ICCProfileOnlyColorSpin) {
  255. const float kPixelEpsilon = 3.0f / 255.f;
  256. ICCProfile icc_profile = ICCProfileForTestingNoAnalyticTrFn();
  257. ColorSpace icc_space = icc_profile.GetColorSpace();
  258. ColorSpace colorspin = ICCProfileForTestingColorSpin().GetColorSpace();
  259. ColorTransform::TriStim input_value(0.25f, 0.5f, 0.75f);
  260. ColorTransform::TriStim transformed_value = input_value;
  261. ColorTransform::TriStim expected_transformed_value(
  262. 0.49694931507110596f, 0.74937951564788818f, 0.31359460949897766f);
  263. // Three steps will be needed.
  264. std::unique_ptr<ColorTransform> icc_to_colorspin(
  265. ColorTransform::NewColorTransform(icc_space, colorspin));
  266. EXPECT_EQ(icc_to_colorspin->NumberOfStepsForTesting(), 3u);
  267. icc_to_colorspin->Transform(&transformed_value, 1);
  268. EXPECT_NEAR(transformed_value.x(), expected_transformed_value.x(),
  269. kPixelEpsilon);
  270. EXPECT_NEAR(transformed_value.y(), expected_transformed_value.y(),
  271. kPixelEpsilon);
  272. EXPECT_NEAR(transformed_value.z(), expected_transformed_value.z(),
  273. kPixelEpsilon);
  274. transformed_value = expected_transformed_value;
  275. std::unique_ptr<ColorTransform> colorspin_to_icc(
  276. ColorTransform::NewColorTransform(colorspin, icc_space));
  277. EXPECT_EQ(colorspin_to_icc->NumberOfStepsForTesting(), 3u);
  278. transformed_value = expected_transformed_value;
  279. colorspin_to_icc->Transform(&transformed_value, 1);
  280. EXPECT_NEAR(input_value.x(), transformed_value.x(), kPixelEpsilon);
  281. EXPECT_NEAR(input_value.y(), transformed_value.y(), kPixelEpsilon);
  282. EXPECT_NEAR(input_value.z(), transformed_value.z(), kPixelEpsilon);
  283. }
  284. TEST(SimpleColorSpace, GetColorSpace) {
  285. const float kPixelEpsilon = 1.5f / 255.f;
  286. ICCProfile srgb_icc = ICCProfileForTestingSRGB();
  287. ColorSpace sRGB = srgb_icc.GetColorSpace();
  288. ColorSpace sRGB2 = sRGB;
  289. std::unique_ptr<ColorTransform> t(
  290. ColorTransform::NewColorTransform(sRGB, sRGB2));
  291. ColorTransform::TriStim tmp(1.0f, 1.0f, 1.0f);
  292. t->Transform(&tmp, 1);
  293. EXPECT_NEAR(tmp.x(), 1.0f, kPixelEpsilon);
  294. EXPECT_NEAR(tmp.y(), 1.0f, kPixelEpsilon);
  295. EXPECT_NEAR(tmp.z(), 1.0f, kPixelEpsilon);
  296. tmp = ColorTransform::TriStim(1.0f, 0.0f, 0.0f);
  297. t->Transform(&tmp, 1);
  298. EXPECT_NEAR(tmp.x(), 1.0f, kPixelEpsilon);
  299. EXPECT_NEAR(tmp.y(), 0.0f, kPixelEpsilon);
  300. EXPECT_NEAR(tmp.z(), 0.0f, kPixelEpsilon);
  301. tmp = ColorTransform::TriStim(0.0f, 1.0f, 0.0f);
  302. t->Transform(&tmp, 1);
  303. EXPECT_NEAR(tmp.x(), 0.0f, kPixelEpsilon);
  304. EXPECT_NEAR(tmp.y(), 1.0f, kPixelEpsilon);
  305. EXPECT_NEAR(tmp.z(), 0.0f, kPixelEpsilon);
  306. tmp = ColorTransform::TriStim(0.0f, 0.0f, 1.0f);
  307. t->Transform(&tmp, 1);
  308. EXPECT_NEAR(tmp.x(), 0.0f, kPixelEpsilon);
  309. EXPECT_NEAR(tmp.y(), 0.0f, kPixelEpsilon);
  310. EXPECT_NEAR(tmp.z(), 1.0f, kPixelEpsilon);
  311. }
  312. TEST(SimpleColorSpace, Scale) {
  313. const float kPixelEpsilon = 1.5f / 255.f;
  314. ColorSpace srgb = ColorSpace::CreateSRGB();
  315. ColorSpace srgb_scaled = srgb.GetScaledColorSpace(2.0f);
  316. std::unique_ptr<ColorTransform> t(
  317. ColorTransform::NewColorTransform(srgb, srgb_scaled));
  318. ColorTransform::TriStim tmp(1.0f, 1.0f, 1.0f);
  319. t->Transform(&tmp, 1);
  320. EXPECT_NEAR(tmp.x(), 0.735356983052449f, kPixelEpsilon);
  321. EXPECT_NEAR(tmp.y(), 0.735356983052449f, kPixelEpsilon);
  322. EXPECT_NEAR(tmp.z(), 0.735356983052449f, kPixelEpsilon);
  323. }
  324. TEST(SimpleColorSpace, ToUndefined) {
  325. ColorSpace null;
  326. ColorSpace nonnull = gfx::ColorSpace::CreateSRGB();
  327. // Video should have 1 step: YUV to RGB.
  328. // Anything else should have 0 steps.
  329. ColorSpace video = gfx::ColorSpace::CreateREC709();
  330. std::unique_ptr<ColorTransform> video_to_null(
  331. ColorTransform::NewColorTransform(video, null));
  332. EXPECT_EQ(video_to_null->NumberOfStepsForTesting(), 1u);
  333. // Without optimization, video should have 2 steps: limited range to full
  334. // range, and YUV to RGB.
  335. ColorTransform::Options options;
  336. options.disable_optimizations = true;
  337. std::unique_ptr<ColorTransform> video_to_null_no_opt(
  338. ColorTransform::NewColorTransform(video, null, options));
  339. EXPECT_EQ(video_to_null_no_opt->NumberOfStepsForTesting(), 2u);
  340. // Test with an ICC profile that can't be represented as matrix+transfer.
  341. ColorSpace luttrcicc = ICCProfileForTestingNoAnalyticTrFn().GetColorSpace();
  342. std::unique_ptr<ColorTransform> luttrcicc_to_null(
  343. ColorTransform::NewColorTransform(luttrcicc, null));
  344. EXPECT_EQ(luttrcicc_to_null->NumberOfStepsForTesting(), 0u);
  345. std::unique_ptr<ColorTransform> luttrcicc_to_nonnull(
  346. ColorTransform::NewColorTransform(luttrcicc, nonnull));
  347. EXPECT_GT(luttrcicc_to_nonnull->NumberOfStepsForTesting(), 0u);
  348. // Test with an ICC profile that can.
  349. ColorSpace adobeicc = ICCProfileForTestingAdobeRGB().GetColorSpace();
  350. std::unique_ptr<ColorTransform> adobeicc_to_null(
  351. ColorTransform::NewColorTransform(adobeicc, null));
  352. EXPECT_EQ(adobeicc_to_null->NumberOfStepsForTesting(), 0u);
  353. std::unique_ptr<ColorTransform> adobeicc_to_nonnull(
  354. ColorTransform::NewColorTransform(adobeicc, nonnull));
  355. EXPECT_GT(adobeicc_to_nonnull->NumberOfStepsForTesting(), 0u);
  356. // And with something analytic.
  357. ColorSpace xyzd50 = gfx::ColorSpace::CreateXYZD50();
  358. std::unique_ptr<ColorTransform> xyzd50_to_null(
  359. ColorTransform::NewColorTransform(xyzd50, null));
  360. EXPECT_EQ(xyzd50_to_null->NumberOfStepsForTesting(), 0u);
  361. std::unique_ptr<ColorTransform> xyzd50_to_nonnull(
  362. ColorTransform::NewColorTransform(xyzd50, nonnull));
  363. EXPECT_GT(xyzd50_to_nonnull->NumberOfStepsForTesting(), 0u);
  364. }
  365. TEST(SimpleColorSpace, DefaultToSRGB) {
  366. // The default value should do no transformation, regardless of destination.
  367. ColorSpace unknown;
  368. std::unique_ptr<ColorTransform> t1(
  369. ColorTransform::NewColorTransform(unknown, ColorSpace::CreateSRGB()));
  370. EXPECT_EQ(t1->NumberOfStepsForTesting(), 0u);
  371. std::unique_ptr<ColorTransform> t2(
  372. ColorTransform::NewColorTransform(unknown, ColorSpace::CreateXYZD50()));
  373. EXPECT_EQ(t2->NumberOfStepsForTesting(), 0u);
  374. }
  375. // This tests to make sure that we don't emit "pow" parts of a
  376. // transfer function unless necessary.
  377. TEST(SimpleColorSpace, ShaderSourceTrFnOptimizations) {
  378. skcms_Matrix3x3 primaries;
  379. gfx::ColorSpace::CreateSRGB().GetPrimaryMatrix(&primaries);
  380. skcms_TransferFunction fn_no_pow = {
  381. 1.f, 2.f, 0.f, 1.f, 0.f, 0.f, 0.f,
  382. };
  383. skcms_TransferFunction fn_yes_pow = {
  384. 2.f, 2.f, 0.f, 1.f, 0.f, 0.f, 0.f,
  385. };
  386. gfx::ColorSpace src;
  387. gfx::ColorSpace dst = gfx::ColorSpace::CreateXYZD50();
  388. std::string shader_string;
  389. src = gfx::ColorSpace::CreateCustom(primaries, fn_no_pow);
  390. shader_string =
  391. ColorTransform::NewColorTransform(src, dst)->GetShaderSource();
  392. EXPECT_EQ(shader_string.find("pow("), std::string::npos);
  393. src = gfx::ColorSpace::CreateCustom(primaries, fn_yes_pow);
  394. shader_string =
  395. ColorTransform::NewColorTransform(src, dst)->GetShaderSource();
  396. EXPECT_NE(shader_string.find("pow("), std::string::npos);
  397. }
  398. // Note: This is not actually "testing" anything -- the goal of this test is to
  399. // to make reviewing shader code simpler by giving an example of the resulting
  400. // shader source. This should be updated whenever shader generation is updated.
  401. // This test produces slightly different results on Android.
  402. TEST(SimpleColorSpace, SampleShaderSource) {
  403. ColorSpace bt709 = ColorSpace::CreateREC709();
  404. ColorSpace output(ColorSpace::PrimaryID::BT2020,
  405. ColorSpace::TransferID::GAMMA28);
  406. std::string source =
  407. ColorTransform::NewColorTransform(bt709, output)->GetShaderSource();
  408. std::string expected =
  409. "float TransferFn1(float v) {\n"
  410. " if (v < 4.04499359e-02)\n"
  411. " v = 7.73993805e-02 * v;\n"
  412. " else\n"
  413. " v = pow(9.47867334e-01 * v + 5.21326549e-02, 2.40000010e+00);\n"
  414. " return v;\n"
  415. "}\n"
  416. "float TransferFn3(float v) {\n"
  417. " if (v < 0.00000000e+00)\n"
  418. " v = 0.00000000e+00 * v;\n"
  419. " else\n"
  420. " v = pow(v, 3.57142866e-01);\n"
  421. " return v;\n"
  422. "}\n"
  423. "vec3 DoColorConversion(vec3 color) {\n"
  424. " color = mat3(1.16438353e+00, 1.16438353e+00, 1.16438353e+00,\n"
  425. " -2.28029018e-09, -2.13248596e-01, 2.11240172e+00,\n"
  426. " 1.79274118e+00, -5.32909274e-01, -5.96049432e-10) "
  427. "* color;\n"
  428. " color += vec3(-9.69429970e-01, 3.00019622e-01, -1.12926018e+00);\n"
  429. " color.r = TransferFn1(color.r);\n"
  430. " color.g = TransferFn1(color.g);\n"
  431. " color.b = TransferFn1(color.b);\n"
  432. " color = mat3(6.27404153e-01, 6.90974295e-02, 1.63914450e-02,\n"
  433. " 3.29283088e-01, 9.19540644e-01, 8.80132765e-02,\n"
  434. " 4.33131158e-02, 1.13623142e-02, 8.95595193e-01) "
  435. "* color;\n"
  436. " color.r = sign(color.r) * TransferFn3(abs(color.r));\n"
  437. " color.g = sign(color.g) * TransferFn3(abs(color.g));\n"
  438. " color.b = sign(color.b) * TransferFn3(abs(color.b));\n"
  439. " return color;\n"
  440. "}\n";
  441. EXPECT_EQ(source, expected);
  442. }
  443. // Checks that the generated SkSL fragment shaders can be parsed by
  444. // SkSL::Compiler.
  445. TEST(SimpleColorSpace, CanParseSkShaderSource) {
  446. std::vector<ColorSpace> common_color_spaces = {
  447. ColorSpace::CreateSRGB(), ColorSpace::CreateDisplayP3D65(),
  448. ColorSpace::CreateExtendedSRGB(), ColorSpace::CreateSRGBLinear(),
  449. ColorSpace::CreateJpeg(), ColorSpace::CreateREC601(),
  450. ColorSpace::CreateREC709()};
  451. for (const auto& src : common_color_spaces) {
  452. for (const auto& dst : common_color_spaces) {
  453. auto transform = ColorTransform::NewColorTransform(src, dst);
  454. std::string source = "half4 main(half4 color) {\n" +
  455. transform->GetSkShaderSource() + " return color; }";
  456. SkRuntimeEffect::Result result = SkRuntimeEffect::MakeForColorFilter(
  457. SkString(source.c_str(), source.length()), /*options=*/{});
  458. EXPECT_NE(result.effect, nullptr);
  459. EXPECT_STREQ(result.errorText.c_str(), "");
  460. }
  461. }
  462. }
  463. class TransferTest : public testing::TestWithParam<ColorSpace::TransferID> {};
  464. TEST_P(TransferTest, BasicTest) {
  465. gfx::ColorSpace space_with_transfer(ColorSpace::PrimaryID::BT709, GetParam(),
  466. ColorSpace::MatrixID::RGB,
  467. ColorSpace::RangeID::FULL);
  468. gfx::ColorSpace space_linear(
  469. ColorSpace::PrimaryID::BT709, ColorSpace::TransferID::LINEAR,
  470. ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
  471. std::unique_ptr<ColorTransform> to_linear(
  472. ColorTransform::NewColorTransform(space_with_transfer, space_linear));
  473. std::unique_ptr<ColorTransform> from_linear(
  474. ColorTransform::NewColorTransform(space_linear, space_with_transfer));
  475. // The transforms will have 1 or 0 steps (0 for linear).
  476. size_t expected_steps = 1u;
  477. if (GetParam() == ColorSpace::TransferID::LINEAR)
  478. expected_steps = 0u;
  479. EXPECT_EQ(to_linear->NumberOfStepsForTesting(), expected_steps);
  480. EXPECT_EQ(from_linear->NumberOfStepsForTesting(), expected_steps);
  481. for (float x = 0.0f; x <= 1.0f; x += 1.0f / 128.0f) {
  482. ColorTransform::TriStim tristim(x, x, x);
  483. to_linear->Transform(&tristim, 1);
  484. from_linear->Transform(&tristim, 1);
  485. EXPECT_NEAR(x, tristim.x(), kMathEpsilon);
  486. }
  487. }
  488. INSTANTIATE_TEST_SUITE_P(ColorSpace,
  489. TransferTest,
  490. testing::ValuesIn(simple_transfers));
  491. class ExtendedTransferTest
  492. : public testing::TestWithParam<ColorSpace::TransferID> {};
  493. TEST_P(ExtendedTransferTest, extendedTest) {
  494. gfx::ColorSpace space_with_transfer(ColorSpace::PrimaryID::BT709, GetParam(),
  495. ColorSpace::MatrixID::RGB,
  496. ColorSpace::RangeID::FULL);
  497. gfx::ColorSpace space_linear(
  498. ColorSpace::PrimaryID::BT709, ColorSpace::TransferID::LINEAR,
  499. ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
  500. std::unique_ptr<ColorTransform> to_linear(
  501. ColorTransform::NewColorTransform(space_with_transfer, space_linear));
  502. std::unique_ptr<ColorTransform> from_linear(
  503. ColorTransform::NewColorTransform(space_linear, space_with_transfer));
  504. for (float x = -2.0f; x <= 2.0f; x += 1.0f / 32.0f) {
  505. ColorTransform::TriStim tristim(x, x, x);
  506. to_linear->Transform(&tristim, 1);
  507. from_linear->Transform(&tristim, 1);
  508. EXPECT_NEAR(x, tristim.x(), kMathEpsilon);
  509. }
  510. }
  511. INSTANTIATE_TEST_SUITE_P(ColorSpace,
  512. ExtendedTransferTest,
  513. testing::ValuesIn(extended_transfers));
  514. typedef std::tuple<ColorSpace::PrimaryID,
  515. ColorSpace::TransferID,
  516. ColorSpace::MatrixID,
  517. ColorSpace::RangeID,
  518. bool>
  519. ColorSpaceTestData;
  520. class ColorSpaceTestBase : public testing::TestWithParam<ColorSpaceTestData> {
  521. public:
  522. ColorSpaceTestBase()
  523. : color_space_(std::get<0>(GetParam()),
  524. std::get<1>(GetParam()),
  525. std::get<2>(GetParam()),
  526. std::get<3>(GetParam())) {
  527. options_.disable_optimizations = std::get<4>(GetParam());
  528. }
  529. protected:
  530. ColorSpace color_space_;
  531. ColorTransform::Options options_;
  532. };
  533. TEST_P(ColorSpaceTestBase, testNullTransform) {
  534. std::unique_ptr<ColorTransform> t(
  535. ColorTransform::NewColorTransform(color_space_, color_space_, options_));
  536. ColorTransform::TriStim tristim(0.4f, 0.5f, 0.6f);
  537. t->Transform(&tristim, 1);
  538. EXPECT_NEAR(tristim.x(), 0.4f, kMathEpsilon);
  539. EXPECT_NEAR(tristim.y(), 0.5f, kMathEpsilon);
  540. EXPECT_NEAR(tristim.z(), 0.6f, kMathEpsilon);
  541. }
  542. TEST_P(ColorSpaceTestBase, toXYZandBack) {
  543. std::unique_ptr<ColorTransform> t1(ColorTransform::NewColorTransform(
  544. color_space_, ColorSpace::CreateXYZD50(), options_));
  545. std::unique_ptr<ColorTransform> t2(ColorTransform::NewColorTransform(
  546. ColorSpace::CreateXYZD50(), color_space_, options_));
  547. ColorTransform::TriStim tristim(0.4f, 0.5f, 0.6f);
  548. t1->Transform(&tristim, 1);
  549. t2->Transform(&tristim, 1);
  550. EXPECT_NEAR(tristim.x(), 0.4f, kMathEpsilon);
  551. EXPECT_NEAR(tristim.y(), 0.5f, kMathEpsilon);
  552. EXPECT_NEAR(tristim.z(), 0.6f, kMathEpsilon);
  553. }
  554. INSTANTIATE_TEST_SUITE_P(
  555. A,
  556. ColorSpaceTestBase,
  557. testing::Combine(testing::ValuesIn(all_primaries),
  558. testing::ValuesIn(simple_transfers),
  559. testing::Values(ColorSpace::MatrixID::BT709),
  560. testing::Values(ColorSpace::RangeID::LIMITED),
  561. testing::ValuesIn(optimizations)));
  562. INSTANTIATE_TEST_SUITE_P(
  563. B,
  564. ColorSpaceTestBase,
  565. testing::Combine(testing::Values(ColorSpace::PrimaryID::BT709),
  566. testing::ValuesIn(simple_transfers),
  567. testing::ValuesIn(all_matrices),
  568. testing::ValuesIn(all_ranges),
  569. testing::ValuesIn(optimizations)));
  570. INSTANTIATE_TEST_SUITE_P(
  571. C,
  572. ColorSpaceTestBase,
  573. testing::Combine(testing::ValuesIn(all_primaries),
  574. testing::Values(ColorSpace::TransferID::BT709),
  575. testing::ValuesIn(all_matrices),
  576. testing::ValuesIn(all_ranges),
  577. testing::ValuesIn(optimizations)));
  578. TEST(ColorSpaceTest, ExtendedSRGBScale) {
  579. ColorSpace space_unscaled = ColorSpace::CreateSRGB();
  580. float scale = 3.14;
  581. skcms_TransferFunction scaled_trfn =
  582. SkTransferFnScaled(*skcms_sRGB_TransferFunction(), scale);
  583. ColorSpace space_scaled(ColorSpace::PrimaryID::BT709,
  584. ColorSpace::TransferID::CUSTOM_HDR,
  585. ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL,
  586. nullptr, &scaled_trfn);
  587. ColorSpace space_target(ColorSpace::PrimaryID::BT709,
  588. ColorSpace::TransferID::LINEAR,
  589. ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
  590. std::unique_ptr<ColorTransform> xform_scaled(
  591. ColorTransform::NewColorTransform(space_scaled, space_target));
  592. std::unique_ptr<ColorTransform> xform_unscaled(
  593. ColorTransform::NewColorTransform(space_unscaled, space_target));
  594. // Make sure that we're testing something in the linear (0.001) and nonlinear
  595. // (the rest) segments of the function.
  596. ColorTransform::TriStim val_scaled(0.001, 0.5, 0.7);
  597. ColorTransform::TriStim val_unscaled = val_scaled;
  598. xform_scaled->Transform(&val_scaled, 1);
  599. xform_unscaled->Transform(&val_unscaled, 1);
  600. EXPECT_NEAR(val_scaled.x() / val_unscaled.x(), scale, kMathEpsilon);
  601. EXPECT_NEAR(val_scaled.y() / val_unscaled.y(), scale, kMathEpsilon);
  602. EXPECT_NEAR(val_scaled.z() / val_unscaled.z(), scale, kMathEpsilon);
  603. }
  604. TEST(ColorSpaceTest, PQSDRWhiteLevel) {
  605. // The PQ function maps |pq_encoded_nits| to |nits|. We mangle it a bit with
  606. // the SDR white level.
  607. float pq_encoded_nits[] = {
  608. 0.485857f,
  609. 0.508078f,
  610. 0.579133f,
  611. };
  612. float nits[] = {80.f, 100.f, 200.f};
  613. for (size_t i = 0; i < 3; ++i) {
  614. // We'll set the SDR white level to the values in |nits| and also the
  615. // default.
  616. const ColorSpace hdr10 = ColorSpace::CreateHDR10();
  617. ColorTransform::Options options;
  618. options.sdr_max_luminance_nits = nits[i];
  619. // Transform to the same color space, but with the LINEAR_HDR transfer
  620. // function.
  621. ColorSpace target(ColorSpace::PrimaryID::BT2020,
  622. ColorSpace::TransferID::LINEAR_HDR,
  623. ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
  624. std::unique_ptr<ColorTransform> xform(
  625. ColorTransform::NewColorTransform(hdr10, target, options));
  626. // Do the transform to the values in |pq_encoded_nits|.
  627. ColorTransform::TriStim val(pq_encoded_nits[0], pq_encoded_nits[1],
  628. pq_encoded_nits[2]);
  629. xform->Transform(&val, 1);
  630. // The white level should be mapped to 1.
  631. switch (i) {
  632. case 0:
  633. EXPECT_NEAR(val.x(), 1.f, kMathEpsilon);
  634. break;
  635. case 1:
  636. EXPECT_NEAR(val.y(), 1.f, kMathEpsilon);
  637. break;
  638. case 2:
  639. EXPECT_NEAR(val.z(), 1.f, kMathEpsilon);
  640. break;
  641. case 3:
  642. // Check that the default white level is 100 nits.
  643. EXPECT_NEAR(val.y(), 1.f, kMathEpsilon);
  644. break;
  645. }
  646. // The nit ratios should be preserved by the transform.
  647. EXPECT_NEAR(val.y() / val.x(), nits[1] / nits[0], kMathEpsilon);
  648. EXPECT_NEAR(val.z() / val.x(), nits[2] / nits[0], kMathEpsilon);
  649. // Test the inverse transform.
  650. std::unique_ptr<ColorTransform> xform_inv(
  651. ColorTransform::NewColorTransform(target, hdr10, options));
  652. xform_inv->Transform(&val, 1);
  653. EXPECT_NEAR(val.x(), pq_encoded_nits[0], kMathEpsilon);
  654. EXPECT_NEAR(val.y(), pq_encoded_nits[1], kMathEpsilon);
  655. EXPECT_NEAR(val.z(), pq_encoded_nits[2], kMathEpsilon);
  656. }
  657. }
  658. TEST(ColorSpaceTest, HLGSDRWhiteLevel) {
  659. // These values are (1.0f * nits[i] / kDefaultSDRWhiteLevel) converted to
  660. // LINEAR_HDR via the HLG transfer function.
  661. constexpr float hlg_encoded_nits[] = {
  662. 0.447214f, // 0.5 * sqrt(1.0 * 80 / 100)
  663. 0.5f, // 0.5 * sqrt(1.0 * 100 / 100)
  664. 0.65641f, // 0.17883277 * ln(1.0 * 200 / 100 - 0.28466892) + 0.55991073
  665. };
  666. constexpr float nits[] = {203.f / 2, 203.f, 203.f * 2};
  667. for (size_t i = 0; i < 3; ++i) {
  668. // We'll set the SDR white level to the values in |nits| and also the
  669. // default.
  670. const ColorSpace hlg = ColorSpace::CreateHLG();
  671. ColorTransform::Options options;
  672. options.sdr_max_luminance_nits = nits[i];
  673. // Transform to the same color space, but with the LINEAR_HDR transfer
  674. // function.
  675. ColorSpace target(ColorSpace::PrimaryID::BT2020,
  676. ColorSpace::TransferID::LINEAR_HDR,
  677. ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
  678. std::unique_ptr<ColorTransform> xform(
  679. ColorTransform::NewColorTransform(hlg, target, options));
  680. // Do the transform to the values in |hlg_encoded_nits|.
  681. ColorTransform::TriStim val(hlg_encoded_nits[0], hlg_encoded_nits[1],
  682. hlg_encoded_nits[2]);
  683. xform->Transform(&val, 1);
  684. // Each |hlg_encoded_nits| value should map back to 1.0f after conversion
  685. // via a ColorSpace with the right SDR white level.
  686. switch (i) {
  687. case 0:
  688. EXPECT_NEAR(val.x(), 1.6f, kMathEpsilon);
  689. break;
  690. case 1:
  691. EXPECT_NEAR(val.y(), 1.f, kMathEpsilon);
  692. break;
  693. case 2:
  694. EXPECT_NEAR(val.z(), 1.f, kMathEpsilon);
  695. break;
  696. case 3:
  697. // Check that the default white level is 100 nits.
  698. EXPECT_NEAR(val.y(), 1.f, kMathEpsilon);
  699. break;
  700. }
  701. // Test the inverse transform.
  702. std::unique_ptr<ColorTransform> xform_inv(
  703. ColorTransform::NewColorTransform(target, hlg, options));
  704. xform_inv->Transform(&val, 1);
  705. EXPECT_NEAR(val.x(), hlg_encoded_nits[0], kMathEpsilon);
  706. EXPECT_NEAR(val.y(), hlg_encoded_nits[1], kMathEpsilon);
  707. EXPECT_NEAR(val.z(), hlg_encoded_nits[2], kMathEpsilon);
  708. }
  709. }
  710. TEST(ColorSpaceTest, PiecewiseHDR) {
  711. // The sRGB function evaluated at a couple of test points.
  712. const float srgb_x0 = 0.01;
  713. const float srgb_y0 = 0.00077399380805;
  714. const float srgb_x1 = 0.5;
  715. const float srgb_y1 = 0.2140411174732872;
  716. // Parameters for CreatePiecewiseHDR to test.
  717. const std::vector<float> test_sdr_joints = {
  718. 0.25f,
  719. 0.5f,
  720. 0.75f,
  721. };
  722. const std::vector<float> test_hdr_levels = {
  723. 1.5f,
  724. 2.0f,
  725. 5.0f,
  726. };
  727. // Go through all combinations.
  728. for (float sdr_joint : test_sdr_joints) {
  729. for (float hdr_level : test_hdr_levels) {
  730. ColorSpace hdr = ColorSpace::CreatePiecewiseHDR(
  731. ColorSpace::PrimaryID::BT709, sdr_joint, hdr_level);
  732. ColorSpace linear(ColorSpace::PrimaryID::BT709,
  733. ColorSpace::TransferID::LINEAR_HDR);
  734. std::unique_ptr<ColorTransform> xform_to(
  735. ColorTransform::NewColorTransform(hdr, linear));
  736. std::unique_ptr<ColorTransform> xform_from(
  737. ColorTransform::NewColorTransform(linear, hdr));
  738. // We're going to to test both sides of the joint points. Use this
  739. // epsilon, which is much smaller than kMathEpsilon, to make that
  740. // adjustment.
  741. const float kSideEpsilon = kMathEpsilon / 100;
  742. const size_t kTestPointCount = 8;
  743. const float test_x[kTestPointCount] = {
  744. // Test the linear segment of the sRGB function.
  745. srgb_x0 * sdr_joint,
  746. // Test the exponential segment of the sRGB function.
  747. srgb_x1 * sdr_joint,
  748. // Test epsilon before the HDR joint
  749. sdr_joint - kSideEpsilon,
  750. // Test the HDR joint
  751. sdr_joint,
  752. // Test epsilon after the HDR joint
  753. sdr_joint + kSideEpsilon,
  754. // Test the middle of the linear HDR segment
  755. sdr_joint + 0.5f * (1.f - sdr_joint),
  756. // Test just before the end of the linear HDR segment.
  757. 1.f - kSideEpsilon,
  758. // Test the endpoint of the linear HDR segment.
  759. 1.f,
  760. };
  761. const float test_y[kTestPointCount] = {
  762. srgb_y0,
  763. srgb_y1,
  764. 1.f - kSideEpsilon,
  765. 1.f,
  766. 1.f + kSideEpsilon,
  767. 0.5f * (1.f + hdr_level),
  768. hdr_level - kSideEpsilon,
  769. hdr_level,
  770. };
  771. for (size_t i = 0; i < kTestPointCount; ++i) {
  772. ColorTransform::TriStim val;
  773. val.set_x(test_x[i]);
  774. xform_to->Transform(&val, 1);
  775. EXPECT_NEAR(val.x(), test_y[i], kMathEpsilon)
  776. << " test_x[i] is " << test_x[i];
  777. val.set_x(test_y[i]);
  778. xform_from->Transform(&val, 1);
  779. EXPECT_NEAR(val.x(), test_x[i], kMathEpsilon)
  780. << " test_y[i] is " << test_y[i];
  781. }
  782. }
  783. }
  784. }
  785. } // namespace gfx