OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkAtomics.h" | 8 #include "SkAtomics.h" |
9 #include "SkColorSpace.h" | 9 #include "SkColorSpace.h" |
10 | 10 |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
107 if (!SkFloatIsFinite(gamma.fVec[i]) || gamma.fVec[i] < 0) { | 107 if (!SkFloatIsFinite(gamma.fVec[i]) || gamma.fVec[i] < 0) { |
108 return nullptr; | 108 return nullptr; |
109 } | 109 } |
110 for (int j = 0; j < 3; ++j) { | 110 for (int j = 0; j < 3; ++j) { |
111 if (!SkFloatIsFinite(toXYZD50.fMat[3*i + j])) { | 111 if (!SkFloatIsFinite(toXYZD50.fMat[3*i + j])) { |
112 return nullptr; | 112 return nullptr; |
113 } | 113 } |
114 } | 114 } |
115 } | 115 } |
116 | 116 |
| 117 #if 0 |
117 // check the matrix for invertibility | 118 // check the matrix for invertibility |
118 float d = det(toXYZD50); | 119 float d = det(toXYZD50); |
119 if (!SkFloatIsFinite(d) || !SkFloatIsFinite(1 / d)) { | 120 if (!SkFloatIsFinite(d) || !SkFloatIsFinite(1 / d)) { |
120 return nullptr; | 121 return nullptr; |
121 } | 122 } |
| 123 #endif |
122 | 124 |
123 return new SkColorSpace(toXYZD50, gamma, kUnknown_Named); | 125 return new SkColorSpace(toXYZD50, gamma, kUnknown_Named); |
124 } | 126 } |
125 | 127 |
126 void SkColorSpace::dump() const { | 128 void SkColorSpace::dump() const { |
127 fToXYZD50.dump(); | 129 fToXYZD50.dump(); |
128 fGamma.dump(); | 130 fGamma.dump(); |
129 } | 131 } |
130 | 132 |
131 ////////////////////////////////////////////////////////////////////////////////
////////////////// | 133 ////////////////////////////////////////////////////////////////////////////////
////////////////// |
(...skipping 18 matching lines...) Expand all Loading... |
150 return new SkColorSpace(gDevice_toXYZD50, gDevice_gamma, kDevice_Nam
ed); | 152 return new SkColorSpace(gDevice_toXYZD50, gDevice_gamma, kDevice_Nam
ed); |
151 case kSRGB_Named: | 153 case kSRGB_Named: |
152 return new SkColorSpace(gSRGB_toXYZD50, gSRGB_gamma, kSRGB_Named); | 154 return new SkColorSpace(gSRGB_toXYZD50, gSRGB_gamma, kSRGB_Named); |
153 default: | 155 default: |
154 break; | 156 break; |
155 } | 157 } |
156 return nullptr; | 158 return nullptr; |
157 } | 159 } |
158 | 160 |
159 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 161 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
| 162 #include "SkEndian.h" |
| 163 #include "SkStream.h" |
| 164 |
| 165 #define return_if_false(pred, msg) \ |
| 166 do { if (!(pred)) { SkDebugf("parsing icc profile: %s\n", msg); return false
; } } while (0) |
| 167 |
| 168 #define return_null(msg) do { SkDebugf("ICC Profile: %s\n", (msg)); return nu
llptr; } while (0) |
| 169 |
| 170 const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' '); |
| 171 const uint32_t kGray_ColorSpace = SkSetFourByteTag('G', 'R', 'A', 'Y'); |
| 172 |
| 173 const uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z'); |
| 174 const uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z'); |
| 175 const uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z'); |
| 176 |
| 177 const uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C'); |
| 178 const uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C'); |
| 179 const uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C'); |
| 180 |
| 181 struct ICCProfileHeader { |
| 182 uint32_t fSize; |
| 183 uint32_t fCMMType; |
| 184 uint32_t fVersion; |
| 185 uint32_t fClassProfile; |
| 186 uint32_t fColorSpace; |
| 187 uint32_t fPCS; |
| 188 uint32_t fCreated[3]; |
| 189 uint32_t fSignature; |
| 190 uint32_t fPlatformTarget_ignored; |
| 191 uint32_t fFlags_ignored; |
| 192 uint32_t fManufacturer_ignored; |
| 193 uint32_t fDeviceModel_ignored; |
| 194 uint32_t fDeviceAttributes_ignored[2]; |
| 195 uint32_t fRenderingIntent; |
| 196 uint32_t fIlluminantXYZ_ignored[3]; |
| 197 uint32_t fCreator_ignored; |
| 198 |
| 199 uint32_t fReserved_ignored[11]; |
| 200 |
| 201 bool init(const uint32_t data[], size_t length) { |
| 202 SkASSERT(128 == sizeof(*this)); |
| 203 if (length < 128) { |
| 204 return false; |
| 205 } |
| 206 uint32_t* ptr = &fSize; |
| 207 for (int i = 0; i < 32; ++i) { |
| 208 ptr[i] = SkEndian_SwapBE32(data[i]); |
| 209 } |
| 210 return true; |
| 211 } |
| 212 |
| 213 bool valid() const { |
| 214 return_if_false(fSize > 128, "size too small"); |
| 215 |
| 216 uint8_t version[4]; |
| 217 memcpy(version, &fVersion, sizeof(version)); |
| 218 return_if_false((version[3] == 2 || version[3] == 4) && version[0] == 0
&& version[1] == 0, |
| 219 "unsupported version"); |
| 220 |
| 221 const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r
'); |
| 222 const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r
'); |
| 223 const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r
'); |
| 224 return_if_false(fClassProfile == kDisplay_Profile || |
| 225 fClassProfile == kInput_Profile || |
| 226 fClassProfile == kOutput_Profile, |
| 227 "unsupported class profile"); |
| 228 |
| 229 return_if_false(fColorSpace == kRGB_ColorSpace || |
| 230 fColorSpace == kGray_ColorSpace, |
| 231 "unsupported class profile"); |
| 232 |
| 233 const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' '); |
| 234 return_if_false(fPCS == kXYZ_PCSSpace, |
| 235 "unsupported class profile"); |
| 236 |
| 237 return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "bad
signature"); |
| 238 |
| 239 enum ICCIntents { |
| 240 kPerceptual = 0, |
| 241 kRelativeColormetric = 1, |
| 242 kSaturation = 2, |
| 243 kAbsoluteColorMetric = 3, |
| 244 }; |
| 245 return_if_false(fRenderingIntent >= 0 || fRenderingIntent <= 3, "bad ren
dering intent"); |
| 246 |
| 247 return true; |
| 248 } |
| 249 }; |
| 250 |
| 251 struct ICCTag { |
| 252 uint32_t fSignature; |
| 253 uint32_t fOffset; |
| 254 uint32_t fLength; |
| 255 |
| 256 const uint32_t* init(const uint32_t data[]) { |
| 257 fSignature = SkEndian_SwapBE32(data[0]); |
| 258 fOffset = SkEndian_SwapBE32(data[1]); |
| 259 fLength = SkEndian_SwapBE32(data[2]); |
| 260 return data + 3; |
| 261 } |
| 262 |
| 263 const uint32_t* addr(const uint32_t* base) const { |
| 264 SkASSERT(SkIsAlign4(fOffset)); |
| 265 return base + (fOffset >> 2); |
| 266 } |
| 267 |
| 268 static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature
) { |
| 269 for (int i = 0; i < count; ++i) { |
| 270 if (tags[i].fSignature == signature) { |
| 271 return &tags[i]; |
| 272 } |
| 273 } |
| 274 return nullptr; |
| 275 } |
| 276 }; |
| 277 |
| 278 void load_3x(float dst[3], const uint32_t data[]) { |
| 279 uint32_t tag = SkEndian_SwapBE32(data[0]); |
| 280 dst[0] = SkFixedToFloat(SkEndian_SwapBE32(data[2])); |
| 281 dst[1] = SkFixedToFloat(SkEndian_SwapBE32(data[2])); |
| 282 dst[2] = SkFixedToFloat(SkEndian_SwapBE32(data[2])); |
| 283 SkDebugf("3x tag %08X %g %g %g\n", tag, dst[0], dst[1], dst[2]); |
| 284 } |
| 285 |
| 286 const uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v'); |
| 287 const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a'); |
| 288 |
| 289 static float load_gamma(const uint32_t data[]) { |
| 290 uint32_t type = SkEndian_SwapBE32(data[0]); |
| 291 switch (type) { |
| 292 case kTAG_CurveType: { |
| 293 int count = SkEndian_SwapBE32(data[2]); |
| 294 const uint16_t* table = (const uint16_t*)&data[3]; |
| 295 if (1 == count) { // table entry is the exponent (bias 256) |
| 296 unsigned value = SkEndian_SwapBE16(table[0]); |
| 297 float gamma = value / 256.0f; |
| 298 SkDebugf("gamma %d %g\n", value, gamma); |
| 299 return gamma; |
| 300 } |
| 301 // general interp table (ingored for now) |
| 302 for (int i = 0; i < count; ++i) { |
| 303 unsigned value = SkEndian_SwapBE16(table[i]); |
| 304 SkDebugf("curve[%d] %d\n", i, value); |
| 305 } |
| 306 return 2.2f; |
| 307 } break; |
| 308 case kTAG_ParaCurveType: { |
| 309 SkDebugf("parametric curve\n"); |
| 310 return 2.2f; |
| 311 } break; |
| 312 default: |
| 313 return 1; |
| 314 } |
| 315 } |
| 316 |
| 317 SkColorSpace* SkColorSpace::NewICCProfile32(const uint32_t base[], size_t length
) { |
| 318 const uint32_t storedSize = SkEndian_SwapBE32(base[0]); |
| 319 if (storedSize < 128+4) { |
| 320 return nullptr; // too small |
| 321 } |
| 322 if (length > storedSize) { |
| 323 length = storedSize; |
| 324 } |
| 325 |
| 326 ICCProfileHeader header; |
| 327 if (!header.init(base, length) || !header.valid()) { |
| 328 return nullptr; |
| 329 } |
| 330 |
| 331 const uint32_t* data = base + (128/4); |
| 332 int tagCount = SkEndian_SwapBE32(*data++); |
| 333 if (tagCount < 0 || tagCount > 100) { |
| 334 return nullptr; |
| 335 } |
| 336 |
| 337 SkDebugf("%d tags\n", tagCount); |
| 338 SkAutoTArray<ICCTag> tags(tagCount); |
| 339 for (int i = 0; i < tagCount; ++i) { |
| 340 data = tags[i].init(data); |
| 341 SkDebugf("[%d] %c%c%c%c %d %d\n", i, |
| 342 (tags[i].fSignature >> 24) & 0xFF, (tags[i].fSignature >> 16) &
0xFF, |
| 343 (tags[i].fSignature >> 8) & 0xFF, (tags[i].fSignature >> 0) &
0xFF, |
| 344 tags[i].fOffset, tags[i].fLength); |
| 345 } |
| 346 |
| 347 SkFloat3x3 toXYZ; |
| 348 SkFloat3 gamma {{ 1, 1, 1 }}; |
| 349 |
| 350 // Load up our matrix |
| 351 switch (header.fColorSpace) { |
| 352 case kRGB_ColorSpace: { |
| 353 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); |
| 354 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); |
| 355 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); |
| 356 if (!r || !g || !b) { |
| 357 return_null("needed rgb tags for XYZ space"); |
| 358 } |
| 359 load_3x(&toXYZ.fMat[0], r->addr(base)); |
| 360 load_3x(&toXYZ.fMat[3], g->addr(base)); |
| 361 load_3x(&toXYZ.fMat[6], b->addr(base)); |
| 362 |
| 363 r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC); |
| 364 g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC); |
| 365 b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC); |
| 366 if (r) { |
| 367 gamma.fVec[0] = load_gamma(r->addr(base)); |
| 368 } |
| 369 if (g) { |
| 370 gamma.fVec[1] = load_gamma(g->addr(base)); |
| 371 } |
| 372 if (b) { |
| 373 gamma.fVec[2] = load_gamma(b->addr(base)); |
| 374 } |
| 375 return SkColorSpace::NewRGB(toXYZ, gamma); |
| 376 } |
| 377 default: |
| 378 break; |
| 379 } |
| 380 return_null("unsupported colorspace"); |
| 381 } |
| 382 |
| 383 SkColorSpace* SkColorSpace::NewICCProfile(const void* ptr, size_t length) { |
| 384 const uint32_t* data; |
| 385 SkAutoTArray<uint32_t> storage; |
| 386 |
| 387 if ((intptr_t)ptr & 3) { // not 4-byte aligned |
| 388 storage.reset(SkAlign4(length) >> 2); |
| 389 memcpy(storage.get(), ptr, length); |
| 390 data = storage.get(); |
| 391 } else { |
| 392 data = (const uint32_t*)ptr; |
| 393 } |
| 394 return NewICCProfile32(data, length); |
| 395 } |
| 396 |
| 397 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
| 398 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
160 | 399 |
161 SkColorSpace::Result SkColorSpace::Concat(const SkColorSpace* src, const SkColor
Space* dst, | 400 SkColorSpace::Result SkColorSpace::Concat(const SkColorSpace* src, const SkColor
Space* dst, |
162 SkFloat3x3* result) { | 401 SkFloat3x3* result) { |
163 if (!src || !dst || (src->named() == kDevice_Named) || (src->named() == dst-
>named())) { | 402 if (!src || !dst || (src->named() == kDevice_Named) || (src->named() == dst-
>named())) { |
164 if (result) { | 403 if (result) { |
165 *result = {{ 1, 0, 0, 0, 1, 0, 0, 0, 1 }}; | 404 *result = {{ 1, 0, 0, 0, 1, 0, 0, 0, 1 }}; |
166 } | 405 } |
167 return kIdentity_Result; | 406 return kIdentity_Result; |
168 } | 407 } |
169 if (result) { | 408 if (result) { |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 } | 475 } |
237 | 476 |
238 // D65 white point of Rec. 709 [8] are: | 477 // D65 white point of Rec. 709 [8] are: |
239 // | 478 // |
240 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 | 479 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 |
241 // | 480 // |
242 // R G B white | 481 // R G B white |
243 // x 0.640 0.300 0.150 0.3127 | 482 // x 0.640 0.300 0.150 0.3127 |
244 // y 0.330 0.600 0.060 0.3290 | 483 // y 0.330 0.600 0.060 0.3290 |
245 // z 0.030 0.100 0.790 0.3582 | 484 // z 0.030 0.100 0.790 0.3582 |
OLD | NEW |