• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 <ui/ColorSpace.h>
18 
19 using namespace std::placeholders;
20 
21 namespace android {
22 
linearResponse(float v)23 static constexpr float linearResponse(float v) {
24     return v;
25 }
26 
rcpResponse(float x,const ColorSpace::TransferParameters & p)27 static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) {
28     return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c;
29 }
30 
response(float x,const ColorSpace::TransferParameters & p)31 static constexpr float response(float x, const ColorSpace::TransferParameters& p) {
32     return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x;
33 }
34 
rcpFullResponse(float x,const ColorSpace::TransferParameters & p)35 static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) {
36     return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c;
37 }
38 
fullResponse(float x,const ColorSpace::TransferParameters & p)39 static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) {
40     return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f;
41 }
42 
absRcpResponse(float x,float g,float a,float b,float c,float d)43 static float absRcpResponse(float x, float g,float a, float b, float c, float d) {
44     float xx = std::abs(x);
45     return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x);
46 }
47 
absResponse(float x,float g,float a,float b,float c,float d)48 static float absResponse(float x, float g, float a, float b, float c, float d) {
49    float xx = std::abs(x);
50    return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x);
51 }
52 
safePow(float x,float e)53 static float safePow(float x, float e) {
54     return powf(x < 0.0f ? 0.0f : x, e);
55 }
56 
toOETF(const ColorSpace::TransferParameters & parameters)57 static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) {
58     if (parameters.e == 0.0f && parameters.f == 0.0f) {
59         return std::bind(rcpResponse, _1, parameters);
60     }
61     return std::bind(rcpFullResponse, _1, parameters);
62 }
63 
toEOTF(const ColorSpace::TransferParameters & parameters)64 static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) {
65     if (parameters.e == 0.0f && parameters.f == 0.0f) {
66         return std::bind(response, _1, parameters);
67     }
68     return std::bind(fullResponse, _1, parameters);
69 }
70 
toOETF(float gamma)71 static ColorSpace::transfer_function toOETF(float gamma) {
72     if (gamma == 1.0f) {
73         return linearResponse;
74     }
75     return std::bind(safePow, _1, 1.0f / gamma);
76 }
77 
toEOTF(float gamma)78 static ColorSpace::transfer_function toEOTF(float gamma) {
79     if (gamma == 1.0f) {
80         return linearResponse;
81     }
82     return std::bind(safePow, _1, gamma);
83 }
84 
computePrimaries(const mat3 & rgbToXYZ)85 static constexpr std::array<float2, 3> computePrimaries(const mat3& rgbToXYZ) {
86     float3 r(rgbToXYZ * float3{1, 0, 0});
87     float3 g(rgbToXYZ * float3{0, 1, 0});
88     float3 b(rgbToXYZ * float3{0, 0, 1});
89 
90     return {{r.xy / dot(r, float3{1}),
91              g.xy / dot(g, float3{1}),
92              b.xy / dot(b, float3{1})}};
93 }
94 
computeWhitePoint(const mat3 & rgbToXYZ)95 static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) {
96     float3 w(rgbToXYZ * float3{1});
97     return w.xy / dot(w, float3{1});
98 }
99 
ColorSpace(const std::string & name,const mat3 & rgbToXYZ,transfer_function OETF,transfer_function EOTF,clamping_function clamper)100 ColorSpace::ColorSpace(
101         const std::string& name,
102         const mat3& rgbToXYZ,
103         transfer_function OETF,
104         transfer_function EOTF,
105         clamping_function clamper) noexcept
106         : mName(name)
107         , mRGBtoXYZ(rgbToXYZ)
108         , mXYZtoRGB(inverse(rgbToXYZ))
109         , mOETF(std::move(OETF))
110         , mEOTF(std::move(EOTF))
111         , mClamper(std::move(clamper))
112         , mPrimaries(computePrimaries(rgbToXYZ))
113         , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
114 }
115 
ColorSpace(const std::string & name,const mat3 & rgbToXYZ,const TransferParameters parameters,clamping_function clamper)116 ColorSpace::ColorSpace(
117         const std::string& name,
118         const mat3& rgbToXYZ,
119         const TransferParameters parameters,
120         clamping_function clamper) noexcept
121         : mName(name)
122         , mRGBtoXYZ(rgbToXYZ)
123         , mXYZtoRGB(inverse(rgbToXYZ))
124         , mParameters(parameters)
125         , mOETF(toOETF(mParameters))
126         , mEOTF(toEOTF(mParameters))
127         , mClamper(std::move(clamper))
128         , mPrimaries(computePrimaries(rgbToXYZ))
129         , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
130 }
131 
ColorSpace(const std::string & name,const mat3 & rgbToXYZ,float gamma,clamping_function clamper)132 ColorSpace::ColorSpace(
133         const std::string& name,
134         const mat3& rgbToXYZ,
135         float gamma,
136         clamping_function clamper) noexcept
137         : mName(name)
138         , mRGBtoXYZ(rgbToXYZ)
139         , mXYZtoRGB(inverse(rgbToXYZ))
140         , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
141         , mOETF(toOETF(gamma))
142         , mEOTF(toEOTF(gamma))
143         , mClamper(std::move(clamper))
144         , mPrimaries(computePrimaries(rgbToXYZ))
145         , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
146 }
147 
ColorSpace(const std::string & name,const std::array<float2,3> & primaries,const float2 & whitePoint,transfer_function OETF,transfer_function EOTF,clamping_function clamper)148 ColorSpace::ColorSpace(
149         const std::string& name,
150         const std::array<float2, 3>& primaries,
151         const float2& whitePoint,
152         transfer_function OETF,
153         transfer_function EOTF,
154         clamping_function clamper) noexcept
155         : mName(name)
156         , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
157         , mXYZtoRGB(inverse(mRGBtoXYZ))
158         , mOETF(std::move(OETF))
159         , mEOTF(std::move(EOTF))
160         , mClamper(std::move(clamper))
161         , mPrimaries(primaries)
162         , mWhitePoint(whitePoint) {
163 }
164 
ColorSpace(const std::string & name,const std::array<float2,3> & primaries,const float2 & whitePoint,const TransferParameters parameters,clamping_function clamper)165 ColorSpace::ColorSpace(
166         const std::string& name,
167         const std::array<float2, 3>& primaries,
168         const float2& whitePoint,
169         const TransferParameters parameters,
170         clamping_function clamper) noexcept
171         : mName(name)
172         , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
173         , mXYZtoRGB(inverse(mRGBtoXYZ))
174         , mParameters(parameters)
175         , mOETF(toOETF(mParameters))
176         , mEOTF(toEOTF(mParameters))
177         , mClamper(std::move(clamper))
178         , mPrimaries(primaries)
179         , mWhitePoint(whitePoint) {
180 }
181 
ColorSpace(const std::string & name,const std::array<float2,3> & primaries,const float2 & whitePoint,float gamma,clamping_function clamper)182 ColorSpace::ColorSpace(
183         const std::string& name,
184         const std::array<float2, 3>& primaries,
185         const float2& whitePoint,
186         float gamma,
187         clamping_function clamper) noexcept
188         : mName(name)
189         , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
190         , mXYZtoRGB(inverse(mRGBtoXYZ))
191         , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
192         , mOETF(toOETF(gamma))
193         , mEOTF(toEOTF(gamma))
194         , mClamper(std::move(clamper))
195         , mPrimaries(primaries)
196         , mWhitePoint(whitePoint) {
197 }
198 
computeXYZMatrix(const std::array<float2,3> & primaries,const float2 & whitePoint)199 constexpr mat3 ColorSpace::computeXYZMatrix(
200         const std::array<float2, 3>& primaries, const float2& whitePoint) {
201     const float2& R = primaries[0];
202     const float2& G = primaries[1];
203     const float2& B = primaries[2];
204     const float2& W = whitePoint;
205 
206     float oneRxRy = (1 - R.x) / R.y;
207     float oneGxGy = (1 - G.x) / G.y;
208     float oneBxBy = (1 - B.x) / B.y;
209     float oneWxWy = (1 - W.x) / W.y;
210 
211     float RxRy = R.x / R.y;
212     float GxGy = G.x / G.y;
213     float BxBy = B.x / B.y;
214     float WxWy = W.x / W.y;
215 
216     float BY =
217             ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) /
218             ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy));
219     float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy);
220     float RY = 1 - GY - BY;
221 
222     float RYRy = RY / R.y;
223     float GYGy = GY / G.y;
224     float BYBy = BY / B.y;
225 
226     return {
227         float3{RYRy * R.x, RY, RYRy * (1 - R.x - R.y)},
228         float3{GYGy * G.x, GY, GYGy * (1 - G.x - G.y)},
229         float3{BYBy * B.x, BY, BYBy * (1 - B.x - B.y)}
230     };
231 }
232 
sRGB()233 const ColorSpace ColorSpace::sRGB() {
234     return {
235         "sRGB IEC61966-2.1",
236         {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
237         {0.3127f, 0.3290f},
238         {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f}
239     };
240 }
241 
linearSRGB()242 const ColorSpace ColorSpace::linearSRGB() {
243     return {
244         "sRGB IEC61966-2.1 (Linear)",
245         {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
246         {0.3127f, 0.3290f}
247     };
248 }
249 
extendedSRGB()250 const ColorSpace ColorSpace::extendedSRGB() {
251     return {
252         "scRGB-nl IEC 61966-2-2:2003",
253         {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
254         {0.3127f, 0.3290f},
255         std::bind(absRcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
256         std::bind(absResponse,    _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
257         std::bind(clamp<float>, _1, -0.799f, 2.399f)
258     };
259 }
260 
linearExtendedSRGB()261 const ColorSpace ColorSpace::linearExtendedSRGB() {
262     return {
263         "scRGB IEC 61966-2-2:2003",
264         {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
265         {0.3127f, 0.3290f},
266         1.0f,
267         std::bind(clamp<float>, _1, -0.5f, 7.499f)
268     };
269 }
270 
NTSC()271 const ColorSpace ColorSpace::NTSC() {
272     return {
273         "NTSC (1953)",
274         {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}},
275         {0.310f, 0.316f},
276         {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
277     };
278 }
279 
BT709()280 const ColorSpace ColorSpace::BT709() {
281     return {
282         "Rec. ITU-R BT.709-5",
283         {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
284         {0.3127f, 0.3290f},
285         {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
286     };
287 }
288 
BT2020()289 const ColorSpace ColorSpace::BT2020() {
290     return {
291         "Rec. ITU-R BT.2020-1",
292         {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}},
293         {0.3127f, 0.3290f},
294         {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
295     };
296 }
297 
AdobeRGB()298 const ColorSpace ColorSpace::AdobeRGB() {
299     return {
300         "Adobe RGB (1998)",
301         {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}},
302         {0.3127f, 0.3290f},
303         2.2f
304     };
305 }
306 
ProPhotoRGB()307 const ColorSpace ColorSpace::ProPhotoRGB() {
308     return {
309         "ROMM RGB ISO 22028-2:2013",
310         {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}},
311         {0.34567f, 0.35850f},
312         {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f}
313     };
314 }
315 
DisplayP3()316 const ColorSpace ColorSpace::DisplayP3() {
317     return {
318         "Display P3",
319         {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
320         {0.3127f, 0.3290f},
321         {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f}
322     };
323 }
324 
DCIP3()325 const ColorSpace ColorSpace::DCIP3() {
326     return {
327         "SMPTE RP 431-2-2007 DCI (P3)",
328         {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
329         {0.314f, 0.351f},
330         2.6f
331     };
332 }
333 
ACES()334 const ColorSpace ColorSpace::ACES() {
335     return {
336         "SMPTE ST 2065-1:2012 ACES",
337         {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}},
338         {0.32168f, 0.33767f},
339         1.0f,
340         std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
341     };
342 }
343 
ACEScg()344 const ColorSpace ColorSpace::ACEScg() {
345     return {
346         "Academy S-2014-004 ACEScg",
347         {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}},
348         {0.32168f, 0.33767f},
349         1.0f,
350         std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
351     };
352 }
353 
createLUT(uint32_t size,const ColorSpace & src,const ColorSpace & dst)354 std::unique_ptr<float3[]> ColorSpace::createLUT(uint32_t size, const ColorSpace& src,
355                                                 const ColorSpace& dst) {
356     size = clamp(size, 2u, 256u);
357     float m = 1.0f / float(size - 1);
358 
359     std::unique_ptr<float3[]> lut(new float3[size * size * size]);
360     float3* data = lut.get();
361 
362     ColorSpaceConnector connector(src, dst);
363 
364     for (uint32_t z = 0; z < size; z++) {
365         for (int32_t y = int32_t(size - 1); y >= 0; y--) {
366             for (uint32_t x = 0; x < size; x++) {
367                 *data++ = connector.transform({x * m, y * m, z * m});
368             }
369         }
370     }
371 
372     return lut;
373 }
374 
375 static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
376 static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
377 static const mat3 BRADFORD = mat3{
378     float3{ 0.8951f, -0.7502f,  0.0389f},
379     float3{ 0.2664f,  1.7135f, -0.0685f},
380     float3{-0.1614f,  0.0367f,  1.0296f}
381 };
382 
adaptation(const mat3 & matrix,const float3 & srcWhitePoint,const float3 & dstWhitePoint)383 static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
384     float3 srcLMS = matrix * srcWhitePoint;
385     float3 dstLMS = matrix * dstWhitePoint;
386     return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
387 }
388 
ColorSpaceConnector(const ColorSpace & src,const ColorSpace & dst)389 ColorSpaceConnector::ColorSpaceConnector(
390         const ColorSpace& src,
391         const ColorSpace& dst) noexcept
392         : mSource(src)
393         , mDestination(dst) {
394 
395     if (all(lessThan(abs(src.getWhitePoint() - dst.getWhitePoint()), float2{1e-3f}))) {
396         mTransform = dst.getXYZtoRGB() * src.getRGBtoXYZ();
397     } else {
398         mat3 rgbToXYZ(src.getRGBtoXYZ());
399         mat3 xyzToRGB(dst.getXYZtoRGB());
400 
401         float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1});
402         float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1});
403 
404         if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
405             rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ();
406         }
407 
408         if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
409             xyzToRGB = inverse(adaptation(BRADFORD, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ());
410         }
411 
412         mTransform = xyzToRGB * rgbToXYZ;
413     }
414 }
415 
416 }; // namespace android
417