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