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 "SkColorPriv.h" | 8 #include "SkColorPriv.h" |
9 #include "SkColorSpace_Base.h" | 9 #include "SkColorSpace_Base.h" |
10 #include "SkColorSpaceXform.h" | 10 #include "SkColorSpaceXform.h" |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
257 v = v * 255.0f; | 257 v = v * 255.0f; |
258 if (v >= 254.5f) { | 258 if (v >= 254.5f) { |
259 return 255; | 259 return 255; |
260 } else if (v >= 0.5f) { | 260 } else if (v >= 0.5f) { |
261 return (uint8_t) (v + 0.5f); | 261 return (uint8_t) (v + 0.5f); |
262 } else { | 262 } else { |
263 return 0; | 263 return 0; |
264 } | 264 } |
265 } | 265 } |
266 | 266 |
| 267 static const int kDstGammaTableSize = |
| 268 SkColorSpaceXform_Base<SkColorSpace::kNonStandard_GammaNamed>::kDstGamma
TableSize; |
| 269 |
267 static void build_table_linear_to_gamma(uint8_t* outTable, float exponent) { | 270 static void build_table_linear_to_gamma(uint8_t* outTable, float exponent) { |
268 float toGammaExp = 1.0f / exponent; | 271 float toGammaExp = 1.0f / exponent; |
269 | 272 |
270 for (int i = 0; i < SkDefaultXform::kDstGammaTableSize; i++) { | 273 for (int i = 0; i < kDstGammaTableSize; i++) { |
271 float x = ((float) i) * (1.0f / ((float) (SkDefaultXform::kDstGammaTable
Size - 1))); | 274 float x = ((float) i) * (1.0f / ((float) (kDstGammaTableSize - 1))); |
272 outTable[i] = clamp_normalized_float_to_byte(powf(x, toGammaExp)); | 275 outTable[i] = clamp_normalized_float_to_byte(powf(x, toGammaExp)); |
273 } | 276 } |
274 } | 277 } |
275 | 278 |
276 // Inverse table lookup. Ex: what index corresponds to the input value? This w
ill | 279 // Inverse table lookup. Ex: what index corresponds to the input value? This w
ill |
277 // have strange results when the table is non-increasing. But any sane gamma | 280 // have strange results when the table is non-increasing. But any sane gamma |
278 // function will be increasing. | 281 // function will be increasing. |
279 static float inverse_interp_lut(float input, const float* table, int tableSize)
{ | 282 static float inverse_interp_lut(float input, const float* table, int tableSize)
{ |
280 if (input <= table[0]) { | 283 if (input <= table[0]) { |
281 return table[0]; | 284 return table[0]; |
(...skipping 12 matching lines...) Expand all Loading... |
294 } | 297 } |
295 | 298 |
296 // Should be unreachable, since we'll return before the loop if input is | 299 // Should be unreachable, since we'll return before the loop if input is |
297 // larger than the last entry. | 300 // larger than the last entry. |
298 SkASSERT(false); | 301 SkASSERT(false); |
299 return 0.0f; | 302 return 0.0f; |
300 } | 303 } |
301 | 304 |
302 static void build_table_linear_to_gamma(uint8_t* outTable, const float* inTable, | 305 static void build_table_linear_to_gamma(uint8_t* outTable, const float* inTable, |
303 int inTableSize) { | 306 int inTableSize) { |
304 for (int i = 0; i < SkDefaultXform::kDstGammaTableSize; i++) { | 307 for (int i = 0; i < kDstGammaTableSize; i++) { |
305 float x = ((float) i) * (1.0f / ((float) (SkDefaultXform::kDstGammaTable
Size - 1))); | 308 float x = ((float) i) * (1.0f / ((float) (kDstGammaTableSize - 1))); |
306 float y = inverse_interp_lut(x, inTable, inTableSize); | 309 float y = inverse_interp_lut(x, inTable, inTableSize); |
307 outTable[i] = clamp_normalized_float_to_byte(y); | 310 outTable[i] = clamp_normalized_float_to_byte(y); |
308 } | 311 } |
309 } | 312 } |
310 | 313 |
311 static float inverse_parametric(float x, float g, float a, float b, float c, flo
at d, float e, | 314 static float inverse_parametric(float x, float g, float a, float b, float c, flo
at d, float e, |
312 float f) { | 315 float f) { |
313 // We need to take the inverse of the following piecewise function. | 316 // We need to take the inverse of the following piecewise function. |
314 // Y = (aX + b)^g + c for X >= d | 317 // Y = (aX + b)^g + c for X >= d |
315 // Y = eX + f otherwise | 318 // Y = eX + f otherwise |
(...skipping 18 matching lines...) Expand all Loading... |
334 // The gamma curve for this segment is constant, so the inverse is undef
ined. | 337 // The gamma curve for this segment is constant, so the inverse is undef
ined. |
335 // Since this is the upper segment, guess one. | 338 // Since this is the upper segment, guess one. |
336 return 1.0f; | 339 return 1.0f; |
337 } | 340 } |
338 | 341 |
339 return (powf(x - c, 1.0f / g) - b) / a; | 342 return (powf(x - c, 1.0f / g) - b) / a; |
340 } | 343 } |
341 | 344 |
342 static void build_table_linear_to_gamma(uint8_t* outTable, float g, float a, | 345 static void build_table_linear_to_gamma(uint8_t* outTable, float g, float a, |
343 float b, float c, float d, float e, floa
t f) { | 346 float b, float c, float d, float e, floa
t f) { |
344 for (int i = 0; i < SkDefaultXform::kDstGammaTableSize; i++) { | 347 for (int i = 0; i < kDstGammaTableSize; i++) { |
345 float x = ((float) i) * (1.0f / ((float) (SkDefaultXform::kDstGammaTable
Size - 1))); | 348 float x = ((float) i) * (1.0f / ((float) (kDstGammaTableSize - 1))); |
346 float y = inverse_parametric(x, g, a, b, c, d, e, f); | 349 float y = inverse_parametric(x, g, a, b, c, d, e, f); |
347 outTable[i] = clamp_normalized_float_to_byte(y); | 350 outTable[i] = clamp_normalized_float_to_byte(y); |
348 } | 351 } |
349 } | 352 } |
350 | 353 |
351 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 354 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
352 | 355 |
353 template <typename T> | 356 template <typename T> |
354 struct GammaFns { | 357 struct GammaFns { |
355 const T* fSRGBTable; | 358 const T* fSRGBTable; |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
457 // It would be really weird for a dst profile to have a color LUT. I do
n't think | 460 // It would be really weird for a dst profile to have a color LUT. I do
n't think |
458 // we need to support this. | 461 // we need to support this. |
459 return nullptr; | 462 return nullptr; |
460 } | 463 } |
461 | 464 |
462 SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor); | 465 SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor); |
463 if (!compute_gamut_xform(&srcToDst, srcSpace->xyz(), dstSpace->xyz())) { | 466 if (!compute_gamut_xform(&srcToDst, srcSpace->xyz(), dstSpace->xyz())) { |
464 return nullptr; | 467 return nullptr; |
465 } | 468 } |
466 | 469 |
467 if (0.0f == srcToDst.getFloat(3, 0) && | 470 switch (dstSpace->gammaNamed()) { |
468 0.0f == srcToDst.getFloat(3, 1) && | 471 case SkColorSpace::kSRGB_GammaNamed: |
469 0.0f == srcToDst.getFloat(3, 2) && | 472 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXform_Base |
470 !as_CSB(srcSpace)->colorLUT()) | 473 <SkColorSpace::kSRGB_GammaNamed>(srcSpace, srcToDst, dstSpac
e)); |
471 { | 474 case SkColorSpace::k2Dot2Curve_GammaNamed: |
472 switch (dstSpace->gammaNamed()) { | 475 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXform_Base |
473 case SkColorSpace::kSRGB_GammaNamed: | 476 <SkColorSpace::k2Dot2Curve_GammaNamed>(srcSpace, srcToDst, d
stSpace)); |
474 return std::unique_ptr<SkColorSpaceXform>( | 477 default: |
475 new SkFastXform<SkColorSpace::kSRGB_GammaNamed>(srcSpace
, srcToDst, | 478 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXform_Base |
476 dstSpace
)); | 479 <SkColorSpace::kNonStandard_GammaNamed>(srcSpace, srcToDst,
dstSpace)); |
477 case SkColorSpace::k2Dot2Curve_GammaNamed: | |
478 return std::unique_ptr<SkColorSpaceXform>( | |
479 new SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed>(sr
cSpace, srcToDst, | |
480 ds
tSpace)); | |
481 default: | |
482 return std::unique_ptr<SkColorSpaceXform>( | |
483 new SkFastXform<SkColorSpace::kNonStandard_GammaNamed>(s
rcSpace, srcToDst, | |
484 d
stSpace)); | |
485 } | |
486 } | 480 } |
487 | |
488 return std::unique_ptr<SkColorSpaceXform>(new SkDefaultXform(srcSpace, srcTo
Dst, dstSpace)); | |
489 } | 481 } |
490 | 482 |
491 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 483 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
492 | 484 |
493 template <SkColorSpace::GammaNamed Dst> | |
494 SkFastXform<Dst>::SkFastXform(const sk_sp<SkColorSpace>& srcSpace, const SkMatri
x44& srcToDst, | |
495 const sk_sp<SkColorSpace>& dstSpace) | |
496 { | |
497 srcToDst.asRowMajorf(fSrcToDst); | |
498 build_gamma_tables(fSrcGammaTables, fSrcGammaTableStorage, 256, srcSpace, kT
oLinear); | |
499 build_gamma_tables(fDstGammaTables, fDstGammaTableStorage, SkDefaultXform::k
DstGammaTableSize, | |
500 dstSpace, kFromLinear); | |
501 } | |
502 | |
503 template <> | |
504 void SkFastXform<SkColorSpace::kSRGB_GammaNamed> | |
505 ::applyTo8888(SkPMColor* dst, const RGBA32* src, int len) const | |
506 { | |
507 SkOpts::color_xform_RGB1_to_srgb(dst, src, len, fSrcGammaTables, fSrcToDst); | |
508 } | |
509 | |
510 template <> | |
511 void SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed> | |
512 ::applyTo8888(SkPMColor* dst, const RGBA32* src, int len) const | |
513 { | |
514 SkOpts::color_xform_RGB1_to_2dot2(dst, src, len, fSrcGammaTables, fSrcToDst)
; | |
515 } | |
516 | |
517 template <> | |
518 void SkFastXform<SkColorSpace::kNonStandard_GammaNamed> | |
519 ::applyTo8888(SkPMColor* dst, const RGBA32* src, int len) const | |
520 { | |
521 SkOpts::color_xform_RGB1_to_table(dst, src, len, fSrcGammaTables, fSrcToDst,
fDstGammaTables); | |
522 } | |
523 | |
524 template <SkColorSpace::GammaNamed T> | |
525 void SkFastXform<T> | |
526 ::applyToF16(RGBAF16* dst, const RGBA32* src, int len) const | |
527 { | |
528 SkOpts::color_xform_RGB1_to_linear(dst, src, len, fSrcGammaTables, fSrcToDst
); | |
529 } | |
530 | |
531 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | |
532 | |
533 SkDefaultXform::SkDefaultXform(const sk_sp<SkColorSpace>& srcSpace, const SkMatr
ix44& srcToDst, | |
534 const sk_sp<SkColorSpace>& dstSpace) | |
535 : fColorLUT(sk_ref_sp((SkColorLookUpTable*) as_CSB(srcSpace)->colorLUT())) | |
536 , fSrcToDst(srcToDst) | |
537 { | |
538 build_gamma_tables(fSrcGammaTables, fSrcGammaTableStorage, 256, srcSpace, kT
oLinear); | |
539 build_gamma_tables(fDstGammaTables, fDstGammaTableStorage, SkDefaultXform::k
DstGammaTableSize, | |
540 dstSpace, kFromLinear); | |
541 } | |
542 | |
543 static float byte_to_float(uint8_t byte) { | 485 static float byte_to_float(uint8_t byte) { |
544 return ((float) byte) * (1.0f / 255.0f); | 486 return ((float) byte) * (1.0f / 255.0f); |
545 } | 487 } |
546 | 488 |
547 // Clamp to the 0-1 range. | 489 // Clamp to the 0-1 range. |
548 static float clamp_normalized_float(float v) { | 490 static float clamp_normalized_float(float v) { |
549 if (v > 1.0f) { | 491 if (v > 1.0f) { |
550 return 1.0f; | 492 return 1.0f; |
551 } else if ((v < 0.0f) || (v != v)) { | 493 } else if ((v < 0.0f) || (v != v)) { |
552 return 0.0f; | 494 return 0.0f; |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
640 } | 582 } |
641 | 583 |
642 // Increment the table ptr in order to handle the next component. | 584 // Increment the table ptr in order to handle the next component. |
643 // Note that this is the how table is designed: all of nXXX | 585 // Note that this is the how table is designed: all of nXXX |
644 // variables are multiples of 3 because there are 3 output | 586 // variables are multiples of 3 because there are 3 output |
645 // components. | 587 // components. |
646 ptr++; | 588 ptr++; |
647 } | 589 } |
648 } | 590 } |
649 | 591 |
650 void SkDefaultXform::applyTo8888(SkPMColor* dst, const RGBA32* src, int len) con
st { | 592 static void handle_color_lut(uint32_t* dst, const uint32_t* src, int len, |
| 593 SkColorLookUpTable* colorLUT) { |
651 while (len-- > 0) { | 594 while (len-- > 0) { |
652 uint8_t r = (*src >> 0) & 0xFF, | 595 uint8_t r = (*src >> 0) & 0xFF, |
653 g = (*src >> 8) & 0xFF, | 596 g = (*src >> 8) & 0xFF, |
654 b = (*src >> 16) & 0xFF; | 597 b = (*src >> 16) & 0xFF; |
655 | 598 |
656 if (fColorLUT) { | 599 float in[3]; |
657 float in[3]; | 600 float out[3]; |
658 float out[3]; | 601 in[0] = byte_to_float(r); |
| 602 in[1] = byte_to_float(g); |
| 603 in[2] = byte_to_float(b); |
| 604 interp_3d_clut(out, in, colorLUT); |
659 | 605 |
660 in[0] = byte_to_float(r); | 606 r = sk_float_round2int(255.0f * clamp_normalized_float(out[0])); |
661 in[1] = byte_to_float(g); | 607 g = sk_float_round2int(255.0f * clamp_normalized_float(out[1])); |
662 in[2] = byte_to_float(b); | 608 b = sk_float_round2int(255.0f * clamp_normalized_float(out[2])); |
| 609 *dst = SkPackARGB_as_RGBA(0xFF, r, g, b); |
663 | 610 |
664 interp_3d_clut(out, in, fColorLUT.get()); | 611 src++; |
665 | |
666 r = sk_float_round2int(255.0f * clamp_normalized_float(out[0])); | |
667 g = sk_float_round2int(255.0f * clamp_normalized_float(out[1])); | |
668 b = sk_float_round2int(255.0f * clamp_normalized_float(out[2])); | |
669 } | |
670 | |
671 // Convert to linear. | |
672 float srcFloats[3]; | |
673 srcFloats[0] = fSrcGammaTables[0][r]; | |
674 srcFloats[1] = fSrcGammaTables[1][g]; | |
675 srcFloats[2] = fSrcGammaTables[2][b]; | |
676 | |
677 // Convert to dst gamut. | |
678 float dstFloats[3]; | |
679 dstFloats[0] = srcFloats[0] * fSrcToDst.getFloat(0, 0) + | |
680 srcFloats[1] * fSrcToDst.getFloat(1, 0) + | |
681 srcFloats[2] * fSrcToDst.getFloat(2, 0) + fSrcToDst.getFl
oat(3, 0); | |
682 dstFloats[1] = srcFloats[0] * fSrcToDst.getFloat(0, 1) + | |
683 srcFloats[1] * fSrcToDst.getFloat(1, 1) + | |
684 srcFloats[2] * fSrcToDst.getFloat(2, 1) + fSrcToDst.getFl
oat(3, 1); | |
685 dstFloats[2] = srcFloats[0] * fSrcToDst.getFloat(0, 2) + | |
686 srcFloats[1] * fSrcToDst.getFloat(1, 2) + | |
687 srcFloats[2] * fSrcToDst.getFloat(2, 2) + fSrcToDst.getFl
oat(3, 2); | |
688 | |
689 // Clamp to 0-1. | |
690 dstFloats[0] = clamp_normalized_float(dstFloats[0]); | |
691 dstFloats[1] = clamp_normalized_float(dstFloats[1]); | |
692 dstFloats[2] = clamp_normalized_float(dstFloats[2]); | |
693 | |
694 // Convert to dst gamma. | |
695 r = fDstGammaTables[0][sk_float_round2int((kDstGammaTableSize - 1) * dst
Floats[0])]; | |
696 g = fDstGammaTables[1][sk_float_round2int((kDstGammaTableSize - 1) * dst
Floats[1])]; | |
697 b = fDstGammaTables[2][sk_float_round2int((kDstGammaTableSize - 1) * dst
Floats[2])]; | |
698 | |
699 *dst = SkPackARGB32NoCheck(0xFF, r, g, b); | |
700 | |
701 dst++; | 612 dst++; |
702 src++; | |
703 } | 613 } |
704 } | 614 } |
705 | 615 |
706 void SkDefaultXform::applyToF16(RGBAF16* dst, const RGBA32* src, int len) const
{ | 616 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
707 // FIXME (msarett): | 617 |
708 // Planning to delete SkDefaultXform. Not going to bother to implement this
. | 618 template <SkColorSpace::GammaNamed Dst> |
709 memset(dst, 0, len * sizeof(RGBAF16)); | 619 SkColorSpaceXform_Base<Dst>::SkColorSpaceXform_Base(const sk_sp<SkColorSpace>& s
rcSpace, |
| 620 const SkMatrix44& srcToDst, |
| 621 const sk_sp<SkColorSpace>& d
stSpace) |
| 622 : fColorLUT(sk_ref_sp((SkColorLookUpTable*) as_CSB(srcSpace)->colorLUT())) |
| 623 { |
| 624 srcToDst.asRowMajorf(fSrcToDst); |
| 625 build_gamma_tables(fSrcGammaTables, fSrcGammaTableStorage, 256, srcSpace, kT
oLinear); |
| 626 build_gamma_tables(fDstGammaTables, fDstGammaTableStorage, kDstGammaTableSiz
e, |
| 627 dstSpace, kFromLinear); |
710 } | 628 } |
| 629 |
| 630 template <> |
| 631 void SkColorSpaceXform_Base<SkColorSpace::kSRGB_GammaNamed> |
| 632 ::applyTo8888(SkPMColor* dst, const RGBA32* src, int len) const |
| 633 { |
| 634 if (fColorLUT) { |
| 635 handle_color_lut(dst, src, len, fColorLUT.get()); |
| 636 src = dst; |
| 637 } |
| 638 |
| 639 SkOpts::color_xform_RGB1_to_srgb(dst, src, len, fSrcGammaTables, fSrcToDst); |
| 640 } |
| 641 |
| 642 template <> |
| 643 void SkColorSpaceXform_Base<SkColorSpace::k2Dot2Curve_GammaNamed> |
| 644 ::applyTo8888(SkPMColor* dst, const RGBA32* src, int len) const |
| 645 { |
| 646 if (fColorLUT) { |
| 647 handle_color_lut(dst, src, len, fColorLUT.get()); |
| 648 src = dst; |
| 649 } |
| 650 |
| 651 SkOpts::color_xform_RGB1_to_2dot2(dst, src, len, fSrcGammaTables, fSrcToDst)
; |
| 652 } |
| 653 |
| 654 template <> |
| 655 void SkColorSpaceXform_Base<SkColorSpace::kNonStandard_GammaNamed> |
| 656 ::applyTo8888(SkPMColor* dst, const RGBA32* src, int len) const |
| 657 { |
| 658 if (fColorLUT) { |
| 659 handle_color_lut(dst, src, len, fColorLUT.get()); |
| 660 src = dst; |
| 661 } |
| 662 |
| 663 SkOpts::color_xform_RGB1_to_table(dst, src, len, fSrcGammaTables, fSrcToDst,
fDstGammaTables); |
| 664 } |
| 665 |
| 666 template <SkColorSpace::GammaNamed T> |
| 667 void SkColorSpaceXform_Base<T> |
| 668 ::applyToF16(RGBAF16* dst, const RGBA32* src, int len) const |
| 669 { |
| 670 if (fColorLUT) { |
| 671 size_t storageBytes = len * sizeof(RGBA32); |
| 672 #if defined(GOOGLE3) |
| 673 // Stack frame size is limited in GOOGLE3. |
| 674 SkAutoSMalloc<256 * sizeof(RGBA32)> storage(storageBytes); |
| 675 #else |
| 676 SkAutoSMalloc<1024 * sizeof(RGBA32)> storage(storageBytes); |
| 677 #endif |
| 678 |
| 679 handle_color_lut((RGBA32*) storage.get(), src, len, fColorLUT.get()); |
| 680 src = (const RGBA32*) storage.get(); |
| 681 } |
| 682 |
| 683 SkOpts::color_xform_RGB1_to_linear(dst, src, len, fSrcGammaTables, fSrcToDst
); |
| 684 } |
OLD | NEW |