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