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 "SkColorSpace.h" | 8 #include "SkColorSpace.h" |
9 #include "SkColorSpace_Base.h" | 9 #include "SkColorSpace_Base.h" |
10 #include "SkEndian.h" | |
10 #include "SkOnce.h" | 11 #include "SkOnce.h" |
11 | 12 |
12 static bool color_space_almost_equal(float a, float b) { | 13 static bool color_space_almost_equal(float a, float b) { |
13 return SkTAbs(a - b) < 0.01f; | 14 return SkTAbs(a - b) < 0.01f; |
14 } | 15 } |
15 | 16 |
16 //////////////////////////////////////////////////////////////////////////////// ////////////////// | 17 //////////////////////////////////////////////////////////////////////////////// ////////////////// |
17 | 18 |
18 SkColorSpace::SkColorSpace(GammaNamed gammaNamed, const SkMatrix44& toXYZD50, Na med named) | 19 SkColorSpace::SkColorSpace(GammaNamed gammaNamed, const SkMatrix44& toXYZD50, Na med named, |
20 sk_sp<SkData> profileData) | |
19 : fGammaNamed(kNonStandard_GammaNamed) | 21 : fGammaNamed(kNonStandard_GammaNamed) |
20 , fToXYZD50(toXYZD50) | 22 , fToXYZD50(toXYZD50) |
21 , fNamed(named) | 23 , fNamed(named) |
24 , fProfileData(std::move(profileData)) | |
22 {} | 25 {} |
23 | 26 |
24 SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkGammas> gammas, const SkMatrix44& t oXYZD50, | 27 SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkGammas> gammas, const SkMatrix44& t oXYZD50, |
25 Named named) | 28 Named named, sk_sp<SkData> profileData) |
26 : INHERITED(kNonStandard_GammaNamed, toXYZD50, named) | 29 : INHERITED(kNonStandard_GammaNamed, toXYZD50, named, std::move(profileData) ) |
27 , fGammas(gammas) | 30 , fGammas(gammas) |
28 {} | 31 {} |
29 | 32 |
30 SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkGammas> gammas, GammaNamed gammaNam ed, | 33 SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkGammas> gammas, GammaNamed gammaNam ed, |
31 const SkMatrix44& toXYZD50, Named named) | 34 const SkMatrix44& toXYZD50, Named named, |
32 : INHERITED(gammaNamed, toXYZD50, named) | 35 sk_sp<SkData> profileData) |
36 : INHERITED(gammaNamed, toXYZD50, named, std::move(profileData)) | |
33 , fGammas(gammas) | 37 , fGammas(gammas) |
34 {} | 38 {} |
35 | 39 |
36 SkColorSpace_Base::SkColorSpace_Base(SkColorLookUpTable* colorLUT, sk_sp<SkGamma s> gammas, | 40 SkColorSpace_Base::SkColorSpace_Base(SkColorLookUpTable* colorLUT, sk_sp<SkGamma s> gammas, |
37 const SkMatrix44& toXYZD50) | 41 const SkMatrix44& toXYZD50, sk_sp<SkData> p rofileData) |
38 : INHERITED(kNonStandard_GammaNamed, toXYZD50, kUnknown_Named) | 42 : INHERITED(kNonStandard_GammaNamed, toXYZD50, kUnknown_Named, std::move(pro fileData)) |
39 , fColorLUT(colorLUT) | 43 , fColorLUT(colorLUT) |
40 , fGammas(gammas) | 44 , fGammas(gammas) |
41 {} | 45 {} |
42 | 46 |
43 const float gSRGB_toXYZD50[] { | 47 const float gSRGB_toXYZD50[] { |
44 0.4358f, 0.2224f, 0.0139f, // * R | 48 0.4358f, 0.2224f, 0.0139f, // * R |
45 0.3853f, 0.7170f, 0.0971f, // * G | 49 0.3853f, 0.7170f, 0.0971f, // * G |
46 0.1430f, 0.0606f, 0.7139f, // * B | 50 0.1430f, 0.0606f, 0.7139f, // * B |
47 }; | 51 }; |
48 | 52 |
(...skipping 26 matching lines...) Expand all Loading... | |
75 color_space_almost_equal(toXYZD50.getFloat(3, 1), 0.0f) && | 79 color_space_almost_equal(toXYZD50.getFloat(3, 1), 0.0f) && |
76 color_space_almost_equal(toXYZD50.getFloat(3, 2), 0.0f) && | 80 color_space_almost_equal(toXYZD50.getFloat(3, 2), 0.0f) && |
77 color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f); | 81 color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f); |
78 } | 82 } |
79 | 83 |
80 static SkOnce g2Dot2CurveGammasOnce; | 84 static SkOnce g2Dot2CurveGammasOnce; |
81 static SkGammas* g2Dot2CurveGammas; | 85 static SkGammas* g2Dot2CurveGammas; |
82 static SkOnce gLinearGammasOnce; | 86 static SkOnce gLinearGammasOnce; |
83 static SkGammas* gLinearGammas; | 87 static SkGammas* gLinearGammas; |
84 | 88 |
85 sk_sp<SkColorSpace> SkColorSpace::NewRGB(float gammaVals[3], const SkMatrix44& t oXYZD50) { | 89 sk_sp<SkColorSpace> SkColorSpace::NewRGB(const float gammaVals[3], const SkMatri x44& toXYZD50) { |
90 return SkColorSpace_Base::NewRGB(gammaVals, toXYZD50, nullptr); | |
91 } | |
92 | |
93 sk_sp<SkColorSpace> SkColorSpace_Base::NewRGB(const float gammaVals[3], const Sk Matrix44& toXYZD50, | |
94 sk_sp<SkData> profileData) { | |
86 sk_sp<SkGammas> gammas = nullptr; | 95 sk_sp<SkGammas> gammas = nullptr; |
87 GammaNamed gammaNamed = kNonStandard_GammaNamed; | 96 GammaNamed gammaNamed = kNonStandard_GammaNamed; |
88 | 97 |
89 // Check if we really have sRGB or Adobe RGB | 98 // Check if we really have sRGB or Adobe RGB |
90 if (color_space_almost_equal(2.2f, gammaVals[0]) && | 99 if (color_space_almost_equal(2.2f, gammaVals[0]) && |
91 color_space_almost_equal(2.2f, gammaVals[1]) && | 100 color_space_almost_equal(2.2f, gammaVals[1]) && |
92 color_space_almost_equal(2.2f, gammaVals[2])) | 101 color_space_almost_equal(2.2f, gammaVals[2])) |
93 { | 102 { |
94 g2Dot2CurveGammasOnce([] { | 103 g2Dot2CurveGammasOnce([] { |
95 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); | 104 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); |
(...skipping 13 matching lines...) Expand all Loading... | |
109 gLinearGammasOnce([] { | 118 gLinearGammasOnce([] { |
110 gLinearGammas = new SkGammas(1.0f, 1.0f, 1.0f); | 119 gLinearGammas = new SkGammas(1.0f, 1.0f, 1.0f); |
111 }); | 120 }); |
112 gammas = sk_ref_sp(gLinearGammas); | 121 gammas = sk_ref_sp(gLinearGammas); |
113 gammaNamed = kLinear_GammaNamed; | 122 gammaNamed = kLinear_GammaNamed; |
114 } | 123 } |
115 | 124 |
116 if (!gammas) { | 125 if (!gammas) { |
117 gammas = sk_sp<SkGammas>(new SkGammas(gammaVals[0], gammaVals[1], gammaV als[2])); | 126 gammas = sk_sp<SkGammas>(new SkGammas(gammaVals[0], gammaVals[1], gammaV als[2])); |
118 } | 127 } |
119 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, gammaNamed, toXYZD5 0, kUnknown_Named)); | 128 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, gammaNamed, toXYZD5 0, kUnknown_Named, |
129 std::move(profileData))); | |
120 } | 130 } |
121 | 131 |
122 sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) { | 132 sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) { |
123 static SkOnce sRGBOnce; | 133 static SkOnce sRGBOnce; |
124 static SkColorSpace* sRGB; | 134 static SkColorSpace* sRGB; |
125 static SkOnce adobeRGBOnce; | 135 static SkOnce adobeRGBOnce; |
126 static SkColorSpace* adobeRGB; | 136 static SkColorSpace* adobeRGB; |
127 | 137 |
128 switch (named) { | 138 switch (named) { |
129 case kSRGB_Named: { | 139 case kSRGB_Named: { |
130 g2Dot2CurveGammasOnce([] { | 140 g2Dot2CurveGammasOnce([] { |
131 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); | 141 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); |
132 }); | 142 }); |
133 | 143 |
134 sRGBOnce([] { | 144 sRGBOnce([] { |
135 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor); | 145 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor); |
136 srgbToxyzD50.set3x3ColMajorf(gSRGB_toXYZD50); | 146 srgbToxyzD50.set3x3ColMajorf(gSRGB_toXYZD50); |
137 sRGB = new SkColorSpace_Base(sk_ref_sp(g2Dot2CurveGammas), k2Dot 2Curve_GammaNamed, | 147 sRGB = new SkColorSpace_Base(sk_ref_sp(g2Dot2CurveGammas), k2Dot 2Curve_GammaNamed, |
138 srgbToxyzD50, kSRGB_Named); | 148 srgbToxyzD50, kSRGB_Named, nullptr) ; |
139 }); | 149 }); |
140 return sk_ref_sp(sRGB); | 150 return sk_ref_sp(sRGB); |
141 } | 151 } |
142 case kAdobeRGB_Named: { | 152 case kAdobeRGB_Named: { |
143 g2Dot2CurveGammasOnce([] { | 153 g2Dot2CurveGammasOnce([] { |
144 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); | 154 g2Dot2CurveGammas = new SkGammas(2.2f, 2.2f, 2.2f); |
145 }); | 155 }); |
146 | 156 |
147 adobeRGBOnce([] { | 157 adobeRGBOnce([] { |
148 SkMatrix44 adobergbToxyzD50(SkMatrix44::kUninitialized_Construct or); | 158 SkMatrix44 adobergbToxyzD50(SkMatrix44::kUninitialized_Construct or); |
149 adobergbToxyzD50.set3x3ColMajorf(gAdobeRGB_toXYZD50); | 159 adobergbToxyzD50.set3x3ColMajorf(gAdobeRGB_toXYZD50); |
150 adobeRGB = new SkColorSpace_Base(sk_ref_sp(g2Dot2CurveGammas), | 160 adobeRGB = new SkColorSpace_Base(sk_ref_sp(g2Dot2CurveGammas), |
151 k2Dot2Curve_GammaNamed, adoberg bToxyzD50, | 161 k2Dot2Curve_GammaNamed, adoberg bToxyzD50, |
152 kAdobeRGB_Named); | 162 kAdobeRGB_Named, nullptr); |
153 }); | 163 }); |
154 return sk_ref_sp(adobeRGB); | 164 return sk_ref_sp(adobeRGB); |
155 } | 165 } |
156 default: | 166 default: |
157 break; | 167 break; |
158 } | 168 } |
159 return nullptr; | 169 return nullptr; |
160 } | 170 } |
161 | 171 |
162 //////////////////////////////////////////////////////////////////////////////// /////////////////// | 172 //////////////////////////////////////////////////////////////////////////////// /////////////////// |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
194 | 204 |
195 // This is equal to the header size according to the ICC specification (128) | 205 // This is equal to the header size according to the ICC specification (128) |
196 // plus the size of the tag count (4). We include the tag count since we | 206 // plus the size of the tag count (4). We include the tag count since we |
197 // always require it to be present anyway. | 207 // always require it to be present anyway. |
198 static const size_t kICCHeaderSize = 132; | 208 static const size_t kICCHeaderSize = 132; |
199 | 209 |
200 // Contains a signature (4), offset (4), and size (4). | 210 // Contains a signature (4), offset (4), and size (4). |
201 static const size_t kICCTagTableEntrySize = 12; | 211 static const size_t kICCTagTableEntrySize = 12; |
202 | 212 |
203 static const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' '); | 213 static const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' '); |
214 static const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r'); | |
scroggo
2016/05/24 15:57:50
Now that we don't have to worry about VS 2013, sho
msarett
2016/05/24 18:38:06
Sure sounds good to me.
| |
215 static const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r'); | |
216 static const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r'); | |
217 static const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' '); | |
218 static const uint32_t kACSP_Signature = SkSetFourByteTag('a', 'c', 's', 'p'); | |
204 | 219 |
205 struct ICCProfileHeader { | 220 struct ICCProfileHeader { |
206 uint32_t fSize; | 221 uint32_t fSize; |
207 | 222 |
208 // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.). | 223 // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.). |
209 // We're always going to use this one. | 224 // We're always going to use this one. |
210 uint32_t fCMMType_ignored; | 225 uint32_t fCMMType_ignored; |
211 | 226 |
212 uint32_t fVersion; | 227 uint32_t fVersion; |
213 uint32_t fProfileClass; | 228 uint32_t fProfileClass; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
259 | 274 |
260 bool valid() const { | 275 bool valid() const { |
261 return_if_false(fSize >= kICCHeaderSize, "Size is too small"); | 276 return_if_false(fSize >= kICCHeaderSize, "Size is too small"); |
262 | 277 |
263 uint8_t majorVersion = fVersion >> 24; | 278 uint8_t majorVersion = fVersion >> 24; |
264 return_if_false(majorVersion <= 4, "Unsupported version"); | 279 return_if_false(majorVersion <= 4, "Unsupported version"); |
265 | 280 |
266 // These are the three basic classes of profiles that we might expect to see embedded | 281 // These are the three basic classes of profiles that we might expect to see embedded |
267 // in images. Four additional classes exist, but they generally are use d as a convenient | 282 // in images. Four additional classes exist, but they generally are use d as a convenient |
268 // way for CMMs to store calculated transforms. | 283 // way for CMMs to store calculated transforms. |
269 const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r'); | |
270 const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r'); | |
271 const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r'); | |
272 return_if_false(fProfileClass == kDisplay_Profile || | 284 return_if_false(fProfileClass == kDisplay_Profile || |
273 fProfileClass == kInput_Profile || | 285 fProfileClass == kInput_Profile || |
274 fProfileClass == kOutput_Profile, | 286 fProfileClass == kOutput_Profile, |
275 "Unsupported profile"); | 287 "Unsupported profile"); |
276 | 288 |
277 // TODO (msarett): | 289 // TODO (msarett): |
278 // All the profiles we've tested so far use RGB as the input color space . | 290 // All the profiles we've tested so far use RGB as the input color space . |
279 return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space"); | 291 return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space"); |
280 | 292 |
281 // TODO (msarett): | 293 // TODO (msarett): |
282 // All the profiles we've tested so far use XYZ as the profile connectio n space. | 294 // All the profiles we've tested so far use XYZ as the profile connectio n space. |
283 const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' '); | |
284 return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space"); | 295 return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space"); |
285 | 296 |
286 return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "Bad signature"); | 297 return_if_false(fSignature == kACSP_Signature, "Bad signature"); |
287 | 298 |
288 // TODO (msarett): | 299 // TODO (msarett): |
289 // Should we treat different rendering intents differently? | 300 // Should we treat different rendering intents differently? |
290 // Valid rendering intents include kPerceptual (0), kRelative (1), | 301 // Valid rendering intents include kPerceptual (0), kRelative (1), |
291 // kSaturation (2), and kAbsolute (3). | 302 // kSaturation (2), and kAbsolute (3). |
292 return_if_false(fRenderingIntent <= 3, "Bad rendering intent"); | 303 return_if_false(fRenderingIntent <= 3, "Bad rendering intent"); |
293 | 304 |
294 return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0 ]), 0.96420f) && | 305 return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0 ]), 0.96420f) && |
295 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1 ]), 1.00000f) && | 306 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1 ]), 1.00000f) && |
296 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2 ]), 0.82491f), | 307 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2 ]), 0.82491f), |
(...skipping 401 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
698 uint32_t offsetToMatrix = read_big_endian_int(src + 16); | 709 uint32_t offsetToMatrix = read_big_endian_int(src + 16); |
699 if (0 != offsetToMatrix && offsetToMatrix < len) { | 710 if (0 != offsetToMatrix && offsetToMatrix < len) { |
700 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) { | 711 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) { |
701 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); | 712 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); |
702 } | 713 } |
703 } | 714 } |
704 | 715 |
705 return true; | 716 return true; |
706 } | 717 } |
707 | 718 |
708 sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* base, size_t len) { | 719 sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* input, size_t len) { |
709 const uint8_t* ptr = (const uint8_t*) base; | |
710 | |
711 if (len < kICCHeaderSize) { | 720 if (len < kICCHeaderSize) { |
712 return_null("Data is not large enough to contain an ICC profile"); | 721 return_null("Data is not large enough to contain an ICC profile"); |
713 } | 722 } |
714 | 723 |
724 // Create our own copy of the input. | |
725 void* memory = sk_malloc_throw(len); | |
726 memcpy(memory, input, len); | |
727 sk_sp<SkData> data = SkData::MakeFromMalloc(memory, len); | |
728 const void* base = data->data(); | |
729 const uint8_t* ptr = (const uint8_t*) base; | |
730 | |
715 // Read the ICC profile header and check to make sure that it is valid. | 731 // Read the ICC profile header and check to make sure that it is valid. |
716 ICCProfileHeader header; | 732 ICCProfileHeader header; |
717 header.init(ptr, len); | 733 header.init(ptr, len); |
718 if (!header.valid()) { | 734 if (!header.valid()) { |
719 return nullptr; | 735 return nullptr; |
720 } | 736 } |
721 | 737 |
722 // Adjust ptr and len before reading the tags. | 738 // Adjust ptr and len before reading the tags. |
723 if (len < header.fSize) { | 739 if (len < header.fSize) { |
724 SkColorSpacePrintf("ICC profile might be truncated.\n"); | 740 SkColorSpacePrintf("ICC profile might be truncated.\n"); |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
786 std::move(curves[2]))); | 802 std::move(curves[2]))); |
787 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); | 803 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); |
788 mat.set3x3ColMajorf(toXYZ); | 804 mat.set3x3ColMajorf(toXYZ); |
789 if (gammas->isValues()) { | 805 if (gammas->isValues()) { |
790 // When we have values, take advantage of the NewFromRGB ini tializer. | 806 // When we have values, take advantage of the NewFromRGB ini tializer. |
791 // This allows us to check for canonical sRGB and Adobe RGB. | 807 // This allows us to check for canonical sRGB and Adobe RGB. |
792 float gammaVals[3]; | 808 float gammaVals[3]; |
793 gammaVals[0] = gammas->fRed.fValue; | 809 gammaVals[0] = gammas->fRed.fValue; |
794 gammaVals[1] = gammas->fGreen.fValue; | 810 gammaVals[1] = gammas->fGreen.fValue; |
795 gammaVals[2] = gammas->fBlue.fValue; | 811 gammaVals[2] = gammas->fBlue.fValue; |
796 return SkColorSpace::NewRGB(gammaVals, mat); | 812 return SkColorSpace_Base::NewRGB(gammaVals, mat, std::move(d ata)); |
797 } else { | 813 } else { |
798 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, mat , kUnknown_Named)); | 814 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, mat , kUnknown_Named, |
815 std::move(d ata))); | |
799 } | 816 } |
800 } | 817 } |
801 | 818 |
802 // Recognize color profile specified by A2B0 tag. | 819 // Recognize color profile specified by A2B0 tag. |
803 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); | 820 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); |
804 if (a2b0) { | 821 if (a2b0) { |
805 SkAutoTDelete<SkColorLookUpTable> colorLUT(new SkColorLookUpTabl e()); | 822 SkAutoTDelete<SkColorLookUpTable> colorLUT(new SkColorLookUpTabl e()); |
806 SkGammaCurve curves[3]; | 823 SkGammaCurve curves[3]; |
807 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); | 824 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); |
808 if (!load_a2b0(colorLUT, curves, &toXYZ, a2b0->addr((const uint8 _t*) base), | 825 if (!load_a2b0(colorLUT, curves, &toXYZ, a2b0->addr((const uint8 _t*) base), |
809 a2b0->fLength)) { | 826 a2b0->fLength)) { |
810 return_null("Failed to parse A2B0 tag"); | 827 return_null("Failed to parse A2B0 tag"); |
811 } | 828 } |
812 | 829 |
813 sk_sp<SkGammas> gammas(new SkGammas(std::move(curves[0]), std::m ove(curves[1]), | 830 sk_sp<SkGammas> gammas(new SkGammas(std::move(curves[0]), std::m ove(curves[1]), |
814 std::move(curves[2]))); | 831 std::move(curves[2]))); |
815 if (colorLUT->fTable) { | 832 if (colorLUT->fTable) { |
816 return sk_sp<SkColorSpace>(new SkColorSpace_Base(colorLUT.re lease(), gammas, | 833 return sk_sp<SkColorSpace>(new SkColorSpace_Base(colorLUT.re lease(), gammas, |
817 toXYZ)); | 834 toXYZ, std: :move(data))); |
818 } else if (gammas->isValues()) { | 835 } else if (gammas->isValues()) { |
819 // When we have values, take advantage of the NewFromRGB ini tializer. | 836 // When we have values, take advantage of the NewFromRGB ini tializer. |
820 // This allows us to check for canonical sRGB and Adobe RGB. | 837 // This allows us to check for canonical sRGB and Adobe RGB. |
821 float gammaVals[3]; | 838 float gammaVals[3]; |
822 gammaVals[0] = gammas->fRed.fValue; | 839 gammaVals[0] = gammas->fRed.fValue; |
823 gammaVals[1] = gammas->fGreen.fValue; | 840 gammaVals[1] = gammas->fGreen.fValue; |
824 gammaVals[2] = gammas->fBlue.fValue; | 841 gammaVals[2] = gammas->fBlue.fValue; |
825 return SkColorSpace::NewRGB(gammaVals, toXYZ); | 842 return SkColorSpace_Base::NewRGB(gammaVals, toXYZ, std::move (data)); |
826 } else { | 843 } else { |
827 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, toX YZ, | 844 return sk_sp<SkColorSpace>(new SkColorSpace_Base(gammas, toX YZ, |
828 kUnknown_Na med)); | 845 kUnknown_Na med, |
846 std::move(d ata))); | |
829 } | 847 } |
830 } | 848 } |
831 | 849 |
832 } | 850 } |
833 default: | 851 default: |
834 break; | 852 break; |
835 } | 853 } |
836 | 854 |
837 return_null("ICC profile contains unsupported colorspace"); | 855 return_null("ICC profile contains unsupported colorspace"); |
838 } | 856 } |
857 | |
858 //////////////////////////////////////////////////////////////////////////////// /////////////////// | |
859 | |
860 // We will write a profile with the minimum nine required tags. | |
861 static const uint32_t kICCNumEntries = 9; | |
862 | |
863 static const uint32_t kTAG_desc = SkSetFourByteTag('d', 'e', 's', 'c'); | |
864 static const uint32_t kTAG_desc_Bytes = 12; | |
865 static const uint32_t kTAG_desc_Offset = kICCHeaderSize + kICCNumEntries * kICCT agTableEntrySize; | |
866 | |
867 static const uint32_t kTAG_XYZ_Bytes = 20; | |
868 static const uint32_t kTAG_rXYZ_Offset = kTAG_desc_Offset + kTAG_desc_Bytes; | |
869 static const uint32_t kTAG_gXYZ_Offset = kTAG_rXYZ_Offset + kTAG_XYZ_Bytes; | |
870 static const uint32_t kTAG_bXYZ_Offset = kTAG_gXYZ_Offset + kTAG_XYZ_Bytes; | |
871 | |
872 static const uint32_t kTAG_TRC_Bytes = 14; | |
873 static const uint32_t kTAG_rTRC_Offset = kTAG_bXYZ_Offset + kTAG_XYZ_Bytes; | |
874 static const uint32_t kTAG_gTRC_Offset = kTAG_rTRC_Offset + SkAlign4(kTAG_TRC_By tes); | |
875 static const uint32_t kTAG_bTRC_Offset = kTAG_gTRC_Offset + SkAlign4(kTAG_TRC_By tes); | |
876 | |
877 static const uint32_t kTAG_wtpt = SkSetFourByteTag('w', 't', 'p', 't'); | |
878 static const uint32_t kTAG_wtpt_Offset = kTAG_bTRC_Offset + SkAlign4(kTAG_TRC_By tes); | |
879 | |
880 static const uint32_t kTAG_cprt = SkSetFourByteTag('c', 'p', 'r', 't'); | |
881 static const uint32_t kTAG_cprt_Bytes = 12; | |
882 static const uint32_t kTAG_cprt_Offset = kTAG_wtpt_Offset + kTAG_XYZ_Bytes; | |
883 | |
884 static const uint32_t kICCProfileSize = kTAG_cprt_Offset + kTAG_cprt_Bytes; | |
885 | |
886 static const uint32_t gICCHeader[kICCHeaderSize / 4] { | |
887 SkEndian_SwapBE32(kICCProfileSize), // Size of the profile | |
888 0, // Preferred CMM type (ignored) | |
889 SkEndian_SwapBE32(0x02100000), // Version 2.1 | |
890 SkEndian_SwapBE32(kDisplay_Profile), // Display device profile | |
891 SkEndian_SwapBE32(kRGB_ColorSpace), // RGB input color space | |
892 SkEndian_SwapBE32(kXYZ_PCSSpace), // XYZ profile connection space | |
893 0, 0, 0, // Date and time (ignored) | |
894 SkEndian_SwapBE32(kACSP_Signature), // Profile signature | |
895 0, // Platform target (ignored) | |
896 0x00000000, // Flags: not embedded, can be used ind ependently | |
897 0, // Device manufacturer (ignored) | |
898 0, // Device model (ignored) | |
899 0, 0, // Device attributes (ignored) | |
900 SkEndian_SwapBE32(1), // Relative colorimetric rendering inte nt | |
901 SkEndian_SwapBE32(0x0000f6d6), // D50 standard illuminant (X) | |
902 SkEndian_SwapBE32(0x00010000), // D50 standard illuminant (Y) | |
903 SkEndian_SwapBE32(0x0000d32d), // D50 standard illuminant (Z) | |
904 0, // Profile creator (ignored) | |
905 0, 0, 0, 0, // Profile id checksum (ignored) | |
906 0, 0, 0, 0, 0, 0, 0, // Reserved (ignored) | |
907 SkEndian_SwapBE32(kICCNumEntries), // Number of tags | |
908 }; | |
909 | |
910 static const uint32_t gICCTagTable[3 * kICCNumEntries] { | |
911 // Profile description | |
912 SkEndian_SwapBE32(kTAG_desc), | |
913 SkEndian_SwapBE32(kTAG_desc_Offset), | |
914 SkEndian_SwapBE32(kTAG_desc_Bytes), | |
915 | |
916 // rXYZ | |
917 SkEndian_SwapBE32(kTAG_rXYZ), | |
918 SkEndian_SwapBE32(kTAG_rXYZ_Offset), | |
919 SkEndian_SwapBE32(kTAG_XYZ_Bytes), | |
920 | |
921 // gXYZ | |
922 SkEndian_SwapBE32(kTAG_gXYZ), | |
923 SkEndian_SwapBE32(kTAG_gXYZ_Offset), | |
924 SkEndian_SwapBE32(kTAG_XYZ_Bytes), | |
925 | |
926 // bXYZ | |
927 SkEndian_SwapBE32(kTAG_bXYZ), | |
928 SkEndian_SwapBE32(kTAG_bXYZ_Offset), | |
929 SkEndian_SwapBE32(kTAG_XYZ_Bytes), | |
930 | |
931 // rTRC | |
932 SkEndian_SwapBE32(kTAG_rTRC), | |
933 SkEndian_SwapBE32(kTAG_rTRC_Offset), | |
934 SkEndian_SwapBE32(kTAG_TRC_Bytes), | |
935 | |
936 // gTRC | |
937 SkEndian_SwapBE32(kTAG_gTRC), | |
938 SkEndian_SwapBE32(kTAG_gTRC_Offset), | |
939 SkEndian_SwapBE32(kTAG_TRC_Bytes), | |
940 | |
941 // bTRC | |
942 SkEndian_SwapBE32(kTAG_bTRC), | |
943 SkEndian_SwapBE32(kTAG_bTRC_Offset), | |
944 SkEndian_SwapBE32(kTAG_TRC_Bytes), | |
945 | |
946 // White point | |
947 SkEndian_SwapBE32(kTAG_wtpt), | |
948 SkEndian_SwapBE32(kTAG_wtpt_Offset), | |
949 SkEndian_SwapBE32(kTAG_XYZ_Bytes), | |
950 | |
951 // Copyright | |
952 SkEndian_SwapBE32(kTAG_cprt), | |
953 SkEndian_SwapBE32(kTAG_cprt_Offset), | |
954 SkEndian_SwapBE32(kTAG_cprt_Bytes), | |
955 }; | |
956 | |
957 static const uint32_t kTAG_TextType = SkSetFourByteTag('m', 'l', 'u', 'c'); | |
958 static const uint32_t gEmptyTextTag[3] { | |
959 SkEndian_SwapBE32(kTAG_TextType), // Type signature | |
960 0, // Reserved | |
961 0, // Zero records | |
962 }; | |
963 | |
964 static void write_xyz_tag(uint32_t* ptr, const SkMatrix44& toXYZ, int row) { | |
965 ptr[0] = SkEndian_SwapBE32(kXYZ_PCSSpace); | |
966 ptr[1] = 0; | |
967 ptr[2] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 0))); | |
968 ptr[3] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 1))); | |
969 ptr[4] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 2))); | |
970 } | |
971 | |
972 static void write_trc_tag(uint32_t* ptr, float value) { | |
973 ptr[0] = SkEndian_SwapBE32(kTAG_CurveType); | |
974 ptr[1] = 0; | |
975 | |
976 // Gamma will be specified with a single value. | |
977 ptr[2] = SkEndian_SwapBE32(1); | |
978 | |
979 // Convert gamma to 16-bit fixed point. | |
980 uint16_t* ptr16 = (uint16_t*) (ptr + 3); | |
981 ptr16[0] = SkEndian_SwapBE16((uint16_t) (value * 256.0f)); | |
982 | |
983 // Pad tag with zero. | |
984 ptr16[1] = 0; | |
985 } | |
986 | |
987 sk_sp<SkData> SkColorSpace::writeToICC() { | |
988 // Return if this object was created from a profile, or if we have already s erialized | |
989 // the profile. | |
990 if (fProfileData) { | |
991 return fProfileData; | |
992 } | |
993 | |
994 // The client may create an SkColorSpace using an SkMatrix44, but currently we only | |
995 // support writing profiles with 3x3 matrices. | |
996 // TODO (msarett): Fix this! | |
997 if (0.0f != fToXYZD50.getFloat(3, 0) || 0.0f != fToXYZD50.getFloat(3, 1) || | |
998 0.0f != fToXYZD50.getFloat(3, 2) || 0.0f != fToXYZD50.getFloat(0, 3) || | |
999 0.0f != fToXYZD50.getFloat(1, 3) || 0.0f != fToXYZD50.getFloat(2, 3)) | |
1000 { | |
1001 return nullptr; | |
1002 } | |
1003 | |
1004 void* profile = sk_malloc_throw(kICCProfileSize); | |
scroggo
2016/05/24 15:57:50
Should this be in an auto deleter of some kind?
msarett
2016/05/24 18:38:06
Yeah I think that's probably safer, though at the
| |
1005 uint8_t* ptr = (uint8_t*) profile; | |
1006 | |
1007 // Write profile header | |
1008 memcpy(ptr, gICCHeader, sizeof(gICCHeader)); | |
1009 ptr += sizeof(gICCHeader); | |
1010 | |
1011 // Write tag table | |
1012 memcpy(ptr, gICCTagTable, sizeof(gICCTagTable)); | |
1013 ptr += sizeof(gICCTagTable); | |
1014 | |
1015 // Write profile description tag | |
1016 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); | |
1017 ptr += sizeof(gEmptyTextTag); | |
1018 | |
1019 // Write XYZ tags | |
1020 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 0); | |
1021 ptr += kTAG_XYZ_Bytes; | |
1022 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 1); | |
1023 ptr += kTAG_XYZ_Bytes; | |
1024 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 2); | |
1025 ptr += kTAG_XYZ_Bytes; | |
1026 | |
1027 // Write TRC tags | |
1028 SkASSERT(as_CSB(this)->fGammas->fRed.isValue()); | |
1029 write_trc_tag((uint32_t*) ptr, as_CSB(this)->fGammas->fRed.fValue); | |
1030 ptr += SkAlign4(kTAG_TRC_Bytes); | |
1031 SkASSERT(as_CSB(this)->fGammas->fGreen.isValue()); | |
1032 write_trc_tag((uint32_t*) ptr, as_CSB(this)->fGammas->fGreen.fValue); | |
1033 ptr += SkAlign4(kTAG_TRC_Bytes); | |
1034 SkASSERT(as_CSB(this)->fGammas->fBlue.isValue()); | |
1035 write_trc_tag((uint32_t*) ptr, as_CSB(this)->fGammas->fBlue.fValue); | |
1036 ptr += SkAlign4(kTAG_TRC_Bytes); | |
1037 | |
1038 // Write white point tag | |
1039 uint32_t* ptr32 = (uint32_t*) ptr; | |
1040 ptr32[0] = SkEndian_SwapBE32(kXYZ_PCSSpace); | |
1041 ptr32[1] = 0; | |
1042 // TODO (msarett): These values correspond to the D65 white point. This may not always be | |
1043 // correct. | |
1044 ptr32[2] = SkEndian_SwapBE32(0x0000f351); | |
1045 ptr32[3] = SkEndian_SwapBE32(0x00010000); | |
1046 ptr32[4] = SkEndian_SwapBE32(0x000116cc); | |
1047 ptr += kTAG_XYZ_Bytes; | |
1048 | |
1049 // Write copyright tag | |
1050 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); | |
1051 | |
1052 // We choose to hold onto the profile here, in case we need to serialize aga in. | |
msarett
2016/05/24 15:36:58
I'm not sure if this is the right decision...
scroggo
2016/05/24 15:57:50
Add a comment? (Or leave it out for now?)
msarett
2016/05/24 18:38:06
Leaving it out for now... That allows this functi
| |
1053 fProfileData = std::move(SkData::MakeFromMalloc(profile, kICCProfileSize)); | |
1054 return fProfileData; | |
1055 } | |
OLD | NEW |