OLD | NEW |
(Empty) | |
| 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, Na
med 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& g
amma) { |
| 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_Nam
ed); |
| 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 SkColor
Space* 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 |
| 178 void SkApply3x3ToPM4f(const SkFloat3x3& m, const SkPM4f src[], SkPM4f dst[], int
count) { |
| 179 SkASSERT(1 == SkPM4f::G); |
| 180 SkASSERT(3 == SkPM4f::A); |
| 181 |
| 182 Sk4f cr, cg, cb; |
| 183 cg = Sk4f::Load(m.fMat + 3); |
| 184 if (0 == SkPM4f::R) { |
| 185 SkASSERT(2 == SkPM4f::B); |
| 186 cr = Sk4f::Load(m.fMat + 0); |
| 187 cb = Sk4f(m.fMat[6], m.fMat[7], m.fMat[8], 0); |
| 188 } else { |
| 189 SkASSERT(0 == SkPM4f::B); |
| 190 SkASSERT(2 == SkPM4f::R); |
| 191 cb = Sk4f::Load(m.fMat + 0); |
| 192 cr = Sk4f(m.fMat[6], m.fMat[7], m.fMat[8], 0); |
| 193 } |
| 194 cr = cr * Sk4f(1, 1, 1, 0); |
| 195 cg = cg * Sk4f(1, 1, 1, 0); |
| 196 cb = cb * Sk4f(1, 1, 1, 0); |
| 197 |
| 198 for (int i = 0; i < count; ++i) { |
| 199 Sk4f r = Sk4f(src[i].fVec[SkPM4f::R]); |
| 200 Sk4f g = Sk4f(src[i].fVec[SkPM4f::G]); |
| 201 Sk4f b = Sk4f(src[i].fVec[SkPM4f::B]); |
| 202 Sk4f a = Sk4f(0, 0, 0, src[i].fVec[SkPM4f::A]); |
| 203 (cr * r + cg * g + cb * b + a).store(&dst[i]); |
| 204 } |
| 205 } |
| 206 |
| 207 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
| 208 |
| 209 void SkColorSpace::Test() { |
| 210 SkFloat3x3 mat {{ 2, 0, 0, 0, 3, 0, 0, 0, 4 }}; |
| 211 SkFloat3x3 inv = invert(mat); |
| 212 mat.dump(); |
| 213 inv.dump(); |
| 214 concat(mat, inv).dump(); |
| 215 concat(inv, mat).dump(); |
| 216 SkDebugf("\n"); |
| 217 |
| 218 mat = gSRGB_toXYZD50; |
| 219 inv = invert(mat); |
| 220 mat.dump(); |
| 221 inv.dump(); |
| 222 concat(mat, inv).dump(); |
| 223 concat(inv, mat).dump(); |
| 224 SkDebugf("\n"); |
| 225 |
| 226 SkAutoTUnref<SkColorSpace> cs0(SkColorSpace::NewNamed(SkColorSpace::kSRGB_Na
med)); |
| 227 SkAutoTUnref<SkColorSpace> cs1(SkColorSpace::NewNamed(SkColorSpace::kSRGB_Na
med)); |
| 228 |
| 229 cs0->dump(); |
| 230 cs1->dump(); |
| 231 SkFloat3x3 xform; |
| 232 (void)SkColorSpace::Concat(cs0, cs1, &xform); |
| 233 xform.dump(); |
| 234 SkDebugf("\n"); |
| 235 } |
| 236 |
| 237 // D65 white point of Rec. 709 [8] are: |
| 238 // |
| 239 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 |
| 240 // |
| 241 // R G B white |
| 242 // x 0.640 0.300 0.150 0.3127 |
| 243 // y 0.330 0.600 0.060 0.3290 |
| 244 // z 0.030 0.100 0.790 0.3582 |
OLD | NEW |