color_space.cc 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304
  1. // Copyright (c) 2012 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 "ui/gfx/color_space.h"
  5. #include <iomanip>
  6. #include <limits>
  7. #include <map>
  8. #include <sstream>
  9. #include "base/atomic_sequence_num.h"
  10. #include "base/lazy_instance.h"
  11. #include "base/logging.h"
  12. #include "base/notreached.h"
  13. #include "base/synchronization/lock.h"
  14. #include "third_party/skia/include/core/SkColorSpace.h"
  15. #include "third_party/skia/include/core/SkData.h"
  16. #include "third_party/skia/include/core/SkICC.h"
  17. #include "third_party/skia/include/core/SkImageInfo.h"
  18. #include "third_party/skia/include/core/SkM44.h"
  19. #include "ui/gfx/display_color_spaces.h"
  20. #include "ui/gfx/icc_profile.h"
  21. #include "ui/gfx/skia_color_space_util.h"
  22. namespace gfx {
  23. namespace {
  24. static bool IsAlmostZero(float value) {
  25. return std::abs(value) < std::numeric_limits<float>::epsilon();
  26. }
  27. static bool FloatsEqualWithinTolerance(const float* a,
  28. const float* b,
  29. int n,
  30. float tol) {
  31. for (int i = 0; i < n; ++i) {
  32. if (std::abs(a[i] - b[i]) > tol) {
  33. return false;
  34. }
  35. }
  36. return true;
  37. }
  38. skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level) {
  39. // Note that SkColorSpace doesn't have the notion of an unspecified SDR white
  40. // level.
  41. if (sdr_white_level == 0.f)
  42. sdr_white_level = ColorSpace::kDefaultSDRWhiteLevel;
  43. // The generic PQ transfer function produces normalized luminance values i.e.
  44. // the range 0-1 represents 0-10000 nits for the reference display, but we
  45. // want to map 1.0 to |sdr_white_level| nits so we need to scale accordingly.
  46. const double w = 10000. / sdr_white_level;
  47. // Distribute scaling factor W by scaling A and B with X ^ (1/F):
  48. // ((A + Bx^C) / (D + Ex^C))^F * W = ((A + Bx^C) / (D + Ex^C) * W^(1/F))^F
  49. // See https://crbug.com/1058580#c32 for discussion.
  50. skcms_TransferFunction fn = SkNamedTransferFn::kPQ;
  51. const double ws = pow(w, 1. / fn.f);
  52. fn.a = ws * fn.a;
  53. fn.b = ws * fn.b;
  54. return fn;
  55. }
  56. skcms_TransferFunction GetHLGSkTransferFunction(float sdr_white_level) {
  57. // Note that SkColorSpace doesn't have the notion of an unspecified SDR white
  58. // level.
  59. if (sdr_white_level == 0.f)
  60. sdr_white_level = ColorSpace::kDefaultSDRWhiteLevel;
  61. // The kHLG constant will evaluate to values in the range [0, 12].
  62. skcms_TransferFunction fn = SkNamedTransferFn::kHLG;
  63. // The value of k is equal to kHLG evaluated at 0.75 (3.77) , divided by kHLG
  64. // evaluated at 1 (12), multiplied by 203 nits. This value is selected such
  65. // that a signal of 0.75 will map to the same value that a PQ signal for 203
  66. // nits will map to.
  67. constexpr float k = 63.84549817071231f;
  68. fn.f = k / sdr_white_level - 1;
  69. return fn;
  70. }
  71. bool PrimaryIdContainsSRGB(ColorSpace::PrimaryID id) {
  72. DCHECK(id != ColorSpace::PrimaryID::INVALID &&
  73. id != ColorSpace::PrimaryID::CUSTOM);
  74. switch (id) {
  75. case ColorSpace::PrimaryID::BT709:
  76. case ColorSpace::PrimaryID::BT2020:
  77. case ColorSpace::PrimaryID::SMPTEST428_1:
  78. case ColorSpace::PrimaryID::SMPTEST431_2:
  79. case ColorSpace::PrimaryID::P3:
  80. case ColorSpace::PrimaryID::XYZ_D50:
  81. case ColorSpace::PrimaryID::ADOBE_RGB:
  82. case ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN:
  83. return true;
  84. default:
  85. return false;
  86. }
  87. }
  88. } // namespace
  89. // static
  90. constexpr float ColorSpace::kDefaultSDRWhiteLevel;
  91. ColorSpace::ColorSpace(PrimaryID primaries,
  92. TransferID transfer,
  93. MatrixID matrix,
  94. RangeID range,
  95. const skcms_Matrix3x3* custom_primary_matrix,
  96. const skcms_TransferFunction* custom_transfer_fn,
  97. bool is_hdr)
  98. : primaries_(primaries),
  99. transfer_(transfer),
  100. matrix_(matrix),
  101. range_(range) {
  102. if (custom_primary_matrix) {
  103. DCHECK_EQ(PrimaryID::CUSTOM, primaries_);
  104. SetCustomPrimaries(*custom_primary_matrix);
  105. }
  106. if (custom_transfer_fn)
  107. SetCustomTransferFunction(*custom_transfer_fn, is_hdr);
  108. }
  109. ColorSpace::ColorSpace(const SkColorSpace& sk_color_space, bool is_hdr)
  110. : ColorSpace(PrimaryID::INVALID,
  111. TransferID::INVALID,
  112. MatrixID::RGB,
  113. RangeID::FULL) {
  114. skcms_TransferFunction fn;
  115. if (sk_color_space.isNumericalTransferFn(&fn)) {
  116. transfer_ = TransferID::CUSTOM;
  117. SetCustomTransferFunction(fn, is_hdr);
  118. } else if (skcms_TransferFunction_isHLGish(&fn)) {
  119. transfer_ = TransferID::HLG;
  120. } else if (skcms_TransferFunction_isPQish(&fn)) {
  121. transfer_ = TransferID::PQ;
  122. } else {
  123. // Construct an invalid result: Unable to extract necessary parameters
  124. return;
  125. }
  126. skcms_Matrix3x3 to_XYZD50;
  127. if (!sk_color_space.toXYZD50(&to_XYZD50)) {
  128. // Construct an invalid result: Unable to extract necessary parameters
  129. return;
  130. }
  131. SetCustomPrimaries(to_XYZD50);
  132. }
  133. bool ColorSpace::IsValid() const {
  134. return primaries_ != PrimaryID::INVALID && transfer_ != TransferID::INVALID &&
  135. matrix_ != MatrixID::INVALID && range_ != RangeID::INVALID;
  136. }
  137. // static
  138. ColorSpace ColorSpace::CreatePiecewiseHDR(
  139. PrimaryID primaries,
  140. float sdr_joint,
  141. float hdr_level,
  142. const skcms_Matrix3x3* custom_primary_matrix) {
  143. // If |sdr_joint| is 1, then this is just sRGB (and so |hdr_level| must be 1).
  144. // An |sdr_joint| higher than 1 breaks.
  145. DCHECK_LE(sdr_joint, 1.f);
  146. if (sdr_joint == 1.f)
  147. DCHECK_EQ(hdr_level, 1.f);
  148. // An |hdr_level| of 1 has no HDR. An |hdr_level| less than 1 breaks.
  149. DCHECK_GE(hdr_level, 1.f);
  150. ColorSpace result(primaries, TransferID::PIECEWISE_HDR, MatrixID::RGB,
  151. RangeID::FULL, custom_primary_matrix, nullptr);
  152. result.transfer_params_[0] = sdr_joint;
  153. result.transfer_params_[1] = hdr_level;
  154. return result;
  155. }
  156. // static
  157. ColorSpace ColorSpace::CreateCustom(const skcms_Matrix3x3& to_XYZD50,
  158. const skcms_TransferFunction& fn) {
  159. ColorSpace result(ColorSpace::PrimaryID::CUSTOM,
  160. ColorSpace::TransferID::CUSTOM, ColorSpace::MatrixID::RGB,
  161. ColorSpace::RangeID::FULL, &to_XYZD50, &fn);
  162. return result;
  163. }
  164. // static
  165. ColorSpace ColorSpace::CreateCustom(const skcms_Matrix3x3& to_XYZD50,
  166. TransferID transfer) {
  167. ColorSpace result(ColorSpace::PrimaryID::CUSTOM, transfer,
  168. ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL,
  169. &to_XYZD50, nullptr);
  170. return result;
  171. }
  172. void ColorSpace::SetCustomPrimaries(const skcms_Matrix3x3& to_XYZD50) {
  173. const PrimaryID kIDsToCheck[] = {
  174. PrimaryID::BT709,
  175. PrimaryID::BT470M,
  176. PrimaryID::BT470BG,
  177. PrimaryID::SMPTE170M,
  178. PrimaryID::SMPTE240M,
  179. PrimaryID::FILM,
  180. PrimaryID::BT2020,
  181. PrimaryID::SMPTEST428_1,
  182. PrimaryID::SMPTEST431_2,
  183. PrimaryID::P3,
  184. PrimaryID::XYZ_D50,
  185. PrimaryID::ADOBE_RGB,
  186. PrimaryID::APPLE_GENERIC_RGB,
  187. PrimaryID::WIDE_GAMUT_COLOR_SPIN,
  188. };
  189. for (PrimaryID id : kIDsToCheck) {
  190. skcms_Matrix3x3 matrix;
  191. GetPrimaryMatrix(id, &matrix);
  192. if (FloatsEqualWithinTolerance(&to_XYZD50.vals[0][0], &matrix.vals[0][0], 9,
  193. 0.001f)) {
  194. primaries_ = id;
  195. return;
  196. }
  197. }
  198. memcpy(custom_primary_matrix_, &to_XYZD50, 9 * sizeof(float));
  199. primaries_ = PrimaryID::CUSTOM;
  200. }
  201. void ColorSpace::SetCustomTransferFunction(const skcms_TransferFunction& fn,
  202. bool is_hdr) {
  203. DCHECK(transfer_ == TransferID::CUSTOM ||
  204. transfer_ == TransferID::CUSTOM_HDR);
  205. // These are all TransferIDs that will return a transfer function from
  206. // GetTransferFunction. When multiple ids map to the same function, this list
  207. // prioritizes the most common name (eg SRGB). This applies only to
  208. // SDR transfer functions.
  209. if (transfer_ == TransferID::CUSTOM) {
  210. const TransferID kIDsToCheck[] = {
  211. TransferID::SRGB, TransferID::LINEAR,
  212. TransferID::GAMMA18, TransferID::GAMMA22,
  213. TransferID::GAMMA24, TransferID::GAMMA28,
  214. TransferID::SMPTE240M, TransferID::BT709_APPLE,
  215. TransferID::SMPTEST428_1,
  216. };
  217. const TransferID kIDsToCheckNoHDR[] = {
  218. TransferID::SRGB,
  219. TransferID::LINEAR,
  220. };
  221. const TransferID kIDsToCheckHDR[] = {
  222. TransferID::SRGB_HDR,
  223. TransferID::LINEAR_HDR,
  224. };
  225. auto check_transfer_fn = [this, &fn](TransferID id) {
  226. skcms_TransferFunction id_fn;
  227. GetTransferFunction(id, &id_fn);
  228. if (!FloatsEqualWithinTolerance(&fn.g, &id_fn.g, 7, 0.001f))
  229. return false;
  230. transfer_ = id;
  231. return true;
  232. };
  233. for (TransferID id : is_hdr ? kIDsToCheckHDR : kIDsToCheckNoHDR) {
  234. if (check_transfer_fn(id))
  235. return;
  236. }
  237. for (TransferID id : kIDsToCheck) {
  238. if (check_transfer_fn(id))
  239. return;
  240. }
  241. }
  242. transfer_params_[0] = fn.a;
  243. transfer_params_[1] = fn.b;
  244. transfer_params_[2] = fn.c;
  245. transfer_params_[3] = fn.d;
  246. transfer_params_[4] = fn.e;
  247. transfer_params_[5] = fn.f;
  248. transfer_params_[6] = fn.g;
  249. }
  250. // static
  251. size_t ColorSpace::TransferParamCount(TransferID transfer) {
  252. switch (transfer) {
  253. case TransferID::CUSTOM:
  254. return 7;
  255. case TransferID::CUSTOM_HDR:
  256. return 7;
  257. case TransferID::PIECEWISE_HDR:
  258. return 2;
  259. default:
  260. return 0;
  261. }
  262. }
  263. bool ColorSpace::operator==(const ColorSpace& other) const {
  264. if (primaries_ != other.primaries_ || transfer_ != other.transfer_ ||
  265. matrix_ != other.matrix_ || range_ != other.range_) {
  266. return false;
  267. }
  268. if (primaries_ == PrimaryID::CUSTOM) {
  269. if (memcmp(custom_primary_matrix_, other.custom_primary_matrix_,
  270. sizeof(custom_primary_matrix_))) {
  271. return false;
  272. }
  273. }
  274. if (size_t param_count = TransferParamCount(transfer_)) {
  275. if (memcmp(transfer_params_, other.transfer_params_,
  276. param_count * sizeof(float))) {
  277. return false;
  278. }
  279. }
  280. return true;
  281. }
  282. bool ColorSpace::IsWide() const {
  283. // These HDR transfer functions are always wide
  284. if (transfer_ == TransferID::SRGB_HDR ||
  285. transfer_ == TransferID::LINEAR_HDR ||
  286. transfer_ == TransferID::CUSTOM_HDR)
  287. return true;
  288. if (primaries_ == PrimaryID::BT2020 ||
  289. primaries_ == PrimaryID::SMPTEST431_2 || primaries_ == PrimaryID::P3 ||
  290. primaries_ == PrimaryID::ADOBE_RGB ||
  291. primaries_ == PrimaryID::WIDE_GAMUT_COLOR_SPIN ||
  292. // TODO(cblume/ccameron): Compute if the custom primaries actually are
  293. // wide. For now, assume so.
  294. primaries_ == PrimaryID::CUSTOM)
  295. return true;
  296. return false;
  297. }
  298. bool ColorSpace::IsHDR() const {
  299. return transfer_ == TransferID::PQ || transfer_ == TransferID::HLG ||
  300. transfer_ == TransferID::LINEAR_HDR ||
  301. transfer_ == TransferID::SRGB_HDR ||
  302. transfer_ == TransferID::CUSTOM_HDR ||
  303. transfer_ == TransferID::PIECEWISE_HDR ||
  304. transfer_ == TransferID::SCRGB_LINEAR_80_NITS;
  305. }
  306. bool ColorSpace::IsToneMappedByDefault() const {
  307. switch (transfer_) {
  308. case TransferID::PQ:
  309. case TransferID::HLG:
  310. return true;
  311. default:
  312. return false;
  313. }
  314. }
  315. bool ColorSpace::IsAffectedBySDRWhiteLevel() const {
  316. switch (transfer_) {
  317. case TransferID::PQ:
  318. case TransferID::HLG:
  319. case TransferID::SCRGB_LINEAR_80_NITS:
  320. return true;
  321. default:
  322. return false;
  323. }
  324. }
  325. bool ColorSpace::FullRangeEncodedValues() const {
  326. return transfer_ == TransferID::LINEAR_HDR ||
  327. transfer_ == TransferID::SRGB_HDR ||
  328. transfer_ == TransferID::CUSTOM_HDR ||
  329. transfer_ == TransferID::PIECEWISE_HDR ||
  330. transfer_ == TransferID::SCRGB_LINEAR_80_NITS ||
  331. transfer_ == TransferID::BT1361_ECG ||
  332. transfer_ == TransferID::IEC61966_2_4;
  333. }
  334. bool ColorSpace::operator!=(const ColorSpace& other) const {
  335. return !(*this == other);
  336. }
  337. bool ColorSpace::operator<(const ColorSpace& other) const {
  338. if (primaries_ < other.primaries_)
  339. return true;
  340. if (primaries_ > other.primaries_)
  341. return false;
  342. if (transfer_ < other.transfer_)
  343. return true;
  344. if (transfer_ > other.transfer_)
  345. return false;
  346. if (matrix_ < other.matrix_)
  347. return true;
  348. if (matrix_ > other.matrix_)
  349. return false;
  350. if (range_ < other.range_)
  351. return true;
  352. if (range_ > other.range_)
  353. return false;
  354. if (primaries_ == PrimaryID::CUSTOM) {
  355. int primary_result =
  356. memcmp(custom_primary_matrix_, other.custom_primary_matrix_,
  357. sizeof(custom_primary_matrix_));
  358. if (primary_result < 0)
  359. return true;
  360. if (primary_result > 0)
  361. return false;
  362. }
  363. if (size_t param_count = TransferParamCount(transfer_)) {
  364. int transfer_result = memcmp(transfer_params_, other.transfer_params_,
  365. param_count * sizeof(float));
  366. if (transfer_result < 0)
  367. return true;
  368. if (transfer_result > 0)
  369. return false;
  370. }
  371. return false;
  372. }
  373. size_t ColorSpace::GetHash() const {
  374. size_t result = (static_cast<size_t>(primaries_) << 0) |
  375. (static_cast<size_t>(transfer_) << 8) |
  376. (static_cast<size_t>(matrix_) << 16) |
  377. (static_cast<size_t>(range_) << 24);
  378. if (primaries_ == PrimaryID::CUSTOM) {
  379. const uint32_t* params =
  380. reinterpret_cast<const uint32_t*>(custom_primary_matrix_);
  381. result ^= params[0];
  382. result ^= params[4];
  383. result ^= params[8];
  384. }
  385. {
  386. // Note that |transfer_params_| must be zero when they are unused.
  387. const uint32_t* params =
  388. reinterpret_cast<const uint32_t*>(transfer_params_);
  389. result ^= params[3];
  390. result ^= params[6];
  391. }
  392. return result;
  393. }
  394. #define PRINT_ENUM_CASE(TYPE, NAME) \
  395. case TYPE::NAME: \
  396. ss << #NAME; \
  397. break;
  398. std::string ColorSpace::ToString() const {
  399. std::stringstream ss;
  400. ss << std::fixed << std::setprecision(4);
  401. if (primaries_ != PrimaryID::CUSTOM)
  402. ss << "{primaries:";
  403. switch (primaries_) {
  404. PRINT_ENUM_CASE(PrimaryID, INVALID)
  405. PRINT_ENUM_CASE(PrimaryID, BT709)
  406. PRINT_ENUM_CASE(PrimaryID, BT470M)
  407. PRINT_ENUM_CASE(PrimaryID, BT470BG)
  408. PRINT_ENUM_CASE(PrimaryID, SMPTE170M)
  409. PRINT_ENUM_CASE(PrimaryID, SMPTE240M)
  410. PRINT_ENUM_CASE(PrimaryID, FILM)
  411. PRINT_ENUM_CASE(PrimaryID, BT2020)
  412. PRINT_ENUM_CASE(PrimaryID, SMPTEST428_1)
  413. PRINT_ENUM_CASE(PrimaryID, SMPTEST431_2)
  414. PRINT_ENUM_CASE(PrimaryID, P3)
  415. PRINT_ENUM_CASE(PrimaryID, XYZ_D50)
  416. PRINT_ENUM_CASE(PrimaryID, ADOBE_RGB)
  417. PRINT_ENUM_CASE(PrimaryID, APPLE_GENERIC_RGB)
  418. PRINT_ENUM_CASE(PrimaryID, WIDE_GAMUT_COLOR_SPIN)
  419. case PrimaryID::CUSTOM:
  420. // |custom_primary_matrix_| is in row-major order.
  421. const float sum_R = custom_primary_matrix_[0] +
  422. custom_primary_matrix_[3] + custom_primary_matrix_[6];
  423. const float sum_G = custom_primary_matrix_[1] +
  424. custom_primary_matrix_[4] + custom_primary_matrix_[7];
  425. const float sum_B = custom_primary_matrix_[2] +
  426. custom_primary_matrix_[5] + custom_primary_matrix_[8];
  427. if (IsAlmostZero(sum_R) || IsAlmostZero(sum_G) || IsAlmostZero(sum_B))
  428. break;
  429. ss << "{primaries_d50_referred: [[" << (custom_primary_matrix_[0] / sum_R)
  430. << ", " << (custom_primary_matrix_[3] / sum_R) << "], "
  431. << " [" << (custom_primary_matrix_[1] / sum_G) << ", "
  432. << (custom_primary_matrix_[4] / sum_G) << "], "
  433. << " [" << (custom_primary_matrix_[2] / sum_B) << ", "
  434. << (custom_primary_matrix_[5] / sum_B) << "]]";
  435. break;
  436. }
  437. ss << ", transfer:";
  438. switch (transfer_) {
  439. PRINT_ENUM_CASE(TransferID, INVALID)
  440. PRINT_ENUM_CASE(TransferID, BT709)
  441. PRINT_ENUM_CASE(TransferID, BT709_APPLE)
  442. PRINT_ENUM_CASE(TransferID, GAMMA18)
  443. PRINT_ENUM_CASE(TransferID, GAMMA22)
  444. PRINT_ENUM_CASE(TransferID, GAMMA24)
  445. PRINT_ENUM_CASE(TransferID, GAMMA28)
  446. PRINT_ENUM_CASE(TransferID, SMPTE170M)
  447. PRINT_ENUM_CASE(TransferID, SMPTE240M)
  448. PRINT_ENUM_CASE(TransferID, LINEAR)
  449. PRINT_ENUM_CASE(TransferID, LOG)
  450. PRINT_ENUM_CASE(TransferID, LOG_SQRT)
  451. PRINT_ENUM_CASE(TransferID, IEC61966_2_4)
  452. PRINT_ENUM_CASE(TransferID, BT1361_ECG)
  453. PRINT_ENUM_CASE(TransferID, SRGB)
  454. PRINT_ENUM_CASE(TransferID, BT2020_10)
  455. PRINT_ENUM_CASE(TransferID, BT2020_12)
  456. PRINT_ENUM_CASE(TransferID, SMPTEST428_1)
  457. PRINT_ENUM_CASE(TransferID, SRGB_HDR)
  458. PRINT_ENUM_CASE(TransferID, LINEAR_HDR)
  459. case TransferID::HLG:
  460. ss << "HLG (SDR white point ";
  461. if (transfer_params_[0] == 0.f)
  462. ss << "default " << kDefaultSDRWhiteLevel;
  463. else
  464. ss << transfer_params_[0];
  465. ss << " nits)";
  466. break;
  467. case TransferID::PQ:
  468. ss << "PQ (SDR white point ";
  469. if (transfer_params_[0] == 0.f)
  470. ss << "default " << kDefaultSDRWhiteLevel;
  471. else
  472. ss << transfer_params_[0];
  473. ss << " nits)";
  474. break;
  475. case TransferID::CUSTOM: {
  476. skcms_TransferFunction fn;
  477. GetTransferFunction(&fn);
  478. ss << fn.c << "*x + " << fn.f << " if x < " << fn.d << " else (" << fn.a
  479. << "*x + " << fn.b << ")**" << fn.g << " + " << fn.e;
  480. break;
  481. }
  482. case TransferID::CUSTOM_HDR: {
  483. skcms_TransferFunction fn;
  484. GetTransferFunction(&fn);
  485. if (fn.g == 1.0f && fn.a > 0.0f && fn.b == 0.0f && fn.c == 0.0f &&
  486. fn.d == 0.0f && fn.e == 0.0f && fn.f == 0.0f) {
  487. ss << "LINEAR_HDR (slope " << fn.a << ")";
  488. break;
  489. }
  490. ss << fn.c << "*x + " << fn.f << " if |x| < " << fn.d << " else sign(x)*("
  491. << fn.a << "*|x| + " << fn.b << ")**" << fn.g << " + " << fn.e;
  492. break;
  493. }
  494. case TransferID::PIECEWISE_HDR: {
  495. skcms_TransferFunction fn;
  496. GetTransferFunction(&fn);
  497. ss << "sRGB to 1 at " << transfer_params_[0] << ", linear to "
  498. << transfer_params_[1] << " at 1";
  499. break;
  500. }
  501. case TransferID::SCRGB_LINEAR_80_NITS:
  502. ss << "scRGB linear (80 nit white)";
  503. break;
  504. }
  505. ss << ", matrix:";
  506. switch (matrix_) {
  507. PRINT_ENUM_CASE(MatrixID, INVALID)
  508. PRINT_ENUM_CASE(MatrixID, RGB)
  509. PRINT_ENUM_CASE(MatrixID, BT709)
  510. PRINT_ENUM_CASE(MatrixID, FCC)
  511. PRINT_ENUM_CASE(MatrixID, BT470BG)
  512. PRINT_ENUM_CASE(MatrixID, SMPTE170M)
  513. PRINT_ENUM_CASE(MatrixID, SMPTE240M)
  514. PRINT_ENUM_CASE(MatrixID, YCOCG)
  515. PRINT_ENUM_CASE(MatrixID, BT2020_NCL)
  516. PRINT_ENUM_CASE(MatrixID, BT2020_CL)
  517. PRINT_ENUM_CASE(MatrixID, YDZDX)
  518. PRINT_ENUM_CASE(MatrixID, GBR)
  519. }
  520. ss << ", range:";
  521. switch (range_) {
  522. PRINT_ENUM_CASE(RangeID, INVALID)
  523. PRINT_ENUM_CASE(RangeID, LIMITED)
  524. PRINT_ENUM_CASE(RangeID, FULL)
  525. PRINT_ENUM_CASE(RangeID, DERIVED)
  526. }
  527. ss << "}";
  528. return ss.str();
  529. }
  530. #undef PRINT_ENUM_CASE
  531. ColorSpace ColorSpace::GetAsFullRangeRGB() const {
  532. ColorSpace result(*this);
  533. if (!IsValid())
  534. return result;
  535. result.matrix_ = MatrixID::RGB;
  536. result.range_ = RangeID::FULL;
  537. return result;
  538. }
  539. ContentColorUsage ColorSpace::GetContentColorUsage() const {
  540. if (IsHDR())
  541. return ContentColorUsage::kHDR;
  542. if (IsWide())
  543. return ContentColorUsage::kWideColorGamut;
  544. return ContentColorUsage::kSRGB;
  545. }
  546. ColorSpace ColorSpace::GetAsRGB() const {
  547. ColorSpace result(*this);
  548. if (IsValid())
  549. result.matrix_ = MatrixID::RGB;
  550. return result;
  551. }
  552. ColorSpace ColorSpace::GetScaledColorSpace(float factor) const {
  553. ColorSpace result(*this);
  554. skcms_Matrix3x3 to_XYZD50;
  555. GetPrimaryMatrix(&to_XYZD50);
  556. for (int row = 0; row < 3; ++row) {
  557. for (int col = 0; col < 3; ++col) {
  558. to_XYZD50.vals[row][col] *= factor;
  559. }
  560. }
  561. result.SetCustomPrimaries(to_XYZD50);
  562. return result;
  563. }
  564. bool ColorSpace::IsSuitableForBlending() const {
  565. switch (transfer_) {
  566. case TransferID::PQ:
  567. // PQ is not an acceptable space to do blending in -- blending 0 and 1
  568. // evenly will get a result of sRGB 0.259 (instead of 0.5).
  569. return false;
  570. case TransferID::HLG:
  571. case TransferID::LINEAR_HDR:
  572. case TransferID::SCRGB_LINEAR_80_NITS:
  573. // If the color space is nearly-linear, then it is not suitable for
  574. // blending -- blending 0 and 1 evenly will get a result of sRGB 0.735
  575. // (instead of 0.5).
  576. return false;
  577. case TransferID::CUSTOM_HDR: {
  578. // A gamma close enough to linear is treated as linear.
  579. skcms_TransferFunction fn;
  580. if (GetTransferFunction(&fn)) {
  581. constexpr float kMinGamma = 1.25;
  582. if (fn.g < kMinGamma)
  583. return false;
  584. }
  585. break;
  586. }
  587. default:
  588. break;
  589. }
  590. return true;
  591. }
  592. ColorSpace ColorSpace::GetWithMatrixAndRange(MatrixID matrix,
  593. RangeID range) const {
  594. ColorSpace result(*this);
  595. if (!IsValid())
  596. return result;
  597. result.matrix_ = matrix;
  598. result.range_ = range;
  599. return result;
  600. }
  601. sk_sp<SkColorSpace> ColorSpace::ToSkColorSpace(
  602. absl::optional<float> sdr_white_level) const {
  603. // Handle only valid, full-range RGB spaces.
  604. if (!IsValid() || matrix_ != MatrixID::RGB || range_ != RangeID::FULL)
  605. return nullptr;
  606. // Use the named SRGB and linear-SRGB instead of the generic constructors.
  607. if (primaries_ == PrimaryID::BT709) {
  608. if (transfer_ == TransferID::SRGB)
  609. return SkColorSpace::MakeSRGB();
  610. if (transfer_ == TransferID::LINEAR || transfer_ == TransferID::LINEAR_HDR)
  611. return SkColorSpace::MakeSRGBLinear();
  612. }
  613. skcms_TransferFunction transfer_fn = SkNamedTransferFn::kSRGB;
  614. switch (transfer_) {
  615. case TransferID::SRGB:
  616. break;
  617. case TransferID::LINEAR:
  618. case TransferID::LINEAR_HDR:
  619. transfer_fn = SkNamedTransferFn::kLinear;
  620. break;
  621. case TransferID::HLG:
  622. transfer_fn = GetHLGSkTransferFunction(
  623. sdr_white_level.value_or(kDefaultSDRWhiteLevel));
  624. break;
  625. case TransferID::PQ:
  626. transfer_fn = GetPQSkTransferFunction(
  627. sdr_white_level.value_or(kDefaultSDRWhiteLevel));
  628. break;
  629. default:
  630. if (!GetTransferFunction(&transfer_fn, sdr_white_level)) {
  631. DLOG(ERROR) << "Failed to get transfer function for SkColorSpace";
  632. return nullptr;
  633. }
  634. break;
  635. }
  636. skcms_Matrix3x3 gamut = SkNamedGamut::kSRGB;
  637. switch (primaries_) {
  638. case PrimaryID::BT709:
  639. break;
  640. case PrimaryID::ADOBE_RGB:
  641. gamut = SkNamedGamut::kAdobeRGB;
  642. break;
  643. case PrimaryID::P3:
  644. gamut = SkNamedGamut::kDisplayP3;
  645. break;
  646. case PrimaryID::BT2020:
  647. gamut = SkNamedGamut::kRec2020;
  648. break;
  649. default:
  650. GetPrimaryMatrix(&gamut);
  651. break;
  652. }
  653. sk_sp<SkColorSpace> sk_color_space =
  654. SkColorSpace::MakeRGB(transfer_fn, gamut);
  655. if (!sk_color_space)
  656. DLOG(ERROR) << "SkColorSpace::MakeRGB failed.";
  657. return sk_color_space;
  658. }
  659. const struct _GLcolorSpace* ColorSpace::AsGLColorSpace() const {
  660. return reinterpret_cast<const struct _GLcolorSpace*>(this);
  661. }
  662. ColorSpace::PrimaryID ColorSpace::GetPrimaryID() const {
  663. return primaries_;
  664. }
  665. ColorSpace::TransferID ColorSpace::GetTransferID() const {
  666. return transfer_;
  667. }
  668. ColorSpace::MatrixID ColorSpace::GetMatrixID() const {
  669. return matrix_;
  670. }
  671. ColorSpace::RangeID ColorSpace::GetRangeID() const {
  672. return range_;
  673. }
  674. bool ColorSpace::HasExtendedSkTransferFn() const {
  675. return matrix_ == MatrixID::RGB;
  676. }
  677. bool ColorSpace::Contains(const ColorSpace& other) const {
  678. if (primaries_ == PrimaryID::INVALID ||
  679. other.primaries_ == PrimaryID::INVALID)
  680. return false;
  681. // Contains() is commonly used to check if a color space contains sRGB. The
  682. // computation can be bypassed for known primary IDs.
  683. if (primaries_ != PrimaryID::CUSTOM && other.primaries_ == PrimaryID::BT709)
  684. return PrimaryIdContainsSRGB(primaries_);
  685. // |matrix| is the primary transform matrix from |other| to this color space.
  686. skcms_Matrix3x3 other_to_xyz;
  687. skcms_Matrix3x3 this_to_xyz;
  688. skcms_Matrix3x3 xyz_to_this;
  689. other.GetPrimaryMatrix(&other_to_xyz);
  690. GetPrimaryMatrix(&this_to_xyz);
  691. skcms_Matrix3x3_invert(&this_to_xyz, &xyz_to_this);
  692. skcms_Matrix3x3 matrix = skcms_Matrix3x3_concat(&xyz_to_this, &other_to_xyz);
  693. // Return true iff each primary is in the range [0, 1] after transforming.
  694. // Transforming a primary vector by |matrix| always results in a column of
  695. // |matrix|. So the multiplication can be skipped, and we can just check if
  696. // each value in the matrix is in the range [0, 1].
  697. constexpr float epsilon = 0.001f;
  698. for (int r = 0; r < 3; r++) {
  699. for (int c = 0; c < 3; c++) {
  700. if (matrix.vals[r][c] < -epsilon || matrix.vals[r][c] > 1 + epsilon)
  701. return false;
  702. }
  703. }
  704. return true;
  705. }
  706. // static
  707. SkColorSpacePrimaries ColorSpace::GetColorSpacePrimaries(
  708. PrimaryID primary_id,
  709. const skcms_Matrix3x3* custom_primary_matrix = nullptr) {
  710. SkColorSpacePrimaries primaries = {0};
  711. if (custom_primary_matrix && primary_id == PrimaryID::CUSTOM) {
  712. auto* matrix = custom_primary_matrix->vals;
  713. const float sum_R = matrix[0][0] + matrix[1][0] + matrix[2][0];
  714. const float sum_G = matrix[0][1] + matrix[1][1] + matrix[2][1];
  715. const float sum_B = matrix[0][2] + matrix[1][2] + matrix[2][2];
  716. primaries.fRX = matrix[0][0] / sum_R;
  717. primaries.fRY = matrix[1][0] / sum_R;
  718. primaries.fGX = matrix[0][1] / sum_G;
  719. primaries.fGY = matrix[1][1] / sum_G;
  720. primaries.fBX = matrix[0][2] / sum_B;
  721. primaries.fBY = matrix[1][2] / sum_B;
  722. // TODO(b/229646816): Currently only returns D65 whitepoint. If possible,
  723. // try to return an original whitepoint in the future.
  724. primaries.fWX = 0.3127f;
  725. primaries.fWY = 0.3290f;
  726. return primaries;
  727. }
  728. switch (primary_id) {
  729. case ColorSpace::PrimaryID::CUSTOM:
  730. case ColorSpace::PrimaryID::INVALID:
  731. break;
  732. case ColorSpace::PrimaryID::BT709:
  733. // BT709 is our default case. Put it after the switch just
  734. // in case we somehow get an id which is not listed in the switch.
  735. // (We don't want to use "default", because we want the compiler
  736. // to tell us if we forgot some enum values.)
  737. primaries.fRX = 0.640f;
  738. primaries.fRY = 0.330f;
  739. primaries.fGX = 0.300f;
  740. primaries.fGY = 0.600f;
  741. primaries.fBX = 0.150f;
  742. primaries.fBY = 0.060f;
  743. primaries.fWX = 0.3127f;
  744. primaries.fWY = 0.3290f;
  745. break;
  746. case ColorSpace::PrimaryID::BT470M:
  747. primaries.fRX = 0.67f;
  748. primaries.fRY = 0.33f;
  749. primaries.fGX = 0.21f;
  750. primaries.fGY = 0.71f;
  751. primaries.fBX = 0.14f;
  752. primaries.fBY = 0.08f;
  753. primaries.fWX = 0.31f;
  754. primaries.fWY = 0.316f;
  755. break;
  756. case ColorSpace::PrimaryID::BT470BG:
  757. primaries.fRX = 0.64f;
  758. primaries.fRY = 0.33f;
  759. primaries.fGX = 0.29f;
  760. primaries.fGY = 0.60f;
  761. primaries.fBX = 0.15f;
  762. primaries.fBY = 0.06f;
  763. primaries.fWX = 0.3127f;
  764. primaries.fWY = 0.3290f;
  765. break;
  766. case ColorSpace::PrimaryID::SMPTE170M:
  767. case ColorSpace::PrimaryID::SMPTE240M:
  768. primaries.fRX = 0.630f;
  769. primaries.fRY = 0.340f;
  770. primaries.fGX = 0.310f;
  771. primaries.fGY = 0.595f;
  772. primaries.fBX = 0.155f;
  773. primaries.fBY = 0.070f;
  774. primaries.fWX = 0.3127f;
  775. primaries.fWY = 0.3290f;
  776. break;
  777. case ColorSpace::PrimaryID::APPLE_GENERIC_RGB:
  778. primaries.fRX = 0.63002f;
  779. primaries.fRY = 0.34000f;
  780. primaries.fGX = 0.29505f;
  781. primaries.fGY = 0.60498f;
  782. primaries.fBX = 0.15501f;
  783. primaries.fBY = 0.07701f;
  784. primaries.fWX = 0.3127f;
  785. primaries.fWY = 0.3290f;
  786. break;
  787. case ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN:
  788. primaries.fRX = 0.01f;
  789. primaries.fRY = 0.98f;
  790. primaries.fGX = 0.01f;
  791. primaries.fGY = 0.01f;
  792. primaries.fBX = 0.98f;
  793. primaries.fBY = 0.01f;
  794. primaries.fWX = 0.3127f;
  795. primaries.fWY = 0.3290f;
  796. break;
  797. case ColorSpace::PrimaryID::FILM:
  798. primaries.fRX = 0.681f;
  799. primaries.fRY = 0.319f;
  800. primaries.fGX = 0.243f;
  801. primaries.fGY = 0.692f;
  802. primaries.fBX = 0.145f;
  803. primaries.fBY = 0.049f;
  804. primaries.fWX = 0.310f;
  805. primaries.fWY = 0.136f;
  806. break;
  807. case ColorSpace::PrimaryID::BT2020:
  808. primaries.fRX = 0.708f;
  809. primaries.fRY = 0.292f;
  810. primaries.fGX = 0.170f;
  811. primaries.fGY = 0.797f;
  812. primaries.fBX = 0.131f;
  813. primaries.fBY = 0.046f;
  814. primaries.fWX = 0.3127f;
  815. primaries.fWY = 0.3290f;
  816. break;
  817. case ColorSpace::PrimaryID::SMPTEST428_1:
  818. primaries.fRX = 1.0f;
  819. primaries.fRY = 0.0f;
  820. primaries.fGX = 0.0f;
  821. primaries.fGY = 1.0f;
  822. primaries.fBX = 0.0f;
  823. primaries.fBY = 0.0f;
  824. primaries.fWX = 1.0f / 3.0f;
  825. primaries.fWY = 1.0f / 3.0f;
  826. break;
  827. case ColorSpace::PrimaryID::SMPTEST431_2:
  828. primaries.fRX = 0.680f;
  829. primaries.fRY = 0.320f;
  830. primaries.fGX = 0.265f;
  831. primaries.fGY = 0.690f;
  832. primaries.fBX = 0.150f;
  833. primaries.fBY = 0.060f;
  834. primaries.fWX = 0.314f;
  835. primaries.fWY = 0.351f;
  836. break;
  837. case ColorSpace::PrimaryID::P3:
  838. primaries.fRX = 0.680f;
  839. primaries.fRY = 0.320f;
  840. primaries.fGX = 0.265f;
  841. primaries.fGY = 0.690f;
  842. primaries.fBX = 0.150f;
  843. primaries.fBY = 0.060f;
  844. primaries.fWX = 0.3127f;
  845. primaries.fWY = 0.3290f;
  846. break;
  847. case ColorSpace::PrimaryID::XYZ_D50:
  848. primaries.fRX = 1.0f;
  849. primaries.fRY = 0.0f;
  850. primaries.fGX = 0.0f;
  851. primaries.fGY = 1.0f;
  852. primaries.fBX = 0.0f;
  853. primaries.fBY = 0.0f;
  854. primaries.fWX = 0.34567f;
  855. primaries.fWY = 0.35850f;
  856. break;
  857. case ColorSpace::PrimaryID::ADOBE_RGB:
  858. primaries.fRX = 0.6400f;
  859. primaries.fRY = 0.3300f;
  860. primaries.fGX = 0.2100f;
  861. primaries.fGY = 0.7100f;
  862. primaries.fBX = 0.1500f;
  863. primaries.fBY = 0.0600f;
  864. primaries.fWX = 0.3127f;
  865. primaries.fWY = 0.3290f;
  866. break;
  867. }
  868. return primaries;
  869. }
  870. SkColorSpacePrimaries ColorSpace::GetColorSpacePrimaries() const {
  871. skcms_Matrix3x3 matrix;
  872. memcpy(&matrix, custom_primary_matrix_, 9 * sizeof(float));
  873. return GetColorSpacePrimaries(primaries_, &matrix);
  874. }
  875. // static
  876. void ColorSpace::GetPrimaryMatrix(PrimaryID primary_id,
  877. skcms_Matrix3x3* to_XYZD50) {
  878. SkColorSpacePrimaries primaries = GetColorSpacePrimaries(primary_id);
  879. if (primary_id == PrimaryID::CUSTOM || primary_id == PrimaryID::INVALID) {
  880. *to_XYZD50 = SkNamedGamut::kXYZ; // Identity
  881. return;
  882. }
  883. primaries.toXYZD50(to_XYZD50);
  884. }
  885. void ColorSpace::GetPrimaryMatrix(skcms_Matrix3x3* to_XYZD50) const {
  886. if (primaries_ == PrimaryID::CUSTOM) {
  887. memcpy(to_XYZD50, custom_primary_matrix_, 9 * sizeof(float));
  888. } else {
  889. GetPrimaryMatrix(primaries_, to_XYZD50);
  890. }
  891. }
  892. SkM44 ColorSpace::GetPrimaryMatrix() const {
  893. skcms_Matrix3x3 toXYZ_3x3;
  894. GetPrimaryMatrix(&toXYZ_3x3);
  895. return SkM44FromRowMajor3x3(&toXYZ_3x3.vals[0][0]);
  896. }
  897. // static
  898. bool ColorSpace::GetTransferFunction(TransferID transfer,
  899. skcms_TransferFunction* fn) {
  900. // Default to F(x) = pow(x, 1)
  901. fn->a = 1;
  902. fn->b = 0;
  903. fn->c = 0;
  904. fn->d = 0;
  905. fn->e = 0;
  906. fn->f = 0;
  907. fn->g = 1;
  908. switch (transfer) {
  909. case ColorSpace::TransferID::LINEAR:
  910. case ColorSpace::TransferID::LINEAR_HDR:
  911. return true;
  912. case ColorSpace::TransferID::GAMMA18:
  913. fn->g = 1.801f;
  914. return true;
  915. case ColorSpace::TransferID::GAMMA22:
  916. fn->g = 2.2f;
  917. return true;
  918. case ColorSpace::TransferID::GAMMA24:
  919. fn->g = 2.4f;
  920. return true;
  921. case ColorSpace::TransferID::GAMMA28:
  922. fn->g = 2.8f;
  923. return true;
  924. case ColorSpace::TransferID::SMPTE240M:
  925. fn->a = 0.899626676224f;
  926. fn->b = 0.100373323776f;
  927. fn->c = 0.250000000000f;
  928. fn->d = 0.091286342118f;
  929. fn->g = 2.222222222222f;
  930. return true;
  931. case ColorSpace::TransferID::BT709:
  932. case ColorSpace::TransferID::SMPTE170M:
  933. case ColorSpace::TransferID::BT2020_10:
  934. case ColorSpace::TransferID::BT2020_12:
  935. // With respect to rendering BT709
  936. // * SMPTE 1886 suggests that we should be using gamma 2.4.
  937. // * Most displays actually use a gamma of 2.2, and most media playing
  938. // software uses the sRGB transfer function.
  939. // * User studies shows that users don't really care.
  940. // * Apple's CoreVideo uses gamma=1.961.
  941. // Bearing all of that in mind, use the same transfer function as sRGB,
  942. // which will allow more optimization, and will more closely match other
  943. // media players.
  944. case ColorSpace::TransferID::SRGB:
  945. case ColorSpace::TransferID::SRGB_HDR:
  946. fn->a = 0.947867345704f;
  947. fn->b = 0.052132654296f;
  948. fn->c = 0.077399380805f;
  949. fn->d = 0.040449937172f;
  950. fn->g = 2.400000000000f;
  951. return true;
  952. case ColorSpace::TransferID::BT709_APPLE:
  953. fn->g = 1.961000000000f;
  954. return true;
  955. case ColorSpace::TransferID::SMPTEST428_1:
  956. fn->a = 1.034080527699f; // (52.37 / 48.0) ^ (1.0 / 2.6) per ITU-T H.273.
  957. fn->g = 2.600000000000f;
  958. return true;
  959. case ColorSpace::TransferID::IEC61966_2_4:
  960. // This could potentially be represented the same as SRGB, but it handles
  961. // negative values differently.
  962. break;
  963. case ColorSpace::TransferID::HLG:
  964. case ColorSpace::TransferID::BT1361_ECG:
  965. case ColorSpace::TransferID::LOG:
  966. case ColorSpace::TransferID::LOG_SQRT:
  967. case ColorSpace::TransferID::PQ:
  968. case ColorSpace::TransferID::CUSTOM:
  969. case ColorSpace::TransferID::CUSTOM_HDR:
  970. case ColorSpace::TransferID::PIECEWISE_HDR:
  971. case ColorSpace::TransferID::SCRGB_LINEAR_80_NITS:
  972. case ColorSpace::TransferID::INVALID:
  973. break;
  974. }
  975. return false;
  976. }
  977. bool ColorSpace::GetTransferFunction(
  978. skcms_TransferFunction* fn,
  979. absl::optional<float> sdr_white_level) const {
  980. switch (transfer_) {
  981. case TransferID::CUSTOM:
  982. case TransferID::CUSTOM_HDR:
  983. fn->a = transfer_params_[0];
  984. fn->b = transfer_params_[1];
  985. fn->c = transfer_params_[2];
  986. fn->d = transfer_params_[3];
  987. fn->e = transfer_params_[4];
  988. fn->f = transfer_params_[5];
  989. fn->g = transfer_params_[6];
  990. return true;
  991. case TransferID::SCRGB_LINEAR_80_NITS:
  992. if (sdr_white_level) {
  993. fn->a = 80.f / *sdr_white_level;
  994. fn->b = 0;
  995. fn->c = 0;
  996. fn->d = 0;
  997. fn->e = 0;
  998. fn->f = 0;
  999. fn->g = 1;
  1000. return true;
  1001. } else {
  1002. // Using SCRGB_LINEAR_80_NITS without specifying an SDR white level is
  1003. // guaranteed to produce incorrect results.
  1004. return false;
  1005. }
  1006. default:
  1007. return GetTransferFunction(transfer_, fn);
  1008. }
  1009. }
  1010. bool ColorSpace::GetInverseTransferFunction(
  1011. skcms_TransferFunction* fn,
  1012. absl::optional<float> sdr_white_level) const {
  1013. if (!GetTransferFunction(fn, sdr_white_level))
  1014. return false;
  1015. *fn = SkTransferFnInverse(*fn);
  1016. return true;
  1017. }
  1018. bool ColorSpace::GetPiecewiseHDRParams(float* sdr_joint,
  1019. float* hdr_level) const {
  1020. if (transfer_ != TransferID::PIECEWISE_HDR)
  1021. return false;
  1022. *sdr_joint = transfer_params_[0];
  1023. *hdr_level = transfer_params_[1];
  1024. return true;
  1025. }
  1026. SkM44 ColorSpace::GetTransferMatrix(int bit_depth) const {
  1027. DCHECK_GE(bit_depth, 8);
  1028. // If chroma samples are real numbers in the range of −0.5 to 0.5, an offset
  1029. // of 0.5 is added to get real numbers in the range of 0 to 1. When
  1030. // represented as an unsigned |bit_depth|-bit integer, this 0.5 offset is
  1031. // approximated by 1 << (bit_depth - 1). chroma_0_5 is this approximate value
  1032. // converted to a real number in the range of 0 to 1.
  1033. //
  1034. // TODO(wtc): For now chroma_0_5 is only used for YCgCo. It should also be
  1035. // used for YUV.
  1036. const float chroma_0_5 =
  1037. static_cast<float>(1 << (bit_depth - 1)) / ((1 << bit_depth) - 1);
  1038. float Kr = 0;
  1039. float Kb = 0;
  1040. switch (matrix_) {
  1041. case ColorSpace::MatrixID::RGB:
  1042. case ColorSpace::MatrixID::INVALID:
  1043. return SkM44();
  1044. case ColorSpace::MatrixID::BT709:
  1045. Kr = 0.2126f;
  1046. Kb = 0.0722f;
  1047. break;
  1048. case ColorSpace::MatrixID::FCC:
  1049. Kr = 0.30f;
  1050. Kb = 0.11f;
  1051. break;
  1052. case ColorSpace::MatrixID::BT470BG:
  1053. case ColorSpace::MatrixID::SMPTE170M:
  1054. Kr = 0.299f;
  1055. Kb = 0.114f;
  1056. break;
  1057. case ColorSpace::MatrixID::SMPTE240M:
  1058. Kr = 0.212f;
  1059. Kb = 0.087f;
  1060. break;
  1061. case ColorSpace::MatrixID::YCOCG: {
  1062. float data[16] = {0.25f, 0.5f, 0.25f, 0.0f, // Y
  1063. -0.25f, 0.5f, -0.25f, chroma_0_5, // Cg
  1064. 0.5f, 0.0f, -0.5f, chroma_0_5, // Co
  1065. 0.0f, 0.0f, 0.0f, 1.0f};
  1066. return SkM44::RowMajor(data);
  1067. }
  1068. // BT2020_CL is a special case.
  1069. // Basically we return a matrix that transforms RYB values
  1070. // to YUV values. (Note that the green component have been replaced
  1071. // with the luminance.)
  1072. case ColorSpace::MatrixID::BT2020_CL: {
  1073. Kr = 0.2627f;
  1074. Kb = 0.0593f;
  1075. float data[16] = {1.0f, 0.0f, 0.0f, 0.0f, // R
  1076. Kr, 1.0f - Kr - Kb, Kb, 0.0f, // Y
  1077. 0.0f, 0.0f, 1.0f, 0.0f, // B
  1078. 0.0f, 0.0f, 0.0f, 1.0f};
  1079. return SkM44::RowMajor(data);
  1080. }
  1081. case ColorSpace::MatrixID::BT2020_NCL:
  1082. Kr = 0.2627f;
  1083. Kb = 0.0593f;
  1084. break;
  1085. case ColorSpace::MatrixID::YDZDX: {
  1086. // clang-format off
  1087. float data[16] = {
  1088. 0.0f, 1.0f, 0.0f, 0.0f, // Y
  1089. 0.0f, -0.5f, 0.986566f / 2.0f, 0.5f, // DX or DZ
  1090. 0.5f, -0.991902f / 2.0f, 0.0f, 0.5f, // DZ or DX
  1091. 0.0f, 0.0f, 0.0f, 1.0f,
  1092. };
  1093. // clang-format on
  1094. return SkM44::RowMajor(data);
  1095. }
  1096. case ColorSpace::MatrixID::GBR: {
  1097. float data[16] = {0.0f, 1.0f, 0.0f, 0.0f, // G
  1098. 0.0f, 0.0f, 1.0f, 0.0f, // B
  1099. 1.0f, 0.0f, 0.0f, 0.0f, // R
  1100. 0.0f, 0.0f, 0.0f, 1.0f};
  1101. return SkM44::RowMajor(data);
  1102. }
  1103. }
  1104. float Kg = 1.0f - Kr - Kb;
  1105. float u_m = 0.5f / (1.0f - Kb);
  1106. float v_m = 0.5f / (1.0f - Kr);
  1107. // clang-format off
  1108. float data[16] = {
  1109. Kr, Kg, Kb, 0.0f, // Y
  1110. u_m * -Kr, u_m * -Kg, u_m * (1.0f - Kb), 0.5f, // U
  1111. v_m * (1.0f - Kr), v_m * -Kg, v_m * -Kb, 0.5f, // V
  1112. 0.0f, 0.0f, 0.0f, 1.0f,
  1113. };
  1114. // clang-format on
  1115. return SkM44::RowMajor(data);
  1116. }
  1117. SkM44 ColorSpace::GetRangeAdjustMatrix(int bit_depth) const {
  1118. DCHECK_GE(bit_depth, 8);
  1119. switch (range_) {
  1120. case RangeID::FULL:
  1121. case RangeID::INVALID:
  1122. return SkM44();
  1123. case RangeID::DERIVED:
  1124. case RangeID::LIMITED:
  1125. break;
  1126. }
  1127. // See ITU-T H.273 (2016), Section 8.3. The following is derived from
  1128. // Equations 20-31.
  1129. const int shift = bit_depth - 8;
  1130. const float a_y = 219 << shift;
  1131. const float c = (1 << bit_depth) - 1;
  1132. const float scale_y = c / a_y;
  1133. switch (matrix_) {
  1134. case MatrixID::RGB:
  1135. case MatrixID::GBR:
  1136. case MatrixID::INVALID:
  1137. case MatrixID::YCOCG:
  1138. return SkM44::Scale(scale_y, scale_y, scale_y)
  1139. .postTranslate(-16.0f / 219.0f, -16.0f / 219.0f, -16.0f / 219.0f);
  1140. case MatrixID::BT709:
  1141. case MatrixID::FCC:
  1142. case MatrixID::BT470BG:
  1143. case MatrixID::SMPTE170M:
  1144. case MatrixID::SMPTE240M:
  1145. case MatrixID::BT2020_NCL:
  1146. case MatrixID::BT2020_CL:
  1147. case MatrixID::YDZDX: {
  1148. const float a_uv = 224 << shift;
  1149. const float scale_uv = c / a_uv;
  1150. const float translate_uv = (a_uv - c) / (2.0f * a_uv);
  1151. return SkM44::Scale(scale_y, scale_uv, scale_uv)
  1152. .postTranslate(-16.0f / 219.0f, translate_uv, translate_uv);
  1153. }
  1154. }
  1155. NOTREACHED();
  1156. return SkM44();
  1157. }
  1158. bool ColorSpace::ToSkYUVColorSpace(int bit_depth, SkYUVColorSpace* out) const {
  1159. switch (matrix_) {
  1160. case MatrixID::BT709:
  1161. *out = range_ == RangeID::FULL ? kRec709_Full_SkYUVColorSpace
  1162. : kRec709_Limited_SkYUVColorSpace;
  1163. return true;
  1164. case MatrixID::BT470BG:
  1165. case MatrixID::SMPTE170M:
  1166. *out = range_ == RangeID::FULL ? kJPEG_SkYUVColorSpace
  1167. : kRec601_Limited_SkYUVColorSpace;
  1168. return true;
  1169. case MatrixID::BT2020_NCL:
  1170. if (bit_depth == 8) {
  1171. *out = range_ == RangeID::FULL ? kBT2020_8bit_Full_SkYUVColorSpace
  1172. : kBT2020_8bit_Limited_SkYUVColorSpace;
  1173. return true;
  1174. }
  1175. if (bit_depth == 10) {
  1176. *out = range_ == RangeID::FULL ? kBT2020_10bit_Full_SkYUVColorSpace
  1177. : kBT2020_10bit_Limited_SkYUVColorSpace;
  1178. return true;
  1179. }
  1180. if (bit_depth == 12) {
  1181. *out = range_ == RangeID::FULL ? kBT2020_12bit_Full_SkYUVColorSpace
  1182. : kBT2020_12bit_Limited_SkYUVColorSpace;
  1183. return true;
  1184. }
  1185. return false;
  1186. default:
  1187. break;
  1188. }
  1189. return false;
  1190. }
  1191. std::ostream& operator<<(std::ostream& out, const ColorSpace& color_space) {
  1192. return out << color_space.ToString();
  1193. }
  1194. } // namespace gfx