blob: a3e7859575265a051f4d07d0865cc22ce61aefa6 [file] [log] [blame]
Romain Guycaaaa662017-03-27 00:40:21 -07001/*
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
Derek Sollenbergerbe3876c2018-04-20 16:13:31 -040019#include <utils/Log.h>
John Reck339cf9b2018-07-18 16:32:27 -070020#include <ui/ColorSpace.h>
21
22#include <algorithm>
Romain Guycaaaa662017-03-27 00:40:21 -070023#include <cmath>
24
25namespace android {
26namespace uirenderer {
27
28static inline bool almostEqual(float a, float b) {
29 return std::abs(a - b) < 1e-2f;
30}
31
32bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace) {
33 if (colorSpace == nullptr) return true;
34 if (colorSpace->isSRGB()) return true;
35
36 SkColorSpaceTransferFn transferFunction;
37 if (colorSpace->isNumericalTransferFn(&transferFunction)) {
38 // sRGB transfer function params:
39 const float sRGBParamA = 1 / 1.055f;
40 const float sRGBParamB = 0.055f / 1.055f;
41 const float sRGBParamC = 1 / 12.92f;
42 const float sRGBParamD = 0.04045f;
43 const float sRGBParamE = 0.0f;
44 const float sRGBParamF = 0.0f;
45 const float sRGBParamG = 2.4f;
46
47 // This comparison will catch Display P3
John Reck1bcacfd2017-11-03 10:12:19 -070048 return almostEqual(sRGBParamA, transferFunction.fA) &&
49 almostEqual(sRGBParamB, transferFunction.fB) &&
50 almostEqual(sRGBParamC, transferFunction.fC) &&
51 almostEqual(sRGBParamD, transferFunction.fD) &&
52 almostEqual(sRGBParamE, transferFunction.fE) &&
53 almostEqual(sRGBParamF, transferFunction.fF) &&
54 almostEqual(sRGBParamG, transferFunction.fG);
Romain Guycaaaa662017-03-27 00:40:21 -070055 }
56
57 return false;
58}
59
Derek Sollenbergerbe3876c2018-04-20 16:13:31 -040060sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
61
62 SkColorSpace::Gamut gamut;
63 switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
64 case HAL_DATASPACE_STANDARD_BT709:
65 gamut = SkColorSpace::kSRGB_Gamut;
66 break;
67 case HAL_DATASPACE_STANDARD_BT2020:
68 gamut = SkColorSpace::kRec2020_Gamut;
69 break;
70 case HAL_DATASPACE_STANDARD_DCI_P3:
71 gamut = SkColorSpace::kDCIP3_D65_Gamut;
72 break;
73 case HAL_DATASPACE_STANDARD_ADOBE_RGB:
74 gamut = SkColorSpace::kAdobeRGB_Gamut;
75 break;
76 case HAL_DATASPACE_STANDARD_UNSPECIFIED:
77 return nullptr;
78 case HAL_DATASPACE_STANDARD_BT601_625:
79 case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
80 case HAL_DATASPACE_STANDARD_BT601_525:
81 case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
82 case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
83 case HAL_DATASPACE_STANDARD_BT470M:
84 case HAL_DATASPACE_STANDARD_FILM:
85 default:
86 ALOGW("Unsupported Gamut: %d", dataspace);
87 return nullptr;
88 }
89
90 switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
91 case HAL_DATASPACE_TRANSFER_LINEAR:
92 return SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, gamut);
93 case HAL_DATASPACE_TRANSFER_SRGB:
94 return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, gamut);
95 case HAL_DATASPACE_TRANSFER_GAMMA2_2:
96 return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
97 case HAL_DATASPACE_TRANSFER_GAMMA2_6:
98 return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
99 case HAL_DATASPACE_TRANSFER_GAMMA2_8:
100 return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
101 case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
102 return nullptr;
103 case HAL_DATASPACE_TRANSFER_SMPTE_170M:
104 case HAL_DATASPACE_TRANSFER_ST2084:
105 case HAL_DATASPACE_TRANSFER_HLG:
106 default:
107 ALOGW("Unsupported Gamma: %d", dataspace);
108 return nullptr;
109 }
110}
111
John Reck339cf9b2018-07-18 16:32:27 -0700112template<typename T>
113static constexpr T clamp(T x, T min, T max) {
114 return x < min ? min : x > max ? max : x;
115}
116
117//static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
118static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
119static const mat3 BRADFORD = mat3{
120 float3{ 0.8951f, -0.7502f, 0.0389f},
121 float3{ 0.2664f, 1.7135f, -0.0685f},
122 float3{-0.1614f, 0.0367f, 1.0296f}
123};
124
125static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
126 float3 srcLMS = matrix * srcWhitePoint;
127 float3 dstLMS = matrix * dstWhitePoint;
128 return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
129}
130
131namespace LabColorSpace {
132
133static constexpr float A = 216.0f / 24389.0f;
134static constexpr float B = 841.0f / 108.0f;
135static constexpr float C = 4.0f / 29.0f;
136static constexpr float D = 6.0f / 29.0f;
137
138float3 toXyz(const Lab& lab) {
139 float3 v { lab.L, lab.a, lab.b };
140 v[0] = clamp(v[0], 0.0f, 100.0f);
141 v[1] = clamp(v[1], -128.0f, 128.0f);
142 v[2] = clamp(v[2], -128.0f, 128.0f);
143
144 float fy = (v[0] + 16.0f) / 116.0f;
145 float fx = fy + (v[1] * 0.002f);
146 float fz = fy - (v[2] * 0.005f);
147 float X = fx > D ? fx * fx * fx : (1.0f / B) * (fx - C);
148 float Y = fy > D ? fy * fy * fy : (1.0f / B) * (fy - C);
149 float Z = fz > D ? fz * fz * fz : (1.0f / B) * (fz - C);
150
151 v[0] = X * ILLUMINANT_D50_XYZ[0];
152 v[1] = Y * ILLUMINANT_D50_XYZ[1];
153 v[2] = Z * ILLUMINANT_D50_XYZ[2];
154
155 return v;
156}
157
158Lab fromXyz(const float3& v) {
159 float X = v[0] / ILLUMINANT_D50_XYZ[0];
160 float Y = v[1] / ILLUMINANT_D50_XYZ[1];
161 float Z = v[2] / ILLUMINANT_D50_XYZ[2];
162
163 float fx = X > A ? pow(X, 1.0f / 3.0f) : B * X + C;
164 float fy = Y > A ? pow(Y, 1.0f / 3.0f) : B * Y + C;
165 float fz = Z > A ? pow(Z, 1.0f / 3.0f) : B * Z + C;
166
167 float L = 116.0f * fy - 16.0f;
168 float a = 500.0f * (fx - fy);
169 float b = 200.0f * (fy - fz);
170
171 return Lab {
172 clamp(L, 0.0f, 100.0f),
173 clamp(a, -128.0f, 128.0f),
174 clamp(b, -128.0f, 128.0f)
175 };
176}
177
178};
179
180Lab sRGBToLab(SkColor color) {
181 auto colorSpace = ColorSpace::sRGB();
182 float3 rgb;
183 rgb.r = SkColorGetR(color) / 255.0f;
184 rgb.g = SkColorGetG(color) / 255.0f;
185 rgb.b = SkColorGetB(color) / 255.0f;
186 float3 xyz = colorSpace.rgbToXYZ(rgb);
187 float3 srcXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
188 xyz = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * xyz;
189 return LabColorSpace::fromXyz(xyz);
190}
191
192SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) {
193 auto colorSpace = ColorSpace::sRGB();
194 float3 xyz = LabColorSpace::toXyz(lab);
195 float3 dstXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
196 xyz = adaptation(BRADFORD, ILLUMINANT_D50_XYZ, dstXYZ) * xyz;
197 float3 rgb = colorSpace.xyzToRGB(xyz);
198 return SkColorSetARGB(alpha,
199 static_cast<uint8_t>(rgb.r * 255),
200 static_cast<uint8_t>(rgb.g * 255),
201 static_cast<uint8_t>(rgb.b * 255));
202}
203
John Reck1bcacfd2017-11-03 10:12:19 -0700204}; // namespace uirenderer
205}; // namespace android