Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(122)

Side by Side Diff: src/core/SkColorSpaceXform.cpp

Issue 2097553002: Add support for 3D colorLUTs to SkColorXform (Closed) Base URL: https://skia.googlesource.com/skia.git@lovedefaultxform
Patch Set: Rebase Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/core/SkColorSpaceXform.h ('k') | src/core/SkColorSpace_Base.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « src/core/SkColorSpaceXform.h ('k') | src/core/SkColorSpace_Base.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698