1781a6f09d9b0bc212842aee67c86b79cf2593e69Jiwen 'Steve' Cai/*
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#ifndef ANDROID_UI_COLOR_SPACE
18#define ANDROID_UI_COLOR_SPACE
19
20#include <array>
21#include <cmath>
22#include <functional>
23#include <memory>
24#include <string>
25
26#include <math/mat3.h>
27#include <math/scalar.h>
28#include <math/vec2.h>
29#include <math/vec3.h>
30
31namespace android {
32
33class ColorSpace {
34public:
35    typedef std::function<float(float)> transfer_function;
36    typedef std::function<float(float)> clamping_function;
37
38    struct TransferParameters {
39        float g = 0.0f;
40        float a = 0.0f;
41        float b = 0.0f;
42        float c = 0.0f;
43        float d = 0.0f;
44        float e = 0.0f;
45        float f = 0.0f;
46    };
47
48    /**
49     * Creates a named color space with the specified RGB->XYZ
50     * conversion matrix. The white point and primaries will be
51     * computed from the supplied matrix.
52     *
53     * The default transfer functions are a linear response x->x
54     * and the default clamping function is a simple saturate
55     * (clamp(x, 0, 1)).
56     */
57    ColorSpace(
58            const std::string& name,
59            const mat3& rgbToXYZ,
60            transfer_function OETF = linearResponse,
61            transfer_function EOTF = linearResponse,
62            clamping_function clamper = saturate<float>
63    ) noexcept;
64
65    /**
66     * Creates a named color space with the specified RGB->XYZ
67     * conversion matrix. The white point and primaries will be
68     * computed from the supplied matrix.
69     *
70     * The transfer functions are defined by the set of supplied
71     * transfer parameters. The default clamping function is a
72     * simple saturate (clamp(x, 0, 1)).
73     */
74    ColorSpace(
75            const std::string& name,
76            const mat3& rgbToXYZ,
77            const TransferParameters parameters,
78            clamping_function clamper = saturate<float>
79    ) noexcept;
80
81    /**
82     * Creates a named color space with the specified RGB->XYZ
83     * conversion matrix. The white point and primaries will be
84     * computed from the supplied matrix.
85     *
86     * The transfer functions are defined by a simple gamma value.
87     * The default clamping function is a saturate (clamp(x, 0, 1)).
88     */
89    ColorSpace(
90            const std::string& name,
91            const mat3& rgbToXYZ,
92            float gamma,
93            clamping_function clamper = saturate<float>
94    ) noexcept;
95
96    /**
97     * Creates a named color space with the specified primaries
98     * and white point. The RGB<>XYZ conversion matrices are
99     * computed from the primaries and white point.
100     *
101     * The default transfer functions are a linear response x->x
102     * and the default clamping function is a simple saturate
103     * (clamp(x, 0, 1)).
104     */
105    ColorSpace(
106            const std::string& name,
107            const std::array<float2, 3>& primaries,
108            const float2& whitePoint,
109            transfer_function OETF = linearResponse,
110            transfer_function EOTF = linearResponse,
111            clamping_function clamper = saturate<float>
112    ) noexcept;
113
114    /**
115     * Creates a named color space with the specified primaries
116     * and white point. The RGB<>XYZ conversion matrices are
117     * computed from the primaries and white point.
118     *
119     * The transfer functions are defined by the set of supplied
120     * transfer parameters. The default clamping function is a
121     * simple saturate (clamp(x, 0, 1)).
122     */
123    ColorSpace(
124            const std::string& name,
125            const std::array<float2, 3>& primaries,
126            const float2& whitePoint,
127            const TransferParameters parameters,
128            clamping_function clamper = saturate<float>
129    ) noexcept;
130
131    /**
132     * Creates a named color space with the specified primaries
133     * and white point. The RGB<>XYZ conversion matrices are
134     * computed from the primaries and white point.
135     *
136     * The transfer functions are defined by a single gamma value.
137     * The default clamping function is a saturate (clamp(x, 0, 1)).
138     */
139    ColorSpace(
140            const std::string& name,
141            const std::array<float2, 3>& primaries,
142            const float2& whitePoint,
143            float gamma,
144            clamping_function clamper = saturate<float>
145    ) noexcept;
146
147    ColorSpace() noexcept = delete;
148
149    /**
150     * Encodes the supplied RGB value using this color space's
151     * opto-electronic transfer function.
152     */
153    constexpr float3 fromLinear(const float3& v) const noexcept {
154        return apply(v, mOETF);
155    }
156
157    /**
158     * Decodes the supplied RGB value using this color space's
159     * electro-optical transfer function.
160     */
161    constexpr float3 toLinear(const float3& v) const noexcept {
162        return apply(v, mEOTF);
163    }
164
165    /**
166     * Converts the supplied XYZ value to RGB. The returned value
167     * is encoded with this color space's opto-electronic transfer
168     * function and clamped by this color space's clamping function.
169     */
170    constexpr float3 xyzToRGB(const float3& xyz) const noexcept {
171        return apply(fromLinear(mXYZtoRGB * xyz), mClamper);
172    }
173
174    /**
175     * Converts the supplied RGB value to XYZ. The input RGB value
176     * is decoded using this color space's electro-optical function
177     * before being converted to XYZ.
178     */
179    constexpr float3 rgbToXYZ(const float3& rgb) const noexcept {
180        return mRGBtoXYZ * toLinear(rgb);
181    }
182
183    constexpr const std::string& getName() const noexcept {
184        return mName;
185    }
186
187    constexpr const mat3& getRGBtoXYZ() const noexcept {
188        return mRGBtoXYZ;
189    }
190
191    constexpr const mat3& getXYZtoRGB() const noexcept {
192        return mXYZtoRGB;
193    }
194
195    constexpr const transfer_function& getOETF() const noexcept {
196        return mOETF;
197    }
198
199    constexpr const transfer_function& getEOTF() const noexcept {
200        return mEOTF;
201    }
202
203    constexpr const clamping_function& getClamper() const noexcept {
204        return mClamper;
205    }
206
207    constexpr const std::array<float2, 3>& getPrimaries() const noexcept {
208        return mPrimaries;
209    }
210
211    constexpr const float2& getWhitePoint() const noexcept {
212        return mWhitePoint;
213    }
214
215    constexpr const TransferParameters& getTransferParameters() const noexcept {
216        return mParameters;
217    }
218
219    /**
220     * Converts the supplied XYZ value to xyY.
221     */
222    static constexpr float2 xyY(const float3& XYZ) {
223        return XYZ.xy / dot(XYZ, float3{1});
224    }
225
226    /**
227     * Converts the supplied xyY value to XYZ.
228     */
229    static constexpr float3 XYZ(const float3& xyY) {
230        return float3{(xyY.x * xyY.z) / xyY.y, xyY.z, ((1 - xyY.x - xyY.y) * xyY.z) / xyY.y};
231    }
232
233    static const ColorSpace sRGB();
234    static const ColorSpace linearSRGB();
235    static const ColorSpace extendedSRGB();
236    static const ColorSpace linearExtendedSRGB();
237    static const ColorSpace NTSC();
238    static const ColorSpace BT709();
239    static const ColorSpace BT2020();
240    static const ColorSpace AdobeRGB();
241    static const ColorSpace ProPhotoRGB();
242    static const ColorSpace DisplayP3();
243    static const ColorSpace DCIP3();
244    static const ColorSpace ACES();
245    static const ColorSpace ACEScg();
246
247    // Creates a NxNxN 3D LUT, where N is the specified size (min=2, max=256)
248    // The 3D lookup coordinates map to the RGB components: u=R, v=G, w=B
249    // The generated 3D LUT is meant to be used as a 3D texture and its Y
250    // axis is thus already flipped
251    // The source color space must define its values in the domain [0..1]
252    // The generated LUT transforms from gamma space to gamma space
253    static std::unique_ptr<float3> createLUT(uint32_t size,
254            const ColorSpace& src, const ColorSpace& dst);
255
256private:
257    static constexpr mat3 computeXYZMatrix(
258            const std::array<float2, 3>& primaries, const float2& whitePoint);
259
260    static constexpr float linearResponse(float v) {
261        return v;
262    }
263
264    std::string mName;
265
266    mat3 mRGBtoXYZ;
267    mat3 mXYZtoRGB;
268
269    TransferParameters mParameters;
270    transfer_function mOETF;
271    transfer_function mEOTF;
272    clamping_function mClamper;
273
274    std::array<float2, 3> mPrimaries;
275    float2 mWhitePoint;
276};
277
278class ColorSpaceConnector {
279public:
280    ColorSpaceConnector(const ColorSpace& src, const ColorSpace& dst) noexcept;
281
282    constexpr const ColorSpace& getSource() const noexcept { return mSource; }
283    constexpr const ColorSpace& getDestination() const noexcept { return mDestination; }
284
285    constexpr const mat3& getTransform() const noexcept { return mTransform; }
286
287    constexpr float3 transform(const float3& v) const noexcept {
288        float3 linear = mSource.toLinear(apply(v, mSource.getClamper()));
289        return apply(mDestination.fromLinear(mTransform * linear), mDestination.getClamper());
290    }
291
292    constexpr float3 transformLinear(const float3& v) const noexcept {
293        float3 linear = apply(v, mSource.getClamper());
294        return apply(mTransform * linear, mDestination.getClamper());
295    }
296
297private:
298    ColorSpace mSource;
299    ColorSpace mDestination;
300    mat3 mTransform;
301};
302
303}; // namespace android
304
305#endif // ANDROID_UI_COLOR_SPACE
306