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