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 "SkColorSpacePriv.h" | 10 #include "SkColorSpacePriv.h" |
11 #include "SkEndian.h" | 11 #include "SkEndian.h" |
12 #include "SkFixed.h" | 12 #include "SkFixed.h" |
13 #include "SkTemplates.h" | 13 #include "SkTemplates.h" |
14 | 14 |
15 #define return_if_false(pred, msg) \ | 15 #define return_if_false(pred, msg) \ |
16 do { \ | 16 do { \ |
17 if (!(pred)) { \ | 17 if (!(pred)) { \ |
18 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ | 18 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ |
19 return false; \ | 19 return false; \ |
20 } \ | 20 } \ |
21 } while (0) | 21 } while (0) |
22 | 22 |
23 #define return_null(msg) \ | 23 #define return_null(msg) \ |
24 do { \ | 24 do { \ |
25 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ | 25 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ |
26 return nullptr; \ | 26 return nullptr; \ |
27 } while (0) | 27 } while (0) |
28 | 28 |
29 static uint16_t read_big_endian_short(const uint8_t* ptr) { | 29 static uint16_t read_big_endian_u16(const uint8_t* ptr) { |
30 return ptr[0] << 8 | ptr[1]; | 30 return ptr[0] << 8 | ptr[1]; |
31 } | 31 } |
32 | 32 |
33 static uint32_t read_big_endian_uint(const uint8_t* ptr) { | 33 static uint32_t read_big_endian_u32(const uint8_t* ptr) { |
34 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; | 34 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; |
35 } | 35 } |
36 | 36 |
37 static int32_t read_big_endian_int(const uint8_t* ptr) { | 37 static int32_t read_big_endian_i32(const uint8_t* ptr) { |
38 return (int32_t) read_big_endian_uint(ptr); | 38 return (int32_t) read_big_endian_u32(ptr); |
39 } | 39 } |
40 | 40 |
41 // This is equal to the header size according to the ICC specification (128) | 41 // This is equal to the header size according to the ICC specification (128) |
42 // plus the size of the tag count (4). We include the tag count since we | 42 // plus the size of the tag count (4). We include the tag count since we |
43 // always require it to be present anyway. | 43 // always require it to be present anyway. |
44 static constexpr size_t kICCHeaderSize = 132; | 44 static constexpr size_t kICCHeaderSize = 132; |
45 | 45 |
46 // Contains a signature (4), offset (4), and size (4). | 46 // Contains a signature (4), offset (4), and size (4). |
47 static constexpr size_t kICCTagTableEntrySize = 12; | 47 static constexpr size_t kICCTagTableEntrySize = 12; |
48 | 48 |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
98 // Reserved for future use. | 98 // Reserved for future use. |
99 uint32_t fReserved_ignored[7]; | 99 uint32_t fReserved_ignored[7]; |
100 | 100 |
101 uint32_t fTagCount; | 101 uint32_t fTagCount; |
102 | 102 |
103 void init(const uint8_t* src, size_t len) { | 103 void init(const uint8_t* src, size_t len) { |
104 SkASSERT(kICCHeaderSize == sizeof(*this)); | 104 SkASSERT(kICCHeaderSize == sizeof(*this)); |
105 | 105 |
106 uint32_t* dst = (uint32_t*) this; | 106 uint32_t* dst = (uint32_t*) this; |
107 for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) { | 107 for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) { |
108 dst[i] = read_big_endian_uint(src); | 108 dst[i] = read_big_endian_u32(src); |
109 } | 109 } |
110 } | 110 } |
111 | 111 |
112 bool valid() const { | 112 bool valid() const { |
113 return_if_false(fSize >= kICCHeaderSize, "Size is too small"); | 113 return_if_false(fSize >= kICCHeaderSize, "Size is too small"); |
114 | 114 |
115 uint8_t majorVersion = fVersion >> 24; | 115 uint8_t majorVersion = fVersion >> 24; |
116 return_if_false(majorVersion <= 4, "Unsupported version"); | 116 return_if_false(majorVersion <= 4, "Unsupported version"); |
117 | 117 |
118 // These are the four basic classes of profiles that we might expect to
see embedded | 118 // These are the four basic classes of profiles that we might expect to
see embedded |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 *result = product32; | 179 *result = product32; |
180 return true; | 180 return true; |
181 } | 181 } |
182 | 182 |
183 struct ICCTag { | 183 struct ICCTag { |
184 uint32_t fSignature; | 184 uint32_t fSignature; |
185 uint32_t fOffset; | 185 uint32_t fOffset; |
186 uint32_t fLength; | 186 uint32_t fLength; |
187 | 187 |
188 const uint8_t* init(const uint8_t* src) { | 188 const uint8_t* init(const uint8_t* src) { |
189 fSignature = read_big_endian_uint(src); | 189 fSignature = read_big_endian_u32(src); |
190 fOffset = read_big_endian_uint(src + 4); | 190 fOffset = read_big_endian_u32(src + 4); |
191 fLength = read_big_endian_uint(src + 8); | 191 fLength = read_big_endian_u32(src + 8); |
192 return src + 12; | 192 return src + 12; |
193 } | 193 } |
194 | 194 |
195 bool valid(size_t len) { | 195 bool valid(size_t len) { |
196 size_t tagEnd; | 196 size_t tagEnd; |
197 return_if_false(safe_add(fOffset, fLength, &tagEnd), | 197 return_if_false(safe_add(fOffset, fLength, &tagEnd), |
198 "Tag too large, overflows integer addition"); | 198 "Tag too large, overflows integer addition"); |
199 return_if_false(tagEnd <= len, "Tag too large for ICC profile"); | 199 return_if_false(tagEnd <= len, "Tag too large for ICC profile"); |
200 return true; | 200 return true; |
201 } | 201 } |
(...skipping 19 matching lines...) Expand all Loading... |
221 static constexpr uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C'); | 221 static constexpr uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C'); |
222 static constexpr uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C'); | 222 static constexpr uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C'); |
223 static constexpr uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0'); | 223 static constexpr uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0'); |
224 | 224 |
225 static bool load_xyz(float dst[3], const uint8_t* src, size_t len) { | 225 static bool load_xyz(float dst[3], const uint8_t* src, size_t len) { |
226 if (len < 20) { | 226 if (len < 20) { |
227 SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len); | 227 SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len); |
228 return false; | 228 return false; |
229 } | 229 } |
230 | 230 |
231 dst[0] = SkFixedToFloat(read_big_endian_int(src + 8)); | 231 dst[0] = SkFixedToFloat(read_big_endian_i32(src + 8)); |
232 dst[1] = SkFixedToFloat(read_big_endian_int(src + 12)); | 232 dst[1] = SkFixedToFloat(read_big_endian_i32(src + 12)); |
233 dst[2] = SkFixedToFloat(read_big_endian_int(src + 16)); | 233 dst[2] = SkFixedToFloat(read_big_endian_i32(src + 16)); |
234 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]); | 234 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]); |
235 return true; | 235 return true; |
236 } | 236 } |
237 | 237 |
238 static constexpr uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', '
v'); | 238 static constexpr uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', '
v'); |
239 static constexpr uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', '
a'); | 239 static constexpr uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', '
a'); |
240 | 240 |
241 static SkGammas::Type set_gamma_value(SkGammas::Data* data, float value) { | 241 static SkGammas::Type set_gamma_value(SkGammas::Data* data, float value) { |
242 if (color_space_almost_equal(2.2f, value)) { | 242 if (color_space_almost_equal(2.2f, value)) { |
243 data->fNamed = SkColorSpace::k2Dot2Curve_GammaNamed; | 243 data->fNamed = SkColorSpace::k2Dot2Curve_GammaNamed; |
244 return SkGammas::Type::kNamed_Type; | 244 return SkGammas::Type::kNamed_Type; |
245 } | 245 } |
246 | 246 |
247 if (color_space_almost_equal(1.0f, value)) { | 247 if (color_space_almost_equal(1.0f, value)) { |
248 data->fNamed = SkColorSpace::kLinear_GammaNamed; | 248 data->fNamed = SkColorSpace::kLinear_GammaNamed; |
249 return SkGammas::Type::kNamed_Type; | 249 return SkGammas::Type::kNamed_Type; |
250 } | 250 } |
251 | 251 |
252 if (color_space_almost_equal(0.0f, value)) { | 252 if (color_space_almost_equal(0.0f, value)) { |
253 return SkGammas::Type::kNone_Type; | 253 return SkGammas::Type::kNone_Type; |
254 } | 254 } |
255 | 255 |
256 data->fValue = value; | 256 data->fValue = value; |
257 return SkGammas::Type::kValue_Type; | 257 return SkGammas::Type::kValue_Type; |
258 } | 258 } |
259 | 259 |
260 static float read_big_endian_16_dot_16(const uint8_t buf[4]) { | 260 static float read_big_endian_16_dot_16(const uint8_t buf[4]) { |
261 // It just so happens that SkFixed is also 16.16! | 261 // It just so happens that SkFixed is also 16.16! |
262 return SkFixedToFloat(read_big_endian_int(buf)); | 262 return SkFixedToFloat(read_big_endian_i32(buf)); |
263 } | 263 } |
264 | 264 |
265 /** | 265 /** |
266 * @param outData Set to the appropriate value on success. If we have tabl
e or | 266 * @param outData Set to the appropriate value on success. If we have tabl
e or |
267 * parametric gamma, it is the responsibility of the caller
to set | 267 * parametric gamma, it is the responsibility of the caller
to set |
268 * fOffset. | 268 * fOffset. |
269 * @param outParams If this is a parametric gamma, this is set to the appropr
iate | 269 * @param outParams If this is a parametric gamma, this is set to the appropr
iate |
270 * parameters on success. | 270 * parameters on success. |
271 * @param outTagBytes Will be set to the length of the tag on success. | 271 * @param outTagBytes Will be set to the length of the tag on success. |
272 * @src Pointer to tag data. | 272 * @src Pointer to tag data. |
273 * @len Length of tag data in bytes. | 273 * @len Length of tag data in bytes. |
274 * | 274 * |
275 * @return kNone_Type on failure, otherwise the type of the gamma ta
g. | 275 * @return kNone_Type on failure, otherwise the type of the gamma ta
g. |
276 */ | 276 */ |
277 static SkGammas::Type parse_gamma(SkGammas::Data* outData, SkGammas::Params* out
Params, | 277 static SkGammas::Type parse_gamma(SkGammas::Data* outData, SkGammas::Params* out
Params, |
278 size_t* outTagBytes, const uint8_t* src, s
ize_t len) { | 278 size_t* outTagBytes, const uint8_t* src, s
ize_t len) { |
279 if (len < 12) { | 279 if (len < 12) { |
280 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); | 280 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
281 return SkGammas::Type::kNone_Type; | 281 return SkGammas::Type::kNone_Type; |
282 } | 282 } |
283 | 283 |
284 // In the case of consecutive gamma tags, we need to count the number of byt
es in the | 284 // In the case of consecutive gamma tags, we need to count the number of byt
es in the |
285 // tag, so that we can move on to the next tag. | 285 // tag, so that we can move on to the next tag. |
286 size_t tagBytes; | 286 size_t tagBytes; |
287 | 287 |
288 uint32_t type = read_big_endian_uint(src); | 288 uint32_t type = read_big_endian_u32(src); |
289 // Bytes 4-7 are reserved and should be set to zero. | 289 // Bytes 4-7 are reserved and should be set to zero. |
290 switch (type) { | 290 switch (type) { |
291 case kTAG_CurveType: { | 291 case kTAG_CurveType: { |
292 uint32_t count = read_big_endian_uint(src + 8); | 292 uint32_t count = read_big_endian_u32(src + 8); |
293 | 293 |
294 // tagBytes = 12 + 2 * count | 294 // tagBytes = 12 + 2 * count |
295 // We need to do safe addition here to avoid integer overflow. | 295 // We need to do safe addition here to avoid integer overflow. |
296 if (!safe_add(count, count, &tagBytes) || | 296 if (!safe_add(count, count, &tagBytes) || |
297 !safe_add((size_t) 12, tagBytes, &tagBytes)) | 297 !safe_add((size_t) 12, tagBytes, &tagBytes)) |
298 { | 298 { |
299 SkColorSpacePrintf("Invalid gamma count"); | 299 SkColorSpacePrintf("Invalid gamma count"); |
300 return SkGammas::Type::kNone_Type; | 300 return SkGammas::Type::kNone_Type; |
301 } | 301 } |
302 | 302 |
303 if (len < tagBytes) { | 303 if (len < tagBytes) { |
304 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); | 304 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
305 return SkGammas::Type::kNone_Type; | 305 return SkGammas::Type::kNone_Type; |
306 } | 306 } |
307 *outTagBytes = tagBytes; | 307 *outTagBytes = tagBytes; |
308 | 308 |
309 if (0 == count) { | 309 if (0 == count) { |
310 // Some tags require a gamma curve, but the author doesn't actua
lly want | 310 // Some tags require a gamma curve, but the author doesn't actua
lly want |
311 // to transform the data. In this case, it is common to see a c
urve with | 311 // to transform the data. In this case, it is common to see a c
urve with |
312 // a count of 0. | 312 // a count of 0. |
313 outData->fNamed = SkColorSpace::kLinear_GammaNamed; | 313 outData->fNamed = SkColorSpace::kLinear_GammaNamed; |
314 return SkGammas::Type::kNamed_Type; | 314 return SkGammas::Type::kNamed_Type; |
315 } | 315 } |
316 | 316 |
317 const uint16_t* table = (const uint16_t*) (src + 12); | 317 const uint16_t* table = (const uint16_t*) (src + 12); |
318 if (1 == count) { | 318 if (1 == count) { |
319 // The table entry is the gamma (with a bias of 256). | 319 // The table entry is the gamma (with a bias of 256). |
320 float value = (read_big_endian_short((const uint8_t*) table)) /
256.0f; | 320 float value = (read_big_endian_u16((const uint8_t*) table)) / 25
6.0f; |
321 SkColorSpacePrintf("gamma %g\n", value); | 321 SkColorSpacePrintf("gamma %g\n", value); |
322 | 322 |
323 return set_gamma_value(outData, value); | 323 return set_gamma_value(outData, value); |
324 } | 324 } |
325 | 325 |
326 // Check for frequently occurring sRGB curves. | 326 // Check for frequently occurring sRGB curves. |
327 // We do this by sampling a few values and see if they match our exp
ectation. | 327 // We do this by sampling a few values and see if they match our exp
ectation. |
328 // A more robust solution would be to compare each value in this cur
ve against | 328 // A more robust solution would be to compare each value in this cur
ve against |
329 // an sRGB curve to see if we remain below an error threshold. At t
his time, | 329 // an sRGB curve to see if we remain below an error threshold. At t
his time, |
330 // we haven't seen any images in the wild that make this kind of | 330 // we haven't seen any images in the wild that make this kind of |
331 // calculation necessary. We encounter identical gamma curves over
and | 331 // calculation necessary. We encounter identical gamma curves over
and |
332 // over again, but relatively few variations. | 332 // over again, but relatively few variations. |
333 if (1024 == count) { | 333 if (1024 == count) { |
334 // The magic values were chosen because they match both the very
common | 334 // The magic values were chosen because they match both the very
common |
335 // HP sRGB gamma table and the less common Canon sRGB gamma tabl
e (which use | 335 // HP sRGB gamma table and the less common Canon sRGB gamma tabl
e (which use |
336 // different rounding rules). | 336 // different rounding rules). |
337 if (0 == read_big_endian_short((const uint8_t*) &table[0]) && | 337 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) && |
338 3366 == read_big_endian_short((const uint8_t*) &table[25
7]) && | 338 3366 == read_big_endian_u16((const uint8_t*) &table[257]
) && |
339 14116 == read_big_endian_short((const uint8_t*) &table[5
13]) && | 339 14116 == read_big_endian_u16((const uint8_t*) &table[513
]) && |
340 34318 == read_big_endian_short((const uint8_t*) &table[7
68]) && | 340 34318 == read_big_endian_u16((const uint8_t*) &table[768
]) && |
341 65535 == read_big_endian_short((const uint8_t*) &table[1
023])) { | 341 65535 == read_big_endian_u16((const uint8_t*) &table[102
3])) { |
342 outData->fNamed = SkColorSpace::kSRGB_GammaNamed; | 342 outData->fNamed = SkColorSpace::kSRGB_GammaNamed; |
343 return SkGammas::Type::kNamed_Type; | 343 return SkGammas::Type::kNamed_Type; |
344 } | 344 } |
345 } | 345 } |
346 | 346 |
347 if (26 == count) { | 347 if (26 == count) { |
348 // The magic values were chosen because they match a very common
LCMS sRGB | 348 // The magic values were chosen because they match a very common
LCMS sRGB |
349 // gamma table. | 349 // gamma table. |
350 if (0 == read_big_endian_short((const uint8_t*) &table[0]) && | 350 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) && |
351 3062 == read_big_endian_short((const uint8_t*) &table[6]
) && | 351 3062 == read_big_endian_u16((const uint8_t*) &table[6])
&& |
352 12824 == read_big_endian_short((const uint8_t*) &table[1
2]) && | 352 12824 == read_big_endian_u16((const uint8_t*) &table[12]
) && |
353 31237 == read_big_endian_short((const uint8_t*) &table[1
8]) && | 353 31237 == read_big_endian_u16((const uint8_t*) &table[18]
) && |
354 65535 == read_big_endian_short((const uint8_t*) &table[2
5])) { | 354 65535 == read_big_endian_u16((const uint8_t*) &table[25]
)) { |
355 outData->fNamed = SkColorSpace::kSRGB_GammaNamed; | 355 outData->fNamed = SkColorSpace::kSRGB_GammaNamed; |
356 return SkGammas::Type::kNamed_Type; | 356 return SkGammas::Type::kNamed_Type; |
357 } | 357 } |
358 } | 358 } |
359 | 359 |
360 if (4096 == count) { | 360 if (4096 == count) { |
361 // The magic values were chosen because they match Nikon, Epson,
and | 361 // The magic values were chosen because they match Nikon, Epson,
and |
362 // LCMS sRGB gamma tables (all of which use different rounding r
ules). | 362 // LCMS sRGB gamma tables (all of which use different rounding r
ules). |
363 if (0 == read_big_endian_short((const uint8_t*) &table[0]) && | 363 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) && |
364 950 == read_big_endian_short((const uint8_t*) &table[515
]) && | 364 950 == read_big_endian_u16((const uint8_t*) &table[515])
&& |
365 3342 == read_big_endian_short((const uint8_t*) &table[10
25]) && | 365 3342 == read_big_endian_u16((const uint8_t*) &table[1025
]) && |
366 14079 == read_big_endian_short((const uint8_t*) &table[2
051]) && | 366 14079 == read_big_endian_u16((const uint8_t*) &table[205
1]) && |
367 65535 == read_big_endian_short((const uint8_t*) &table[4
095])) { | 367 65535 == read_big_endian_u16((const uint8_t*) &table[409
5])) { |
368 outData->fNamed = SkColorSpace::kSRGB_GammaNamed; | 368 outData->fNamed = SkColorSpace::kSRGB_GammaNamed; |
369 return SkGammas::Type::kNamed_Type; | 369 return SkGammas::Type::kNamed_Type; |
370 } | 370 } |
371 } | 371 } |
372 | 372 |
373 // Otherwise, we will represent gamma with a table. | 373 // Otherwise, we will represent gamma with a table. |
374 outData->fTable.fSize = count; | 374 outData->fTable.fSize = count; |
375 return SkGammas::Type::kTable_Type; | 375 return SkGammas::Type::kTable_Type; |
376 } | 376 } |
377 case kTAG_ParaCurveType: { | 377 case kTAG_ParaCurveType: { |
378 enum ParaCurveType { | 378 enum ParaCurveType { |
379 kExponential_ParaCurveType = 0, | 379 kExponential_ParaCurveType = 0, |
380 kGAB_ParaCurveType = 1, | 380 kGAB_ParaCurveType = 1, |
381 kGABC_ParaCurveType = 2, | 381 kGABC_ParaCurveType = 2, |
382 kGABDE_ParaCurveType = 3, | 382 kGABDE_ParaCurveType = 3, |
383 kGABCDEF_ParaCurveType = 4, | 383 kGABCDEF_ParaCurveType = 4, |
384 }; | 384 }; |
385 | 385 |
386 // Determine the format of the parametric curve tag. | 386 // Determine the format of the parametric curve tag. |
387 uint16_t format = read_big_endian_short(src + 8); | 387 uint16_t format = read_big_endian_u16(src + 8); |
388 if (format > kGABCDEF_ParaCurveType) { | 388 if (format > kGABCDEF_ParaCurveType) { |
389 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type); | 389 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type); |
390 return SkGammas::Type::kNone_Type; | 390 return SkGammas::Type::kNone_Type; |
391 } | 391 } |
392 | 392 |
393 if (kExponential_ParaCurveType == format) { | 393 if (kExponential_ParaCurveType == format) { |
394 tagBytes = 12 + 4; | 394 tagBytes = 12 + 4; |
395 if (len < tagBytes) { | 395 if (len < tagBytes) { |
396 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len)
; | 396 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len)
; |
397 return SkGammas::Type::kNone_Type; | 397 return SkGammas::Type::kNone_Type; |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
590 case SkGammas::Type::kNamed_Type: | 590 case SkGammas::Type::kNamed_Type: |
591 case SkGammas::Type::kValue_Type: | 591 case SkGammas::Type::kValue_Type: |
592 // Nothing to do here. | 592 // Nothing to do here. |
593 return 0; | 593 return 0; |
594 case SkGammas::Type::kTable_Type: { | 594 case SkGammas::Type::kTable_Type: { |
595 data->fTable.fOffset = offset; | 595 data->fTable.fOffset = offset; |
596 | 596 |
597 float* outTable = (float*) storage; | 597 float* outTable = (float*) storage; |
598 const uint16_t* inTable = (const uint16_t*) (src + 12); | 598 const uint16_t* inTable = (const uint16_t*) (src + 12); |
599 for (int i = 0; i < data->fTable.fSize; i++) { | 599 for (int i = 0; i < data->fTable.fSize; i++) { |
600 outTable[i] = (read_big_endian_short((const uint8_t*) &inTable[i
])) / 65535.0f; | 600 outTable[i] = (read_big_endian_u16((const uint8_t*) &inTable[i])
) / 65535.0f; |
601 } | 601 } |
602 | 602 |
603 return sizeof(float) * data->fTable.fSize; | 603 return sizeof(float) * data->fTable.fSize; |
604 } | 604 } |
605 case SkGammas::Type::kParam_Type: | 605 case SkGammas::Type::kParam_Type: |
606 data->fTable.fOffset = offset; | 606 data->fTable.fOffset = offset; |
607 memcpy(storage, ¶ms, sizeof(SkGammas::Params)); | 607 memcpy(storage, ¶ms, sizeof(SkGammas::Params)); |
608 return sizeof(SkGammas::Params); | 608 return sizeof(SkGammas::Params); |
609 default: | 609 default: |
610 SkASSERT(false); | 610 SkASSERT(false); |
611 return 0; | 611 return 0; |
612 } | 612 } |
613 } | 613 } |
614 | 614 |
615 static constexpr uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' '); | 615 static constexpr uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' '); |
616 | 616 |
617 static bool load_color_lut(SkColorLookUpTable* colorLUT, uint32_t inputChannels, | 617 static bool load_color_lut(sk_sp<SkColorLookUpTable>* colorLUT, uint32_t inputCh
annels, |
618 uint32_t outputChannels, const uint8_t* src, size_t l
en) { | 618 const uint8_t* src, size_t len) { |
619 // 16 bytes reserved for grid points, 2 for precision, 2 for padding. | 619 // 16 bytes reserved for grid points, 2 for precision, 2 for padding. |
620 // The color LUT data follows after this header. | 620 // The color LUT data follows after this header. |
621 static constexpr uint32_t kColorLUTHeaderSize = 20; | 621 static constexpr uint32_t kColorLUTHeaderSize = 20; |
622 if (len < kColorLUTHeaderSize) { | 622 if (len < kColorLUTHeaderSize) { |
623 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); | 623 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); |
624 return false; | 624 return false; |
625 } | 625 } |
626 size_t dataLen = len - kColorLUTHeaderSize; | 626 size_t dataLen = len - kColorLUTHeaderSize; |
627 | 627 |
628 SkASSERT(3 == inputChannels && 3 == outputChannels); | 628 SkASSERT(3 == inputChannels); |
629 colorLUT->fInputChannels = inputChannels; | 629 uint8_t gridPoints[3]; |
630 colorLUT->fOutputChannels = outputChannels; | |
631 uint32_t numEntries = 1; | 630 uint32_t numEntries = 1; |
632 for (uint32_t i = 0; i < inputChannels; i++) { | 631 for (uint32_t i = 0; i < inputChannels; i++) { |
633 colorLUT->fGridPoints[i] = src[i]; | 632 gridPoints[i] = src[i]; |
634 if (0 == src[i]) { | 633 if (0 == src[i]) { |
635 SkColorSpacePrintf("Each input channel must have at least one grid p
oint."); | 634 SkColorSpacePrintf("Each input channel must have at least one grid p
oint."); |
636 return false; | 635 return false; |
637 } | 636 } |
638 | 637 |
639 if (!safe_mul(numEntries, src[i], &numEntries)) { | 638 if (!safe_mul(numEntries, src[i], &numEntries)) { |
640 SkColorSpacePrintf("Too many entries in Color LUT."); | 639 SkColorSpacePrintf("Too many entries in Color LUT."); |
641 return false; | 640 return false; |
642 } | 641 } |
643 } | 642 } |
644 | 643 |
645 if (!safe_mul(numEntries, outputChannels, &numEntries)) { | 644 if (!safe_mul(numEntries, SkColorLookUpTable::kOutputChannels, &numEntries))
{ |
646 SkColorSpacePrintf("Too many entries in Color LUT."); | 645 SkColorSpacePrintf("Too many entries in Color LUT."); |
647 return false; | 646 return false; |
648 } | 647 } |
649 | 648 |
650 // Space is provided for a maximum of the 16 input channels. Now we determi
ne the precision | 649 // Space is provided for a maximum of the 16 input channels. Now we determi
ne the precision |
651 // of the table values. | 650 // of the table values. |
652 uint8_t precision = src[16]; | 651 uint8_t precision = src[16]; |
653 switch (precision) { | 652 switch (precision) { |
654 case 1: // 8-bit data | 653 case 1: // 8-bit data |
655 case 2: // 16-bit data | 654 case 2: // 16-bit data |
656 break; | 655 break; |
657 default: | 656 default: |
658 SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit.\n")
; | 657 SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit.\n")
; |
659 return false; | 658 return false; |
660 } | 659 } |
661 | 660 |
662 uint32_t clutBytes; | 661 uint32_t clutBytes; |
663 if (!safe_mul(numEntries, precision, &clutBytes)) { | 662 if (!safe_mul(numEntries, precision, &clutBytes)) { |
664 SkColorSpacePrintf("Too many entries in Color LUT."); | 663 SkColorSpacePrintf("Too many entries in Color LUT."); |
665 return false; | 664 return false; |
666 } | 665 } |
667 | 666 |
668 if (dataLen < clutBytes) { | 667 if (dataLen < clutBytes) { |
669 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); | 668 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); |
670 return false; | 669 return false; |
671 } | 670 } |
672 | 671 |
673 // Movable struct colorLUT has ownership of fTable. | 672 // Movable struct colorLUT has ownership of fTable. |
674 colorLUT->fTable = std::unique_ptr<float[]>(new float[numEntries]); | 673 void* memory = sk_malloc_throw(sizeof(SkColorLookUpTable) + sizeof(float) *
numEntries); |
| 674 *colorLUT = sk_sp<SkColorLookUpTable>(new (memory) SkColorLookUpTable(inputC
hannels, |
| 675 gridPo
ints)); |
| 676 |
| 677 float* table = SkTAddOffset<float>(memory, sizeof(SkColorLookUpTable)); |
675 const uint8_t* ptr = src + kColorLUTHeaderSize; | 678 const uint8_t* ptr = src + kColorLUTHeaderSize; |
676 for (uint32_t i = 0; i < numEntries; i++, ptr += precision) { | 679 for (uint32_t i = 0; i < numEntries; i++, ptr += precision) { |
677 if (1 == precision) { | 680 if (1 == precision) { |
678 colorLUT->fTable[i] = ((float) ptr[i]) / 255.0f; | 681 table[i] = ((float) ptr[i]) / 255.0f; |
679 } else { | 682 } else { |
680 colorLUT->fTable[i] = ((float) read_big_endian_short(ptr)) / 65535.0
f; | 683 table[i] = ((float) read_big_endian_u16(ptr)) / 65535.0f; |
681 } | 684 } |
682 } | 685 } |
683 | 686 |
684 return true; | 687 return true; |
685 } | 688 } |
686 | 689 |
687 static bool load_matrix(SkMatrix44* toXYZ, const uint8_t* src, size_t len) { | 690 static bool load_matrix(SkMatrix44* toXYZ, const uint8_t* src, size_t len) { |
688 if (len < 48) { | 691 if (len < 48) { |
689 SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len); | 692 SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len); |
690 return false; | 693 return false; |
691 } | 694 } |
692 | 695 |
693 // For this matrix to behave like our "to XYZ D50" matrices, it needs to be
scaled. | 696 // For this matrix to behave like our "to XYZ D50" matrices, it needs to be
scaled. |
694 constexpr float scale = 65535.0 / 32768.0; | 697 constexpr float scale = 65535.0 / 32768.0; |
695 float array[16]; | 698 float array[16]; |
696 array[ 0] = scale * SkFixedToFloat(read_big_endian_int(src)); | 699 array[ 0] = scale * SkFixedToFloat(read_big_endian_i32(src)); |
697 array[ 1] = scale * SkFixedToFloat(read_big_endian_int(src + 4)); | 700 array[ 1] = scale * SkFixedToFloat(read_big_endian_i32(src + 4)); |
698 array[ 2] = scale * SkFixedToFloat(read_big_endian_int(src + 8)); | 701 array[ 2] = scale * SkFixedToFloat(read_big_endian_i32(src + 8)); |
699 array[ 3] = scale * SkFixedToFloat(read_big_endian_int(src + 36)); // transl
ate R | 702 array[ 3] = scale * SkFixedToFloat(read_big_endian_i32(src + 36)); // transl
ate R |
700 array[ 4] = scale * SkFixedToFloat(read_big_endian_int(src + 12)); | 703 array[ 4] = scale * SkFixedToFloat(read_big_endian_i32(src + 12)); |
701 array[ 5] = scale * SkFixedToFloat(read_big_endian_int(src + 16)); | 704 array[ 5] = scale * SkFixedToFloat(read_big_endian_i32(src + 16)); |
702 array[ 6] = scale * SkFixedToFloat(read_big_endian_int(src + 20)); | 705 array[ 6] = scale * SkFixedToFloat(read_big_endian_i32(src + 20)); |
703 array[ 7] = scale * SkFixedToFloat(read_big_endian_int(src + 40)); // transl
ate G | 706 array[ 7] = scale * SkFixedToFloat(read_big_endian_i32(src + 40)); // transl
ate G |
704 array[ 8] = scale * SkFixedToFloat(read_big_endian_int(src + 24)); | 707 array[ 8] = scale * SkFixedToFloat(read_big_endian_i32(src + 24)); |
705 array[ 9] = scale * SkFixedToFloat(read_big_endian_int(src + 28)); | 708 array[ 9] = scale * SkFixedToFloat(read_big_endian_i32(src + 28)); |
706 array[10] = scale * SkFixedToFloat(read_big_endian_int(src + 32)); | 709 array[10] = scale * SkFixedToFloat(read_big_endian_i32(src + 32)); |
707 array[11] = scale * SkFixedToFloat(read_big_endian_int(src + 44)); // transl
ate B | 710 array[11] = scale * SkFixedToFloat(read_big_endian_i32(src + 44)); // transl
ate B |
708 array[12] = 0.0f; | 711 array[12] = 0.0f; |
709 array[13] = 0.0f; | 712 array[13] = 0.0f; |
710 array[14] = 0.0f; | 713 array[14] = 0.0f; |
711 array[15] = 1.0f; | 714 array[15] = 1.0f; |
712 toXYZ->setColMajorf(array); | 715 toXYZ->setColMajorf(array); |
713 return true; | 716 return true; |
714 } | 717 } |
715 | 718 |
716 static bool load_a2b0(SkColorLookUpTable* colorLUT, SkColorSpace::GammaNamed* ga
mmaNamed, | 719 static bool load_a2b0(sk_sp<SkColorLookUpTable>* colorLUT, SkColorSpace::GammaNa
med* gammaNamed, |
717 sk_sp<SkGammas>* gammas, SkMatrix44* toXYZ, const uint8_t*
src, size_t len) { | 720 sk_sp<SkGammas>* gammas, SkMatrix44* toXYZ, const uint8_t*
src, size_t len) { |
718 if (len < 32) { | 721 if (len < 32) { |
719 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len); | 722 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len); |
720 return false; | 723 return false; |
721 } | 724 } |
722 | 725 |
723 uint32_t type = read_big_endian_uint(src); | 726 uint32_t type = read_big_endian_u32(src); |
724 if (kTAG_AtoBType != type) { | 727 if (kTAG_AtoBType != type) { |
725 // FIXME (msarett): Need to support lut8Type and lut16Type. | 728 // FIXME (msarett): Need to support lut8Type and lut16Type. |
726 SkColorSpacePrintf("Unsupported A to B tag type.\n"); | 729 SkColorSpacePrintf("Unsupported A to B tag type.\n"); |
727 return false; | 730 return false; |
728 } | 731 } |
729 | 732 |
730 // Read the number of channels. The four bytes that we skipped are reserved
and | 733 // Read the number of channels. The four bytes that we skipped are reserved
and |
731 // must be zero. | 734 // must be zero. |
732 uint8_t inputChannels = src[8]; | 735 uint8_t inputChannels = src[8]; |
733 uint8_t outputChannels = src[9]; | 736 uint8_t outputChannels = src[9]; |
734 if (3 != inputChannels || 3 != outputChannels) { | 737 if (3 != inputChannels || SkColorLookUpTable::kOutputChannels != outputChann
els) { |
735 // We only handle (supposedly) RGB inputs and RGB outputs. The numbers
of input | 738 // We only handle (supposedly) RGB inputs and RGB outputs. The numbers
of input |
736 // channels and output channels both must be 3. | 739 // channels and output channels both must be 3. |
| 740 // TODO (msarett): |
| 741 // Support different numbers of input channels. Ex: CMYK (4). |
737 SkColorSpacePrintf("Input and output channels must equal 3 in A to B tag
.\n"); | 742 SkColorSpacePrintf("Input and output channels must equal 3 in A to B tag
.\n"); |
738 return false; | 743 return false; |
739 } | 744 } |
740 | 745 |
741 // Read the offsets of each element in the A to B tag. With the exception o
f A curves and | 746 // Read the offsets of each element in the A to B tag. With the exception o
f A curves and |
742 // B curves (which we do not yet support), we will handle these elements in
the order in | 747 // B curves (which we do not yet support), we will handle these elements in
the order in |
743 // which they should be applied (rather than the order in which they occur i
n the tag). | 748 // which they should be applied (rather than the order in which they occur i
n the tag). |
744 // If the offset is non-zero it indicates that the element is present. | 749 // If the offset is non-zero it indicates that the element is present. |
745 uint32_t offsetToACurves = read_big_endian_int(src + 28); | 750 uint32_t offsetToACurves = read_big_endian_i32(src + 28); |
746 uint32_t offsetToBCurves = read_big_endian_int(src + 12); | 751 uint32_t offsetToBCurves = read_big_endian_i32(src + 12); |
747 if ((0 != offsetToACurves) || (0 != offsetToBCurves)) { | 752 if ((0 != offsetToACurves) || (0 != offsetToBCurves)) { |
748 // FIXME (msarett): Handle A and B curves. | 753 // FIXME (msarett): Handle A and B curves. |
749 // Note that the A curve is technically required in order to have a colo
r LUT. | 754 // Note that the A curve is technically required in order to have a colo
r LUT. |
750 // However, all the A curves I have seen so far have are just placeholde
rs that | 755 // However, all the A curves I have seen so far have are just placeholde
rs that |
751 // don't actually transform the data. | 756 // don't actually transform the data. |
752 SkColorSpacePrintf("Ignoring A and/or B curve. Output may be wrong.\n")
; | 757 SkColorSpacePrintf("Ignoring A and/or B curve. Output may be wrong.\n")
; |
753 } | 758 } |
754 | 759 |
755 uint32_t offsetToColorLUT = read_big_endian_int(src + 24); | 760 uint32_t offsetToColorLUT = read_big_endian_i32(src + 24); |
756 if (0 != offsetToColorLUT && offsetToColorLUT < len) { | 761 if (0 != offsetToColorLUT && offsetToColorLUT < len) { |
757 if (!load_color_lut(colorLUT, inputChannels, outputChannels, src + offse
tToColorLUT, | 762 if (!load_color_lut(colorLUT, inputChannels, src + offsetToColorLUT, |
758 len - offsetToColorLUT)) { | 763 len - offsetToColorLUT)) { |
759 SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n"); | 764 SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n"); |
760 } | 765 } |
761 } | 766 } |
762 | 767 |
763 uint32_t offsetToMCurves = read_big_endian_int(src + 20); | 768 uint32_t offsetToMCurves = read_big_endian_i32(src + 20); |
764 if (0 != offsetToMCurves && offsetToMCurves < len) { | 769 if (0 != offsetToMCurves && offsetToMCurves < len) { |
765 const uint8_t* rTagPtr = src + offsetToMCurves; | 770 const uint8_t* rTagPtr = src + offsetToMCurves; |
766 size_t tagLen = len - offsetToMCurves; | 771 size_t tagLen = len - offsetToMCurves; |
767 | 772 |
768 SkGammas::Data rData; | 773 SkGammas::Data rData; |
769 SkGammas::Params rParams; | 774 SkGammas::Params rParams; |
770 | 775 |
771 // On an invalid first gamma, tagBytes remains set as zero. This causes
the two | 776 // On an invalid first gamma, tagBytes remains set as zero. This causes
the two |
772 // subsequent to be treated as identical (which is what we want). | 777 // subsequent to be treated as identical (which is what we want). |
773 size_t tagBytes = 0; | 778 size_t tagBytes = 0; |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
829 | 834 |
830 (*gammas)->fBlueType = bType; | 835 (*gammas)->fBlueType = bType; |
831 load_gammas(memory, offset, bType, &bData, bParams, bTagPtr); | 836 load_gammas(memory, offset, bType, &bData, bParams, bTagPtr); |
832 | 837 |
833 (*gammas)->fRedData = rData; | 838 (*gammas)->fRedData = rData; |
834 (*gammas)->fGreenData = gData; | 839 (*gammas)->fGreenData = gData; |
835 (*gammas)->fBlueData = bData; | 840 (*gammas)->fBlueData = bData; |
836 } | 841 } |
837 } | 842 } |
838 | 843 |
839 uint32_t offsetToMatrix = read_big_endian_int(src + 16); | 844 uint32_t offsetToMatrix = read_big_endian_i32(src + 16); |
840 if (0 != offsetToMatrix && offsetToMatrix < len) { | 845 if (0 != offsetToMatrix && offsetToMatrix < len) { |
841 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) { | 846 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) { |
842 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); | 847 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); |
843 toXYZ->setIdentity(); | 848 toXYZ->setIdentity(); |
844 } | 849 } |
845 } | 850 } |
846 | 851 |
847 return true; | 852 return true; |
848 } | 853 } |
849 | 854 |
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1028 } else { | 1033 } else { |
1029 return SkColorSpace_Base::NewRGB(gammaNamed, mat); | 1034 return SkColorSpace_Base::NewRGB(gammaNamed, mat); |
1030 } | 1035 } |
1031 } | 1036 } |
1032 | 1037 |
1033 // Recognize color profile specified by A2B0 tag. | 1038 // Recognize color profile specified by A2B0 tag. |
1034 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); | 1039 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); |
1035 if (a2b0) { | 1040 if (a2b0) { |
1036 GammaNamed gammaNamed = kNonStandard_GammaNamed; | 1041 GammaNamed gammaNamed = kNonStandard_GammaNamed; |
1037 sk_sp<SkGammas> gammas = nullptr; | 1042 sk_sp<SkGammas> gammas = nullptr; |
1038 sk_sp<SkColorLookUpTable> colorLUT = sk_make_sp<SkColorLookUpTab
le>(); | 1043 sk_sp<SkColorLookUpTable> colorLUT = nullptr; |
1039 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); | 1044 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); |
1040 if (!load_a2b0(colorLUT.get(), &gammaNamed, &gammas, &toXYZ, a2b
0->addr(base), | 1045 if (!load_a2b0(&colorLUT, &gammaNamed, &gammas, &toXYZ, a2b0->ad
dr(base), |
1041 a2b0->fLength)) { | 1046 a2b0->fLength)) { |
1042 return_null("Failed to parse A2B0 tag"); | 1047 return_null("Failed to parse A2B0 tag"); |
1043 } | 1048 } |
1044 | 1049 |
1045 colorLUT = colorLUT->fTable ? colorLUT : nullptr; | |
1046 if (colorLUT || kNonStandard_GammaNamed == gammaNamed) { | 1050 if (colorLUT || kNonStandard_GammaNamed == gammaNamed) { |
1047 return sk_sp<SkColorSpace>(new SkColorSpace_Base(std::move(c
olorLUT), | 1051 return sk_sp<SkColorSpace>(new SkColorSpace_Base(std::move(c
olorLUT), |
1048 gammaNamed,
std::move(gammas), | 1052 gammaNamed,
std::move(gammas), |
1049 toXYZ, std:
:move(data))); | 1053 toXYZ, std:
:move(data))); |
1050 } else { | 1054 } else { |
1051 return SkColorSpace_Base::NewRGB(gammaNamed, toXYZ); | 1055 return SkColorSpace_Base::NewRGB(gammaNamed, toXYZ); |
1052 } | 1056 } |
1053 } | 1057 } |
1054 } | 1058 } |
1055 default: | 1059 default: |
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1279 ptr32[4] = SkEndian_SwapBE32(0x000116cc); | 1283 ptr32[4] = SkEndian_SwapBE32(0x000116cc); |
1280 ptr += kTAG_XYZ_Bytes; | 1284 ptr += kTAG_XYZ_Bytes; |
1281 | 1285 |
1282 // Write copyright tag | 1286 // Write copyright tag |
1283 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); | 1287 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); |
1284 | 1288 |
1285 // TODO (msarett): Should we try to hold onto the data so we can return imme
diately if | 1289 // TODO (msarett): Should we try to hold onto the data so we can return imme
diately if |
1286 // the client calls again? | 1290 // the client calls again? |
1287 return SkData::MakeFromMalloc(profile.release(), kICCProfileSize); | 1291 return SkData::MakeFromMalloc(profile.release(), kICCProfileSize); |
1288 } | 1292 } |
OLD | NEW |