1 /*
2
3 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "color_space.h"
18
19 namespace OHOS {
20 namespace ColorManager {
21 const ColorSpacePrimaries CSP_ADOBE_RGB = {0.640f, 0.330f, 0.210f, 0.710f, 0.150f, 0.060f, 0.3127f, 0.3290f};
22 const ColorSpacePrimaries CSP_P3_DCI = {0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.314f, 0.351f};
23 const ColorSpacePrimaries CSP_P3_D65 = {0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.3127f, 0.3290f};
24 const ColorSpacePrimaries CSP_BT709 = {0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f, 0.3127f, 0.3290f};
25 const ColorSpacePrimaries CSP_BT601_P = {0.640f, 0.330f, 0.290f, 0.600f, 0.150f, 0.060f, 0.3127f, 0.3290f};
26 const ColorSpacePrimaries CSP_BT601_N = {0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f};
27 const ColorSpacePrimaries CSP_BT2020 = {0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f, 0.3127f, 0.3290f};
28 const ColorSpacePrimaries CSP_NTSC_1953 = {0.670f, 0.330f, 0.210f, 0.710f, 0.140f, 0.080f, 0.310f, 0.316f};
29 const ColorSpacePrimaries CSP_PRO_PHOTO_RGB = {0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f, 0.34567f,
30 0.35850f};
31
32 // use unique g value to represent HLG and PG transfer function
33 constexpr float HLG_G = -3.0f;
34 constexpr float PQ_G = -2.0f;
35
36 const TransferFunc TF_ADOBE_RGB = {2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
37 const TransferFunc TF_GAMMA_2_6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
38 const TransferFunc TF_SRGB = {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f};
39 const TransferFunc TF_BT709 = {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f};
40 const TransferFunc TF_HLG = {HLG_G, 2.0f, 2.0f, 1 / 0.17883277f, 0.28466892f, 0.55991073f, 0.0f};
41 const TransferFunc TF_PQ = {PQ_G, -107 / 128.0f, 1.0f, 32 / 2523.0f, 2413 / 128.0f, -2392 / 128.0f, 8192 / 1305.0f};
42 const TransferFunc TF_LINEAR = {1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
43 const TransferFunc TF_PRO_PHOTO_RGB = {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f};
44
45 const ColorSpace CS_ADOBE_RGB = {CSP_ADOBE_RGB, TF_ADOBE_RGB};
46 const ColorSpace CS_DCI_P3 = {CSP_P3_DCI, TF_GAMMA_2_6};
47 const ColorSpace CS_DISPLAY_P3 = {CSP_P3_D65, TF_SRGB};
48 const ColorSpace CS_SRGB = {CSP_BT709, TF_SRGB};
49 const ColorSpace CS_BT709 = {CSP_BT709, TF_BT709};
50 const ColorSpace CS_BT601_EBU = {CSP_BT601_P, TF_BT709};
51 const ColorSpace CS_BT601_SMPTE_C = {CSP_BT601_N, TF_BT709};
52 const ColorSpace CS_BT2020_HLG = {CSP_BT2020, TF_HLG};
53 const ColorSpace CS_BT2020_PQ = {CSP_BT2020, TF_PQ};
54 const ColorSpace CS_P3_HLG = {CSP_P3_D65, TF_HLG};
55 const ColorSpace CS_P3_PQ = {CSP_P3_D65, TF_PQ};
56 const ColorSpace CS_LINEAR_P3 = {CSP_P3_D65, TF_LINEAR};
57 const ColorSpace CS_LINEAR_SRGB = {CSP_BT709, TF_LINEAR};
58 const ColorSpace CS_LINEAR_BT2020 = {CSP_BT2020, TF_LINEAR};
59 const ColorSpace CS_DISPLAY_BT2020_SRGB = {CSP_BT2020, TF_SRGB};
60 const ColorSpace CS_BT2020 = {CSP_BT2020, TF_BT709};
61 const ColorSpace CS_NTSC_1953 = {CSP_NTSC_1953, TF_BT709};
62 const ColorSpace CS_PRO_PHOTO_RGB = {CSP_PRO_PHOTO_RGB, TF_PRO_PHOTO_RGB};
63
64 const std::map<ColorSpaceName, ColorSpace> NamedColorSpace = {
65 { ColorSpaceName::ADOBE_RGB, CS_ADOBE_RGB },
66 { ColorSpaceName::DCI_P3, CS_DCI_P3 },
67 { ColorSpaceName::DISPLAY_P3, CS_DISPLAY_P3 },
68 { ColorSpaceName::SRGB, CS_SRGB },
69 { ColorSpaceName::BT709, CS_BT709 },
70 { ColorSpaceName::BT601_EBU, CS_BT601_EBU },
71 { ColorSpaceName::BT601_SMPTE_C, CS_BT601_SMPTE_C },
72 { ColorSpaceName::BT2020_HLG, CS_BT2020_HLG },
73 { ColorSpaceName::BT2020_PQ, CS_BT2020_PQ },
74 { ColorSpaceName::P3_HLG, CS_P3_HLG },
75 { ColorSpaceName::P3_PQ, CS_P3_PQ },
76 { ColorSpaceName::ADOBE_RGB_LIMIT, CS_ADOBE_RGB },
77 { ColorSpaceName::DISPLAY_P3_LIMIT, CS_DISPLAY_P3 },
78 { ColorSpaceName::SRGB_LIMIT, CS_SRGB },
79 { ColorSpaceName::BT709_LIMIT, CS_BT709 },
80 { ColorSpaceName::BT601_EBU_LIMIT, CS_BT601_EBU },
81 { ColorSpaceName::BT601_SMPTE_C_LIMIT, CS_BT601_SMPTE_C },
82 { ColorSpaceName::BT2020_HLG_LIMIT, CS_BT2020_HLG },
83 { ColorSpaceName::BT2020_PQ_LIMIT, CS_BT2020_PQ },
84 { ColorSpaceName::P3_HLG_LIMIT, CS_P3_HLG },
85 { ColorSpaceName::P3_PQ_LIMIT, CS_P3_PQ },
86 { ColorSpaceName::LINEAR_P3, CS_LINEAR_P3 },
87 { ColorSpaceName::LINEAR_SRGB, CS_LINEAR_SRGB },
88 { ColorSpaceName::LINEAR_BT709, CS_LINEAR_SRGB },
89 { ColorSpaceName::LINEAR_BT2020, CS_LINEAR_BT2020 },
90 { ColorSpaceName::DISPLAY_SRGB, CS_SRGB },
91 { ColorSpaceName::DISPLAY_P3_SRGB, CS_DISPLAY_P3 },
92 { ColorSpaceName::DISPLAY_P3_HLG, CS_P3_HLG },
93 { ColorSpaceName::DISPLAY_P3_PQ, CS_P3_PQ },
94 { ColorSpaceName::DISPLAY_BT2020_SRGB, CS_DISPLAY_BT2020_SRGB },
95 { ColorSpaceName::DISPLAY_BT2020_HLG, CS_BT2020_HLG },
96 { ColorSpaceName::DISPLAY_BT2020_PQ, CS_BT2020_PQ },
97 { ColorSpaceName::BT2020, CS_BT2020 },
98 { ColorSpaceName::NTSC_1953, CS_NTSC_1953 },
99 { ColorSpaceName::PRO_PHOTO_RGB, CS_PRO_PHOTO_RGB },
100 };
101
ColorSpace(ColorSpaceName name)102 ColorSpace::ColorSpace(ColorSpaceName name)
103 {
104 if (NamedColorSpace.find(name) != NamedColorSpace.end()) {
105 const ColorSpace &colorSpace = NamedColorSpace.at(name);
106 colorSpaceName = name;
107 whitePoint = colorSpace.whitePoint;
108 toXYZ = colorSpace.toXYZ;
109 transferFunc = colorSpace.transferFunc;
110 } else {
111 colorSpaceName = ColorSpaceName::NONE;
112 }
113 }
114
ColorSpace(const ColorSpacePrimaries & primaries,const TransferFunc & transferFunc)115 ColorSpace::ColorSpace(const ColorSpacePrimaries &primaries, const TransferFunc &transferFunc)
116 : colorSpaceName(ColorSpaceName::CUSTOM),
117 toXYZ(ComputeXYZD50(primaries)),
118 transferFunc(transferFunc)
119 {
120 std::array<float, 2> whiteP = {primaries.wX, primaries.wY}; // 2 means two dimension x, y
121 whitePoint = whiteP;
122 }
123
ColorSpace(const ColorSpacePrimaries & primaries,float gamma)124 ColorSpace::ColorSpace(const ColorSpacePrimaries &primaries, float gamma)
125 : colorSpaceName(ColorSpaceName::CUSTOM),
126 toXYZ(ComputeXYZD50(primaries))
127 {
128 std::array<float, 2> whiteP = {primaries.wX, primaries.wY}; // 2 means two dimension x, y
129 whitePoint = whiteP;
130 transferFunc = {};
131 transferFunc.g = gamma;
132 transferFunc.a = 1.0f;
133 }
134
ColorSpace(const Matrix3x3 & toXYZ,const std::array<float,2> & whitePoint,const TransferFunc & transferFunc)135 ColorSpace::ColorSpace(const Matrix3x3& toXYZ, const std::array<float, 2>& whitePoint, const TransferFunc &transferFunc)
136 : colorSpaceName(ColorSpaceName::CUSTOM),
137 toXYZ(DXToD50(toXYZ, whitePoint)),
138 whitePoint(whitePoint),
139 transferFunc(transferFunc)
140 {
141 }
142
ColorSpace(const Matrix3x3 & toXYZ,const std::array<float,2> & whitePoint,float gamma)143 ColorSpace::ColorSpace(const Matrix3x3 &toXYZ, const std::array<float, 2>& whitePoint, float gamma)
144 : colorSpaceName(ColorSpaceName::CUSTOM), toXYZ(DXToD50(toXYZ, whitePoint)), whitePoint(whitePoint)
145 {
146 transferFunc = {};
147 transferFunc.g = gamma;
148 transferFunc.a = 1.0f;
149 }
150
ColorSpace(const sk_sp<SkColorSpace> src,ColorSpaceName name)151 ColorSpace::ColorSpace(const sk_sp<SkColorSpace> src, ColorSpaceName name)
152 : colorSpaceName(name)
153 {
154 if (src) {
155 float func[7];
156 src->transferFn(func);
157 transferFunc = {func[0], func[1], func[2], func[3], func[4], func[5], func[6]};
158 skcms_Matrix3x3 toXYZD50;
159 src->toXYZD50(&toXYZD50);
160 toXYZ = SkToXYZToMatrix3(toXYZD50);
161 whitePoint = ComputeWhitePoint(toXYZ);
162 }
163 }
164
ColorSpace(const skcms_ICCProfile & srcIcc,ColorSpaceName name)165 ColorSpace::ColorSpace(const skcms_ICCProfile& srcIcc, ColorSpaceName name)
166 : colorSpaceName(name)
167 {
168 if (srcIcc.has_toXYZD50 && srcIcc.has_trc) {
169 skcms_TransferFunction func = srcIcc.trc[0].parametric;
170 transferFunc = {func.g, func.a, func.b, func.c, func.d, func.e, func.f};
171 toXYZ = SkToXYZToMatrix3(srcIcc.toXYZD50);
172 whitePoint = ComputeWhitePoint(toXYZ);
173 }
174 }
175
operator *(const Matrix3x3 & a,const Matrix3x3 & b)176 Matrix3x3 operator*(const Matrix3x3& a, const Matrix3x3& b)
177 {
178 Matrix3x3 c = {};
179 for (size_t i = 0; i < a.size(); ++i) {
180 for (size_t j = 0; j < b.size(); ++j) {
181 for (size_t k = 0; k < b[0].size(); ++k) {
182 c[i][j] += a[i][k] * b[k][j];
183 }
184 }
185 }
186 return c;
187 }
188
operator *(const Vector3 & x,const Matrix3x3 & a)189 Vector3 operator*(const Vector3& x, const Matrix3x3& a)
190 {
191 Vector3 b = {};
192 for (size_t i = 0; i < x.size(); ++i) {
193 for (size_t j = 0; j < a.size(); ++j) {
194 b[i] += x[j] * a[j][i];
195 }
196 }
197 return b;
198 }
199
operator *(const Matrix3x3 & a,const Vector3 & x)200 Vector3 operator*(const Matrix3x3& a, const Vector3& x)
201 {
202 Vector3 b = {};
203 for (size_t i = 0; i < a.size(); ++i) {
204 for (size_t j = 0; j < x.size(); ++j) {
205 b[i] += a[i][j] * x[j];
206 }
207 }
208 return b;
209 }
210
operator /(const Vector3 & a,const Vector3 & b)211 Matrix3x3 operator/(const Vector3& a, const Vector3& b)
212 {
213 Matrix3x3 diag = {};
214 for (size_t i = 0; i < a.size(); ++i) {
215 diag[i][i] += a[i] / b[i];
216 }
217 return diag;
218 }
219
ComputeXYZD50(const ColorSpacePrimaries & primaries)220 Matrix3x3 ComputeXYZD50(const ColorSpacePrimaries& primaries)
221 {
222 float oneRxRy = (1 - primaries.rX) / primaries.rY;
223 float oneGxGy = (1 - primaries.gX) / primaries.gY;
224 float oneBxBy = (1 - primaries.bX) / primaries.bY;
225 float oneWxWy = (1 - primaries.wX) / primaries.wY;
226
227 float RxRy = primaries.rX / primaries.rY;
228 float GxGy = primaries.gX / primaries.gY;
229 float BxBy = primaries.bX / primaries.bY;
230 float WxWy = primaries.wX / primaries.wY;
231
232 float BY = ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) /
233 ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy));
234 float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy);
235 float RY = 1 - GY - BY;
236
237 float RYRy = RY / primaries.rY;
238 float GYGy = GY / primaries.gY;
239 float BYBy = BY / primaries.bY;
240
241 Matrix3x3 toXYZ = {{{RYRy * primaries.rX, GYGy * primaries.gX, BYBy * primaries.bX},
242 {RY, GY, BY},
243 {RYRy * (1 - primaries.rX - primaries.rY),
244 GYGy * (1 - primaries.gX - primaries.gY),
245 BYBy * (1 - primaries.bX - primaries.bY)}}};
246 std::array<float, DIMES_2> wp = {primaries.wX, primaries.wY};
247
248 return DXToD50(toXYZ, wp);
249 }
250
AdaptationToD50(const Vector3 & srcWhitePoint)251 static Matrix3x3 AdaptationToD50(const Vector3& srcWhitePoint)
252 {
253 Vector3 srcLMS = BRADFORD * srcWhitePoint;
254 Vector3 dstLMS = BRADFORD * ILLUMINANT_D50_XYZ;
255 return BRADFORD_INV * (dstLMS / srcLMS) * BRADFORD;
256 }
257
DXToD50(const Matrix3x3 & toXYZ,const std::array<float,DIMES_2> & wp)258 Matrix3x3 DXToD50(const Matrix3x3 &toXYZ, const std::array<float, DIMES_2> &wp)
259 {
260 if ((wp[0] == ILLUMINANT_D50_XY[0]) && (wp[1] == ILLUMINANT_D50_XY[1])) {
261 return toXYZ;
262 }
263 Vector3 srcXYZ = XYZ(Vector3 {wp[0], wp[1], 1.0f});
264 return AdaptationToD50(srcXYZ) * toXYZ;
265 }
266
IsFinite(float x)267 static bool IsFinite(float x)
268 {
269 return x * 0 == 0;
270 }
271
272 // inverse src Matrix to dst Matrix
Invert(const Matrix3x3 & src)273 Matrix3x3 Invert(const Matrix3x3& src)
274 {
275 double a00 = src[0][0];
276 double a01 = src[1][0];
277 double a02 = src[2][0];
278 double a10 = src[0][1];
279 double a11 = src[1][1];
280 double a12 = src[2][1];
281 double a20 = src[0][2];
282 double a21 = src[1][2];
283 double a22 = src[2][2];
284
285 double b0 = a00 * a11 - a01 * a10;
286 double b1 = a00 * a12 - a02 * a10;
287 double b2 = a01 * a12 - a02 * a11;
288 double b3 = a20;
289 double b4 = a21;
290 double b5 = a22;
291
292 double determinant = b0 * b5 - b1 * b4 + b2 * b3;
293
294 if (determinant == 0) {
295 return {};
296 }
297
298 double invdet = 1.0 / determinant;
299 if (invdet > +FLT_MAX || invdet < -FLT_MAX || !IsFinite((float)invdet)) {
300 return {};
301 }
302
303 Matrix3x3 dst {};
304
305 b0 *= invdet;
306 b1 *= invdet;
307 b2 *= invdet;
308 b3 *= invdet;
309 b4 *= invdet;
310 b5 *= invdet;
311
312 dst[0][0] = static_cast<float>(a11 * b5 - a12 * b4); // compute dst[0][0] value
313 dst[1][0] = static_cast<float>(a02 * b4 - a01 * b5); // compute dst[1][0] value
314 dst[2][0] = static_cast<float>(+b2); // compute dst[2][0] value
315 dst[0][1] = static_cast<float>(a12 * b3 - a10 * b5); // compute dst[0][1] value
316 dst[1][1] = static_cast<float>(a00 * b5 - a02 * b3); // compute dst[1][1] value
317 dst[2][1] = static_cast<float>(-b1); // compute dst[2][1] value
318 dst[0][2] = static_cast<float>(a10 * b4 - a11 * b3); // compute dst[0][2] value
319 dst[1][2] = static_cast<float>(a01 * b3 - a00 * b4); // compute dst[1][2] value
320 dst[2][2] = static_cast<float>(+b0); // compute dst[2][2] value
321
322 for (size_t r = 0; r < dst.size(); ++r) {
323 for (size_t c = 0; c < dst[0].size(); ++c) {
324 if (!IsFinite(dst[r][c])) {
325 return {};
326 }
327 }
328 }
329 return dst;
330 }
331
ToLinear(Vector3 v) const332 Vector3 ColorSpace::ToLinear(Vector3 v) const
333 {
334 auto &p = transferFunc;
335 Vector3 res = v;
336 for (auto& n : res) {
337 if (FloatEqual(p.g, HLG_G)) {
338 float coef = -1 / (p.g * p.a * p.b);
339 n = n > 1 / p.a ? coef * (std::exp(p.c * (n - p.e)) + p.d) : n * n / -p.g;
340 } else if (FloatEqual(p.g, PQ_G)) {
341 float tmp = std::pow(n, p.c);
342 n = std::pow(std::max(p.a + p.b * tmp, 0.0f) / (p.d + p.e * tmp), p.f);
343 } else {
344 n = n >= p.d ? std::pow(p.a * n + p.b, p.g) + p.e : p.c * n + p.f;
345 }
346 }
347 return res;
348 }
349
ToNonLinear(Vector3 v) const350 Vector3 ColorSpace::ToNonLinear(Vector3 v) const
351 {
352 auto &p = transferFunc;
353 Vector3 res = v;
354 for (auto& n : res) {
355 if (FloatEqual(p.g, HLG_G)) {
356 float coef = -p.g * p.a * p.b;
357 n = n > 1 / coef ? std::log(coef * n - p.d) / p.c + p.e : std::sqrt(-p.g * n);
358 } else if (FloatEqual(p.g, PQ_G)) {
359 float tmp = std::pow(n, 1 / p.f);
360 n = std::pow((-p.a + p.d * tmp) / (p.b - p.e * tmp), 1 / p.c);
361 } else {
362 n = n >= p.d * p.c ? (std::pow(n - p.e, 1.0f / p.g) - p.b) / p.a : (n - p.f) / p.c;
363 }
364 }
365 return res;
366 }
367
ToSkColorSpace() const368 sk_sp<SkColorSpace> ColorSpace::ToSkColorSpace() const
369 {
370 skcms_Matrix3x3 toXYZ = ToSkiaXYZ();
371 skcms_TransferFunction skTransferFun = {
372 transferFunc.g,
373 transferFunc.a,
374 transferFunc.b,
375 transferFunc.c,
376 transferFunc.d,
377 transferFunc.e,
378 transferFunc.f,
379 };
380 return SkColorSpace::MakeRGB(skTransferFun, toXYZ);
381 }
382
ToSkiaXYZ() const383 skcms_Matrix3x3 ColorSpace::ToSkiaXYZ() const
384 {
385 skcms_Matrix3x3 skToXYZ;
386 for (int i = 0; i < DIMES_3; ++i) {
387 for (int j = 0; j < DIMES_3; ++j) {
388 skToXYZ.vals[i][j] = toXYZ[i][j];
389 }
390 }
391 return skToXYZ;
392 }
393 } //! namespace ColorManager
394 } //! namespace OHOS
395