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