Line data Source code
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 : #ifndef SkColorSpacePriv_DEFINED
8 : #define SkColorSpacePriv_DEFINED
9 :
10 : #include <math.h>
11 :
12 : #include "SkColorSpace_Base.h"
13 :
14 : #define SkColorSpacePrintf(...)
15 :
16 : static constexpr float gSRGB_toXYZD50[] {
17 : 0.4360747f, 0.3850649f, 0.1430804f, // Rx, Gx, Bx
18 : 0.2225045f, 0.7168786f, 0.0606169f, // Ry, Gy, Gz
19 : 0.0139322f, 0.0971045f, 0.7141733f, // Rz, Gz, Bz
20 : };
21 :
22 : static constexpr float gAdobeRGB_toXYZD50[] {
23 : 0.6097559f, 0.2052401f, 0.1492240f, // Rx, Gx, Bx
24 : 0.3111242f, 0.6256560f, 0.0632197f, // Ry, Gy, Gz
25 : 0.0194811f, 0.0608902f, 0.7448387f, // Rz, Gz, Bz
26 : };
27 :
28 : static constexpr float gDCIP3_toXYZD50[] {
29 : 0.515102f, 0.291965f, 0.157153f, // Rx, Gx, Bx
30 : 0.241182f, 0.692236f, 0.0665819f, // Ry, Gy, Gz
31 : -0.00104941f, 0.0418818f, 0.784378f, // Rz, Gz, Bz
32 : };
33 :
34 : static constexpr float gRec2020_toXYZD50[] {
35 : 0.673459f, 0.165661f, 0.125100f, // Rx, Gx, Bx
36 : 0.279033f, 0.675338f, 0.0456288f, // Ry, Gy, Gz
37 : -0.00193139f, 0.0299794f, 0.797162f, // Rz, Gz, Bz
38 : };
39 :
40 0 : static inline void to_xyz_d50(SkMatrix44* toXYZD50, SkColorSpace::Gamut gamut) {
41 0 : switch (gamut) {
42 : case SkColorSpace::kSRGB_Gamut:
43 0 : toXYZD50->set3x3RowMajorf(gSRGB_toXYZD50);
44 0 : break;
45 : case SkColorSpace::kAdobeRGB_Gamut:
46 0 : toXYZD50->set3x3RowMajorf(gAdobeRGB_toXYZD50);
47 0 : break;
48 : case SkColorSpace::kDCIP3_D65_Gamut:
49 0 : toXYZD50->set3x3RowMajorf(gDCIP3_toXYZD50);
50 0 : break;
51 : case SkColorSpace::kRec2020_Gamut:
52 0 : toXYZD50->set3x3RowMajorf(gRec2020_toXYZD50);
53 0 : break;
54 : }
55 0 : }
56 :
57 0 : static inline bool color_space_almost_equal(float a, float b) {
58 0 : return SkTAbs(a - b) < 0.01f;
59 : }
60 :
61 : // Let's use a stricter version for transfer functions. Worst case, these are encoded
62 : // in ICC format, which offers 16-bits of fractional precision.
63 0 : static inline bool transfer_fn_almost_equal(float a, float b) {
64 0 : return SkTAbs(a - b) < 0.001f;
65 : }
66 :
67 0 : static inline bool is_zero_to_one(float v) {
68 : // Because we allow a value just barely larger than 1, the client can use an
69 : // entirely linear transfer function.
70 0 : return (0.0f <= v) && (v <= nextafterf(1.0f, 2.0f));
71 : }
72 :
73 0 : static inline bool is_valid_transfer_fn(const SkColorSpaceTransferFn& coeffs) {
74 0 : if (SkScalarIsNaN(coeffs.fA) || SkScalarIsNaN(coeffs.fB) ||
75 0 : SkScalarIsNaN(coeffs.fC) || SkScalarIsNaN(coeffs.fD) ||
76 0 : SkScalarIsNaN(coeffs.fE) || SkScalarIsNaN(coeffs.fF) ||
77 0 : SkScalarIsNaN(coeffs.fG))
78 : {
79 0 : return false;
80 : }
81 :
82 0 : if (!is_zero_to_one(coeffs.fD)) {
83 0 : return false;
84 : }
85 :
86 0 : if (coeffs.fD == 0.0f) {
87 : // Y = (aX + b)^g + e for always
88 0 : if (0.0f == coeffs.fA || 0.0f == coeffs.fG) {
89 : SkColorSpacePrintf("A or G is zero, constant transfer function "
90 : "is nonsense");
91 0 : return false;
92 : }
93 : }
94 :
95 0 : if (coeffs.fD >= 1.0f) {
96 : // Y = cX + f for always
97 0 : if (0.0f == coeffs.fC) {
98 : SkColorSpacePrintf("C is zero, constant transfer function is "
99 : "nonsense");
100 0 : return false;
101 : }
102 : }
103 :
104 0 : if ((0.0f == coeffs.fA || 0.0f == coeffs.fG) && 0.0f == coeffs.fC) {
105 : SkColorSpacePrintf("A or G, and C are zero, constant transfer function "
106 : "is nonsense");
107 0 : return false;
108 : }
109 :
110 0 : if (coeffs.fC < 0.0f) {
111 : SkColorSpacePrintf("Transfer function must be increasing");
112 0 : return false;
113 : }
114 :
115 0 : if (coeffs.fA < 0.0f || coeffs.fG < 0.0f) {
116 : SkColorSpacePrintf("Transfer function must be positive or increasing");
117 0 : return false;
118 : }
119 :
120 0 : return true;
121 : }
122 :
123 0 : static inline bool is_almost_srgb(const SkColorSpaceTransferFn& coeffs) {
124 0 : return transfer_fn_almost_equal(1.0f / 1.055f, coeffs.fA) &&
125 0 : transfer_fn_almost_equal(0.055f / 1.055f, coeffs.fB) &&
126 0 : transfer_fn_almost_equal(1.0f / 12.92f, coeffs.fC) &&
127 0 : transfer_fn_almost_equal(0.04045f, coeffs.fD) &&
128 0 : transfer_fn_almost_equal(0.00000f, coeffs.fE) &&
129 0 : transfer_fn_almost_equal(0.00000f, coeffs.fF) &&
130 0 : transfer_fn_almost_equal(2.40000f, coeffs.fG);
131 : }
132 :
133 0 : static inline bool is_almost_2dot2(const SkColorSpaceTransferFn& coeffs) {
134 0 : return transfer_fn_almost_equal(1.0f, coeffs.fA) &&
135 0 : transfer_fn_almost_equal(0.0f, coeffs.fB) &&
136 0 : transfer_fn_almost_equal(0.0f, coeffs.fE) &&
137 0 : transfer_fn_almost_equal(2.2f, coeffs.fG) &&
138 0 : coeffs.fD <= 0.0f;
139 : }
140 :
141 0 : static inline bool is_almost_linear(const SkColorSpaceTransferFn& coeffs) {
142 : // OutputVal = InputVal ^ 1.0f
143 : const bool linearExp =
144 0 : transfer_fn_almost_equal(1.0f, coeffs.fA) &&
145 0 : transfer_fn_almost_equal(0.0f, coeffs.fB) &&
146 0 : transfer_fn_almost_equal(0.0f, coeffs.fE) &&
147 0 : transfer_fn_almost_equal(1.0f, coeffs.fG) &&
148 0 : coeffs.fD <= 0.0f;
149 :
150 : // OutputVal = 1.0f * InputVal
151 : const bool linearFn =
152 0 : transfer_fn_almost_equal(1.0f, coeffs.fC) &&
153 0 : transfer_fn_almost_equal(0.0f, coeffs.fF) &&
154 0 : coeffs.fD >= 1.0f;
155 :
156 0 : return linearExp || linearFn;
157 : }
158 :
159 0 : static inline void value_to_parametric(SkColorSpaceTransferFn* coeffs, float exponent) {
160 0 : coeffs->fA = 1.0f;
161 0 : coeffs->fB = 0.0f;
162 0 : coeffs->fC = 0.0f;
163 0 : coeffs->fD = 0.0f;
164 0 : coeffs->fE = 0.0f;
165 0 : coeffs->fF = 0.0f;
166 0 : coeffs->fG = exponent;
167 0 : }
168 :
169 0 : static inline bool named_to_parametric(SkColorSpaceTransferFn* coeffs,
170 : SkGammaNamed gammaNamed) {
171 0 : switch (gammaNamed) {
172 : case kSRGB_SkGammaNamed:
173 0 : coeffs->fA = 1.0f / 1.055f;
174 0 : coeffs->fB = 0.055f / 1.055f;
175 0 : coeffs->fC = 1.0f / 12.92f;
176 0 : coeffs->fD = 0.04045f;
177 0 : coeffs->fE = 0.0f;
178 0 : coeffs->fF = 0.0f;
179 0 : coeffs->fG = 2.4f;
180 0 : return true;
181 : case k2Dot2Curve_SkGammaNamed:
182 0 : value_to_parametric(coeffs, 2.2f);
183 0 : return true;
184 : case kLinear_SkGammaNamed:
185 0 : coeffs->fA = 0.0f;
186 0 : coeffs->fB = 0.0f;
187 0 : coeffs->fC = 1.0f;
188 : // Make sure that we use the linear segment of the transfer function even
189 : // when the x-value is 1.0f.
190 0 : coeffs->fD = nextafterf(1.0f, 2.0f);
191 0 : coeffs->fE = 0.0f;
192 0 : coeffs->fF = 0.0f;
193 0 : coeffs->fG = 0.0f;
194 0 : return true;
195 : default:
196 0 : return false;
197 : }
198 : }
199 : #endif // SkColorSpacePriv_DEFINED
|