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