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 <utils/Log.h>
20 #include <ui/ColorSpace.h>
21
22 #include <algorithm>
23 #include <cmath>
24
25 namespace android {
26 namespace uirenderer {
27
ColorTypeToPixelFormat(SkColorType colorType)28 android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) {
29 switch (colorType) {
30 case kRGBA_8888_SkColorType:
31 return PIXEL_FORMAT_RGBA_8888;
32 case kRGBA_F16_SkColorType:
33 return PIXEL_FORMAT_RGBA_FP16;
34 case kRGB_565_SkColorType:
35 return PIXEL_FORMAT_RGB_565;
36 case kRGB_888x_SkColorType:
37 return PIXEL_FORMAT_RGBX_8888;
38 case kRGBA_1010102_SkColorType:
39 return PIXEL_FORMAT_RGBA_1010102;
40 case kARGB_4444_SkColorType:
41 return PIXEL_FORMAT_RGBA_4444;
42 default:
43 ALOGV("Unsupported colorType: %d, return RGBA_8888 by default", (int)colorType);
44 return PIXEL_FORMAT_RGBA_8888;
45 }
46 }
47
PixelFormatToColorType(android::PixelFormat format)48 SkColorType PixelFormatToColorType(android::PixelFormat format) {
49 switch (format) {
50 case PIXEL_FORMAT_RGBX_8888: return kRGB_888x_SkColorType;
51 case PIXEL_FORMAT_RGBA_8888: return kRGBA_8888_SkColorType;
52 case PIXEL_FORMAT_RGBA_FP16: return kRGBA_F16_SkColorType;
53 case PIXEL_FORMAT_RGB_565: return kRGB_565_SkColorType;
54 case PIXEL_FORMAT_RGBA_1010102: return kRGBA_1010102_SkColorType;
55 case PIXEL_FORMAT_RGBA_4444: return kARGB_4444_SkColorType;
56 default:
57 ALOGV("Unsupported PixelFormat: %d, return kUnknown_SkColorType by default", format);
58 return kUnknown_SkColorType;
59 }
60 }
61
DataSpaceToColorSpace(android_dataspace dataspace)62 sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
63 if (dataspace == HAL_DATASPACE_UNKNOWN) {
64 return SkColorSpace::MakeSRGB();
65 }
66
67 skcms_Matrix3x3 gamut;
68 switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
69 case HAL_DATASPACE_STANDARD_BT709:
70 gamut = SkNamedGamut::kSRGB;
71 break;
72 case HAL_DATASPACE_STANDARD_BT2020:
73 gamut = SkNamedGamut::kRec2020;
74 break;
75 case HAL_DATASPACE_STANDARD_DCI_P3:
76 gamut = SkNamedGamut::kDCIP3;
77 break;
78 case HAL_DATASPACE_STANDARD_ADOBE_RGB:
79 gamut = SkNamedGamut::kAdobeRGB;
80 break;
81 case HAL_DATASPACE_STANDARD_UNSPECIFIED:
82 return nullptr;
83 case HAL_DATASPACE_STANDARD_BT601_625:
84 case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
85 case HAL_DATASPACE_STANDARD_BT601_525:
86 case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
87 case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
88 case HAL_DATASPACE_STANDARD_BT470M:
89 case HAL_DATASPACE_STANDARD_FILM:
90 default:
91 ALOGV("Unsupported Gamut: %d", dataspace);
92 return nullptr;
93 }
94
95 switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
96 case HAL_DATASPACE_TRANSFER_LINEAR:
97 return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
98 case HAL_DATASPACE_TRANSFER_SRGB:
99 return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
100 case HAL_DATASPACE_TRANSFER_GAMMA2_2:
101 return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
102 case HAL_DATASPACE_TRANSFER_GAMMA2_6:
103 return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
104 case HAL_DATASPACE_TRANSFER_GAMMA2_8:
105 return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
106 case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
107 return nullptr;
108 case HAL_DATASPACE_TRANSFER_SMPTE_170M:
109 case HAL_DATASPACE_TRANSFER_ST2084:
110 case HAL_DATASPACE_TRANSFER_HLG:
111 default:
112 ALOGV("Unsupported Gamma: %d", dataspace);
113 return nullptr;
114 }
115 }
116
117 template<typename T>
clamp(T x,T min,T max)118 static constexpr T clamp(T x, T min, T max) {
119 return x < min ? min : x > max ? max : x;
120 }
121
122 //static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
123 static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
124 static const mat3 BRADFORD = mat3{
125 float3{ 0.8951f, -0.7502f, 0.0389f},
126 float3{ 0.2664f, 1.7135f, -0.0685f},
127 float3{-0.1614f, 0.0367f, 1.0296f}
128 };
129
adaptation(const mat3 & matrix,const float3 & srcWhitePoint,const float3 & dstWhitePoint)130 static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
131 float3 srcLMS = matrix * srcWhitePoint;
132 float3 dstLMS = matrix * dstWhitePoint;
133 return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
134 }
135
136 namespace LabColorSpace {
137
138 static constexpr float A = 216.0f / 24389.0f;
139 static constexpr float B = 841.0f / 108.0f;
140 static constexpr float C = 4.0f / 29.0f;
141 static constexpr float D = 6.0f / 29.0f;
142
toXyz(const Lab & lab)143 float3 toXyz(const Lab& lab) {
144 float3 v { lab.L, lab.a, lab.b };
145 v[0] = clamp(v[0], 0.0f, 100.0f);
146 v[1] = clamp(v[1], -128.0f, 128.0f);
147 v[2] = clamp(v[2], -128.0f, 128.0f);
148
149 float fy = (v[0] + 16.0f) / 116.0f;
150 float fx = fy + (v[1] * 0.002f);
151 float fz = fy - (v[2] * 0.005f);
152 float X = fx > D ? fx * fx * fx : (1.0f / B) * (fx - C);
153 float Y = fy > D ? fy * fy * fy : (1.0f / B) * (fy - C);
154 float Z = fz > D ? fz * fz * fz : (1.0f / B) * (fz - C);
155
156 v[0] = X * ILLUMINANT_D50_XYZ[0];
157 v[1] = Y * ILLUMINANT_D50_XYZ[1];
158 v[2] = Z * ILLUMINANT_D50_XYZ[2];
159
160 return v;
161 }
162
fromXyz(const float3 & v)163 Lab fromXyz(const float3& v) {
164 float X = v[0] / ILLUMINANT_D50_XYZ[0];
165 float Y = v[1] / ILLUMINANT_D50_XYZ[1];
166 float Z = v[2] / ILLUMINANT_D50_XYZ[2];
167
168 float fx = X > A ? pow(X, 1.0f / 3.0f) : B * X + C;
169 float fy = Y > A ? pow(Y, 1.0f / 3.0f) : B * Y + C;
170 float fz = Z > A ? pow(Z, 1.0f / 3.0f) : B * Z + C;
171
172 float L = 116.0f * fy - 16.0f;
173 float a = 500.0f * (fx - fy);
174 float b = 200.0f * (fy - fz);
175
176 return Lab {
177 clamp(L, 0.0f, 100.0f),
178 clamp(a, -128.0f, 128.0f),
179 clamp(b, -128.0f, 128.0f)
180 };
181 }
182
183 };
184
sRGBToLab(SkColor color)185 Lab sRGBToLab(SkColor color) {
186 auto colorSpace = ColorSpace::sRGB();
187 float3 rgb;
188 rgb.r = SkColorGetR(color) / 255.0f;
189 rgb.g = SkColorGetG(color) / 255.0f;
190 rgb.b = SkColorGetB(color) / 255.0f;
191 float3 xyz = colorSpace.rgbToXYZ(rgb);
192 float3 srcXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
193 xyz = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * xyz;
194 return LabColorSpace::fromXyz(xyz);
195 }
196
LabToSRGB(const Lab & lab,SkAlpha alpha)197 SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) {
198 auto colorSpace = ColorSpace::sRGB();
199 float3 xyz = LabColorSpace::toXyz(lab);
200 float3 dstXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
201 xyz = adaptation(BRADFORD, ILLUMINANT_D50_XYZ, dstXYZ) * xyz;
202 float3 rgb = colorSpace.xyzToRGB(xyz);
203 return SkColorSetARGB(alpha,
204 static_cast<uint8_t>(rgb.r * 255),
205 static_cast<uint8_t>(rgb.g * 255),
206 static_cast<uint8_t>(rgb.b * 255));
207 }
208
209 } // namespace uirenderer
210 } // namespace android
211