| 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 |