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" |
11 #include "SkOpts.h" | 11 #include "SkOpts.h" |
12 | 12 |
13 static inline bool compute_gamut_xform(SkMatrix44* srcToDst, const SkMatrix44& s
rcToXYZ, | 13 static inline bool compute_gamut_xform(SkMatrix44* srcToDst, const SkMatrix44& s
rcToXYZ, |
14 const SkMatrix44& dstToXYZ) { | 14 const SkMatrix44& dstToXYZ) { |
15 if (!dstToXYZ.invert(srcToDst)) { | 15 if (!dstToXYZ.invert(srcToDst)) { |
16 return false; | 16 return false; |
17 } | 17 } |
18 | 18 |
19 srcToDst->postConcat(srcToXYZ); | 19 srcToDst->postConcat(srcToXYZ); |
20 return true; | 20 return true; |
21 } | 21 } |
22 | 22 |
23 std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpa
ce>& srcSpace, | 23 std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpa
ce>& srcSpace, |
24 const sk_sp<SkColorSpa
ce>& dstSpace) { | 24 const sk_sp<SkColorSpa
ce>& dstSpace) { |
25 if (!srcSpace || !dstSpace) { | 25 if (!srcSpace || !dstSpace) { |
26 // Invalid input | 26 // Invalid input |
27 return nullptr; | 27 return nullptr; |
28 } | 28 } |
29 | 29 |
30 if (as_CSB(srcSpace)->colorLUT() || as_CSB(dstSpace)->colorLUT()) { | 30 if (as_CSB(dstSpace)->colorLUT()) { |
31 // Unimplemented | 31 // It would be really weird for a dst profile to have a color LUT. I do
n't think |
| 32 // we need to support this. |
32 return nullptr; | 33 return nullptr; |
33 } | 34 } |
34 | 35 |
35 SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor); | 36 SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor); |
36 if (!compute_gamut_xform(&srcToDst, srcSpace->xyz(), dstSpace->xyz())) { | 37 if (!compute_gamut_xform(&srcToDst, srcSpace->xyz(), dstSpace->xyz())) { |
37 return nullptr; | 38 return nullptr; |
38 } | 39 } |
39 | 40 |
40 if (0.0f == srcToDst.getFloat(3, 0) && | 41 if (0.0f == srcToDst.getFloat(3, 0) && |
41 0.0f == srcToDst.getFloat(3, 1) && | 42 0.0f == srcToDst.getFloat(3, 1) && |
42 0.0f == srcToDst.getFloat(3, 2)) | 43 0.0f == srcToDst.getFloat(3, 2) && |
| 44 !as_CSB(srcSpace)->colorLUT()) |
43 { | 45 { |
44 switch (srcSpace->gammaNamed()) { | 46 switch (srcSpace->gammaNamed()) { |
45 case SkColorSpace::kSRGB_GammaNamed: | 47 case SkColorSpace::kSRGB_GammaNamed: |
46 if (SkColorSpace::kSRGB_GammaNamed == dstSpace->gammaNamed()) { | 48 if (SkColorSpace::kSRGB_GammaNamed == dstSpace->gammaNamed()) { |
47 return std::unique_ptr<SkColorSpaceXform>( | 49 return std::unique_ptr<SkColorSpaceXform>( |
48 new SkFastXform<SkColorSpace::kSRGB_GammaNamed, | 50 new SkFastXform<SkColorSpace::kSRGB_GammaNamed, |
49 SkColorSpace::kSRGB_GammaNamed>(srcT
oDst)); | 51 SkColorSpace::kSRGB_GammaNamed>(srcT
oDst)); |
50 } else if (SkColorSpace::k2Dot2Curve_GammaNamed == dstSpace->gam
maNamed()) { | 52 } else if (SkColorSpace::k2Dot2Curve_GammaNamed == dstSpace->gam
maNamed()) { |
51 return std::unique_ptr<SkColorSpaceXform>( | 53 return std::unique_ptr<SkColorSpaceXform>( |
52 new SkFastXform<SkColorSpace::kSRGB_GammaNamed, | 54 new SkFastXform<SkColorSpace::kSRGB_GammaNamed, |
53 SkColorSpace::k2Dot2Curve_GammaNamed
>(srcToDst)); | 55 SkColorSpace::k2Dot2Curve_GammaNamed
>(srcToDst)); |
54 } | 56 } |
55 break; | 57 break; |
56 case SkColorSpace::k2Dot2Curve_GammaNamed: | 58 case SkColorSpace::k2Dot2Curve_GammaNamed: |
57 if (SkColorSpace::kSRGB_GammaNamed == dstSpace->gammaNamed()) { | 59 if (SkColorSpace::kSRGB_GammaNamed == dstSpace->gammaNamed()) { |
58 return std::unique_ptr<SkColorSpaceXform>( | 60 return std::unique_ptr<SkColorSpaceXform>( |
59 new SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed
, | 61 new SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed
, |
60 SkColorSpace::kSRGB_GammaNamed>(srcT
oDst)); | 62 SkColorSpace::kSRGB_GammaNamed>(srcT
oDst)); |
61 } else if (SkColorSpace::k2Dot2Curve_GammaNamed == dstSpace->gam
maNamed()) { | 63 } else if (SkColorSpace::k2Dot2Curve_GammaNamed == dstSpace->gam
maNamed()) { |
62 return std::unique_ptr<SkColorSpaceXform>( | 64 return std::unique_ptr<SkColorSpaceXform>( |
63 new SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed
, | 65 new SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed
, |
64 SkColorSpace::k2Dot2Curve_GammaNamed
>(srcToDst)); | 66 SkColorSpace::k2Dot2Curve_GammaNamed
>(srcToDst)); |
65 } | 67 } |
66 break; | 68 break; |
67 default: | 69 default: |
68 break; | 70 break; |
69 } | 71 } |
70 } | 72 } |
71 | 73 |
72 return std::unique_ptr<SkColorSpaceXform>( | 74 return std::unique_ptr<SkColorSpaceXform>(new SkDefaultXform(srcSpace, srcTo
Dst, dstSpace)); |
73 new SkDefaultXform(srcSpace, srcToDst, dstSpace)); | |
74 } | 75 } |
75 | 76 |
76 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 77 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
77 | 78 |
78 static void build_src_to_dst(float srcToDstArray[12], const SkMatrix44& srcToDst
Matrix) { | 79 static void build_src_to_dst(float srcToDstArray[12], const SkMatrix44& srcToDst
Matrix) { |
79 // Build the following row major matrix: | 80 // Build the following row major matrix: |
80 // rX gX bX 0 | 81 // rX gX bX 0 |
81 // rY gY bY 0 | 82 // rY gY bY 0 |
82 // rZ gZ bZ 0 | 83 // rZ gZ bZ 0 |
83 // Swap R and B if necessary to make sure that we output SkPMColor order. | 84 // Swap R and B if necessary to make sure that we output SkPMColor order. |
(...skipping 449 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
533 float b, float c, float d, float e, floa
t f) { | 534 float b, float c, float d, float e, floa
t f) { |
534 for (int i = 0; i < outTableSize; i++) { | 535 for (int i = 0; i < outTableSize; i++) { |
535 float x = ((float) i) * (1.0f / ((float) (outTableSize - 1))); | 536 float x = ((float) i) * (1.0f / ((float) (outTableSize - 1))); |
536 float y = inverse_parametric(x, g, a, b, c, d, e, f); | 537 float y = inverse_parametric(x, g, a, b, c, d, e, f); |
537 outTable[i] = clamp_normalized_float_to_byte(y); | 538 outTable[i] = clamp_normalized_float_to_byte(y); |
538 } | 539 } |
539 } | 540 } |
540 | 541 |
541 SkDefaultXform::SkDefaultXform(const sk_sp<SkColorSpace>& srcSpace, const SkMatr
ix44& srcToDst, | 542 SkDefaultXform::SkDefaultXform(const sk_sp<SkColorSpace>& srcSpace, const SkMatr
ix44& srcToDst, |
542 const sk_sp<SkColorSpace>& dstSpace) | 543 const sk_sp<SkColorSpace>& dstSpace) |
543 : fSrcToDst(srcToDst) | 544 : fColorLUT(sk_ref_sp((SkColorLookUpTable*) as_CSB(srcSpace)->colorLUT())) |
| 545 , fSrcToDst(srcToDst) |
544 { | 546 { |
545 // Build tables to transform src gamma to linear. | 547 // Build tables to transform src gamma to linear. |
546 switch (srcSpace->gammaNamed()) { | 548 switch (srcSpace->gammaNamed()) { |
547 case SkColorSpace::kSRGB_GammaNamed: | 549 case SkColorSpace::kSRGB_GammaNamed: |
548 fSrcGammaTables[0] = fSrcGammaTables[1] = fSrcGammaTables[2] = sk_li
near_from_srgb; | 550 fSrcGammaTables[0] = fSrcGammaTables[1] = fSrcGammaTables[2] = sk_li
near_from_srgb; |
549 break; | 551 break; |
550 case SkColorSpace::k2Dot2Curve_GammaNamed: | 552 case SkColorSpace::k2Dot2Curve_GammaNamed: |
551 fSrcGammaTables[0] = fSrcGammaTables[1] = fSrcGammaTables[2] = sk_li
near_from_2dot2; | 553 fSrcGammaTables[0] = fSrcGammaTables[1] = fSrcGammaTables[2] = sk_li
near_from_2dot2; |
552 break; | 554 break; |
553 case SkColorSpace::kLinear_GammaNamed: | 555 case SkColorSpace::kLinear_GammaNamed: |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
680 build_table_linear_to_gamma(&fDstGammaTableStorage[i * kDstG
ammaTableSize], | 682 build_table_linear_to_gamma(&fDstGammaTableStorage[i * kDstG
ammaTableSize], |
681 kDstGammaTableSize, curve.fG, cu
rve.fA, curve.fB, | 683 kDstGammaTableSize, curve.fG, cu
rve.fA, curve.fB, |
682 curve.fC, curve.fD, curve.fE, cu
rve.fF); | 684 curve.fC, curve.fD, curve.fE, cu
rve.fF); |
683 fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTab
leSize]; | 685 fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTab
leSize]; |
684 } | 686 } |
685 } | 687 } |
686 } | 688 } |
687 } | 689 } |
688 } | 690 } |
689 | 691 |
| 692 static float byte_to_float(uint8_t byte) { |
| 693 return ((float) byte) * (1.0f / 255.0f); |
| 694 } |
| 695 |
690 // Clamp to the 0-1 range. | 696 // Clamp to the 0-1 range. |
691 static float clamp_normalized_float(float v) { | 697 static float clamp_normalized_float(float v) { |
692 if (v > 1.0f) { | 698 if (v > 1.0f) { |
693 return 1.0f; | 699 return 1.0f; |
694 } else if ((v < 0.0f) || (v != v)) { | 700 } else if ((v < 0.0f) || (v != v)) { |
695 return 0.0f; | 701 return 0.0f; |
696 } else { | 702 } else { |
697 return v; | 703 return v; |
698 } | 704 } |
699 } | 705 } |
700 | 706 |
| 707 static void interp_3d_clut(float dst[3], float src[3], const SkColorLookUpTable*
colorLUT) { |
| 708 // Call the src components x, y, and z. |
| 709 uint8_t maxX = colorLUT->fGridPoints[0] - 1; |
| 710 uint8_t maxY = colorLUT->fGridPoints[1] - 1; |
| 711 uint8_t maxZ = colorLUT->fGridPoints[2] - 1; |
| 712 |
| 713 // An approximate index into each of the three dimensions of the table. |
| 714 float x = src[0] * maxX; |
| 715 float y = src[1] * maxY; |
| 716 float z = src[2] * maxZ; |
| 717 |
| 718 // This gives us the low index for our interpolation. |
| 719 int ix = sk_float_floor2int(x); |
| 720 int iy = sk_float_floor2int(y); |
| 721 int iz = sk_float_floor2int(z); |
| 722 |
| 723 // Make sure the low index is not also the max index. |
| 724 ix = (maxX == ix) ? ix - 1 : ix; |
| 725 iy = (maxY == iy) ? iy - 1 : iy; |
| 726 iz = (maxZ == iz) ? iz - 1 : iz; |
| 727 |
| 728 // Weighting factors for the interpolation. |
| 729 float diffX = x - ix; |
| 730 float diffY = y - iy; |
| 731 float diffZ = z - iz; |
| 732 |
| 733 // Constants to help us navigate the 3D table. |
| 734 // Ex: Assume x = a, y = b, z = c. |
| 735 // table[a * n001 + b * n010 + c * n100] logically equals table[a][b][c]
. |
| 736 const int n000 = 0; |
| 737 const int n001 = 3 * colorLUT->fGridPoints[1] * colorLUT->fGridPoints[2]; |
| 738 const int n010 = 3 * colorLUT->fGridPoints[2]; |
| 739 const int n011 = n001 + n010; |
| 740 const int n100 = 3; |
| 741 const int n101 = n100 + n001; |
| 742 const int n110 = n100 + n010; |
| 743 const int n111 = n110 + n001; |
| 744 |
| 745 // Base ptr into the table. |
| 746 float* ptr = &colorLUT->fTable[ix*n001 + iy*n010 + iz*n100]; |
| 747 |
| 748 // The code below performs a tetrahedral interpolation for each of the three |
| 749 // dst components. Once the tetrahedron containing the interpolation point
is |
| 750 // identified, the interpolation is a weighted sum of grid values at the |
| 751 // vertices of the tetrahedron. The claim is that tetrahedral interpolation |
| 752 // provides a more accurate color conversion. |
| 753 // blogs.mathworks.com/steve/2006/11/24/tetrahedral-interpolation-for-colors
pace-conversion/ |
| 754 // |
| 755 // I have one test image, and visually I can't tell the difference between |
| 756 // tetrahedral and trilinear interpolation. In terms of computation, the |
| 757 // tetrahedral code requires more branches but less computation. The |
| 758 // SampleICC library provides an option for the client to choose either |
| 759 // tetrahedral or trilinear. |
| 760 for (int i = 0; i < 3; i++) { |
| 761 if (diffZ < diffY) { |
| 762 if (diffZ < diffX) { |
| 763 dst[i] = (ptr[n000] + diffZ * (ptr[n110] - ptr[n010]) + |
| 764 diffY * (ptr[n010] - ptr[n000]) + |
| 765 diffX * (ptr[n111] - ptr[n110])); |
| 766 } else if (diffY < diffX) { |
| 767 dst[i] = (ptr[n000] + diffZ * (ptr[n111] - ptr[n011]) + |
| 768 diffY * (ptr[n011] - ptr[n001]) + |
| 769 diffX * (ptr[n001] - ptr[n000])); |
| 770 } else { |
| 771 dst[i] = (ptr[n000] + diffZ * (ptr[n111] - ptr[n011]) + |
| 772 diffY * (ptr[n010] - ptr[n000]) + |
| 773 diffX * (ptr[n011] - ptr[n010])); |
| 774 } |
| 775 } else { |
| 776 if (diffZ < diffX) { |
| 777 dst[i] = (ptr[n000] + diffZ * (ptr[n101] - ptr[n001]) + |
| 778 diffY * (ptr[n111] - ptr[n101]) + |
| 779 diffX * (ptr[n001] - ptr[n000])); |
| 780 } else if (diffY < diffX) { |
| 781 dst[i] = (ptr[n000] + diffZ * (ptr[n100] - ptr[n000]) + |
| 782 diffY * (ptr[n111] - ptr[n101]) + |
| 783 diffX * (ptr[n101] - ptr[n100])); |
| 784 } else { |
| 785 dst[i] = (ptr[n000] + diffZ * (ptr[n100] - ptr[n000]) + |
| 786 diffY * (ptr[n110] - ptr[n100]) + |
| 787 diffX * (ptr[n111] - ptr[n110])); |
| 788 } |
| 789 } |
| 790 |
| 791 // Increment the table ptr in order to handle the next component. |
| 792 // Note that this is the how table is designed: all of nXXX |
| 793 // variables are multiples of 3 because there are 3 output |
| 794 // components. |
| 795 ptr++; |
| 796 } |
| 797 } |
| 798 |
701 void SkDefaultXform::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_
t len) const { | 799 void SkDefaultXform::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_
t len) const { |
702 while (len-- > 0) { | 800 while (len-- > 0) { |
| 801 uint8_t r = (*src >> 0) & 0xFF, |
| 802 g = (*src >> 8) & 0xFF, |
| 803 b = (*src >> 16) & 0xFF; |
| 804 |
| 805 if (fColorLUT) { |
| 806 float in[3]; |
| 807 float out[3]; |
| 808 |
| 809 in[0] = byte_to_float(r); |
| 810 in[1] = byte_to_float(g); |
| 811 in[2] = byte_to_float(b); |
| 812 |
| 813 interp_3d_clut(out, in, fColorLUT.get()); |
| 814 |
| 815 r = sk_float_round2int(255.0f * clamp_normalized_float(out[0])); |
| 816 g = sk_float_round2int(255.0f * clamp_normalized_float(out[1])); |
| 817 b = sk_float_round2int(255.0f * clamp_normalized_float(out[2])); |
| 818 } |
| 819 |
703 // Convert to linear. | 820 // Convert to linear. |
704 float srcFloats[3]; | 821 float srcFloats[3]; |
705 srcFloats[0] = fSrcGammaTables[0][(*src >> 0) & 0xFF]; | 822 srcFloats[0] = fSrcGammaTables[0][r]; |
706 srcFloats[1] = fSrcGammaTables[1][(*src >> 8) & 0xFF]; | 823 srcFloats[1] = fSrcGammaTables[1][g]; |
707 srcFloats[2] = fSrcGammaTables[2][(*src >> 16) & 0xFF]; | 824 srcFloats[2] = fSrcGammaTables[2][b]; |
708 | 825 |
709 // Convert to dst gamut. | 826 // Convert to dst gamut. |
710 float dstFloats[3]; | 827 float dstFloats[3]; |
711 dstFloats[0] = srcFloats[0] * fSrcToDst.getFloat(0, 0) + | 828 dstFloats[0] = srcFloats[0] * fSrcToDst.getFloat(0, 0) + |
712 srcFloats[1] * fSrcToDst.getFloat(1, 0) + | 829 srcFloats[1] * fSrcToDst.getFloat(1, 0) + |
713 srcFloats[2] * fSrcToDst.getFloat(2, 0) + fSrcToDst.getFl
oat(3, 0); | 830 srcFloats[2] * fSrcToDst.getFloat(2, 0) + fSrcToDst.getFl
oat(3, 0); |
714 dstFloats[1] = srcFloats[0] * fSrcToDst.getFloat(0, 1) + | 831 dstFloats[1] = srcFloats[0] * fSrcToDst.getFloat(0, 1) + |
715 srcFloats[1] * fSrcToDst.getFloat(1, 1) + | 832 srcFloats[1] * fSrcToDst.getFloat(1, 1) + |
716 srcFloats[2] * fSrcToDst.getFloat(2, 1) + fSrcToDst.getFl
oat(3, 1); | 833 srcFloats[2] * fSrcToDst.getFloat(2, 1) + fSrcToDst.getFl
oat(3, 1); |
717 dstFloats[2] = srcFloats[0] * fSrcToDst.getFloat(0, 2) + | 834 dstFloats[2] = srcFloats[0] * fSrcToDst.getFloat(0, 2) + |
718 srcFloats[1] * fSrcToDst.getFloat(1, 2) + | 835 srcFloats[1] * fSrcToDst.getFloat(1, 2) + |
719 srcFloats[2] * fSrcToDst.getFloat(2, 2) + fSrcToDst.getFl
oat(3, 2); | 836 srcFloats[2] * fSrcToDst.getFloat(2, 2) + fSrcToDst.getFl
oat(3, 2); |
720 | 837 |
721 // Clamp to 0-1. | 838 // Clamp to 0-1. |
722 dstFloats[0] = clamp_normalized_float(dstFloats[0]); | 839 dstFloats[0] = clamp_normalized_float(dstFloats[0]); |
723 dstFloats[1] = clamp_normalized_float(dstFloats[1]); | 840 dstFloats[1] = clamp_normalized_float(dstFloats[1]); |
724 dstFloats[2] = clamp_normalized_float(dstFloats[2]); | 841 dstFloats[2] = clamp_normalized_float(dstFloats[2]); |
725 | 842 |
726 // Convert to dst gamma. | 843 // Convert to dst gamma. |
727 uint8_t r = fDstGammaTables[0][sk_float_round2int((kDstGammaTableSize -
1) * dstFloats[0])]; | 844 r = fDstGammaTables[0][sk_float_round2int((kDstGammaTableSize - 1) * dst
Floats[0])]; |
728 uint8_t g = fDstGammaTables[1][sk_float_round2int((kDstGammaTableSize -
1) * dstFloats[1])]; | 845 g = fDstGammaTables[1][sk_float_round2int((kDstGammaTableSize - 1) * dst
Floats[1])]; |
729 uint8_t b = fDstGammaTables[2][sk_float_round2int((kDstGammaTableSize -
1) * dstFloats[2])]; | 846 b = fDstGammaTables[2][sk_float_round2int((kDstGammaTableSize - 1) * dst
Floats[2])]; |
730 | 847 |
731 *dst = SkPackARGB32NoCheck(0xFF, r, g, b); | 848 *dst = SkPackARGB32NoCheck(0xFF, r, g, b); |
732 | 849 |
733 dst++; | 850 dst++; |
734 src++; | 851 src++; |
735 } | 852 } |
736 } | 853 } |
OLD | NEW |