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 #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 31 namespace android { 32 33 class ColorSpace { 34 public: 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 */ fromLinear(const float3 & v)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 */ toLinear(const float3 & v)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 */ xyzToRGB(const float3 & xyz)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 */ rgbToXYZ(const float3 & rgb)179 constexpr float3 rgbToXYZ(const float3& rgb) const noexcept { 180 return mRGBtoXYZ * toLinear(rgb); 181 } 182 getName()183 constexpr const std::string& getName() const noexcept { 184 return mName; 185 } 186 getRGBtoXYZ()187 constexpr const mat3& getRGBtoXYZ() const noexcept { 188 return mRGBtoXYZ; 189 } 190 getXYZtoRGB()191 constexpr const mat3& getXYZtoRGB() const noexcept { 192 return mXYZtoRGB; 193 } 194 getOETF()195 constexpr const transfer_function& getOETF() const noexcept { 196 return mOETF; 197 } 198 getEOTF()199 constexpr const transfer_function& getEOTF() const noexcept { 200 return mEOTF; 201 } 202 getClamper()203 constexpr const clamping_function& getClamper() const noexcept { 204 return mClamper; 205 } 206 getPrimaries()207 constexpr const std::array<float2, 3>& getPrimaries() const noexcept { 208 return mPrimaries; 209 } 210 getWhitePoint()211 constexpr const float2& getWhitePoint() const noexcept { 212 return mWhitePoint; 213 } 214 getTransferParameters()215 constexpr const TransferParameters& getTransferParameters() const noexcept { 216 return mParameters; 217 } 218 219 /** 220 * Converts the supplied XYZ value to xyY. 221 */ xyY(const float3 & XYZ)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 */ XYZ(const float3 & xyY)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 256 private: 257 static constexpr mat3 computeXYZMatrix( 258 const std::array<float2, 3>& primaries, const float2& whitePoint); 259 linearResponse(float v)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 278 class ColorSpaceConnector { 279 public: 280 ColorSpaceConnector(const ColorSpace& src, const ColorSpace& dst) noexcept; 281 getSource()282 constexpr const ColorSpace& getSource() const noexcept { return mSource; } getDestination()283 constexpr const ColorSpace& getDestination() const noexcept { return mDestination; } 284 getTransform()285 constexpr const mat3& getTransform() const noexcept { return mTransform; } 286 transform(const float3 & v)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 transformLinear(const float3 & v)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 297 private: 298 ColorSpace mSource; 299 ColorSpace mDestination; 300 mat3 mTransform; 301 }; 302 303 }; // namespace android 304 305 #endif // ANDROID_UI_COLOR_SPACE 306