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 8 #include "SkAtomics.h" 9 #include "SkColorSpace.h" 10 11 static inline bool SkFloatIsFinite(float x) { return 0 == x * 0; } 12 13 // 14 // SkFloat3x3 15 // 16 // In memory order, values are a, b, c, d, e, f, g, h, i 17 // 18 // When applied to a color component vector (e.g. [ r, r, r ] or [ g, g, g ] we do 19 // 20 // [ r r r ] * [ a b c ] + [ g g g ] * [ d e f ] + [ b b b ] * [ g h i ] 21 // 22 // Thus in our point-on-the-right notation, the matrix looks like 23 // 24 // [ a d g ] [ r ] 25 // [ b e h ] * [ g ] 26 // [ c f i ] [ b ] 27 // 28 static SkFloat3x3 concat(const SkFloat3x3& left, const SkFloat3x3& rite) { 29 SkFloat3x3 result; 30 for (int row = 0; row < 3; ++row) { 31 for (int col = 0; col < 3; ++col) { 32 double tmp = 0; 33 for (int i = 0; i < 3; ++i) { 34 tmp += (double)left.fMat[row + i * 3] * rite.fMat[i + col * 3]; 35 } 36 result.fMat[row + col * 3] = (double)tmp; 37 } 38 } 39 return result; 40 } 41 42 static double det(const SkFloat3x3& m) { 43 return (double)m.fMat[0] * m.fMat[4] * m.fMat[8] + 44 (double)m.fMat[3] * m.fMat[7] * m.fMat[2] + 45 (double)m.fMat[6] * m.fMat[1] * m.fMat[5] - 46 (double)m.fMat[0] * m.fMat[7] * m.fMat[5] - 47 (double)m.fMat[3] * m.fMat[1] * m.fMat[8] - 48 (double)m.fMat[6] * m.fMat[4] * m.fMat[2]; 49 } 50 51 static double det2x2(const SkFloat3x3& m, int a, int b, int c, int d) { 52 return (double)m.fMat[a] * m.fMat[b] - (double)m.fMat[c] * m.fMat[d]; 53 } 54 55 static SkFloat3x3 invert(const SkFloat3x3& m) { 56 double d = det(m); 57 SkASSERT(SkFloatIsFinite((float)d)); 58 double scale = 1 / d; 59 SkASSERT(SkFloatIsFinite((float)scale)); 60 61 return {{ 62 (float)(scale * det2x2(m, 4, 8, 5, 7)), 63 (float)(scale * det2x2(m, 7, 2, 8, 1)), 64 (float)(scale * det2x2(m, 1, 5, 2, 4)), 65 66 (float)(scale * det2x2(m, 6, 5, 8, 3)), 67 (float)(scale * det2x2(m, 0, 8, 2, 6)), 68 (float)(scale * det2x2(m, 3, 2, 5, 0)), 69 70 (float)(scale * det2x2(m, 3, 7, 4, 6)), 71 (float)(scale * det2x2(m, 6, 1, 7, 0)), 72 (float)(scale * det2x2(m, 0, 4, 1, 3)), 73 }}; 74 } 75 76 void SkFloat3::dump() const { 77 SkDebugf("[%7.4f %7.4f %7.4f]\n", fVec[0], fVec[1], fVec[2]); 78 } 79 80 void SkFloat3x3::dump() const { 81 SkDebugf("[%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f]\n", 82 fMat[0], fMat[1], fMat[2], 83 fMat[3], fMat[4], fMat[5], 84 fMat[6], fMat[7], fMat[8]); 85 } 86 87 ////////////////////////////////////////////////////////////////////////////////////////////////// 88 89 static int32_t gUniqueColorSpaceID; 90 91 SkColorSpace::SkColorSpace(const SkFloat3x3& toXYZD50, const SkFloat3& gamma, Named named) 92 : fToXYZD50(toXYZD50) 93 , fGamma(gamma) 94 , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID)) 95 , fNamed(named) 96 { 97 for (int i = 0; i < 3; ++i) { 98 SkASSERT(SkFloatIsFinite(gamma.fVec[i])); 99 for (int j = 0; j < 3; ++j) { 100 SkASSERT(SkFloatIsFinite(toXYZD50.fMat[3*i + j])); 101 } 102 } 103 } 104 105 SkColorSpace* SkColorSpace::NewRGB(const SkFloat3x3& toXYZD50, const SkFloat3& gamma) { 106 for (int i = 0; i < 3; ++i) { 107 if (!SkFloatIsFinite(gamma.fVec[i]) || gamma.fVec[i] < 0) { 108 return nullptr; 109 } 110 for (int j = 0; j < 3; ++j) { 111 if (!SkFloatIsFinite(toXYZD50.fMat[3*i + j])) { 112 return nullptr; 113 } 114 } 115 } 116 117 // check the matrix for invertibility 118 float d = det(toXYZD50); 119 if (!SkFloatIsFinite(d) || !SkFloatIsFinite(1 / d)) { 120 return nullptr; 121 } 122 123 return new SkColorSpace(toXYZD50, gamma, kUnknown_Named); 124 } 125 126 void SkColorSpace::dump() const { 127 fToXYZD50.dump(); 128 fGamma.dump(); 129 } 130 131 ////////////////////////////////////////////////////////////////////////////////////////////////// 132 133 const SkFloat3 gDevice_gamma {{ 0, 0, 0 }}; 134 const SkFloat3x3 gDevice_toXYZD50 {{ 135 1, 0, 0, 136 0, 1, 0, 137 0, 0, 1 138 }}; 139 140 const SkFloat3 gSRGB_gamma {{ 2.2f, 2.2f, 2.2f }}; 141 const SkFloat3x3 gSRGB_toXYZD50 {{ 142 0.4358f, 0.2224f, 0.0139f, // * R 143 0.3853f, 0.7170f, 0.0971f, // * G 144 0.1430f, 0.0606f, 0.7139f, // * B 145 }}; 146 147 SkColorSpace* SkColorSpace::NewNamed(Named named) { 148 switch (named) { 149 case kDevice_Named: 150 return new SkColorSpace(gDevice_toXYZD50, gDevice_gamma, kDevice_Named); 151 case kSRGB_Named: 152 return new SkColorSpace(gSRGB_toXYZD50, gSRGB_gamma, kSRGB_Named); 153 default: 154 break; 155 } 156 return nullptr; 157 } 158 159 /////////////////////////////////////////////////////////////////////////////////////////////////// 160 161 SkColorSpace::Result SkColorSpace::Concat(const SkColorSpace* src, const SkColorSpace* dst, 162 SkFloat3x3* result) { 163 if (!src || !dst || (src->named() == kDevice_Named) || (src->named() == dst->named())) { 164 if (result) { 165 *result = {{ 1, 0, 0, 0, 1, 0, 0, 0, 1 }}; 166 } 167 return kIdentity_Result; 168 } 169 if (result) { 170 *result = concat(src->fToXYZD50, invert(dst->fToXYZD50)); 171 } 172 return kNormal_Result; 173 } 174 175 #include "SkColor.h" 176 #include "SkNx.h" 177 #include "SkPM4f.h" 178 179 void SkApply3x3ToPM4f(const SkFloat3x3& m, const SkPM4f src[], SkPM4f dst[], int count) { 180 SkASSERT(1 == SkPM4f::G); 181 SkASSERT(3 == SkPM4f::A); 182 183 Sk4f cr, cg, cb; 184 cg = Sk4f::Load(m.fMat + 3); 185 if (0 == SkPM4f::R) { 186 SkASSERT(2 == SkPM4f::B); 187 cr = Sk4f::Load(m.fMat + 0); 188 cb = Sk4f(m.fMat[6], m.fMat[7], m.fMat[8], 0); 189 } else { 190 SkASSERT(0 == SkPM4f::B); 191 SkASSERT(2 == SkPM4f::R); 192 cb = Sk4f::Load(m.fMat + 0); 193 cr = Sk4f(m.fMat[6], m.fMat[7], m.fMat[8], 0); 194 } 195 cr = cr * Sk4f(1, 1, 1, 0); 196 cg = cg * Sk4f(1, 1, 1, 0); 197 cb = cb * Sk4f(1, 1, 1, 0); 198 199 for (int i = 0; i < count; ++i) { 200 Sk4f r = Sk4f(src[i].fVec[SkPM4f::R]); 201 Sk4f g = Sk4f(src[i].fVec[SkPM4f::G]); 202 Sk4f b = Sk4f(src[i].fVec[SkPM4f::B]); 203 Sk4f a = Sk4f(0, 0, 0, src[i].fVec[SkPM4f::A]); 204 (cr * r + cg * g + cb * b + a).store(&dst[i]); 205 } 206 } 207 208 /////////////////////////////////////////////////////////////////////////////////////////////////// 209 210 void SkColorSpace::Test() { 211 SkFloat3x3 mat {{ 2, 0, 0, 0, 3, 0, 0, 0, 4 }}; 212 SkFloat3x3 inv = invert(mat); 213 mat.dump(); 214 inv.dump(); 215 concat(mat, inv).dump(); 216 concat(inv, mat).dump(); 217 SkDebugf("\n"); 218 219 mat = gSRGB_toXYZD50; 220 inv = invert(mat); 221 mat.dump(); 222 inv.dump(); 223 concat(mat, inv).dump(); 224 concat(inv, mat).dump(); 225 SkDebugf("\n"); 226 227 SkAutoTUnref<SkColorSpace> cs0(SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)); 228 SkAutoTUnref<SkColorSpace> cs1(SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)); 229 230 cs0->dump(); 231 cs1->dump(); 232 SkFloat3x3 xform; 233 (void)SkColorSpace::Concat(cs0, cs1, &xform); 234 xform.dump(); 235 SkDebugf("\n"); 236 } 237 238 // D65 white point of Rec. 709 [8] are: 239 // 240 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 241 // 242 // R G B white 243 // x 0.640 0.300 0.150 0.3127 244 // y 0.330 0.600 0.060 0.3290 245 // z 0.030 0.100 0.790 0.3582 246