1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
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.h"
18 
19 #include <ui/ColorSpace.h>
20 #include <utils/Log.h>
21 
22 #ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows
23 #include <android/hardware_buffer.h>
24 #include <android/native_window.h>
25 #endif
26 
27 #include <algorithm>
28 #include <cmath>
29 #include <Properties.h>
30 
31 namespace android {
32 namespace uirenderer {
33 
34 #ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows
createImageInfo(int32_t width,int32_t height,int32_t format,sk_sp<SkColorSpace> colorSpace)35 static inline SkImageInfo createImageInfo(int32_t width, int32_t height, int32_t format,
36                                           sk_sp<SkColorSpace> colorSpace) {
37     SkColorType colorType = kUnknown_SkColorType;
38     SkAlphaType alphaType = kOpaque_SkAlphaType;
39     switch (format) {
40         case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
41             colorType = kN32_SkColorType;
42             alphaType = kPremul_SkAlphaType;
43             break;
44         case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
45             colorType = kN32_SkColorType;
46             alphaType = kOpaque_SkAlphaType;
47             break;
48         case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
49             colorType = kRGB_565_SkColorType;
50             alphaType = kOpaque_SkAlphaType;
51             break;
52         case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
53             colorType = kRGBA_1010102_SkColorType;
54             alphaType = kPremul_SkAlphaType;
55             break;
56         case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
57             colorType = kRGBA_F16_SkColorType;
58             alphaType = kPremul_SkAlphaType;
59             break;
60         case AHARDWAREBUFFER_FORMAT_R8_UNORM:
61             colorType = kAlpha_8_SkColorType;
62             alphaType = kPremul_SkAlphaType;
63             break;
64         default:
65             ALOGV("Unsupported format: %d, return unknown by default", format);
66             break;
67     }
68     return SkImageInfo::Make(width, height, colorType, alphaType, colorSpace);
69 }
70 
ANativeWindowToImageInfo(const ANativeWindow_Buffer & buffer,sk_sp<SkColorSpace> colorSpace)71 SkImageInfo ANativeWindowToImageInfo(const ANativeWindow_Buffer& buffer,
72                                      sk_sp<SkColorSpace> colorSpace) {
73     return createImageInfo(buffer.width, buffer.height, buffer.format, colorSpace);
74 }
75 
BufferDescriptionToImageInfo(const AHardwareBuffer_Desc & bufferDesc,sk_sp<SkColorSpace> colorSpace)76 SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc,
77                                          sk_sp<SkColorSpace> colorSpace) {
78     return createImageInfo(bufferDesc.width, bufferDesc.height, bufferDesc.format, colorSpace);
79 }
80 
ColorTypeToBufferFormat(SkColorType colorType)81 uint32_t ColorTypeToBufferFormat(SkColorType colorType) {
82     switch (colorType) {
83         case kRGBA_8888_SkColorType:
84             return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
85         case kRGBA_F16_SkColorType:
86             return AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT;
87         case kRGB_565_SkColorType:
88             return AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
89         case kRGB_888x_SkColorType:
90             return AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
91         case kRGBA_1010102_SkColorType:
92             return AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
93         case kARGB_4444_SkColorType:
94             // Hardcoding the value from android::PixelFormat
95             static constexpr uint64_t kRGBA4444 = 7;
96             return kRGBA4444;
97         case kAlpha_8_SkColorType:
98               return AHARDWAREBUFFER_FORMAT_R8_UNORM;
99         default:
100             ALOGV("Unsupported colorType: %d, return RGBA_8888 by default", (int)colorType);
101             return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
102     }
103 }
104 
BufferFormatToColorType(uint32_t format)105 SkColorType BufferFormatToColorType(uint32_t format) {
106     switch (format) {
107         case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
108             return kN32_SkColorType;
109         case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
110             return kN32_SkColorType;
111         case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
112             return kRGB_565_SkColorType;
113         case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
114             return kRGBA_1010102_SkColorType;
115         case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
116             return kRGBA_F16_SkColorType;
117         case AHARDWAREBUFFER_FORMAT_R8_UNORM:
118             return kAlpha_8_SkColorType;
119         default:
120             ALOGV("Unsupported format: %d, return unknown by default", format);
121             return kUnknown_SkColorType;
122     }
123 }
124 #endif
125 
126 namespace {
127 static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
128 
129 // Skia's SkNamedGamut::kDisplayP3 is based on a white point of D65. This gamut
130 // matches the white point used by ColorSpace.Named.DCIP3.
131 static constexpr skcms_Matrix3x3 kDCIP3 = {{
132         {0.486143, 0.323835, 0.154234},
133         {0.226676, 0.710327, 0.0629966},
134         {0.000800549, 0.0432385, 0.78275},
135 }};
136 
nearlyEqual(float a,float b)137 static bool nearlyEqual(float a, float b) {
138     // By trial and error, this is close enough to match for the ADataSpaces we
139     // compare for.
140     return ::fabs(a - b) < .002f;
141 }
142 
nearlyEqual(const skcms_TransferFunction & x,const skcms_TransferFunction & y)143 static bool nearlyEqual(const skcms_TransferFunction& x, const skcms_TransferFunction& y) {
144     return nearlyEqual(x.g, y.g)
145         && nearlyEqual(x.a, y.a)
146         && nearlyEqual(x.b, y.b)
147         && nearlyEqual(x.c, y.c)
148         && nearlyEqual(x.d, y.d)
149         && nearlyEqual(x.e, y.e)
150         && nearlyEqual(x.f, y.f);
151 }
152 
nearlyEqual(const skcms_Matrix3x3 & x,const skcms_Matrix3x3 & y)153 static bool nearlyEqual(const skcms_Matrix3x3& x, const skcms_Matrix3x3& y) {
154     for (int i = 0; i < 3; i++) {
155         for (int j = 0; j < 3; j++) {
156             if (!nearlyEqual(x.vals[i][j], y.vals[i][j])) return false;
157         }
158     }
159     return true;
160 }
161 
162 } // anonymous namespace
163 
ColorSpaceToADataSpace(SkColorSpace * colorSpace,SkColorType colorType)164 android_dataspace ColorSpaceToADataSpace(SkColorSpace* colorSpace, SkColorType colorType) {
165     if (!colorSpace) {
166         return HAL_DATASPACE_UNKNOWN;
167     }
168 
169     if (colorSpace->isSRGB()) {
170         if (colorType == kRGBA_F16_SkColorType) {
171             return HAL_DATASPACE_V0_SCRGB;
172         }
173         return HAL_DATASPACE_V0_SRGB;
174     }
175 
176     skcms_TransferFunction fn;
177     if (!colorSpace->isNumericalTransferFn(&fn)) {
178         auto res = skcms_TransferFunction_getType(&fn);
179         if (res == skcms_TFType_PQish) {
180             return HAL_DATASPACE_BT2020_PQ;
181         }
182         if (res == skcms_TFType_HLGish) {
183             return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG);
184         }
185         LOG_ALWAYS_FATAL("Only select non-numerical transfer functions are supported");
186     }
187 
188     skcms_Matrix3x3 gamut;
189     LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&gamut));
190 
191     if (nearlyEqual(gamut, SkNamedGamut::kSRGB)) {
192         if (nearlyEqual(fn, SkNamedTransferFn::kLinear)) {
193             // Skia doesn't differentiate amongst the RANGES. In Java, we associate
194             // LINEAR_EXTENDED_SRGB with F16, and LINEAR_SRGB with other Configs.
195             // Make the same association here.
196             if (colorType == kRGBA_F16_SkColorType) {
197                 return HAL_DATASPACE_V0_SCRGB_LINEAR;
198             }
199             return HAL_DATASPACE_V0_SRGB_LINEAR;
200         }
201 
202         if (nearlyEqual(fn, SkNamedTransferFn::kRec2020)) {
203             return HAL_DATASPACE_V0_BT709;
204         }
205     }
206 
207     if (nearlyEqual(fn, SkNamedTransferFn::kSRGB) && nearlyEqual(gamut, SkNamedGamut::kDisplayP3)) {
208         return HAL_DATASPACE_DISPLAY_P3;
209     }
210 
211     if (nearlyEqual(fn, SkNamedTransferFn::k2Dot2) && nearlyEqual(gamut, SkNamedGamut::kAdobeRGB)) {
212         return HAL_DATASPACE_ADOBE_RGB;
213     }
214 
215     if (nearlyEqual(fn, SkNamedTransferFn::kRec2020) &&
216         nearlyEqual(gamut, SkNamedGamut::kRec2020)) {
217         return HAL_DATASPACE_BT2020;
218     }
219 
220     if (nearlyEqual(fn, k2Dot6) && nearlyEqual(gamut, kDCIP3)) {
221         return HAL_DATASPACE_DCI_P3;
222     }
223 
224     return HAL_DATASPACE_UNKNOWN;
225 }
226 
DataSpaceToColorSpace(android_dataspace dataspace)227 sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
228     if (dataspace == HAL_DATASPACE_UNKNOWN) {
229         return SkColorSpace::MakeSRGB();
230     }
231     if (dataspace == HAL_DATASPACE_DCI_P3) {
232         // This cannot be handled by the switch statements below because it
233         // needs to use the locally-defined kDCIP3 gamut, rather than the one in
234         // Skia (SkNamedGamut), which is used for other data spaces with
235         // HAL_DATASPACE_STANDARD_DCI_P3 (e.g. HAL_DATASPACE_DISPLAY_P3).
236         return SkColorSpace::MakeRGB(k2Dot6, kDCIP3);
237     }
238 
239     skcms_Matrix3x3 gamut;
240     switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
241         case HAL_DATASPACE_STANDARD_BT709:
242             gamut = SkNamedGamut::kSRGB;
243             break;
244         case HAL_DATASPACE_STANDARD_BT2020:
245         case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
246             gamut = SkNamedGamut::kRec2020;
247             break;
248         case HAL_DATASPACE_STANDARD_DCI_P3:
249             gamut = SkNamedGamut::kDisplayP3;
250             break;
251         case HAL_DATASPACE_STANDARD_ADOBE_RGB:
252             gamut = SkNamedGamut::kAdobeRGB;
253             break;
254         case HAL_DATASPACE_STANDARD_UNSPECIFIED:
255             return nullptr;
256         case HAL_DATASPACE_STANDARD_BT601_625:
257         case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
258         case HAL_DATASPACE_STANDARD_BT601_525:
259         case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
260         case HAL_DATASPACE_STANDARD_BT470M:
261         case HAL_DATASPACE_STANDARD_FILM:
262         default:
263             ALOGV("Unsupported Gamut: %d", dataspace);
264             return nullptr;
265     }
266 
267     // HLG
268     if ((dataspace & HAL_DATASPACE_TRANSFER_MASK) == HAL_DATASPACE_TRANSFER_HLG) {
269         const auto hlgFn = GetHLGScaleTransferFunction();
270         if (hlgFn.has_value()) {
271             return SkColorSpace::MakeRGB(hlgFn.value(), gamut);
272         }
273     }
274 
275     switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
276         case HAL_DATASPACE_TRANSFER_LINEAR:
277             return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
278         case HAL_DATASPACE_TRANSFER_SRGB:
279             return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
280         case HAL_DATASPACE_TRANSFER_GAMMA2_2:
281             return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
282         case HAL_DATASPACE_TRANSFER_GAMMA2_6:
283             return SkColorSpace::MakeRGB(k2Dot6, gamut);
284         case HAL_DATASPACE_TRANSFER_GAMMA2_8:
285             return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
286         case HAL_DATASPACE_TRANSFER_ST2084:
287             return SkColorSpace::MakeRGB({-2.0, -1.555223, 1.860454, 32 / 2523.0, 2413 / 128.0,
288                                           -2392 / 128.0, 8192 / 1305.0},
289                                          gamut);
290         case HAL_DATASPACE_TRANSFER_SMPTE_170M:
291             return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut);
292         case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
293             return nullptr;
294         default:
295             ALOGV("Unsupported Gamma: %d", dataspace);
296             return nullptr;
297     }
298 }
299 
300 template<typename T>
clamp(T x,T min,T max)301 static constexpr T clamp(T x, T min, T max) {
302     return x < min ? min : x > max ? max : x;
303 }
304 
305 //static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
306 static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
307 static const mat3 BRADFORD = mat3{
308         float3{ 0.8951f, -0.7502f,  0.0389f},
309         float3{ 0.2664f,  1.7135f, -0.0685f},
310         float3{-0.1614f,  0.0367f,  1.0296f}
311 };
312 
adaptation(const mat3 & matrix,const float3 & srcWhitePoint,const float3 & dstWhitePoint)313 static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
314     float3 srcLMS = matrix * srcWhitePoint;
315     float3 dstLMS = matrix * dstWhitePoint;
316     return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
317 }
318 
319 namespace LabColorSpace {
320 
321 static constexpr float A = 216.0f / 24389.0f;
322 static constexpr float B = 841.0f / 108.0f;
323 static constexpr float C = 4.0f / 29.0f;
324 static constexpr float D = 6.0f / 29.0f;
325 
toXyz(const Lab & lab)326 float3 toXyz(const Lab& lab) {
327     float3 v { lab.L, lab.a, lab.b };
328     v[0] = clamp(v[0], 0.0f, 100.0f);
329     v[1] = clamp(v[1], -128.0f, 128.0f);
330     v[2] = clamp(v[2], -128.0f, 128.0f);
331 
332     float fy = (v[0] + 16.0f) / 116.0f;
333     float fx = fy + (v[1] * 0.002f);
334     float fz = fy - (v[2] * 0.005f);
335     float X = fx > D ? fx * fx * fx : (1.0f / B) * (fx - C);
336     float Y = fy > D ? fy * fy * fy : (1.0f / B) * (fy - C);
337     float Z = fz > D ? fz * fz * fz : (1.0f / B) * (fz - C);
338 
339     v[0] = X * ILLUMINANT_D50_XYZ[0];
340     v[1] = Y * ILLUMINANT_D50_XYZ[1];
341     v[2] = Z * ILLUMINANT_D50_XYZ[2];
342 
343     return v;
344 }
345 
fromXyz(const float3 & v)346 Lab fromXyz(const float3& v) {
347     float X = v[0] / ILLUMINANT_D50_XYZ[0];
348     float Y = v[1] / ILLUMINANT_D50_XYZ[1];
349     float Z = v[2] / ILLUMINANT_D50_XYZ[2];
350 
351     float fx = X > A ? pow(X, 1.0f / 3.0f) : B * X + C;
352     float fy = Y > A ? pow(Y, 1.0f / 3.0f) : B * Y + C;
353     float fz = Z > A ? pow(Z, 1.0f / 3.0f) : B * Z + C;
354 
355     float L = 116.0f * fy - 16.0f;
356     float a = 500.0f * (fx - fy);
357     float b = 200.0f * (fy - fz);
358 
359     return Lab {
360             clamp(L, 0.0f, 100.0f),
361             clamp(a, -128.0f, 128.0f),
362             clamp(b, -128.0f, 128.0f)
363     };
364 }
365 
366 };
367 
sRGBToLab(SkColor color)368 Lab sRGBToLab(SkColor color) {
369     auto colorSpace = ColorSpace::sRGB();
370     float3 rgb;
371     rgb.r = SkColorGetR(color) / 255.0f;
372     rgb.g = SkColorGetG(color) / 255.0f;
373     rgb.b = SkColorGetB(color) / 255.0f;
374     float3 xyz = colorSpace.rgbToXYZ(rgb);
375     float3 srcXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
376     xyz = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * xyz;
377     return LabColorSpace::fromXyz(xyz);
378 }
379 
LabToSRGB(const Lab & lab,SkAlpha alpha)380 SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) {
381     auto colorSpace = ColorSpace::sRGB();
382     float3 xyz = LabColorSpace::toXyz(lab);
383     float3 dstXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
384     xyz = adaptation(BRADFORD, ILLUMINANT_D50_XYZ, dstXYZ) * xyz;
385     float3 rgb = colorSpace.xyzToRGB(xyz);
386     return SkColorSetARGB(alpha,
387             static_cast<uint8_t>(rgb.r * 255),
388             static_cast<uint8_t>(rgb.g * 255),
389             static_cast<uint8_t>(rgb.b * 255));
390 }
391 
GetPQSkTransferFunction(float sdr_white_level)392 skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level) {
393     if (sdr_white_level <= 0.f) {
394         sdr_white_level = Properties::defaultSdrWhitePoint;
395     }
396     // The generic PQ transfer function produces normalized luminance values i.e.
397     // the range 0-1 represents 0-10000 nits for the reference display, but we
398     // want to map 1.0 to |sdr_white_level| nits so we need to scale accordingly.
399     const double w = 10000. / sdr_white_level;
400     // Distribute scaling factor W by scaling A and B with X ^ (1/F):
401     // ((A + Bx^C) / (D + Ex^C))^F * W = ((A + Bx^C) / (D + Ex^C) * W^(1/F))^F
402     // See https://crbug.com/1058580#c32 for discussion.
403     skcms_TransferFunction fn = SkNamedTransferFn::kPQ;
404     const double ws = pow(w, 1. / fn.f);
405     fn.a = ws * fn.a;
406     fn.b = ws * fn.b;
407     return fn;
408 }
409 
trfn_apply_gain(const skcms_TransferFunction trfn,float gain)410 static skcms_TransferFunction trfn_apply_gain(const skcms_TransferFunction trfn, float gain) {
411     float pow_gain_ginv = sk_float_pow(gain, 1 / trfn.g);
412     skcms_TransferFunction result;
413     result.g = trfn.g;
414     result.a = trfn.a * pow_gain_ginv;
415     result.b = trfn.b * pow_gain_ginv;
416     result.c = trfn.c * gain;
417     result.d = trfn.d;
418     result.e = trfn.e * gain;
419     result.f = trfn.f * gain;
420     return result;
421 }
422 
GetExtendedTransferFunction(float sdrHdrRatio)423 skcms_TransferFunction GetExtendedTransferFunction(float sdrHdrRatio) {
424     if (sdrHdrRatio <= 1.f) {
425         return SkNamedTransferFn::kSRGB;
426     }
427     // Scale the transfer by the sdrHdrRatio
428     return trfn_apply_gain(SkNamedTransferFn::kSRGB, sdrHdrRatio);
429 }
430 
431 // Skia skcms' default HLG maps encoded [0, 1] to linear [1, 12] in order to follow ARIB
432 // but LinearEffect expects to map 1.0 == 203 nits
GetHLGScaleTransferFunction()433 std::optional<skcms_TransferFunction> GetHLGScaleTransferFunction() {
434     skcms_TransferFunction hlgFn;
435     if (skcms_TransferFunction_makeScaledHLGish(&hlgFn, 0.314509843, 2.f, 2.f, 1.f / 0.17883277f,
436                                                 0.28466892f, 0.55991073f)) {
437         return std::make_optional<skcms_TransferFunction>(hlgFn);
438     }
439     return {};
440 }
441 
442 }  // namespace uirenderer
443 }  // namespace android
444