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 #endif
105
106 namespace {
107 static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
108
109 // Skia's SkNamedGamut::kDisplayP3 is based on a white point of D65. This gamut
110 // matches the white point used by ColorSpace.Named.DCIP3.
111 static constexpr skcms_Matrix3x3 kDCIP3 = {{
112 {0.486143, 0.323835, 0.154234},
113 {0.226676, 0.710327, 0.0629966},
114 {0.000800549, 0.0432385, 0.78275},
115 }};
116
nearlyEqual(float a,float b)117 static bool nearlyEqual(float a, float b) {
118 // By trial and error, this is close enough to match for the ADataSpaces we
119 // compare for.
120 return ::fabs(a - b) < .002f;
121 }
122
nearlyEqual(const skcms_TransferFunction & x,const skcms_TransferFunction & y)123 static bool nearlyEqual(const skcms_TransferFunction& x, const skcms_TransferFunction& y) {
124 return nearlyEqual(x.g, y.g)
125 && nearlyEqual(x.a, y.a)
126 && nearlyEqual(x.b, y.b)
127 && nearlyEqual(x.c, y.c)
128 && nearlyEqual(x.d, y.d)
129 && nearlyEqual(x.e, y.e)
130 && nearlyEqual(x.f, y.f);
131 }
132
nearlyEqual(const skcms_Matrix3x3 & x,const skcms_Matrix3x3 & y)133 static bool nearlyEqual(const skcms_Matrix3x3& x, const skcms_Matrix3x3& y) {
134 for (int i = 0; i < 3; i++) {
135 for (int j = 0; j < 3; j++) {
136 if (!nearlyEqual(x.vals[i][j], y.vals[i][j])) return false;
137 }
138 }
139 return true;
140 }
141
142 } // anonymous namespace
143
ColorSpaceToADataSpace(SkColorSpace * colorSpace,SkColorType colorType)144 android_dataspace ColorSpaceToADataSpace(SkColorSpace* colorSpace, SkColorType colorType) {
145 if (!colorSpace) {
146 return HAL_DATASPACE_UNKNOWN;
147 }
148
149 if (colorSpace->isSRGB()) {
150 if (colorType == kRGBA_F16_SkColorType) {
151 return HAL_DATASPACE_V0_SCRGB;
152 }
153 return HAL_DATASPACE_V0_SRGB;
154 }
155
156 skcms_TransferFunction fn;
157 if (!colorSpace->isNumericalTransferFn(&fn)) {
158 // pq with the default white point
159 auto rec2020PQ = SkColorSpace::MakeRGB(GetPQSkTransferFunction(), SkNamedGamut::kRec2020);
160 if (SkColorSpace::Equals(colorSpace, rec2020PQ.get())) {
161 return HAL_DATASPACE_BT2020_PQ;
162 }
163 // standard PQ
164 rec2020PQ = SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, SkNamedGamut::kRec2020);
165 if (SkColorSpace::Equals(colorSpace, rec2020PQ.get())) {
166 return HAL_DATASPACE_BT2020_PQ;
167 }
168 // HLG
169 const auto hlgFn = GetHLGScaleTransferFunction();
170 if (hlgFn.has_value()) {
171 auto rec2020HLG = SkColorSpace::MakeRGB(hlgFn.value(), SkNamedGamut::kRec2020);
172 if (SkColorSpace::Equals(colorSpace, rec2020HLG.get())) {
173 return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG);
174 }
175 }
176 LOG_ALWAYS_FATAL("Only select non-numerical transfer functions are supported");
177 }
178
179 skcms_Matrix3x3 gamut;
180 LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&gamut));
181
182 if (nearlyEqual(gamut, SkNamedGamut::kSRGB)) {
183 if (nearlyEqual(fn, SkNamedTransferFn::kLinear)) {
184 // Skia doesn't differentiate amongst the RANGES. In Java, we associate
185 // LINEAR_EXTENDED_SRGB with F16, and LINEAR_SRGB with other Configs.
186 // Make the same association here.
187 if (colorType == kRGBA_F16_SkColorType) {
188 return HAL_DATASPACE_V0_SCRGB_LINEAR;
189 }
190 return HAL_DATASPACE_V0_SRGB_LINEAR;
191 }
192
193 if (nearlyEqual(fn, SkNamedTransferFn::kRec2020)) {
194 return HAL_DATASPACE_V0_BT709;
195 }
196 }
197
198 if (nearlyEqual(fn, SkNamedTransferFn::kSRGB) && nearlyEqual(gamut, SkNamedGamut::kDisplayP3)) {
199 return HAL_DATASPACE_DISPLAY_P3;
200 }
201
202 if (nearlyEqual(fn, SkNamedTransferFn::k2Dot2) && nearlyEqual(gamut, SkNamedGamut::kAdobeRGB)) {
203 return HAL_DATASPACE_ADOBE_RGB;
204 }
205
206 if (nearlyEqual(fn, SkNamedTransferFn::kRec2020) &&
207 nearlyEqual(gamut, SkNamedGamut::kRec2020)) {
208 return HAL_DATASPACE_BT2020;
209 }
210
211 if (nearlyEqual(fn, k2Dot6) && nearlyEqual(gamut, kDCIP3)) {
212 return HAL_DATASPACE_DCI_P3;
213 }
214
215 return HAL_DATASPACE_UNKNOWN;
216 }
217
DataSpaceToColorSpace(android_dataspace dataspace)218 sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
219 if (dataspace == HAL_DATASPACE_UNKNOWN) {
220 return SkColorSpace::MakeSRGB();
221 }
222 if (dataspace == HAL_DATASPACE_DCI_P3) {
223 // This cannot be handled by the switch statements below because it
224 // needs to use the locally-defined kDCIP3 gamut, rather than the one in
225 // Skia (SkNamedGamut), which is used for other data spaces with
226 // HAL_DATASPACE_STANDARD_DCI_P3 (e.g. HAL_DATASPACE_DISPLAY_P3).
227 return SkColorSpace::MakeRGB(k2Dot6, kDCIP3);
228 }
229
230 skcms_Matrix3x3 gamut;
231 switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
232 case HAL_DATASPACE_STANDARD_BT709:
233 gamut = SkNamedGamut::kSRGB;
234 break;
235 case HAL_DATASPACE_STANDARD_BT2020:
236 case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
237 gamut = SkNamedGamut::kRec2020;
238 break;
239 case HAL_DATASPACE_STANDARD_DCI_P3:
240 gamut = SkNamedGamut::kDisplayP3;
241 break;
242 case HAL_DATASPACE_STANDARD_ADOBE_RGB:
243 gamut = SkNamedGamut::kAdobeRGB;
244 break;
245 case HAL_DATASPACE_STANDARD_UNSPECIFIED:
246 return nullptr;
247 case HAL_DATASPACE_STANDARD_BT601_625:
248 case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
249 case HAL_DATASPACE_STANDARD_BT601_525:
250 case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
251 case HAL_DATASPACE_STANDARD_BT470M:
252 case HAL_DATASPACE_STANDARD_FILM:
253 default:
254 ALOGV("Unsupported Gamut: %d", dataspace);
255 return nullptr;
256 }
257
258 // HLG
259 if ((dataspace & HAL_DATASPACE_TRANSFER_MASK) == HAL_DATASPACE_TRANSFER_HLG) {
260 const auto hlgFn = GetHLGScaleTransferFunction();
261 if (hlgFn.has_value()) {
262 return SkColorSpace::MakeRGB(hlgFn.value(), gamut);
263 }
264 }
265
266 switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
267 case HAL_DATASPACE_TRANSFER_LINEAR:
268 return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
269 case HAL_DATASPACE_TRANSFER_SRGB:
270 return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
271 case HAL_DATASPACE_TRANSFER_GAMMA2_2:
272 return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
273 case HAL_DATASPACE_TRANSFER_GAMMA2_6:
274 return SkColorSpace::MakeRGB(k2Dot6, gamut);
275 case HAL_DATASPACE_TRANSFER_GAMMA2_8:
276 return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
277 case HAL_DATASPACE_TRANSFER_ST2084:
278 return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
279 case HAL_DATASPACE_TRANSFER_SMPTE_170M:
280 return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut);
281 case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
282 return nullptr;
283 default:
284 ALOGV("Unsupported Gamma: %d", dataspace);
285 return nullptr;
286 }
287 }
288
289 template<typename T>
clamp(T x,T min,T max)290 static constexpr T clamp(T x, T min, T max) {
291 return x < min ? min : x > max ? max : x;
292 }
293
294 //static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
295 static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
296 static const mat3 BRADFORD = mat3{
297 float3{ 0.8951f, -0.7502f, 0.0389f},
298 float3{ 0.2664f, 1.7135f, -0.0685f},
299 float3{-0.1614f, 0.0367f, 1.0296f}
300 };
301
adaptation(const mat3 & matrix,const float3 & srcWhitePoint,const float3 & dstWhitePoint)302 static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
303 float3 srcLMS = matrix * srcWhitePoint;
304 float3 dstLMS = matrix * dstWhitePoint;
305 return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
306 }
307
308 namespace LabColorSpace {
309
310 static constexpr float A = 216.0f / 24389.0f;
311 static constexpr float B = 841.0f / 108.0f;
312 static constexpr float C = 4.0f / 29.0f;
313 static constexpr float D = 6.0f / 29.0f;
314
toXyz(const Lab & lab)315 float3 toXyz(const Lab& lab) {
316 float3 v { lab.L, lab.a, lab.b };
317 v[0] = clamp(v[0], 0.0f, 100.0f);
318 v[1] = clamp(v[1], -128.0f, 128.0f);
319 v[2] = clamp(v[2], -128.0f, 128.0f);
320
321 float fy = (v[0] + 16.0f) / 116.0f;
322 float fx = fy + (v[1] * 0.002f);
323 float fz = fy - (v[2] * 0.005f);
324 float X = fx > D ? fx * fx * fx : (1.0f / B) * (fx - C);
325 float Y = fy > D ? fy * fy * fy : (1.0f / B) * (fy - C);
326 float Z = fz > D ? fz * fz * fz : (1.0f / B) * (fz - C);
327
328 v[0] = X * ILLUMINANT_D50_XYZ[0];
329 v[1] = Y * ILLUMINANT_D50_XYZ[1];
330 v[2] = Z * ILLUMINANT_D50_XYZ[2];
331
332 return v;
333 }
334
fromXyz(const float3 & v)335 Lab fromXyz(const float3& v) {
336 float X = v[0] / ILLUMINANT_D50_XYZ[0];
337 float Y = v[1] / ILLUMINANT_D50_XYZ[1];
338 float Z = v[2] / ILLUMINANT_D50_XYZ[2];
339
340 float fx = X > A ? pow(X, 1.0f / 3.0f) : B * X + C;
341 float fy = Y > A ? pow(Y, 1.0f / 3.0f) : B * Y + C;
342 float fz = Z > A ? pow(Z, 1.0f / 3.0f) : B * Z + C;
343
344 float L = 116.0f * fy - 16.0f;
345 float a = 500.0f * (fx - fy);
346 float b = 200.0f * (fy - fz);
347
348 return Lab {
349 clamp(L, 0.0f, 100.0f),
350 clamp(a, -128.0f, 128.0f),
351 clamp(b, -128.0f, 128.0f)
352 };
353 }
354
355 };
356
sRGBToLab(SkColor color)357 Lab sRGBToLab(SkColor color) {
358 auto colorSpace = ColorSpace::sRGB();
359 float3 rgb;
360 rgb.r = SkColorGetR(color) / 255.0f;
361 rgb.g = SkColorGetG(color) / 255.0f;
362 rgb.b = SkColorGetB(color) / 255.0f;
363 float3 xyz = colorSpace.rgbToXYZ(rgb);
364 float3 srcXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
365 xyz = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * xyz;
366 return LabColorSpace::fromXyz(xyz);
367 }
368
LabToSRGB(const Lab & lab,SkAlpha alpha)369 SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) {
370 auto colorSpace = ColorSpace::sRGB();
371 float3 xyz = LabColorSpace::toXyz(lab);
372 float3 dstXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
373 xyz = adaptation(BRADFORD, ILLUMINANT_D50_XYZ, dstXYZ) * xyz;
374 float3 rgb = colorSpace.xyzToRGB(xyz);
375 return SkColorSetARGB(alpha,
376 static_cast<uint8_t>(rgb.r * 255),
377 static_cast<uint8_t>(rgb.g * 255),
378 static_cast<uint8_t>(rgb.b * 255));
379 }
380
GetPQSkTransferFunction(float sdr_white_level)381 skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level) {
382 if (sdr_white_level <= 0.f) {
383 sdr_white_level = Properties::defaultSdrWhitePoint;
384 }
385 // The generic PQ transfer function produces normalized luminance values i.e.
386 // the range 0-1 represents 0-10000 nits for the reference display, but we
387 // want to map 1.0 to |sdr_white_level| nits so we need to scale accordingly.
388 const double w = 10000. / sdr_white_level;
389 // Distribute scaling factor W by scaling A and B with X ^ (1/F):
390 // ((A + Bx^C) / (D + Ex^C))^F * W = ((A + Bx^C) / (D + Ex^C) * W^(1/F))^F
391 // See https://crbug.com/1058580#c32 for discussion.
392 skcms_TransferFunction fn = SkNamedTransferFn::kPQ;
393 const double ws = pow(w, 1. / fn.f);
394 fn.a = ws * fn.a;
395 fn.b = ws * fn.b;
396 return fn;
397 }
398
399 // Skia skcms' default HLG maps encoded [0, 1] to linear [1, 12] in order to follow ARIB
400 // but LinearEffect expects a decoded [0, 1] range instead to follow Rec 2100.
GetHLGScaleTransferFunction()401 std::optional<skcms_TransferFunction> GetHLGScaleTransferFunction() {
402 skcms_TransferFunction hlgFn;
403 if (skcms_TransferFunction_makeScaledHLGish(&hlgFn, 1.f / 12.f, 2.f, 2.f, 1.f / 0.17883277f,
404 0.28466892f, 0.55991073f)) {
405 return std::make_optional<skcms_TransferFunction>(hlgFn);
406 }
407 return {};
408 }
409
410 } // namespace uirenderer
411 } // namespace android
412