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