Chromium Code Reviews| 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 "SkAtomics.h" | 8 #include "SkAtomics.h" |
| 9 #include "SkColorSpace.h" | 9 #include "SkColorSpace.h" |
| 10 | 10 |
| 11 static inline bool SkFloatIsFinite(float x) { return 0 == x * 0; } | |
| 12 | |
| 13 // | |
| 14 // SkFloat3x3 | |
| 15 // | |
| 16 // In memory order, values are a, b, c, d, e, f, g, h, i | |
| 17 // | |
| 18 // When applied to a color component vector (e.g. [ r, r, r ] or [ g, g, g ] we do | |
| 19 // | |
| 20 // [ r r r ] * [ a b c ] + [ g g g ] * [ d e f ] + [ b b b ] * [ g h i ] | |
| 21 // | |
| 22 // Thus in our point-on-the-right notation, the matrix looks like | |
| 23 // | |
| 24 // [ a d g ] [ r ] | |
| 25 // [ b e h ] * [ g ] | |
| 26 // [ c f i ] [ b ] | |
| 27 // | |
| 28 static SkFloat3x3 concat(const SkFloat3x3& left, const SkFloat3x3& rite) { | |
| 29 SkFloat3x3 result; | |
| 30 for (int row = 0; row < 3; ++row) { | |
| 31 for (int col = 0; col < 3; ++col) { | |
| 32 double tmp = 0; | |
| 33 for (int i = 0; i < 3; ++i) { | |
| 34 tmp += (double)left.fMat[row + i * 3] * rite.fMat[i + col * 3]; | |
| 35 } | |
| 36 result.fMat[row + col * 3] = (double)tmp; | |
| 37 } | |
| 38 } | |
| 39 return result; | |
| 40 } | |
| 41 | |
| 42 static double det(const SkFloat3x3& m) { | |
| 43 return (double)m.fMat[0] * m.fMat[4] * m.fMat[8] + | |
| 44 (double)m.fMat[3] * m.fMat[7] * m.fMat[2] + | |
| 45 (double)m.fMat[6] * m.fMat[1] * m.fMat[5] - | |
| 46 (double)m.fMat[0] * m.fMat[7] * m.fMat[5] - | |
| 47 (double)m.fMat[3] * m.fMat[1] * m.fMat[8] - | |
| 48 (double)m.fMat[6] * m.fMat[4] * m.fMat[2]; | |
| 49 } | |
| 50 | |
| 51 static double det2x2(const SkFloat3x3& m, int a, int b, int c, int d) { | |
| 52 return (double)m.fMat[a] * m.fMat[b] - (double)m.fMat[c] * m.fMat[d]; | |
| 53 } | |
| 54 | |
| 55 static SkFloat3x3 invert(const SkFloat3x3& m) { | |
| 56 double d = det(m); | |
| 57 SkASSERT(SkFloatIsFinite((float)d)); | |
| 58 double scale = 1 / d; | |
| 59 SkASSERT(SkFloatIsFinite((float)scale)); | |
| 60 | |
| 61 return {{ | |
| 62 (float)(scale * det2x2(m, 4, 8, 5, 7)), | |
| 63 (float)(scale * det2x2(m, 7, 2, 8, 1)), | |
| 64 (float)(scale * det2x2(m, 1, 5, 2, 4)), | |
| 65 | |
| 66 (float)(scale * det2x2(m, 6, 5, 8, 3)), | |
| 67 (float)(scale * det2x2(m, 0, 8, 2, 6)), | |
| 68 (float)(scale * det2x2(m, 3, 2, 5, 0)), | |
| 69 | |
| 70 (float)(scale * det2x2(m, 3, 7, 4, 6)), | |
| 71 (float)(scale * det2x2(m, 6, 1, 7, 0)), | |
| 72 (float)(scale * det2x2(m, 0, 4, 1, 3)), | |
| 73 }}; | |
| 74 } | |
| 75 | |
| 76 void SkFloat3::dump() const { | 11 void SkFloat3::dump() const { |
| 77 SkDebugf("[%7.4f %7.4f %7.4f]\n", fVec[0], fVec[1], fVec[2]); | 12 SkDebugf("[%7.4f %7.4f %7.4f]\n", fVec[0], fVec[1], fVec[2]); |
| 78 } | 13 } |
| 79 | 14 |
| 80 void SkFloat3x3::dump() const { | 15 void SkFloat3x3::dump() const { |
| 81 SkDebugf("[%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f]\n", | 16 SkDebugf("[%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f]\n", |
| 82 fMat[0], fMat[1], fMat[2], | 17 fMat[0], fMat[1], fMat[2], |
| 83 fMat[3], fMat[4], fMat[5], | 18 fMat[3], fMat[4], fMat[5], |
| 84 fMat[6], fMat[7], fMat[8]); | 19 fMat[6], fMat[7], fMat[8]); |
| 85 } | 20 } |
| 86 | 21 |
| 87 //////////////////////////////////////////////////////////////////////////////// ////////////////// | 22 //////////////////////////////////////////////////////////////////////////////// ////////////////// |
| 88 | 23 |
| 89 static int32_t gUniqueColorSpaceID; | 24 static int32_t gUniqueColorSpaceID; |
| 90 | 25 |
| 91 SkColorSpace::SkColorSpace(const SkFloat3& gamma, const SkFloat3x3& toXYZD50, Na med named) | 26 SkColorSpace::SkColorSpace(const SkFloat3& gamma, const SkFloat3x3& toXYZD50, Na med named) |
| 92 : fGamma(gamma) | 27 : fGamma(gamma) |
| 93 , fToXYZD50(toXYZD50) | 28 , fToXYZD50(toXYZD50) |
| 94 , fToXYZOffset({{ 0.0f, 0.0f, 0.0f }}) | 29 , fToXYZOffset({{ 0.0f, 0.0f, 0.0f }}) |
| 95 , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID)) | 30 , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID)) |
| 96 , fNamed(named) | 31 , fNamed(named) |
| 97 { | 32 {} |
| 98 for (int i = 0; i < 3; ++i) { | |
| 99 SkASSERT(SkFloatIsFinite(gamma.fVec[i])); | |
| 100 for (int j = 0; j < 3; ++j) { | |
| 101 SkASSERT(SkFloatIsFinite(toXYZD50.fMat[3*i + j])); | |
| 102 } | |
| 103 } | |
| 104 } | |
| 105 | 33 |
| 106 SkColorSpace::SkColorSpace(SkColorLookUpTable colorLUT, const SkFloat3& gamma, | 34 SkColorSpace::SkColorSpace(SkColorLookUpTable colorLUT, const SkFloat3& gamma, |
| 107 const SkFloat3x3& toXYZD50, const SkFloat3& toXYZOffs et) | 35 const SkFloat3x3& toXYZD50, const SkFloat3& toXYZOffs et) |
| 108 : fColorLUT(std::move(colorLUT)) | 36 : fColorLUT(std::move(colorLUT)) |
| 109 , fGamma(gamma) | 37 , fGamma(gamma) |
| 110 , fToXYZD50(toXYZD50) | 38 , fToXYZD50(toXYZD50) |
| 111 , fToXYZOffset(toXYZOffset) | 39 , fToXYZOffset(toXYZOffset) |
| 112 , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID)) | 40 , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID)) |
| 113 , fNamed(kUnknown_Named) | 41 , fNamed(kUnknown_Named) |
| 114 {} | 42 {} |
| 115 | 43 |
| 116 sk_sp<SkColorSpace> SkColorSpace::NewRGB(const SkFloat3x3& toXYZD50, const SkFlo at3& gamma) { | 44 sk_sp<SkColorSpace> SkColorSpace::NewRGB(const SkFloat3x3& toXYZD50, const SkFlo at3& gamma) { |
| 117 for (int i = 0; i < 3; ++i) { | |
| 118 if (!SkFloatIsFinite(gamma.fVec[i]) || gamma.fVec[i] < 0) { | |
|
msarett
2016/04/28 15:03:25
We're going to need these types of checks/clamps.
| |
| 119 return nullptr; | |
| 120 } | |
| 121 for (int j = 0; j < 3; ++j) { | |
| 122 if (!SkFloatIsFinite(toXYZD50.fMat[3*i + j])) { | |
| 123 return nullptr; | |
| 124 } | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 // check the matrix for invertibility | |
| 129 float d = det(toXYZD50); | |
| 130 if (!SkFloatIsFinite(d) || !SkFloatIsFinite(1 / d)) { | |
| 131 return nullptr; | |
| 132 } | |
| 133 | |
| 134 return sk_sp<SkColorSpace>(new SkColorSpace(gamma, toXYZD50, kUnknown_Named) ); | 45 return sk_sp<SkColorSpace>(new SkColorSpace(gamma, toXYZD50, kUnknown_Named) ); |
| 135 } | 46 } |
| 136 | 47 |
| 137 void SkColorSpace::dump() const { | |
| 138 fToXYZD50.dump(); | |
| 139 fGamma.dump(); | |
| 140 } | |
| 141 | |
| 142 //////////////////////////////////////////////////////////////////////////////// ////////////////// | |
| 143 | |
| 144 const SkFloat3 gDevice_gamma {{ 0, 0, 0 }}; | |
| 145 const SkFloat3x3 gDevice_toXYZD50 {{ | |
| 146 1, 0, 0, | |
| 147 0, 1, 0, | |
| 148 0, 0, 1 | |
| 149 }}; | |
| 150 | |
| 151 const SkFloat3 gSRGB_gamma {{ 2.2f, 2.2f, 2.2f }}; | 48 const SkFloat3 gSRGB_gamma {{ 2.2f, 2.2f, 2.2f }}; |
| 152 const SkFloat3x3 gSRGB_toXYZD50 {{ | 49 const SkFloat3x3 gSRGB_toXYZD50 {{ |
| 153 0.4358f, 0.2224f, 0.0139f, // * R | 50 0.4358f, 0.2224f, 0.0139f, // * R |
| 154 0.3853f, 0.7170f, 0.0971f, // * G | 51 0.3853f, 0.7170f, 0.0971f, // * G |
| 155 0.1430f, 0.0606f, 0.7139f, // * B | 52 0.1430f, 0.0606f, 0.7139f, // * B |
| 156 }}; | 53 }}; |
| 157 | 54 |
| 158 sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) { | 55 sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) { |
| 159 switch (named) { | 56 switch (named) { |
| 160 case kDevice_Named: | |
| 161 return sk_sp<SkColorSpace>(new SkColorSpace(gDevice_gamma, gDevice_t oXYZD50, | |
| 162 kDevice_Named)); | |
| 163 case kSRGB_Named: | 57 case kSRGB_Named: |
| 164 return sk_sp<SkColorSpace>(new SkColorSpace(gSRGB_gamma, gSRGB_toXYZ D50, kSRGB_Named)); | 58 return sk_sp<SkColorSpace>(new SkColorSpace(gSRGB_gamma, gSRGB_toXYZ D50, kSRGB_Named)); |
| 165 default: | 59 default: |
| 166 break; | 60 break; |
| 167 } | 61 } |
| 168 return nullptr; | 62 return nullptr; |
| 169 } | 63 } |
| 170 | 64 |
| 171 //////////////////////////////////////////////////////////////////////////////// /////////////////// | 65 //////////////////////////////////////////////////////////////////////////////// /////////////////// |
| 172 | 66 |
| (...skipping 538 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 711 toXYZOffset)); | 605 toXYZOffset)); |
| 712 } | 606 } |
| 713 | 607 |
| 714 } | 608 } |
| 715 default: | 609 default: |
| 716 break; | 610 break; |
| 717 } | 611 } |
| 718 | 612 |
| 719 return_null("ICC profile contains unsupported colorspace"); | 613 return_null("ICC profile contains unsupported colorspace"); |
| 720 } | 614 } |
| 721 | |
| 722 //////////////////////////////////////////////////////////////////////////////// /////////////////// | |
| 723 | |
| 724 SkColorSpace::Result SkColorSpace::Concat(const SkColorSpace* src, const SkColor Space* dst, | |
| 725 SkFloat3x3* result) { | |
| 726 if (!src || !dst || (src->named() == kDevice_Named) || (src->named() == dst- >named())) { | |
| 727 if (result) { | |
| 728 *result = {{ 1, 0, 0, 0, 1, 0, 0, 0, 1 }}; | |
| 729 } | |
| 730 return kIdentity_Result; | |
| 731 } | |
| 732 if (result) { | |
| 733 *result = concat(src->fToXYZD50, invert(dst->fToXYZD50)); | |
| 734 } | |
| 735 return kNormal_Result; | |
| 736 } | |
| 737 | |
| 738 #include "SkColor.h" | |
| 739 #include "SkNx.h" | |
| 740 #include "SkPM4f.h" | |
| 741 | |
| 742 void SkApply3x3ToPM4f(const SkFloat3x3& m, const SkPM4f src[], SkPM4f dst[], int count) { | |
| 743 SkASSERT(1 == SkPM4f::G); | |
| 744 SkASSERT(3 == SkPM4f::A); | |
| 745 | |
| 746 Sk4f cr, cg, cb; | |
| 747 cg = Sk4f::Load(m.fMat + 3); | |
| 748 if (0 == SkPM4f::R) { | |
| 749 SkASSERT(2 == SkPM4f::B); | |
| 750 cr = Sk4f::Load(m.fMat + 0); | |
| 751 cb = Sk4f(m.fMat[6], m.fMat[7], m.fMat[8], 0); | |
| 752 } else { | |
| 753 SkASSERT(0 == SkPM4f::B); | |
| 754 SkASSERT(2 == SkPM4f::R); | |
| 755 cb = Sk4f::Load(m.fMat + 0); | |
| 756 cr = Sk4f(m.fMat[6], m.fMat[7], m.fMat[8], 0); | |
| 757 } | |
| 758 cr = cr * Sk4f(1, 1, 1, 0); | |
| 759 cg = cg * Sk4f(1, 1, 1, 0); | |
| 760 cb = cb * Sk4f(1, 1, 1, 0); | |
| 761 | |
| 762 for (int i = 0; i < count; ++i) { | |
| 763 Sk4f r = Sk4f(src[i].fVec[SkPM4f::R]); | |
| 764 Sk4f g = Sk4f(src[i].fVec[SkPM4f::G]); | |
| 765 Sk4f b = Sk4f(src[i].fVec[SkPM4f::B]); | |
| 766 Sk4f a = Sk4f(0, 0, 0, src[i].fVec[SkPM4f::A]); | |
| 767 (cr * r + cg * g + cb * b + a).store(&dst[i]); | |
| 768 } | |
| 769 } | |
| 770 | |
| 771 //////////////////////////////////////////////////////////////////////////////// /////////////////// | |
| 772 | |
| 773 void SkColorSpace::Test() { | |
| 774 SkFloat3x3 mat {{ 2, 0, 0, 0, 3, 0, 0, 0, 4 }}; | |
| 775 SkFloat3x3 inv = invert(mat); | |
| 776 mat.dump(); | |
| 777 inv.dump(); | |
| 778 concat(mat, inv).dump(); | |
| 779 concat(inv, mat).dump(); | |
| 780 SkDebugf("\n"); | |
| 781 | |
| 782 mat = gSRGB_toXYZD50; | |
| 783 inv = invert(mat); | |
| 784 mat.dump(); | |
| 785 inv.dump(); | |
| 786 concat(mat, inv).dump(); | |
| 787 concat(inv, mat).dump(); | |
| 788 SkDebugf("\n"); | |
| 789 | |
| 790 sk_sp<SkColorSpace> cs0(SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)); | |
| 791 sk_sp<SkColorSpace> cs1(SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)); | |
| 792 | |
| 793 cs0->dump(); | |
| 794 cs1->dump(); | |
| 795 SkFloat3x3 xform; | |
| 796 (void)SkColorSpace::Concat(cs0.get(), cs1.get(), &xform); | |
| 797 xform.dump(); | |
| 798 SkDebugf("\n"); | |
| 799 } | |
| 800 | |
| 801 // D65 white point of Rec. 709 [8] are: | |
| 802 // | |
| 803 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 | |
| 804 // | |
| 805 // R G B white | |
| 806 // x 0.640 0.300 0.150 0.3127 | |
| 807 // y 0.330 0.600 0.060 0.3290 | |
| 808 // z 0.030 0.100 0.790 0.3582 | |
| OLD | NEW |