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 "SkColorSpace.h" | 8 #include "SkColorSpace.h" |
9 #include "SkColorSpace_Base.h" | 9 #include "SkColorSpace_Base.h" |
10 #include "SkColorSpacePriv.h" | 10 #include "SkColorSpacePriv.h" |
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
231 dst[0] = SkFixedToFloat(read_big_endian_int(src + 8)); | 231 dst[0] = SkFixedToFloat(read_big_endian_int(src + 8)); |
232 dst[1] = SkFixedToFloat(read_big_endian_int(src + 12)); | 232 dst[1] = SkFixedToFloat(read_big_endian_int(src + 12)); |
233 dst[2] = SkFixedToFloat(read_big_endian_int(src + 16)); | 233 dst[2] = SkFixedToFloat(read_big_endian_int(src + 16)); |
234 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]); | 234 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]); |
235 return true; | 235 return true; |
236 } | 236 } |
237 | 237 |
238 static constexpr uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', '
v'); | 238 static constexpr uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', '
v'); |
239 static constexpr uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', '
a'); | 239 static constexpr uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', '
a'); |
240 | 240 |
241 static bool load_gammas(SkGammaCurve* gammas, uint32_t numGammas, const uint8_t*
src, size_t len) { | 241 static SkGammas::Type set_gamma_value(SkGammas::Data* data, float value) { |
242 for (uint32_t i = 0; i < numGammas; i++) { | 242 if (color_space_almost_equal(2.2f, value)) { |
243 if (len < 12) { | 243 data->fNamed = SkColorSpace::k2Dot2Curve_GammaNamed; |
244 // FIXME (msarett): | 244 return SkGammas::Type::kNamed_Type; |
245 // We could potentially return false here after correctly parsing *s
ome* of the | 245 } |
246 // gammas correctly. Should we somehow try to indicate a partial su
ccess? | 246 |
247 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); | 247 if (color_space_almost_equal(1.0f, value)) { |
248 return false; | 248 data->fNamed = SkColorSpace::kLinear_GammaNamed; |
| 249 return SkGammas::Type::kNamed_Type; |
| 250 } |
| 251 |
| 252 if (color_space_almost_equal(0.0f, value)) { |
| 253 return SkGammas::Type::kNone_Type; |
| 254 } |
| 255 |
| 256 data->fValue = value; |
| 257 return SkGammas::Type::kValue_Type; |
| 258 } |
| 259 |
| 260 static float read_big_endian_16_dot_16(const uint8_t buf[4]) { |
| 261 // It just so happens that SkFixed is also 16.16! |
| 262 return SkFixedToFloat(read_big_endian_int(buf)); |
| 263 } |
| 264 |
| 265 /** |
| 266 * @param outData Set to the appropriate value on success. If we have tabl
e or |
| 267 * parametric gamma, it is the responsibility of the caller
to set |
| 268 * fOffset. |
| 269 * @param outParams If this is a parametric gamma, this is set to the appropr
iate |
| 270 * parameters on success. |
| 271 * @param outTagBytes Will be set to the length of the tag on success. |
| 272 * @src Pointer to tag data. |
| 273 * @len Length of tag data in bytes. |
| 274 * |
| 275 * @return kNone_Type on failure, otherwise the type of the gamma ta
g. |
| 276 */ |
| 277 static SkGammas::Type parse_gamma(SkGammas::Data* outData, SkGammas::Params* out
Params, |
| 278 size_t* outTagBytes, const uint8_t* src, s
ize_t len) { |
| 279 if (len < 12) { |
| 280 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
| 281 return SkGammas::Type::kNone_Type; |
| 282 } |
| 283 |
| 284 // In the case of consecutive gamma tags, we need to count the number of byt
es in the |
| 285 // tag, so that we can move on to the next tag. |
| 286 size_t tagBytes; |
| 287 |
| 288 uint32_t type = read_big_endian_uint(src); |
| 289 // Bytes 4-7 are reserved and should be set to zero. |
| 290 switch (type) { |
| 291 case kTAG_CurveType: { |
| 292 uint32_t count = read_big_endian_uint(src + 8); |
| 293 |
| 294 // tagBytes = 12 + 2 * count |
| 295 // We need to do safe addition here to avoid integer overflow. |
| 296 if (!safe_add(count, count, &tagBytes) || |
| 297 !safe_add((size_t) 12, tagBytes, &tagBytes)) |
| 298 { |
| 299 SkColorSpacePrintf("Invalid gamma count"); |
| 300 return SkGammas::Type::kNone_Type; |
| 301 } |
| 302 |
| 303 if (len < tagBytes) { |
| 304 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
| 305 return SkGammas::Type::kNone_Type; |
| 306 } |
| 307 *outTagBytes = tagBytes; |
| 308 |
| 309 if (0 == count) { |
| 310 // Some tags require a gamma curve, but the author doesn't actua
lly want |
| 311 // to transform the data. In this case, it is common to see a c
urve with |
| 312 // a count of 0. |
| 313 outData->fNamed = SkColorSpace::kLinear_GammaNamed; |
| 314 return SkGammas::Type::kNamed_Type; |
| 315 } |
| 316 |
| 317 const uint16_t* table = (const uint16_t*) (src + 12); |
| 318 if (1 == count) { |
| 319 // The table entry is the gamma (with a bias of 256). |
| 320 float value = (read_big_endian_short((const uint8_t*) table)) /
256.0f; |
| 321 SkColorSpacePrintf("gamma %g\n", value); |
| 322 |
| 323 return set_gamma_value(outData, value); |
| 324 } |
| 325 |
| 326 // Check for frequently occurring sRGB curves. |
| 327 // We do this by sampling a few values and see if they match our exp
ectation. |
| 328 // A more robust solution would be to compare each value in this cur
ve against |
| 329 // an sRGB curve to see if we remain below an error threshold. At t
his time, |
| 330 // we haven't seen any images in the wild that make this kind of |
| 331 // calculation necessary. We encounter identical gamma curves over
and |
| 332 // over again, but relatively few variations. |
| 333 if (1024 == count) { |
| 334 // The magic values were chosen because they match both the very
common |
| 335 // HP sRGB gamma table and the less common Canon sRGB gamma tabl
e (which use |
| 336 // different rounding rules). |
| 337 if (0 == read_big_endian_short((const uint8_t*) &table[0]) && |
| 338 3366 == read_big_endian_short((const uint8_t*) &table[25
7]) && |
| 339 14116 == read_big_endian_short((const uint8_t*) &table[5
13]) && |
| 340 34318 == read_big_endian_short((const uint8_t*) &table[7
68]) && |
| 341 65535 == read_big_endian_short((const uint8_t*) &table[1
023])) { |
| 342 outData->fNamed = SkColorSpace::kSRGB_GammaNamed; |
| 343 return SkGammas::Type::kNamed_Type; |
| 344 } |
| 345 } |
| 346 |
| 347 if (26 == count) { |
| 348 // The magic values were chosen because they match a very common
LCMS sRGB |
| 349 // gamma table. |
| 350 if (0 == read_big_endian_short((const uint8_t*) &table[0]) && |
| 351 3062 == read_big_endian_short((const uint8_t*) &table[6]
) && |
| 352 12824 == read_big_endian_short((const uint8_t*) &table[1
2]) && |
| 353 31237 == read_big_endian_short((const uint8_t*) &table[1
8]) && |
| 354 65535 == read_big_endian_short((const uint8_t*) &table[2
5])) { |
| 355 outData->fNamed = SkColorSpace::kSRGB_GammaNamed; |
| 356 return SkGammas::Type::kNamed_Type; |
| 357 } |
| 358 } |
| 359 |
| 360 if (4096 == count) { |
| 361 // The magic values were chosen because they match Nikon, Epson,
and |
| 362 // LCMS sRGB gamma tables (all of which use different rounding r
ules). |
| 363 if (0 == read_big_endian_short((const uint8_t*) &table[0]) && |
| 364 950 == read_big_endian_short((const uint8_t*) &table[515
]) && |
| 365 3342 == read_big_endian_short((const uint8_t*) &table[10
25]) && |
| 366 14079 == read_big_endian_short((const uint8_t*) &table[2
051]) && |
| 367 65535 == read_big_endian_short((const uint8_t*) &table[4
095])) { |
| 368 outData->fNamed = SkColorSpace::kSRGB_GammaNamed; |
| 369 return SkGammas::Type::kNamed_Type; |
| 370 } |
| 371 } |
| 372 |
| 373 // Otherwise, we will represent gamma with a table. |
| 374 outData->fTable.fSize = count; |
| 375 return SkGammas::Type::kTable_Type; |
249 } | 376 } |
250 | 377 case kTAG_ParaCurveType: { |
251 // We need to count the number of bytes in the tag, so we are able to mo
ve to the | 378 enum ParaCurveType { |
252 // next tag on the next loop iteration. | 379 kExponential_ParaCurveType = 0, |
253 size_t tagBytes; | 380 kGAB_ParaCurveType = 1, |
254 | 381 kGABC_ParaCurveType = 2, |
255 uint32_t type = read_big_endian_uint(src); | 382 kGABDE_ParaCurveType = 3, |
256 switch (type) { | 383 kGABCDEF_ParaCurveType = 4, |
257 case kTAG_CurveType: { | 384 }; |
258 uint32_t count = read_big_endian_uint(src + 8); | 385 |
259 | 386 // Determine the format of the parametric curve tag. |
260 // tagBytes = 12 + 2 * count | 387 uint16_t format = read_big_endian_short(src + 8); |
261 // We need to do safe addition here to avoid integer overflow. | 388 if (format > kGABCDEF_ParaCurveType) { |
262 if (!safe_add(count, count, &tagBytes) || | 389 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type); |
263 !safe_add((size_t) 12, tagBytes, &tagBytes)) | 390 return SkGammas::Type::kNone_Type; |
264 { | 391 } |
265 SkColorSpacePrintf("Invalid gamma count"); | 392 |
266 return false; | 393 if (kExponential_ParaCurveType == format) { |
267 } | 394 tagBytes = 12 + 4; |
268 | 395 if (len < tagBytes) { |
269 if (0 == count) { | 396 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len)
; |
270 // Some tags require a gamma curve, but the author doesn't a
ctually want | 397 return SkGammas::Type::kNone_Type; |
271 // to transform the data. In this case, it is common to see
a curve with | 398 } |
272 // a count of 0. | 399 |
273 gammas[i].fNamed = SkColorSpace::kLinear_GammaNamed; | 400 // Y = X^g |
| 401 float g = read_big_endian_16_dot_16(src + 12); |
| 402 |
| 403 *outTagBytes = tagBytes; |
| 404 return set_gamma_value(outData, g); |
| 405 } |
| 406 |
| 407 // Here's where the real parametric gammas start. There are many |
| 408 // permutations of the same equations. |
| 409 // |
| 410 // Y = (aX + b)^g + c for X >= d |
| 411 // Y = eX + f otherwise |
| 412 // |
| 413 // We will fill in with zeros as necessary to always match the above
form. |
| 414 if (len < 24) { |
| 415 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
| 416 return SkGammas::Type::kNone_Type; |
| 417 } |
| 418 float g = read_big_endian_16_dot_16(src + 12); |
| 419 float a = read_big_endian_16_dot_16(src + 16); |
| 420 float b = read_big_endian_16_dot_16(src + 20); |
| 421 float c = 0.0f, d = 0.0f, e = 0.0f, f = 0.0f; |
| 422 switch(format) { |
| 423 case kGAB_ParaCurveType: |
| 424 tagBytes = 12 + 12; |
| 425 |
| 426 // Y = (aX + b)^g for X >= -b/a |
| 427 // Y = 0 otherwise |
| 428 d = -b / a; |
274 break; | 429 break; |
275 } else if (len < tagBytes) { | 430 case kGABC_ParaCurveType: |
276 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len)
; | 431 tagBytes = 12 + 16; |
277 return false; | |
278 } | |
279 | |
280 const uint16_t* table = (const uint16_t*) (src + 12); | |
281 if (1 == count) { | |
282 // The table entry is the gamma (with a bias of 256). | |
283 float value = (read_big_endian_short((const uint8_t*) table)
) / 256.0f; | |
284 set_gamma_value(&gammas[i], value); | |
285 SkColorSpacePrintf("gamma %g\n", value); | |
286 break; | |
287 } | |
288 | |
289 // Check for frequently occurring sRGB curves. | |
290 // We do this by sampling a few values and see if they match our
expectation. | |
291 // A more robust solution would be to compare each value in this
curve against | |
292 // an sRGB curve to see if we remain below an error threshold.
At this time, | |
293 // we haven't seen any images in the wild that make this kind of | |
294 // calculation necessary. We encounter identical gamma curves o
ver and | |
295 // over again, but relatively few variations. | |
296 if (1024 == count) { | |
297 // The magic values were chosen because they match a very co
mmon sRGB | |
298 // gamma table and the less common Canon sRGB gamma table (w
hich use | |
299 // different rounding rules). | |
300 if (0 == read_big_endian_short((const uint8_t*) &table[0]) &
& | |
301 3366 == read_big_endian_short((const uint8_t*) &tabl
e[257]) && | |
302 14116 == read_big_endian_short((const uint8_t*) &tab
le[513]) && | |
303 34318 == read_big_endian_short((const uint8_t*) &tab
le[768]) && | |
304 65535 == read_big_endian_short((const uint8_t*) &tab
le[1023])) { | |
305 gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed; | |
306 break; | |
307 } | |
308 } else if (26 == count) { | |
309 // The magic values were chosen because they match a very co
mmon sRGB | |
310 // gamma table. | |
311 if (0 == read_big_endian_short((const uint8_t*) &table[0]) &
& | |
312 3062 == read_big_endian_short((const uint8_t*) &tabl
e[6]) && | |
313 12824 == read_big_endian_short((const uint8_t*) &tab
le[12]) && | |
314 31237 == read_big_endian_short((const uint8_t*) &tab
le[18]) && | |
315 65535 == read_big_endian_short((const uint8_t*) &tab
le[25])) { | |
316 gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed; | |
317 break; | |
318 } | |
319 } else if (4096 == count) { | |
320 // The magic values were chosen because they match Nikon, Ep
son, and | |
321 // LCMS sRGB gamma tables (all of which use different roundi
ng rules). | |
322 if (0 == read_big_endian_short((const uint8_t*) &table[0]) &
& | |
323 950 == read_big_endian_short((const uint8_t*) &table
[515]) && | |
324 3342 == read_big_endian_short((const uint8_t*) &tabl
e[1025]) && | |
325 14079 == read_big_endian_short((const uint8_t*) &tab
le[2051]) && | |
326 65535 == read_big_endian_short((const uint8_t*) &tab
le[4095])) { | |
327 gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed; | |
328 break; | |
329 } | |
330 } | |
331 | |
332 // Otherwise, fill in the interpolation table. | |
333 gammas[i].fTableSize = count; | |
334 gammas[i].fTable = std::unique_ptr<float[]>(new float[count]); | |
335 for (uint32_t j = 0; j < count; j++) { | |
336 gammas[i].fTable[j] = | |
337 (read_big_endian_short((const uint8_t*) &table[j]))
/ 65535.0f; | |
338 } | |
339 break; | |
340 } | |
341 case kTAG_ParaCurveType: { | |
342 enum ParaCurveType { | |
343 kExponential_ParaCurveType = 0, | |
344 kGAB_ParaCurveType = 1, | |
345 kGABC_ParaCurveType = 2, | |
346 kGABDE_ParaCurveType = 3, | |
347 kGABCDEF_ParaCurveType = 4, | |
348 }; | |
349 | |
350 // Determine the format of the parametric curve tag. | |
351 uint16_t format = read_big_endian_short(src + 8); | |
352 if (kExponential_ParaCurveType == format) { | |
353 tagBytes = 12 + 4; | |
354 if (len < tagBytes) { | 432 if (len < tagBytes) { |
355 SkColorSpacePrintf("gamma tag is too small (%d bytes)",
len); | 433 SkColorSpacePrintf("gamma tag is too small (%d bytes)",
len); |
356 return false; | 434 return SkGammas::Type::kNone_Type; |
357 } | 435 } |
358 | 436 |
359 // Y = X^g | 437 // Y = (aX + b)^g + c for X >= -b/a |
360 int32_t g = read_big_endian_int(src + 12); | 438 // Y = c otherwise |
361 set_gamma_value(&gammas[i], SkFixedToFloat(g)); | 439 c = read_big_endian_16_dot_16(src + 24); |
362 } else { | 440 d = -b / a; |
363 // Here's where the real parametric gammas start. There are
many | 441 f = c; |
364 // permutations of the same equations. | 442 break; |
365 // | 443 case kGABDE_ParaCurveType: |
| 444 tagBytes = 12 + 20; |
| 445 if (len < tagBytes) { |
| 446 SkColorSpacePrintf("gamma tag is too small (%d bytes)",
len); |
| 447 return SkGammas::Type::kNone_Type; |
| 448 } |
| 449 |
| 450 // Y = (aX + b)^g for X >= d |
| 451 // Y = eX otherwise |
| 452 d = read_big_endian_16_dot_16(src + 28); |
| 453 |
| 454 // Not a bug! We define |e| to always be the coefficient on
X in the |
| 455 // second equation. The spec calls this |c| in this particu
lar equation. |
| 456 // We don't follow their convention because then |c| would h
ave a |
| 457 // different meaning in each of our cases. |
| 458 e = read_big_endian_16_dot_16(src + 24); |
| 459 break; |
| 460 case kGABCDEF_ParaCurveType: |
| 461 tagBytes = 12 + 28; |
| 462 if (len < tagBytes) { |
| 463 SkColorSpacePrintf("gamma tag is too small (%d bytes)",
len); |
| 464 return SkGammas::Type::kNone_Type; |
| 465 } |
| 466 |
366 // Y = (aX + b)^g + c for X >= d | 467 // Y = (aX + b)^g + c for X >= d |
367 // Y = eX + f otherwise | 468 // Y = eX + f otherwise |
368 // | 469 // NOTE: The ICC spec writes "cX" in place of "eX" but I thi
nk |
369 // We will fill in with zeros as necessary to always match t
he above form. | 470 // it's a typo. |
370 float g = 0.0f, a = 0.0f, b = 0.0f, c = 0.0f, d = 0.0f, e =
0.0f, f = 0.0f; | 471 c = read_big_endian_16_dot_16(src + 24); |
371 switch(format) { | 472 d = read_big_endian_16_dot_16(src + 28); |
372 case kGAB_ParaCurveType: { | 473 e = read_big_endian_16_dot_16(src + 32); |
373 tagBytes = 12 + 12; | 474 f = read_big_endian_16_dot_16(src + 36); |
374 if (len < tagBytes) { | 475 break; |
375 SkColorSpacePrintf("gamma tag is too small (%d b
ytes)", len); | 476 default: |
376 return false; | 477 SkASSERT(false); |
377 } | 478 return SkGammas::Type::kNone_Type; |
378 | 479 } |
379 // Y = (aX + b)^g for X >= -b/a | 480 |
380 // Y = 0 otherwise | 481 // Recognize and simplify a very common parametric representation of
sRGB gamma. |
381 g = SkFixedToFloat(read_big_endian_int(src + 12)); | 482 if (color_space_almost_equal(0.9479f, a) && |
382 a = SkFixedToFloat(read_big_endian_int(src + 16)); | 483 color_space_almost_equal(0.0521f, b) && |
383 if (0.0f == a) { | 484 color_space_almost_equal(0.0000f, c) && |
384 return false; | 485 color_space_almost_equal(0.0405f, d) && |
385 } | 486 color_space_almost_equal(0.0774f, e) && |
386 | 487 color_space_almost_equal(0.0000f, f) && |
387 b = SkFixedToFloat(read_big_endian_int(src + 20)); | 488 color_space_almost_equal(2.4000f, g)) { |
388 d = -b / a; | 489 outData->fNamed = SkColorSpace::kSRGB_GammaNamed; |
389 break; | 490 return SkGammas::Type::kNamed_Type; |
390 } | 491 } |
391 case kGABC_ParaCurveType: | 492 |
392 tagBytes = 12 + 16; | 493 // Fail on invalid gammas. |
393 if (len < tagBytes) { | 494 if (SkScalarIsNaN(d)) { |
394 SkColorSpacePrintf("gamma tag is too small (%d b
ytes)", len); | 495 return SkGammas::Type::kNone_Type; |
395 return false; | 496 } |
396 } | 497 |
397 | 498 if (d <= 0.0f) { |
398 // Y = (aX + b)^g + c for X >= -b/a | 499 // Y = (aX + b)^g + c for always |
399 // Y = c otherwise | 500 if (0.0f == a || 0.0f == g) { |
400 g = SkFixedToFloat(read_big_endian_int(src + 12)); | 501 SkColorSpacePrintf("A or G is zero, constant gamma function
" |
401 a = SkFixedToFloat(read_big_endian_int(src + 16)); | 502 "is nonsense"); |
402 if (0.0f == a) { | 503 return SkGammas::Type::kNone_Type; |
403 return false; | 504 } |
404 } | 505 } |
405 | 506 |
406 b = SkFixedToFloat(read_big_endian_int(src + 20)); | 507 if (d >= 1.0f) { |
407 c = SkFixedToFloat(read_big_endian_int(src + 24)); | 508 // Y = eX + f for always |
408 d = -b / a; | 509 if (0.0f == e) { |
409 f = c; | 510 SkColorSpacePrintf("E is zero, constant gamma function is " |
410 break; | 511 "nonsense"); |
411 case kGABDE_ParaCurveType: | 512 return SkGammas::Type::kNone_Type; |
412 tagBytes = 12 + 20; | 513 } |
413 if (len < tagBytes) { | 514 } |
414 SkColorSpacePrintf("gamma tag is too small (%d b
ytes)", len); | 515 |
415 return false; | 516 if ((0.0f == a || 0.0f == g) && 0.0f == e) { |
416 } | 517 SkColorSpacePrintf("A or G, and E are zero, constant gamma funct
ion " |
417 | 518 "is nonsense"); |
418 // Y = (aX + b)^g for X >= d | 519 return SkGammas::Type::kNone_Type; |
419 // Y = cX otherwise | 520 } |
420 g = SkFixedToFloat(read_big_endian_int(src + 12)); | 521 |
421 a = SkFixedToFloat(read_big_endian_int(src + 16)); | 522 *outTagBytes = tagBytes; |
422 b = SkFixedToFloat(read_big_endian_int(src + 20)); | 523 |
423 d = SkFixedToFloat(read_big_endian_int(src + 28)); | 524 outParams->fG = g; |
424 e = SkFixedToFloat(read_big_endian_int(src + 24)); | 525 outParams->fA = a; |
425 break; | 526 outParams->fB = b; |
426 case kGABCDEF_ParaCurveType: | 527 outParams->fC = c; |
427 tagBytes = 12 + 28; | 528 outParams->fD = d; |
428 if (len < tagBytes) { | 529 outParams->fE = e; |
429 SkColorSpacePrintf("gamma tag is too small (%d b
ytes)", len); | 530 outParams->fF = f; |
430 return false; | 531 return SkGammas::Type::kParam_Type; |
431 } | |
432 | |
433 // Y = (aX + b)^g + c for X >= d | |
434 // Y = eX + f otherwise | |
435 // NOTE: The ICC spec writes "cX" in place of "eX" b
ut I think | |
436 // it's a typo. | |
437 g = SkFixedToFloat(read_big_endian_int(src + 12)); | |
438 a = SkFixedToFloat(read_big_endian_int(src + 16)); | |
439 b = SkFixedToFloat(read_big_endian_int(src + 20)); | |
440 c = SkFixedToFloat(read_big_endian_int(src + 24)); | |
441 d = SkFixedToFloat(read_big_endian_int(src + 28)); | |
442 e = SkFixedToFloat(read_big_endian_int(src + 32)); | |
443 f = SkFixedToFloat(read_big_endian_int(src + 36)); | |
444 break; | |
445 default: | |
446 SkColorSpacePrintf("Invalid parametric curve type\n"
); | |
447 return false; | |
448 } | |
449 | |
450 // Recognize and simplify a very common parametric represent
ation of sRGB gamma. | |
451 if (color_space_almost_equal(0.9479f, a) && | |
452 color_space_almost_equal(0.0521f, b) && | |
453 color_space_almost_equal(0.0000f, c) && | |
454 color_space_almost_equal(0.0405f, d) && | |
455 color_space_almost_equal(0.0774f, e) && | |
456 color_space_almost_equal(0.0000f, f) && | |
457 color_space_almost_equal(2.4000f, g)) { | |
458 gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed; | |
459 } else { | |
460 // Fail on invalid gammas. | |
461 if (d <= 0.0f) { | |
462 // Y = (aX + b)^g + c for always | |
463 if (0.0f == a || 0.0f == g) { | |
464 SkColorSpacePrintf("A or G is zero, constant gam
ma function " | |
465 "is nonsense"); | |
466 return false; | |
467 } | |
468 } else if (d >= 1.0f) { | |
469 // Y = eX + f for always | |
470 if (0.0f == e) { | |
471 SkColorSpacePrintf("E is zero, constant gamma fu
nction is " | |
472 "nonsense"); | |
473 return false; | |
474 } | |
475 } else if ((0.0f == a || 0.0f == g) && 0.0f == e) { | |
476 SkColorSpacePrintf("A or G, and E are zero, constant
gamma function " | |
477 "is nonsense"); | |
478 return false; | |
479 } | |
480 | |
481 gammas[i].fG = g; | |
482 gammas[i].fA = a; | |
483 gammas[i].fB = b; | |
484 gammas[i].fC = c; | |
485 gammas[i].fD = d; | |
486 gammas[i].fE = e; | |
487 gammas[i].fF = f; | |
488 } | |
489 } | |
490 | |
491 break; | |
492 } | |
493 default: | |
494 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type); | |
495 return false; | |
496 } | 532 } |
497 | 533 default: |
498 // Ensure that we have successfully read a gamma representation. | 534 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type); |
499 SkASSERT(gammas[i].isNamed() || gammas[i].isValue() || gammas[i].isTable
() || | 535 return SkGammas::Type::kNone_Type; |
500 gammas[i].isParametric()); | 536 } |
501 | 537 } |
502 // Adjust src and len if there is another gamma curve to load. | 538 |
503 if (i != numGammas - 1) { | 539 /** |
504 // Each curve is padded to 4-byte alignment. | 540 * Returns the additional size in bytes needed to store the gamma tag. |
505 tagBytes = SkAlign4(tagBytes); | 541 */ |
506 if (len < tagBytes) { | 542 static size_t gamma_alloc_size(SkGammas::Type type, const SkGammas::Data& data)
{ |
507 return false; | 543 switch (type) { |
508 } | 544 case SkGammas::Type::kNamed_Type: |
509 | 545 case SkGammas::Type::kValue_Type: |
510 src += tagBytes; | 546 return 0; |
511 len -= tagBytes; | 547 case SkGammas::Type::kTable_Type: |
| 548 return sizeof(float) * data.fTable.fSize; |
| 549 case SkGammas::Type::kParam_Type: |
| 550 return sizeof(SkGammas::Params); |
| 551 default: |
| 552 SkASSERT(false); |
| 553 return 0; |
| 554 } |
| 555 } |
| 556 |
| 557 /** |
| 558 * Sets invalid gamma to the default value. |
| 559 */ |
| 560 static void handle_invalid_gamma(SkGammas::Type* type, SkGammas::Data* data) { |
| 561 if (SkGammas::Type::kNone_Type == *type) { |
| 562 *type = SkGammas::Type::kNamed_Type; |
| 563 data->fNamed = SkColorSpace::kSRGB_GammaNamed; |
| 564 } |
| 565 } |
| 566 |
| 567 /** |
| 568 * Finish loading the gammas, now that we have allocated memory for the SkGamma
s struct. |
| 569 * |
| 570 * There's nothing to do for the simple cases, but for table gammas we need to
actually |
| 571 * read the table into heap memory. And for parametric gammas, we need to copy
over the |
| 572 * parameter values. |
| 573 * |
| 574 * @param memory Pointer to start of the SkGammas memory block |
| 575 * @param offset Bytes of memory (after the SkGammas struct) that are already i
n use. |
| 576 * @param data In-out variable. Will fill in the offset to the table or para
meters |
| 577 * if necessary. |
| 578 * @param params Parameters for gamma curve. Only initialized/used when we hav
e a |
| 579 * parametric gamma. |
| 580 * @param src Pointer to start of the gamma tag. |
| 581 * |
| 582 * @return Additional bytes of memory that are being used by this gamma c
urve. |
| 583 */ |
| 584 static size_t load_gammas(void* memory, size_t offset, SkGammas::Type type, |
| 585 SkGammas::Data* data, const SkGammas::Params& params, |
| 586 const uint8_t* src) { |
| 587 void* storage = SkTAddOffset<void>(memory, offset + sizeof(SkGammas)); |
| 588 |
| 589 switch (type) { |
| 590 case SkGammas::Type::kNamed_Type: |
| 591 case SkGammas::Type::kValue_Type: |
| 592 // Nothing to do here. |
| 593 return 0; |
| 594 case SkGammas::Type::kTable_Type: { |
| 595 data->fTable.fOffset = offset; |
| 596 |
| 597 float* outTable = (float*) storage; |
| 598 const uint16_t* inTable = (const uint16_t*) (src + 12); |
| 599 for (int i = 0; i < data->fTable.fSize; i++) { |
| 600 outTable[i] = (read_big_endian_short((const uint8_t*) &inTable[i
])) / 65535.0f; |
| 601 } |
| 602 |
| 603 return sizeof(float) * data->fTable.fSize; |
512 } | 604 } |
513 } | 605 case SkGammas::Type::kParam_Type: |
514 | 606 data->fTable.fOffset = offset; |
515 return true; | 607 memcpy(storage, ¶ms, sizeof(SkGammas::Params)); |
| 608 return sizeof(SkGammas::Params); |
| 609 default: |
| 610 SkASSERT(false); |
| 611 return 0; |
| 612 } |
516 } | 613 } |
517 | 614 |
518 static constexpr uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' '); | 615 static constexpr uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' '); |
519 | 616 |
520 bool load_color_lut(SkColorLookUpTable* colorLUT, uint32_t inputChannels, uint32
_t outputChannels, | 617 static bool load_color_lut(SkColorLookUpTable* colorLUT, uint32_t inputChannels, |
521 const uint8_t* src, size_t len) { | 618 uint32_t outputChannels, const uint8_t* src, size_t l
en) { |
522 // 16 bytes reserved for grid points, 2 for precision, 2 for padding. | 619 // 16 bytes reserved for grid points, 2 for precision, 2 for padding. |
523 // The color LUT data follows after this header. | 620 // The color LUT data follows after this header. |
524 static constexpr uint32_t kColorLUTHeaderSize = 20; | 621 static constexpr uint32_t kColorLUTHeaderSize = 20; |
525 if (len < kColorLUTHeaderSize) { | 622 if (len < kColorLUTHeaderSize) { |
526 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); | 623 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); |
527 return false; | 624 return false; |
528 } | 625 } |
529 size_t dataLen = len - kColorLUTHeaderSize; | 626 size_t dataLen = len - kColorLUTHeaderSize; |
530 | 627 |
531 SkASSERT(3 == inputChannels && 3 == outputChannels); | 628 SkASSERT(3 == inputChannels && 3 == outputChannels); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
580 if (1 == precision) { | 677 if (1 == precision) { |
581 colorLUT->fTable[i] = ((float) ptr[i]) / 255.0f; | 678 colorLUT->fTable[i] = ((float) ptr[i]) / 255.0f; |
582 } else { | 679 } else { |
583 colorLUT->fTable[i] = ((float) read_big_endian_short(ptr)) / 65535.0
f; | 680 colorLUT->fTable[i] = ((float) read_big_endian_short(ptr)) / 65535.0
f; |
584 } | 681 } |
585 } | 682 } |
586 | 683 |
587 return true; | 684 return true; |
588 } | 685 } |
589 | 686 |
590 bool load_matrix(SkMatrix44* toXYZ, const uint8_t* src, size_t len) { | 687 static bool load_matrix(SkMatrix44* toXYZ, const uint8_t* src, size_t len) { |
591 if (len < 48) { | 688 if (len < 48) { |
592 SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len); | 689 SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len); |
593 return false; | 690 return false; |
594 } | 691 } |
595 | 692 |
596 // For this matrix to behave like our "to XYZ D50" matrices, it needs to be
scaled. | 693 // For this matrix to behave like our "to XYZ D50" matrices, it needs to be
scaled. |
597 constexpr float scale = 65535.0 / 32768.0; | 694 constexpr float scale = 65535.0 / 32768.0; |
598 float array[16]; | 695 float array[16]; |
599 array[ 0] = scale * SkFixedToFloat(read_big_endian_int(src)); | 696 array[ 0] = scale * SkFixedToFloat(read_big_endian_int(src)); |
600 array[ 1] = scale * SkFixedToFloat(read_big_endian_int(src + 4)); | 697 array[ 1] = scale * SkFixedToFloat(read_big_endian_int(src + 4)); |
601 array[ 2] = scale * SkFixedToFloat(read_big_endian_int(src + 8)); | 698 array[ 2] = scale * SkFixedToFloat(read_big_endian_int(src + 8)); |
602 array[ 3] = scale * SkFixedToFloat(read_big_endian_int(src + 36)); // transl
ate R | 699 array[ 3] = scale * SkFixedToFloat(read_big_endian_int(src + 36)); // transl
ate R |
603 array[ 4] = scale * SkFixedToFloat(read_big_endian_int(src + 12)); | 700 array[ 4] = scale * SkFixedToFloat(read_big_endian_int(src + 12)); |
604 array[ 5] = scale * SkFixedToFloat(read_big_endian_int(src + 16)); | 701 array[ 5] = scale * SkFixedToFloat(read_big_endian_int(src + 16)); |
605 array[ 6] = scale * SkFixedToFloat(read_big_endian_int(src + 20)); | 702 array[ 6] = scale * SkFixedToFloat(read_big_endian_int(src + 20)); |
606 array[ 7] = scale * SkFixedToFloat(read_big_endian_int(src + 40)); // transl
ate G | 703 array[ 7] = scale * SkFixedToFloat(read_big_endian_int(src + 40)); // transl
ate G |
607 array[ 8] = scale * SkFixedToFloat(read_big_endian_int(src + 24)); | 704 array[ 8] = scale * SkFixedToFloat(read_big_endian_int(src + 24)); |
608 array[ 9] = scale * SkFixedToFloat(read_big_endian_int(src + 28)); | 705 array[ 9] = scale * SkFixedToFloat(read_big_endian_int(src + 28)); |
609 array[10] = scale * SkFixedToFloat(read_big_endian_int(src + 32)); | 706 array[10] = scale * SkFixedToFloat(read_big_endian_int(src + 32)); |
610 array[11] = scale * SkFixedToFloat(read_big_endian_int(src + 44)); // transl
ate B | 707 array[11] = scale * SkFixedToFloat(read_big_endian_int(src + 44)); // transl
ate B |
611 array[12] = 0.0f; | 708 array[12] = 0.0f; |
612 array[13] = 0.0f; | 709 array[13] = 0.0f; |
613 array[14] = 0.0f; | 710 array[14] = 0.0f; |
614 array[15] = 1.0f; | 711 array[15] = 1.0f; |
615 toXYZ->setColMajorf(array); | 712 toXYZ->setColMajorf(array); |
616 return true; | 713 return true; |
617 } | 714 } |
618 | 715 |
619 bool load_a2b0(SkColorLookUpTable* colorLUT, SkGammaCurve* gammas, SkMatrix44* t
oXYZ, | 716 static bool load_a2b0(SkColorLookUpTable* colorLUT, SkColorSpace::GammaNamed* ga
mmaNamed, |
620 const uint8_t* src, size_t len) { | 717 sk_sp<SkGammas>* gammas, SkMatrix44* toXYZ, const uint8_t*
src, size_t len) { |
621 if (len < 32) { | 718 if (len < 32) { |
622 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len); | 719 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len); |
623 return false; | 720 return false; |
624 } | 721 } |
625 | 722 |
626 uint32_t type = read_big_endian_uint(src); | 723 uint32_t type = read_big_endian_uint(src); |
627 if (kTAG_AtoBType != type) { | 724 if (kTAG_AtoBType != type) { |
628 // FIXME (msarett): Need to support lut8Type and lut16Type. | 725 // FIXME (msarett): Need to support lut8Type and lut16Type. |
629 SkColorSpacePrintf("Unsupported A to B tag type.\n"); | 726 SkColorSpacePrintf("Unsupported A to B tag type.\n"); |
630 return false; | 727 return false; |
(...skipping 27 matching lines...) Expand all Loading... |
658 uint32_t offsetToColorLUT = read_big_endian_int(src + 24); | 755 uint32_t offsetToColorLUT = read_big_endian_int(src + 24); |
659 if (0 != offsetToColorLUT && offsetToColorLUT < len) { | 756 if (0 != offsetToColorLUT && offsetToColorLUT < len) { |
660 if (!load_color_lut(colorLUT, inputChannels, outputChannels, src + offse
tToColorLUT, | 757 if (!load_color_lut(colorLUT, inputChannels, outputChannels, src + offse
tToColorLUT, |
661 len - offsetToColorLUT)) { | 758 len - offsetToColorLUT)) { |
662 SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n"); | 759 SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n"); |
663 } | 760 } |
664 } | 761 } |
665 | 762 |
666 uint32_t offsetToMCurves = read_big_endian_int(src + 20); | 763 uint32_t offsetToMCurves = read_big_endian_int(src + 20); |
667 if (0 != offsetToMCurves && offsetToMCurves < len) { | 764 if (0 != offsetToMCurves && offsetToMCurves < len) { |
668 if (!load_gammas(gammas, outputChannels, src + offsetToMCurves, len - of
fsetToMCurves)) { | 765 const uint8_t* rTagPtr = src + offsetToMCurves; |
669 SkColorSpacePrintf("Failed to read M curves from A to B tag. Using
linear gamma.\n"); | 766 size_t tagLen = len - offsetToMCurves; |
670 gammas[0].fNamed = SkColorSpace::kLinear_GammaNamed; | 767 |
671 gammas[1].fNamed = SkColorSpace::kLinear_GammaNamed; | 768 SkGammas::Data rData; |
672 gammas[2].fNamed = SkColorSpace::kLinear_GammaNamed; | 769 SkGammas::Params rParams; |
| 770 |
| 771 // On an invalid first gamma, tagBytes remains set as zero. This causes
the two |
| 772 // subsequent to be treated as identical (which is what we want). |
| 773 size_t tagBytes = 0; |
| 774 SkGammas::Type rType = parse_gamma(&rData, &rParams, &tagBytes, rTagPtr,
tagLen); |
| 775 handle_invalid_gamma(&rType, &rData); |
| 776 size_t alignedTagBytes = SkAlign4(tagBytes); |
| 777 |
| 778 if ((3 * alignedTagBytes <= tagLen) && |
| 779 !memcmp(rTagPtr, rTagPtr + 1 * alignedTagBytes, tagBytes) && |
| 780 !memcmp(rTagPtr, rTagPtr + 2 * alignedTagBytes, tagBytes)) |
| 781 { |
| 782 if (SkGammas::Type::kNamed_Type == rType) { |
| 783 *gammaNamed = rData.fNamed; |
| 784 } else { |
| 785 size_t allocSize = sizeof(SkGammas) + gamma_alloc_size(rType, rD
ata); |
| 786 void* memory = sk_malloc_throw(allocSize); |
| 787 *gammas = sk_sp<SkGammas>(new (memory) SkGammas()); |
| 788 load_gammas(memory, 0, rType, &rData, rParams, rTagPtr); |
| 789 |
| 790 (*gammas)->fRedType = rType; |
| 791 (*gammas)->fGreenType = rType; |
| 792 (*gammas)->fBlueType = rType; |
| 793 |
| 794 (*gammas)->fRedData = rData; |
| 795 (*gammas)->fGreenData = rData; |
| 796 (*gammas)->fBlueData = rData; |
| 797 } |
| 798 } else { |
| 799 const uint8_t* gTagPtr = rTagPtr + alignedTagBytes; |
| 800 tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0; |
| 801 SkGammas::Data gData; |
| 802 SkGammas::Params gParams; |
| 803 tagBytes = 0; |
| 804 SkGammas::Type gType = parse_gamma(&gData, &gParams, &tagBytes, gTag
Ptr, |
| 805 tagLen); |
| 806 handle_invalid_gamma(&gType, &gData); |
| 807 |
| 808 alignedTagBytes = SkAlign4(tagBytes); |
| 809 const uint8_t* bTagPtr = gTagPtr + alignedTagBytes; |
| 810 tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0; |
| 811 SkGammas::Data bData; |
| 812 SkGammas::Params bParams; |
| 813 SkGammas::Type bType = parse_gamma(&bData, &bParams, &tagBytes, bTag
Ptr, |
| 814 tagLen); |
| 815 handle_invalid_gamma(&bType, &bData); |
| 816 |
| 817 size_t allocSize = sizeof(SkGammas) + gamma_alloc_size(rType, rData) |
| 818 + gamma_alloc_size(gType, gData) |
| 819 + gamma_alloc_size(bType, bData)
; |
| 820 void* memory = sk_malloc_throw(allocSize); |
| 821 *gammas = sk_sp<SkGammas>(new (memory) SkGammas()); |
| 822 |
| 823 uint32_t offset = 0; |
| 824 (*gammas)->fRedType = rType; |
| 825 offset += load_gammas(memory, offset, rType, &rData, rParams, rTagPt
r); |
| 826 |
| 827 (*gammas)->fGreenType = gType; |
| 828 offset += load_gammas(memory, offset, gType, &gData, gParams, gTagPt
r); |
| 829 |
| 830 (*gammas)->fBlueType = bType; |
| 831 load_gammas(memory, offset, bType, &bData, bParams, bTagPtr); |
| 832 |
| 833 (*gammas)->fRedData = rData; |
| 834 (*gammas)->fGreenData = gData; |
| 835 (*gammas)->fBlueData = bData; |
673 } | 836 } |
674 } | 837 } |
675 | 838 |
676 uint32_t offsetToMatrix = read_big_endian_int(src + 16); | 839 uint32_t offsetToMatrix = read_big_endian_int(src + 16); |
677 if (0 != offsetToMatrix && offsetToMatrix < len) { | 840 if (0 != offsetToMatrix && offsetToMatrix < len) { |
678 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) { | 841 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) { |
679 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); | 842 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); |
680 toXYZ->setIdentity(); | 843 toXYZ->setIdentity(); |
681 } | 844 } |
682 } | 845 } |
683 | 846 |
684 return true; | 847 return true; |
685 } | 848 } |
686 | 849 |
| 850 static bool tag_equals(const ICCTag* a, const ICCTag* b, const uint8_t* base) { |
| 851 if (!a || !b) { |
| 852 return a == b; |
| 853 } |
| 854 |
| 855 if (a->fLength != b->fLength) { |
| 856 return false; |
| 857 } |
| 858 |
| 859 if (a->fOffset == b->fOffset) { |
| 860 return true; |
| 861 } |
| 862 |
| 863 return !memcmp(a->addr(base), b->addr(base), a->fLength); |
| 864 } |
| 865 |
687 sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* input, size_t len) { | 866 sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* input, size_t len) { |
688 if (!input || len < kICCHeaderSize) { | 867 if (!input || len < kICCHeaderSize) { |
689 return_null("Data is null or not large enough to contain an ICC profile"
); | 868 return_null("Data is null or not large enough to contain an ICC profile"
); |
690 } | 869 } |
691 | 870 |
692 // Create our own copy of the input. | 871 // Create our own copy of the input. |
693 void* memory = sk_malloc_throw(len); | 872 void* memory = sk_malloc_throw(len); |
694 memcpy(memory, input, len); | 873 memcpy(memory, input, len); |
695 sk_sp<SkData> data = SkData::MakeFromMalloc(memory, len); | 874 sk_sp<SkData> data = SkData::MakeFromMalloc(memory, len); |
696 const void* base = data->data(); | 875 const uint8_t* base = data->bytes(); |
697 const uint8_t* ptr = (const uint8_t*) base; | 876 const uint8_t* ptr = base; |
698 | 877 |
699 // Read the ICC profile header and check to make sure that it is valid. | 878 // Read the ICC profile header and check to make sure that it is valid. |
700 ICCProfileHeader header; | 879 ICCProfileHeader header; |
701 header.init(ptr, len); | 880 header.init(ptr, len); |
702 if (!header.valid()) { | 881 if (!header.valid()) { |
703 return nullptr; | 882 return nullptr; |
704 } | 883 } |
705 | 884 |
706 // Adjust ptr and len before reading the tags. | 885 // Adjust ptr and len before reading the tags. |
707 if (len < header.fSize) { | 886 if (len < header.fSize) { |
(...skipping 25 matching lines...) Expand all Loading... |
733 } | 912 } |
734 | 913 |
735 switch (header.fInputColorSpace) { | 914 switch (header.fInputColorSpace) { |
736 case kRGB_ColorSpace: { | 915 case kRGB_ColorSpace: { |
737 // Recognize the rXYZ, gXYZ, and bXYZ tags. | 916 // Recognize the rXYZ, gXYZ, and bXYZ tags. |
738 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); | 917 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); |
739 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); | 918 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); |
740 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); | 919 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); |
741 if (r && g && b) { | 920 if (r && g && b) { |
742 float toXYZ[9]; | 921 float toXYZ[9]; |
743 if (!load_xyz(&toXYZ[0], r->addr((const uint8_t*) base), r->fLen
gth) || | 922 if (!load_xyz(&toXYZ[0], r->addr(base), r->fLength) || |
744 !load_xyz(&toXYZ[3], g->addr((const uint8_t*) base), g->fLen
gth) || | 923 !load_xyz(&toXYZ[3], g->addr(base), g->fLength) || |
745 !load_xyz(&toXYZ[6], b->addr((const uint8_t*) base), b->fLen
gth)) | 924 !load_xyz(&toXYZ[6], b->addr(base), b->fLength)) |
746 { | 925 { |
747 return_null("Need valid rgb tags for XYZ space"); | 926 return_null("Need valid rgb tags for XYZ space"); |
748 } | 927 } |
749 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); | 928 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); |
750 mat.set3x3RowMajorf(toXYZ); | 929 mat.set3x3RowMajorf(toXYZ); |
751 | 930 |
752 // It is not uncommon to see missing or empty gamma tags. This
indicates | |
753 // that we should use unit gamma. | |
754 SkGammaCurve curves[3]; | |
755 r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC); | 931 r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC); |
756 g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC); | 932 g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC); |
757 b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC); | 933 b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC); |
758 if (!r || !load_gammas(&curves[0], 1, r->addr((const uint8_t*) b
ase), r->fLength)) | 934 |
759 { | 935 // If some, but not all, of the gamma tags are missing, assume t
hat all |
760 SkColorSpacePrintf("Failed to read R gamma tag.\n"); | 936 // gammas are meant to be the same. This behavior is an arbitra
ry guess, |
761 curves[0].fNamed = SkColorSpace::kLinear_GammaNamed; | 937 // but it simplifies the code below. |
762 } | 938 if ((!r || !g || !b) && (r || g || b)) { |
763 if (!g || !load_gammas(&curves[1], 1, g->addr((const uint8_t*) b
ase), g->fLength)) | 939 if (!r) { |
764 { | 940 r = g ? g : b; |
765 SkColorSpacePrintf("Failed to read G gamma tag.\n"); | 941 } |
766 curves[1].fNamed = SkColorSpace::kLinear_GammaNamed; | 942 |
767 } | 943 if (!g) { |
768 if (!b || !load_gammas(&curves[2], 1, b->addr((const uint8_t*) b
ase), b->fLength)) | 944 g = r ? r : b; |
769 { | 945 } |
770 SkColorSpacePrintf("Failed to read B gamma tag.\n"); | 946 |
771 curves[2].fNamed = SkColorSpace::kLinear_GammaNamed; | 947 if (!b) { |
| 948 b = r ? r : g; |
| 949 } |
772 } | 950 } |
773 | 951 |
774 GammaNamed gammaNamed = SkGammas::Named(curves); | 952 GammaNamed gammaNamed = kNonStandard_GammaNamed; |
| 953 sk_sp<SkGammas> gammas = nullptr; |
| 954 size_t tagBytes; |
| 955 if (r && g && b) { |
| 956 if (tag_equals(r, g, base) && tag_equals(g, b, base)) { |
| 957 SkGammas::Data data; |
| 958 SkGammas::Params params; |
| 959 SkGammas::Type Type = |
| 960 parse_gamma(&data, ¶ms, &tagBytes, r->addr(b
ase), r->fLength); |
| 961 handle_invalid_gamma(&Type, &data); |
| 962 |
| 963 if (SkGammas::Type::kNamed_Type == Type) { |
| 964 gammaNamed = data.fNamed; |
| 965 } else { |
| 966 size_t allocSize = sizeof(SkGammas) + gamma_alloc_si
ze(Type, data); |
| 967 void* memory = sk_malloc_throw(allocSize); |
| 968 gammas = sk_sp<SkGammas>(new (memory) SkGammas()); |
| 969 load_gammas(memory, 0, Type, &data, params, r->addr(
base)); |
| 970 |
| 971 gammas->fRedType = Type; |
| 972 gammas->fGreenType = Type; |
| 973 gammas->fBlueType = Type; |
| 974 |
| 975 gammas->fRedData = data; |
| 976 gammas->fGreenData = data; |
| 977 gammas->fBlueData = data; |
| 978 } |
| 979 } else { |
| 980 SkGammas::Data rData; |
| 981 SkGammas::Params rParams; |
| 982 SkGammas::Type rType = |
| 983 parse_gamma(&rData, &rParams, &tagBytes, r->addr
(base), r->fLength); |
| 984 handle_invalid_gamma(&rType, &rData); |
| 985 |
| 986 SkGammas::Data gData; |
| 987 SkGammas::Params gParams; |
| 988 SkGammas::Type gType = |
| 989 parse_gamma(&gData, &gParams, &tagBytes, g->addr
(base), g->fLength); |
| 990 handle_invalid_gamma(&gType, &gData); |
| 991 |
| 992 SkGammas::Data bData; |
| 993 SkGammas::Params bParams; |
| 994 SkGammas::Type bType = |
| 995 parse_gamma(&bData, &bParams, &tagBytes, b->addr
(base), b->fLength); |
| 996 handle_invalid_gamma(&bType, &bData); |
| 997 |
| 998 size_t allocSize = sizeof(SkGammas) + gamma_alloc_size(r
Type, rData) |
| 999 + gamma_alloc_size(g
Type, gData) |
| 1000 + gamma_alloc_size(b
Type, bData); |
| 1001 void* memory = sk_malloc_throw(allocSize); |
| 1002 gammas = sk_sp<SkGammas>(new (memory) SkGammas()); |
| 1003 |
| 1004 uint32_t offset = 0; |
| 1005 gammas->fRedType = rType; |
| 1006 offset += load_gammas(memory, offset, rType, &rData, rPa
rams, |
| 1007 r->addr(base)); |
| 1008 |
| 1009 gammas->fGreenType = gType; |
| 1010 offset += load_gammas(memory, offset, gType, &gData, gPa
rams, |
| 1011 g->addr(base)); |
| 1012 |
| 1013 gammas->fBlueType = bType; |
| 1014 load_gammas(memory, offset, bType, &bData, bParams, b->a
ddr(base)); |
| 1015 |
| 1016 gammas->fRedData = rData; |
| 1017 gammas->fGreenData = gData; |
| 1018 gammas->fBlueData = bData; |
| 1019 } |
| 1020 } else { |
| 1021 gammaNamed = kLinear_GammaNamed; |
| 1022 } |
| 1023 |
775 if (kNonStandard_GammaNamed == gammaNamed) { | 1024 if (kNonStandard_GammaNamed == gammaNamed) { |
776 sk_sp<SkGammas> gammas = sk_make_sp<SkGammas>(std::move(curv
es[0]), | 1025 return sk_sp<SkColorSpace>(new SkColorSpace_Base(nullptr, ga
mmaNamed, |
777 std::move(curv
es[1]), | 1026 std::move(g
ammas), mat, |
778 std::move(curv
es[2])); | 1027 std::move(d
ata))); |
779 return sk_sp<SkColorSpace>(new SkColorSpace_Base(nullptr, st
d::move(gammas), | |
780 mat, std::m
ove(data))); | |
781 } else { | 1028 } else { |
782 return SkColorSpace_Base::NewRGB(gammaNamed, mat); | 1029 return SkColorSpace_Base::NewRGB(gammaNamed, mat); |
783 } | 1030 } |
784 } | 1031 } |
785 | 1032 |
786 // Recognize color profile specified by A2B0 tag. | 1033 // Recognize color profile specified by A2B0 tag. |
787 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); | 1034 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); |
788 if (a2b0) { | 1035 if (a2b0) { |
| 1036 GammaNamed gammaNamed = kNonStandard_GammaNamed; |
| 1037 sk_sp<SkGammas> gammas = nullptr; |
789 sk_sp<SkColorLookUpTable> colorLUT = sk_make_sp<SkColorLookUpTab
le>(); | 1038 sk_sp<SkColorLookUpTable> colorLUT = sk_make_sp<SkColorLookUpTab
le>(); |
790 SkGammaCurve curves[3]; | |
791 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); | 1039 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); |
792 if (!load_a2b0(colorLUT.get(), curves, &toXYZ, a2b0->addr((const
uint8_t*) base), | 1040 if (!load_a2b0(colorLUT.get(), &gammaNamed, &gammas, &toXYZ, a2b
0->addr(base), |
793 a2b0->fLength)) { | 1041 a2b0->fLength)) { |
794 return_null("Failed to parse A2B0 tag"); | 1042 return_null("Failed to parse A2B0 tag"); |
795 } | 1043 } |
796 | 1044 |
797 GammaNamed gammaNamed = SkGammas::Named(curves); | |
798 colorLUT = colorLUT->fTable ? colorLUT : nullptr; | 1045 colorLUT = colorLUT->fTable ? colorLUT : nullptr; |
799 if (colorLUT || kNonStandard_GammaNamed == gammaNamed) { | 1046 if (colorLUT || kNonStandard_GammaNamed == gammaNamed) { |
800 sk_sp<SkGammas> gammas = sk_make_sp<SkGammas>(std::move(curv
es[0]), | |
801 std::move(curv
es[1]), | |
802 std::move(curv
es[2])); | |
803 | |
804 return sk_sp<SkColorSpace>(new SkColorSpace_Base(std::move(c
olorLUT), | 1047 return sk_sp<SkColorSpace>(new SkColorSpace_Base(std::move(c
olorLUT), |
805 std::move(g
ammas), toXYZ, | 1048 gammaNamed,
std::move(gammas), |
806 std::move(d
ata))); | 1049 toXYZ, std:
:move(data))); |
807 } else { | 1050 } else { |
808 return SkColorSpace_Base::NewRGB(gammaNamed, toXYZ); | 1051 return SkColorSpace_Base::NewRGB(gammaNamed, toXYZ); |
809 } | 1052 } |
810 } | 1053 } |
811 } | 1054 } |
812 default: | 1055 default: |
813 break; | 1056 break; |
814 } | 1057 } |
815 | 1058 |
816 return_null("ICC profile contains unsupported colorspace"); | 1059 return_null("ICC profile contains unsupported colorspace"); |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
938 ptr[2] = SkEndian_SwapBE32(1); | 1181 ptr[2] = SkEndian_SwapBE32(1); |
939 | 1182 |
940 // Convert gamma to 16-bit fixed point. | 1183 // Convert gamma to 16-bit fixed point. |
941 uint16_t* ptr16 = (uint16_t*) (ptr + 3); | 1184 uint16_t* ptr16 = (uint16_t*) (ptr + 3); |
942 ptr16[0] = SkEndian_SwapBE16((uint16_t) (value * 256.0f)); | 1185 ptr16[0] = SkEndian_SwapBE16((uint16_t) (value * 256.0f)); |
943 | 1186 |
944 // Pad tag with zero. | 1187 // Pad tag with zero. |
945 ptr16[1] = 0; | 1188 ptr16[1] = 0; |
946 } | 1189 } |
947 | 1190 |
948 static float get_gamma_value(const SkGammaCurve* curve) { | |
949 switch (curve->fNamed) { | |
950 case SkColorSpace::kSRGB_GammaNamed: | |
951 // FIXME (msarett): | |
952 // kSRGB cannot be represented by a value. Here we fall through to
2.2f, | |
953 // which is a close guess. To be more accurate, we need to represen
t sRGB | |
954 // gamma with a parametric curve. | |
955 case SkColorSpace::k2Dot2Curve_GammaNamed: | |
956 return 2.2f; | |
957 case SkColorSpace::kLinear_GammaNamed: | |
958 return 1.0f; | |
959 default: | |
960 SkASSERT(curve->isValue()); | |
961 return curve->fValue; | |
962 } | |
963 } | |
964 | |
965 sk_sp<SkData> SkColorSpace_Base::writeToICC() const { | 1191 sk_sp<SkData> SkColorSpace_Base::writeToICC() const { |
966 // Return if this object was created from a profile, or if we have already s
erialized | 1192 // Return if this object was created from a profile, or if we have already s
erialized |
967 // the profile. | 1193 // the profile. |
968 if (fProfileData) { | 1194 if (fProfileData) { |
969 return fProfileData; | 1195 return fProfileData; |
970 } | 1196 } |
971 | 1197 |
972 // The client may create an SkColorSpace using an SkMatrix44, but currently
we only | 1198 // The client may create an SkColorSpace using an SkMatrix44, but currently
we only |
973 // support writing profiles with 3x3 matrices. | 1199 // support writing profiles with 3x3 matrices. |
974 // TODO (msarett): Fix this! | 1200 // TODO (msarett): Fix this! |
(...skipping 23 matching lines...) Expand all Loading... |
998 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 0); | 1224 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 0); |
999 ptr += kTAG_XYZ_Bytes; | 1225 ptr += kTAG_XYZ_Bytes; |
1000 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 1); | 1226 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 1); |
1001 ptr += kTAG_XYZ_Bytes; | 1227 ptr += kTAG_XYZ_Bytes; |
1002 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 2); | 1228 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 2); |
1003 ptr += kTAG_XYZ_Bytes; | 1229 ptr += kTAG_XYZ_Bytes; |
1004 | 1230 |
1005 // Write TRC tags | 1231 // Write TRC tags |
1006 GammaNamed gammaNamed = this->gammaNamed(); | 1232 GammaNamed gammaNamed = this->gammaNamed(); |
1007 if (kNonStandard_GammaNamed == gammaNamed) { | 1233 if (kNonStandard_GammaNamed == gammaNamed) { |
1008 write_trc_tag((uint32_t*) ptr, get_gamma_value(&as_CSB(this)->fGammas->f
Red)); | 1234 // FIXME (msarett): |
| 1235 // Write the correct gamma representation rather than 2.2f. |
| 1236 write_trc_tag((uint32_t*) ptr, 2.2f); |
1009 ptr += SkAlign4(kTAG_TRC_Bytes); | 1237 ptr += SkAlign4(kTAG_TRC_Bytes); |
1010 write_trc_tag((uint32_t*) ptr, get_gamma_value(&as_CSB(this)->fGammas->f
Green)); | 1238 write_trc_tag((uint32_t*) ptr, 2.2f); |
1011 ptr += SkAlign4(kTAG_TRC_Bytes); | 1239 ptr += SkAlign4(kTAG_TRC_Bytes); |
1012 write_trc_tag((uint32_t*) ptr, get_gamma_value(&as_CSB(this)->fGammas->f
Blue)); | 1240 write_trc_tag((uint32_t*) ptr, 2.2f); |
1013 ptr += SkAlign4(kTAG_TRC_Bytes); | 1241 ptr += SkAlign4(kTAG_TRC_Bytes); |
1014 } else { | 1242 } else { |
1015 switch (gammaNamed) { | 1243 switch (gammaNamed) { |
1016 case SkColorSpace::kSRGB_GammaNamed: | 1244 case SkColorSpace::kSRGB_GammaNamed: |
1017 // FIXME (msarett): | 1245 // FIXME (msarett): |
1018 // kSRGB cannot be represented by a value. Here we fall through
to 2.2f, | 1246 // kSRGB cannot be represented by a value. Here we fall through
to 2.2f, |
1019 // which is a close guess. To be more accurate, we need to repr
esent sRGB | 1247 // which is a close guess. To be more accurate, we need to repr
esent sRGB |
1020 // gamma with a parametric curve. | 1248 // gamma with a parametric curve. |
1021 case SkColorSpace::k2Dot2Curve_GammaNamed: | 1249 case SkColorSpace::k2Dot2Curve_GammaNamed: |
1022 write_trc_tag((uint32_t*) ptr, 2.2f); | 1250 write_trc_tag((uint32_t*) ptr, 2.2f); |
(...skipping 28 matching lines...) Expand all Loading... |
1051 ptr32[4] = SkEndian_SwapBE32(0x000116cc); | 1279 ptr32[4] = SkEndian_SwapBE32(0x000116cc); |
1052 ptr += kTAG_XYZ_Bytes; | 1280 ptr += kTAG_XYZ_Bytes; |
1053 | 1281 |
1054 // Write copyright tag | 1282 // Write copyright tag |
1055 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); | 1283 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); |
1056 | 1284 |
1057 // TODO (msarett): Should we try to hold onto the data so we can return imme
diately if | 1285 // TODO (msarett): Should we try to hold onto the data so we can return imme
diately if |
1058 // the client calls again? | 1286 // the client calls again? |
1059 return SkData::MakeFromMalloc(profile.release(), kICCProfileSize); | 1287 return SkData::MakeFromMalloc(profile.release(), kICCProfileSize); |
1060 } | 1288 } |
OLD | NEW |