xref: /aosp_15_r20/external/skia/tests/ICCTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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