1 /*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkColorSpace.h"
9 #include "include/core/SkData.h"
10 #include "include/core/SkRefCnt.h"
11 #include "include/core/SkTypes.h"
12 #include "include/encode/SkICC.h"
13 #include "modules/skcms/skcms.h"
14 #include "tests/Test.h"
15 #include "tools/Resources.h"
16
17 #include <cmath>
18 #include <cstdint>
19 #include <cstdlib>
20
DEF_TEST(AdobeRGB,r)21 DEF_TEST(AdobeRGB, r) {
22 if (sk_sp<SkData> profile = GetResourceAsData("icc_profiles/AdobeRGB1998.icc")) {
23 skcms_ICCProfile parsed;
24 REPORTER_ASSERT(r, skcms_Parse(profile->data(), profile->size(), &parsed));
25 REPORTER_ASSERT(r, !parsed.has_CICP);
26
27 auto got = SkColorSpace::Make(parsed);
28 auto want = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB);
29 REPORTER_ASSERT(r, SkColorSpace::Equals(got.get(), want.get()));
30 }
31 }
32
DEF_TEST(HDR_ICC,r)33 DEF_TEST(HDR_ICC, r) {
34 constexpr size_t kTestCount = 3;
35 sk_sp<SkData> profile[kTestCount] = {
36 SkWriteICCProfile(SkNamedTransferFn::kPQ, SkNamedGamut::kRec2020),
37 SkWriteICCProfile(SkNamedTransferFn::kHLG, SkNamedGamut::kDisplayP3),
38 SkWriteICCProfile(SkNamedTransferFn::kSRGB, SkNamedGamut::kSRGB),
39 };
40
41 sk_sp<SkData> dst_profile[kTestCount] = {
42 SkWriteICCProfile(SkNamedTransferFn::kLinear, SkNamedGamut::kRec2020),
43 SkWriteICCProfile(SkNamedTransferFn::kLinear, SkNamedGamut::kDisplayP3),
44 SkWriteICCProfile(SkNamedTransferFn::kLinear, SkNamedGamut::kSRGB),
45 };
46
47 constexpr size_t kPixelCount = 7;
48
49 // clang-format off
50 float pixels[kPixelCount][3] = {
51 { 0.00f, 0.00f, 0.00f, },
52 { 0.50f, 0.50f, 0.50f, },
53 { 0.50f, 0.00f, 0.00f, },
54 { 0.00f, 0.50f, 0.00f, },
55 { 0.00f, 0.00f, 0.50f, },
56 { 0.25f, 0.50f, 0.00f, },
57 { 0.75f, 0.75f, 0.75f, },
58 };
59
60 // The tone mapped value of PQ 0.5 and 0.75.
61 constexpr float kPQ_05 = 0.3182877451f;
62 constexpr float kPQ_075 = 0.9943588777f;
63
64 // The tone mapped value of PQ 0.25, when maxRGB is 0.5.
65 constexpr float kPQ_025 = 0.020679904f;
66
67 // The tone mapped value of HLG 0.5 and 0.75 (when all channels are equal).
68 constexpr float kHLG_05 = 0.20188954163f;
69 constexpr float kHLG_075 = 0.5208149688f;
70
71 // The linearized values of sRGB 0.25, 0.5, and 0.75.
72 constexpr float kSRGB_025 = 0.05087607f;
73 constexpr float kSRGB_05 = 0.21404112f;
74 constexpr float kSRGB_075 = 0.52252153f;
75
76 float dst_pixels_expected[kTestCount][kPixelCount][3] = {
77 {
78 { 0.f, 0.f, 0.f, },
79 { kPQ_05, kPQ_05, kPQ_05, },
80 { kPQ_05, 0.f, 0.f, },
81 { 0.f, kPQ_05, 0.f, },
82 { 0.f, 0.f, kPQ_05, },
83 { kPQ_025, kPQ_05, 0.f, },
84 { kPQ_075, kPQ_075, kPQ_075, }, // PQ maps 0.75 ~ 1000 nits to 1.0
85 },
86 {
87 { 0.f, 0.f, 0.f, },
88 { kHLG_05, kHLG_05, kHLG_05, },
89 { 0.1618f, 0.f, 0.f, }, // HLG will map 0.5 to different values
90 { 0.f, 0.1895f, 0.f, }, // if it is the R, G, or B channel, because
91 { 0.f, 0.f, 0.1251f, }, // of the OOTF.
92 { 0.0513f, 0.1924f, 0.f, },
93 { kHLG_075, kHLG_075, kHLG_075, },
94 },
95 {
96 { 0.f, 0.f, 0.f, },
97 { kSRGB_05, kSRGB_05, kSRGB_05, }, // This is just the sRGB transfer function
98 { kSRGB_05, 0.f, 0.f, },
99 { 0.f, kSRGB_05, 0.f, },
100 { 0.f, 0.f, kSRGB_05, },
101 { kSRGB_025, kSRGB_05, 0.f, },
102 { kSRGB_075, kSRGB_075, kSRGB_075, },
103 },
104 };
105 // clang-format on
106 bool cicp_expected[kTestCount] = {true, true, false};
107 bool a2b_expected[kTestCount] = {true, true, false};
108 uint32_t cicp_primaries_expected[kTestCount] = {9, 12, 0};
109 uint32_t cicp_trfn_expected[kTestCount] = {16, 18, 0};
110
111 for (size_t test = 0; test < kTestCount; ++test) {
112 skcms_ICCProfile parsed;
113 REPORTER_ASSERT(r, skcms_Parse(profile[test]->data(), profile[test]->size(), &parsed));
114
115 REPORTER_ASSERT(r, parsed.has_A2B == a2b_expected[test]);
116 REPORTER_ASSERT(r, parsed.has_CICP == cicp_expected[test]);
117 if (cicp_expected[test]) {
118 REPORTER_ASSERT(r, parsed.CICP.color_primaries == cicp_primaries_expected[test]);
119 REPORTER_ASSERT(r, parsed.CICP.transfer_characteristics == cicp_trfn_expected[test]);
120 REPORTER_ASSERT(r, parsed.CICP.matrix_coefficients == 0);
121 REPORTER_ASSERT(r, parsed.CICP.video_full_range_flag == 1);
122 }
123
124 skcms_ICCProfile dst_parsed;
125 REPORTER_ASSERT(
126 r, skcms_Parse(dst_profile[test]->data(), dst_profile[test]->size(), &dst_parsed));
127
128 for (size_t pixel = 0; pixel < kPixelCount; ++pixel) {
129 float dst_pixel_actual[3]{0.f, 0.f, 0.f};
130 bool xform_result = skcms_Transform(pixels[pixel],
131 skcms_PixelFormat_RGB_fff,
132 skcms_AlphaFormat_Opaque,
133 &parsed,
134 dst_pixel_actual,
135 skcms_PixelFormat_RGB_fff,
136 skcms_AlphaFormat_Opaque,
137 &dst_parsed,
138 1);
139 REPORTER_ASSERT(r, xform_result);
140
141 auto approx_equal = [=](float x, float y) { return std::abs(x - y) < 1.f / 64.f; };
142 for (size_t i = 0; i < 3; ++i) {
143 REPORTER_ASSERT(
144 r, approx_equal(dst_pixel_actual[i], dst_pixels_expected[test][pixel][i]));
145 }
146 }
147 }
148 }
149