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