1 /* 2 * Copyright 2016 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 #ifndef SkColorSpacePriv_DEFINED 8 #define SkColorSpacePriv_DEFINED 9 10 #include <math.h> 11 12 #include "SkColorSpace.h" 13 #include "SkFixed.h" 14 15 #define SkColorSpacePrintf(...) 16 17 static constexpr float gSRGB_toXYZD50[] { 18 0.4360747f, 0.3850649f, 0.1430804f, // Rx, Gx, Bx 19 0.2225045f, 0.7168786f, 0.0606169f, // Ry, Gy, Gz 20 0.0139322f, 0.0971045f, 0.7141733f, // Rz, Gz, Bz 21 }; 22 23 static constexpr float gAdobeRGB_toXYZD50[] { 24 #ifdef SK_SUPPORT_LEGACY_ADOBE_XYZ 25 0.6097559f, 0.2052401f, 0.1492240f, // Rx, Gx, Bx 26 0.3111242f, 0.6256560f, 0.0632197f, // Ry, Gy, Gz 27 0.0194811f, 0.0608902f, 0.7448387f, // Rz, Gz, Bz 28 #else 29 // ICC fixed-point (16.16) repesentation of: 30 // 0.60974, 0.20528, 0.14919, 31 // 0.31111, 0.62567, 0.06322, 32 // 0.01947, 0.06087, 0.74457, 33 SkFixedToFloat(0x9c18), SkFixedToFloat(0x348d), SkFixedToFloat(0x2631), // Rx, Gx, Bx 34 SkFixedToFloat(0x4fa5), SkFixedToFloat(0xa02c), SkFixedToFloat(0x102f), // Ry, Gy, Gz 35 SkFixedToFloat(0x04fc), SkFixedToFloat(0x0f95), SkFixedToFloat(0xbe9c), // Rz, Gz, Bz 36 #endif 37 }; 38 39 static constexpr float gDCIP3_toXYZD50[] { 40 0.515102f, 0.291965f, 0.157153f, // Rx, Gx, Bx 41 0.241182f, 0.692236f, 0.0665819f, // Ry, Gy, Gz 42 -0.00104941f, 0.0418818f, 0.784378f, // Rz, Gz, Bz 43 }; 44 45 static constexpr float gRec2020_toXYZD50[] { 46 0.673459f, 0.165661f, 0.125100f, // Rx, Gx, Bx 47 0.279033f, 0.675338f, 0.0456288f, // Ry, Gy, Gz 48 -0.00193139f, 0.0299794f, 0.797162f, // Rz, Gz, Bz 49 }; 50 51 static constexpr SkColorSpaceTransferFn gSRGB_TransferFn = 52 { 2.4f, 1.0f / 1.055f, 0.055f / 1.055f, 1.0f / 12.92f, 0.04045f, 0.0f, 0.0f }; 53 54 static constexpr SkColorSpaceTransferFn g2Dot2_TransferFn = 55 { 2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; 56 57 // gLinear_TransferFn.fD > 1.0f: Make sure that we use the linear segment of 58 // the transfer function even when the x-value is 1.0f. 59 static constexpr SkColorSpaceTransferFn gLinear_TransferFn = 60 { 0.0f, 0.0f, 0.0f, 1.0f, 1.0000001f, 0.0f, 0.0f }; 61 62 static constexpr SkColorSpaceTransferFn gDCIP3_TransferFn = 63 { 2.399994f, 0.947998047f, 0.0520019531f, 0.0769958496f, 0.0390014648f, 0.0f, 0.0f }; 64 65 static inline void to_xyz_d50(SkMatrix44* toXYZD50, SkColorSpace::Gamut gamut) { 66 switch (gamut) { 67 case SkColorSpace::kSRGB_Gamut: 68 toXYZD50->set3x3RowMajorf(gSRGB_toXYZD50); 69 break; 70 case SkColorSpace::kAdobeRGB_Gamut: 71 toXYZD50->set3x3RowMajorf(gAdobeRGB_toXYZD50); 72 break; 73 case SkColorSpace::kDCIP3_D65_Gamut: 74 toXYZD50->set3x3RowMajorf(gDCIP3_toXYZD50); 75 break; 76 case SkColorSpace::kRec2020_Gamut: 77 toXYZD50->set3x3RowMajorf(gRec2020_toXYZD50); 78 break; 79 } 80 } 81 82 static inline bool color_space_almost_equal(float a, float b) { 83 return SkTAbs(a - b) < 0.01f; 84 } 85 86 // Let's use a stricter version for transfer functions. Worst case, these are encoded 87 // in ICC format, which offers 16-bits of fractional precision. 88 static inline bool transfer_fn_almost_equal(float a, float b) { 89 return SkTAbs(a - b) < 0.001f; 90 } 91 92 static inline bool is_zero_to_one(float v) { 93 // Because we allow a value just barely larger than 1, the client can use an 94 // entirely linear transfer function. 95 return (0.0f <= v) && (v <= nextafterf(1.0f, 2.0f)); 96 } 97 98 static inline bool is_valid_transfer_fn(const SkColorSpaceTransferFn& coeffs) { 99 if (SkScalarIsNaN(coeffs.fA) || SkScalarIsNaN(coeffs.fB) || 100 SkScalarIsNaN(coeffs.fC) || SkScalarIsNaN(coeffs.fD) || 101 SkScalarIsNaN(coeffs.fE) || SkScalarIsNaN(coeffs.fF) || 102 SkScalarIsNaN(coeffs.fG)) 103 { 104 return false; 105 } 106 107 if (!is_zero_to_one(coeffs.fD)) { 108 return false; 109 } 110 111 if (coeffs.fD == 0.0f) { 112 // Y = (aX + b)^g + e for always 113 if (0.0f == coeffs.fA || 0.0f == coeffs.fG) { 114 SkColorSpacePrintf("A or G is zero, constant transfer function " 115 "is nonsense"); 116 return false; 117 } 118 } 119 120 if (coeffs.fD >= 1.0f) { 121 // Y = cX + f for always 122 if (0.0f == coeffs.fC) { 123 SkColorSpacePrintf("C is zero, constant transfer function is " 124 "nonsense"); 125 return false; 126 } 127 } 128 129 if ((0.0f == coeffs.fA || 0.0f == coeffs.fG) && 0.0f == coeffs.fC) { 130 SkColorSpacePrintf("A or G, and C are zero, constant transfer function " 131 "is nonsense"); 132 return false; 133 } 134 135 if (coeffs.fC < 0.0f) { 136 SkColorSpacePrintf("Transfer function must be increasing"); 137 return false; 138 } 139 140 if (coeffs.fA < 0.0f || coeffs.fG < 0.0f) { 141 SkColorSpacePrintf("Transfer function must be positive or increasing"); 142 return false; 143 } 144 145 return true; 146 } 147 148 static inline bool is_almost_srgb(const SkColorSpaceTransferFn& coeffs) { 149 return transfer_fn_almost_equal(gSRGB_TransferFn.fA, coeffs.fA) && 150 transfer_fn_almost_equal(gSRGB_TransferFn.fB, coeffs.fB) && 151 transfer_fn_almost_equal(gSRGB_TransferFn.fC, coeffs.fC) && 152 transfer_fn_almost_equal(gSRGB_TransferFn.fD, coeffs.fD) && 153 transfer_fn_almost_equal(gSRGB_TransferFn.fE, coeffs.fE) && 154 transfer_fn_almost_equal(gSRGB_TransferFn.fF, coeffs.fF) && 155 transfer_fn_almost_equal(gSRGB_TransferFn.fG, coeffs.fG); 156 } 157 158 static inline bool is_almost_2dot2(const SkColorSpaceTransferFn& coeffs) { 159 return transfer_fn_almost_equal(1.0f, coeffs.fA) && 160 transfer_fn_almost_equal(0.0f, coeffs.fB) && 161 transfer_fn_almost_equal(0.0f, coeffs.fE) && 162 transfer_fn_almost_equal(2.2f, coeffs.fG) && 163 coeffs.fD <= 0.0f; 164 } 165 166 static inline bool is_almost_linear(const SkColorSpaceTransferFn& coeffs) { 167 // OutputVal = InputVal ^ 1.0f 168 const bool linearExp = 169 transfer_fn_almost_equal(1.0f, coeffs.fA) && 170 transfer_fn_almost_equal(0.0f, coeffs.fB) && 171 transfer_fn_almost_equal(0.0f, coeffs.fE) && 172 transfer_fn_almost_equal(1.0f, coeffs.fG) && 173 coeffs.fD <= 0.0f; 174 175 // OutputVal = 1.0f * InputVal 176 const bool linearFn = 177 transfer_fn_almost_equal(1.0f, coeffs.fC) && 178 transfer_fn_almost_equal(0.0f, coeffs.fF) && 179 coeffs.fD >= 1.0f; 180 181 return linearExp || linearFn; 182 } 183 184 static inline bool is_just_gamma(const SkColorSpaceTransferFn& coeffs) { 185 return transfer_fn_almost_equal(coeffs.fA, 1.0f) 186 && transfer_fn_almost_equal(coeffs.fB, 0.0f) 187 && transfer_fn_almost_equal(coeffs.fC, 0.0f) 188 && transfer_fn_almost_equal(coeffs.fD, 0.0f) 189 && transfer_fn_almost_equal(coeffs.fE, 0.0f) 190 && transfer_fn_almost_equal(coeffs.fF, 0.0f); 191 } 192 193 194 static inline void value_to_parametric(SkColorSpaceTransferFn* coeffs, float exponent) { 195 coeffs->fA = 1.0f; 196 coeffs->fB = 0.0f; 197 coeffs->fC = 0.0f; 198 coeffs->fD = 0.0f; 199 coeffs->fE = 0.0f; 200 coeffs->fF = 0.0f; 201 coeffs->fG = exponent; 202 } 203 204 static inline bool named_to_parametric(SkColorSpaceTransferFn* coeffs, 205 SkGammaNamed gammaNamed) { 206 switch (gammaNamed) { 207 case kSRGB_SkGammaNamed: 208 *coeffs = gSRGB_TransferFn; 209 return true; 210 case k2Dot2Curve_SkGammaNamed: 211 *coeffs = g2Dot2_TransferFn; 212 return true; 213 case kLinear_SkGammaNamed: 214 *coeffs = gLinear_TransferFn; 215 return true; 216 default: 217 return false; 218 } 219 } 220 #endif // SkColorSpacePriv_DEFINED 221