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