1 /*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkAutoMalloc.h"
9 #include "SkColorSpace.h"
10 #include "SkColorSpacePriv.h"
11 #include "SkColorSpace_A2B.h"
12 #include "SkColorSpace_XYZ.h"
13 #include "SkEndian.h"
14 #include "SkFixed.h"
15 #include "SkICCPriv.h"
16 #include "SkTemplates.h"
17
18 #define return_if_false(pred, msg) \
19 do { \
20 if (!(pred)) { \
21 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \
22 return false; \
23 } \
24 } while (0)
25
26 #define return_null(msg) \
27 do { \
28 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \
29 return nullptr; \
30 } while (0)
31
read_big_endian_u16(const uint8_t * ptr)32 static uint16_t read_big_endian_u16(const uint8_t* ptr) {
33 return ptr[0] << 8 | ptr[1];
34 }
35
read_big_endian_u32(const uint8_t * ptr)36 static uint32_t read_big_endian_u32(const uint8_t* ptr) {
37 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
38 }
39
read_big_endian_i32(const uint8_t * ptr)40 static int32_t read_big_endian_i32(const uint8_t* ptr) {
41 return (int32_t) read_big_endian_u32(ptr);
42 }
43
44 static constexpr float kWhitePointD50[] = { 0.96420f, 1.00000f, 0.82491f, };
45
46 struct ICCProfileHeader {
47 uint32_t fSize;
48
49 // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.).
50 // We're always going to use this one.
51 uint32_t fCMMType_ignored;
52
53 uint32_t fVersion;
54 uint32_t fProfileClass;
55 uint32_t fInputColorSpace;
56 uint32_t fPCS;
57 uint32_t fDateTime_ignored[3];
58 uint32_t fSignature;
59
60 // Indicates the platform that this profile was created for (ex: Apple, Microsoft). This
61 // doesn't really matter to us.
62 uint32_t fPlatformTarget_ignored;
63
64 // Flags can indicate:
65 // (1) Whether this profile was embedded in a file. This flag is consistently wrong.
66 // Ex: The profile came from a file but indicates that it did not.
67 // (2) Whether we are allowed to use the profile independently of the color data. If set,
68 // this may allow us to use the embedded profile for testing separate from the original
69 // image.
70 uint32_t fFlags_ignored;
71
72 // We support many output devices. It doesn't make sense to think about the attributes of
73 // the device in the context of the image profile.
74 uint32_t fDeviceManufacturer_ignored;
75 uint32_t fDeviceModel_ignored;
76 uint32_t fDeviceAttributes_ignored[2];
77
78 uint32_t fRenderingIntent;
79 int32_t fIlluminantXYZ[3];
80
81 // We don't care who created the profile.
82 uint32_t fCreator_ignored;
83
84 // This is an MD5 checksum. Could be useful for checking if profiles are equal.
85 uint32_t fProfileId_ignored[4];
86
87 // Reserved for future use.
88 uint32_t fReserved_ignored[7];
89
90 uint32_t fTagCount;
91
initICCProfileHeader92 void init(const uint8_t* src, size_t len) {
93 SkASSERT(kICCHeaderSize == sizeof(*this));
94
95 uint32_t* dst = (uint32_t*) this;
96 for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) {
97 dst[i] = read_big_endian_u32(src);
98 }
99 }
100
validICCProfileHeader101 bool valid() const {
102 return_if_false(fSize >= kICCHeaderSize, "Size is too small");
103
104 uint8_t majorVersion = fVersion >> 24;
105 return_if_false(majorVersion <= 4, "Unsupported version");
106
107 // These are the four basic classes of profiles that we might expect to see embedded
108 // in images. Additional classes exist, but they generally are used as a convenient
109 // way for CMMs to store calculated transforms.
110 return_if_false(fProfileClass == kDisplay_Profile ||
111 fProfileClass == kInput_Profile ||
112 fProfileClass == kOutput_Profile ||
113 fProfileClass == kColorSpace_Profile,
114 "Unsupported profile");
115
116 switch (fInputColorSpace) {
117 case kRGB_ColorSpace:
118 SkColorSpacePrintf("RGB Input Color Space");
119 break;
120 case kCMYK_ColorSpace:
121 SkColorSpacePrintf("CMYK Input Color Space\n");
122 break;
123 case kGray_ColorSpace:
124 SkColorSpacePrintf("Gray Input Color Space\n");
125 break;
126 default:
127 SkColorSpacePrintf("Unsupported Input Color Space: %c%c%c%c\n",
128 (fInputColorSpace>>24)&0xFF, (fInputColorSpace>>16)&0xFF,
129 (fInputColorSpace>> 8)&0xFF, (fInputColorSpace>> 0)&0xFF);
130 return false;
131 }
132
133 switch (fPCS) {
134 case kXYZ_PCSSpace:
135 SkColorSpacePrintf("XYZ PCS\n");
136 break;
137 case kLAB_PCSSpace:
138 SkColorSpacePrintf("Lab PCS\n");
139 break;
140 default:
141 // ICC currently (V4.3) only specifices XYZ and Lab PCS spaces
142 SkColorSpacePrintf("Unsupported PCS space: %c%c%c%c\n",
143 (fPCS>>24)&0xFF, (fPCS>>16)&0xFF,
144 (fPCS>> 8)&0xFF, (fPCS>> 0)&0xFF);
145 return false;
146 }
147
148 return_if_false(fSignature == kACSP_Signature, "Bad signature");
149
150 // TODO (msarett):
151 // Should we treat different rendering intents differently?
152 // Valid rendering intents include kPerceptual (0), kRelative (1),
153 // kSaturation (2), and kAbsolute (3).
154 if (fRenderingIntent > 3) {
155 // Warn rather than fail here. Occasionally, we see perfectly
156 // normal profiles with wacky rendering intents.
157 SkColorSpacePrintf("Warning, bad rendering intent.\n");
158 }
159
160 return_if_false(
161 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0]), kWhitePointD50[0]) &&
162 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1]), kWhitePointD50[1]) &&
163 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2]), kWhitePointD50[2]),
164 "Illuminant must be D50");
165
166 return_if_false(fTagCount <= 100, "Too many tags");
167
168 return true;
169 }
170 };
171
172 template <class T>
safe_add(T arg1,T arg2,size_t * result)173 static bool safe_add(T arg1, T arg2, size_t* result) {
174 SkASSERT(arg1 >= 0);
175 SkASSERT(arg2 >= 0);
176 if (arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) {
177 T sum = arg1 + arg2;
178 if (sum <= std::numeric_limits<size_t>::max()) {
179 *result = static_cast<size_t>(sum);
180 return true;
181 }
182 }
183 return false;
184 }
185
safe_mul(uint32_t arg1,uint32_t arg2,uint32_t * result)186 static bool safe_mul(uint32_t arg1, uint32_t arg2, uint32_t* result) {
187 uint64_t product64 = (uint64_t) arg1 * (uint64_t) arg2;
188 uint32_t product32 = (uint32_t) product64;
189 if (product32 != product64) {
190 return false;
191 }
192
193 *result = product32;
194 return true;
195 }
196
197 struct ICCTag {
198 uint32_t fSignature;
199 uint32_t fOffset;
200 uint32_t fLength;
201
initICCTag202 const uint8_t* init(const uint8_t* src) {
203 fSignature = read_big_endian_u32(src);
204 fOffset = read_big_endian_u32(src + 4);
205 fLength = read_big_endian_u32(src + 8);
206 return src + 12;
207 }
208
validICCTag209 bool valid(size_t len) {
210 size_t tagEnd;
211 return_if_false(safe_add(fOffset, fLength, &tagEnd),
212 "Tag too large, overflows integer addition");
213 return_if_false(tagEnd <= len, "Tag too large for ICC profile");
214 return true;
215 }
216
addrICCTag217 const uint8_t* addr(const uint8_t* src) const {
218 return src + fOffset;
219 }
220
FindICCTag221 static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature) {
222 for (int i = 0; i < count; ++i) {
223 if (tags[i].fSignature == signature) {
224 return &tags[i];
225 }
226 }
227 return nullptr;
228 }
229 };
230
load_xyz(float dst[3],const uint8_t * src,size_t len)231 static bool load_xyz(float dst[3], const uint8_t* src, size_t len) {
232 if (len < 20) {
233 SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len);
234 return false;
235 }
236
237 dst[0] = SkFixedToFloat(read_big_endian_i32(src + 8));
238 dst[1] = SkFixedToFloat(read_big_endian_i32(src + 12));
239 dst[2] = SkFixedToFloat(read_big_endian_i32(src + 16));
240 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]);
241 return true;
242 }
243
set_gamma_value(SkGammas::Data * data,float value)244 static SkGammas::Type set_gamma_value(SkGammas::Data* data, float value) {
245 if (color_space_almost_equal(2.2f, value)) {
246 data->fNamed = k2Dot2Curve_SkGammaNamed;
247 return SkGammas::Type::kNamed_Type;
248 }
249
250 if (color_space_almost_equal(1.0f, value)) {
251 data->fNamed = kLinear_SkGammaNamed;
252 return SkGammas::Type::kNamed_Type;
253 }
254
255 if (color_space_almost_equal(0.0f, value)) {
256 return SkGammas::Type::kNone_Type;
257 }
258
259 data->fValue = value;
260 return SkGammas::Type::kValue_Type;
261 }
262
read_big_endian_16_dot_16(const uint8_t buf[4])263 static float read_big_endian_16_dot_16(const uint8_t buf[4]) {
264 // It just so happens that SkFixed is also 16.16!
265 return SkFixedToFloat(read_big_endian_i32(buf));
266 }
267
268 /**
269 * @param outData Set to the appropriate value on success. If we have table or
270 * parametric gamma, it is the responsibility of the caller to set
271 * fOffset.
272 * @param outParams If this is a parametric gamma, this is set to the appropriate
273 * parameters on success.
274 * @param outTagBytes Will be set to the length of the tag on success.
275 * @src Pointer to tag data.
276 * @len Length of tag data in bytes.
277 *
278 * @return kNone_Type on failure, otherwise the type of the gamma tag.
279 */
parse_gamma(SkGammas::Data * outData,SkColorSpaceTransferFn * outParams,size_t * outTagBytes,const uint8_t * src,size_t len)280 static SkGammas::Type parse_gamma(SkGammas::Data* outData, SkColorSpaceTransferFn* outParams,
281 size_t* outTagBytes, const uint8_t* src, size_t len) {
282 if (len < 12) {
283 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
284 return SkGammas::Type::kNone_Type;
285 }
286
287 // In the case of consecutive gamma tags, we need to count the number of bytes in the
288 // tag, so that we can move on to the next tag.
289 size_t tagBytes;
290
291 uint32_t type = read_big_endian_u32(src);
292 // Bytes 4-7 are reserved and should be set to zero.
293 switch (type) {
294 case kTAG_CurveType: {
295 uint32_t count = read_big_endian_u32(src + 8);
296
297 // tagBytes = 12 + 2 * count
298 // We need to do safe addition here to avoid integer overflow.
299 if (!safe_add(count, count, &tagBytes) ||
300 !safe_add((size_t) 12, tagBytes, &tagBytes))
301 {
302 SkColorSpacePrintf("Invalid gamma count");
303 return SkGammas::Type::kNone_Type;
304 }
305
306 if (len < tagBytes) {
307 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
308 return SkGammas::Type::kNone_Type;
309 }
310 *outTagBytes = tagBytes;
311
312 if (0 == count) {
313 // Some tags require a gamma curve, but the author doesn't actually want
314 // to transform the data. In this case, it is common to see a curve with
315 // a count of 0.
316 outData->fNamed = kLinear_SkGammaNamed;
317 return SkGammas::Type::kNamed_Type;
318 }
319
320 const uint16_t* table = (const uint16_t*) (src + 12);
321 if (1 == count) {
322 // The table entry is the gamma (with a bias of 256).
323 float value = (read_big_endian_u16((const uint8_t*) table)) / 256.0f;
324 SkColorSpacePrintf("gamma %g\n", value);
325
326 return set_gamma_value(outData, value);
327 }
328
329 // This optimization is especially important for A2B profiles, where we do
330 // not resize tables or interpolate lookups.
331 if (2 == count) {
332 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
333 65535 == read_big_endian_u16((const uint8_t*) &table[1])) {
334 outData->fNamed = kLinear_SkGammaNamed;
335 return SkGammas::Type::kNamed_Type;
336 }
337 }
338
339 // Check for frequently occurring sRGB curves.
340 // We do this by sampling a few values and see if they match our expectation.
341 // A more robust solution would be to compare each value in this curve against
342 // an sRGB curve to see if we remain below an error threshold. At this time,
343 // we haven't seen any images in the wild that make this kind of
344 // calculation necessary. We encounter identical gamma curves over and
345 // over again, but relatively few variations.
346 if (1024 == count) {
347 // The magic values were chosen because they match both the very common
348 // HP sRGB gamma table and the less common Canon sRGB gamma table (which use
349 // different rounding rules).
350 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
351 3366 == read_big_endian_u16((const uint8_t*) &table[257]) &&
352 14116 == read_big_endian_u16((const uint8_t*) &table[513]) &&
353 34318 == read_big_endian_u16((const uint8_t*) &table[768]) &&
354 65535 == read_big_endian_u16((const uint8_t*) &table[1023])) {
355 outData->fNamed = kSRGB_SkGammaNamed;
356 return SkGammas::Type::kNamed_Type;
357 }
358 }
359
360 if (26 == count) {
361 // The magic values match a clever "minimum size" approach to representing sRGB.
362 // code.facebook.com/posts/411525055626587/under-the-hood-improving-facebook-photos
363 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
364 3062 == read_big_endian_u16((const uint8_t*) &table[6]) &&
365 12824 == read_big_endian_u16((const uint8_t*) &table[12]) &&
366 31237 == read_big_endian_u16((const uint8_t*) &table[18]) &&
367 65535 == read_big_endian_u16((const uint8_t*) &table[25])) {
368 outData->fNamed = kSRGB_SkGammaNamed;
369 return SkGammas::Type::kNamed_Type;
370 }
371 }
372
373 if (4096 == count) {
374 // The magic values were chosen because they match Nikon, Epson, and
375 // lcms2 sRGB gamma tables (all of which use different rounding rules).
376 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
377 950 == read_big_endian_u16((const uint8_t*) &table[515]) &&
378 3342 == read_big_endian_u16((const uint8_t*) &table[1025]) &&
379 14079 == read_big_endian_u16((const uint8_t*) &table[2051]) &&
380 65535 == read_big_endian_u16((const uint8_t*) &table[4095])) {
381 outData->fNamed = kSRGB_SkGammaNamed;
382 return SkGammas::Type::kNamed_Type;
383 }
384 }
385
386 // Otherwise, we will represent gamma with a table.
387 outData->fTable.fSize = count;
388 return SkGammas::Type::kTable_Type;
389 }
390 case kTAG_ParaCurveType: {
391 // Determine the format of the parametric curve tag.
392 uint16_t format = read_big_endian_u16(src + 8);
393 if (format > kGABCDEF_ParaCurveType) {
394 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
395 return SkGammas::Type::kNone_Type;
396 }
397
398 if (kExponential_ParaCurveType == format) {
399 tagBytes = 12 + 4;
400 if (len < tagBytes) {
401 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
402 return SkGammas::Type::kNone_Type;
403 }
404
405 // Y = X^g
406 float g = read_big_endian_16_dot_16(src + 12);
407
408 *outTagBytes = tagBytes;
409 return set_gamma_value(outData, g);
410 }
411
412 // Here's where the real parametric gammas start. There are many
413 // permutations of the same equations.
414 //
415 // Y = (aX + b)^g + e for X >= d
416 // Y = cX + f otherwise
417 //
418 // We will fill in with zeros as necessary to always match the above form.
419 if (len < 24) {
420 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
421 return SkGammas::Type::kNone_Type;
422 }
423 float g = read_big_endian_16_dot_16(src + 12);
424 float a = read_big_endian_16_dot_16(src + 16);
425 float b = read_big_endian_16_dot_16(src + 20);
426 float c = 0.0f, d = 0.0f, e = 0.0f, f = 0.0f;
427 switch(format) {
428 case kGAB_ParaCurveType:
429 tagBytes = 12 + 12;
430
431 // Y = (aX + b)^g for X >= -b/a
432 // Y = 0 otherwise
433 d = -b / a;
434 break;
435 case kGABC_ParaCurveType:
436 tagBytes = 12 + 16;
437 if (len < tagBytes) {
438 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
439 return SkGammas::Type::kNone_Type;
440 }
441
442 // Y = (aX + b)^g + e for X >= -b/a
443 // Y = e otherwise
444 e = read_big_endian_16_dot_16(src + 24);
445 d = -b / a;
446 f = e;
447 break;
448 case kGABDE_ParaCurveType:
449 tagBytes = 12 + 20;
450 if (len < tagBytes) {
451 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
452 return SkGammas::Type::kNone_Type;
453 }
454
455 // Y = (aX + b)^g for X >= d
456 // Y = cX otherwise
457 c = read_big_endian_16_dot_16(src + 24);
458 d = read_big_endian_16_dot_16(src + 28);
459 break;
460 case kGABCDEF_ParaCurveType:
461 tagBytes = 12 + 28;
462 if (len < tagBytes) {
463 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
464 return SkGammas::Type::kNone_Type;
465 }
466
467 // Y = (aX + b)^g + e for X >= d
468 // Y = cX + f otherwise
469 c = read_big_endian_16_dot_16(src + 24);
470 d = read_big_endian_16_dot_16(src + 28);
471 e = read_big_endian_16_dot_16(src + 32);
472 f = read_big_endian_16_dot_16(src + 36);
473 break;
474 default:
475 SkASSERT(false);
476 return SkGammas::Type::kNone_Type;
477 }
478
479 outParams->fG = g;
480 outParams->fA = a;
481 outParams->fB = b;
482 outParams->fC = c;
483 outParams->fD = d;
484 outParams->fE = e;
485 outParams->fF = f;
486
487 if (!is_valid_transfer_fn(*outParams)) {
488 return SkGammas::Type::kNone_Type;
489 }
490
491 if (is_almost_srgb(*outParams)) {
492 outData->fNamed = kSRGB_SkGammaNamed;
493 return SkGammas::Type::kNamed_Type;
494 }
495
496 if (is_almost_2dot2(*outParams)) {
497 outData->fNamed = k2Dot2Curve_SkGammaNamed;
498 return SkGammas::Type::kNamed_Type;
499 }
500
501 *outTagBytes = tagBytes;
502 return SkGammas::Type::kParam_Type;
503 }
504 default:
505 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
506 return SkGammas::Type::kNone_Type;
507 }
508 }
509
510 /**
511 * Returns the additional size in bytes needed to store the gamma tag.
512 */
gamma_alloc_size(SkGammas::Type type,const SkGammas::Data & data)513 static size_t gamma_alloc_size(SkGammas::Type type, const SkGammas::Data& data) {
514 switch (type) {
515 case SkGammas::Type::kNamed_Type:
516 case SkGammas::Type::kValue_Type:
517 return 0;
518 case SkGammas::Type::kTable_Type:
519 return sizeof(float) * data.fTable.fSize;
520 case SkGammas::Type::kParam_Type:
521 return sizeof(SkColorSpaceTransferFn);
522 default:
523 SkASSERT(false);
524 return 0;
525 }
526 }
527
528 /**
529 * Sets invalid gamma to the default value.
530 */
handle_invalid_gamma(SkGammas::Type * type,SkGammas::Data * data)531 static void handle_invalid_gamma(SkGammas::Type* type, SkGammas::Data* data) {
532 if (SkGammas::Type::kNone_Type == *type) {
533 *type = SkGammas::Type::kNamed_Type;
534
535 // Guess sRGB in the case of a malformed transfer function.
536 data->fNamed = kSRGB_SkGammaNamed;
537 }
538 }
539
540 /**
541 * Finish loading the gammas, now that we have allocated memory for the SkGammas struct.
542 *
543 * There's nothing to do for the simple cases, but for table gammas we need to actually
544 * read the table into heap memory. And for parametric gammas, we need to copy over the
545 * parameter values.
546 *
547 * @param memory Pointer to start of the SkGammas memory block
548 * @param offset Bytes of memory (after the SkGammas struct) that are already in use.
549 * @param data In-out variable. Will fill in the offset to the table or parameters
550 * if necessary.
551 * @param params Parameters for gamma curve. Only initialized/used when we have a
552 * parametric gamma.
553 * @param src Pointer to start of the gamma tag.
554 *
555 * @return Additional bytes of memory that are being used by this gamma curve.
556 */
load_gammas(void * memory,size_t offset,SkGammas::Type type,SkGammas::Data * data,const SkColorSpaceTransferFn & params,const uint8_t * src)557 static size_t load_gammas(void* memory, size_t offset, SkGammas::Type type,
558 SkGammas::Data* data, const SkColorSpaceTransferFn& params,
559 const uint8_t* src) {
560 void* storage = SkTAddOffset<void>(memory, offset + sizeof(SkGammas));
561
562 switch (type) {
563 case SkGammas::Type::kNamed_Type:
564 case SkGammas::Type::kValue_Type:
565 // Nothing to do here.
566 return 0;
567 case SkGammas::Type::kTable_Type: {
568 data->fTable.fOffset = offset;
569
570 float* outTable = (float*) storage;
571 const uint16_t* inTable = (const uint16_t*) (src + 12);
572 for (int i = 0; i < data->fTable.fSize; i++) {
573 outTable[i] = (read_big_endian_u16((const uint8_t*) &inTable[i])) / 65535.0f;
574 }
575
576 return sizeof(float) * data->fTable.fSize;
577 }
578 case SkGammas::Type::kParam_Type:
579 data->fTable.fOffset = offset;
580 memcpy(storage, ¶ms, sizeof(SkColorSpaceTransferFn));
581 return sizeof(SkColorSpaceTransferFn);
582 default:
583 SkASSERT(false);
584 return 0;
585 }
586 }
587
588 static constexpr uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' ');
589 static constexpr uint32_t kTAG_lut8Type = SkSetFourByteTag('m', 'f', 't', '1');
590 static constexpr uint32_t kTAG_lut16Type = SkSetFourByteTag('m', 'f', 't', '2');
591
load_color_lut(sk_sp<SkColorLookUpTable> * colorLUT,uint32_t inputChannels,size_t precision,const uint8_t gridPoints[3],const uint8_t * src,size_t len)592 static bool load_color_lut(sk_sp<SkColorLookUpTable>* colorLUT, uint32_t inputChannels,
593 size_t precision, const uint8_t gridPoints[3], const uint8_t* src,
594 size_t len) {
595 switch (precision) {
596 case 1: // 8-bit data
597 case 2: // 16-bit data
598 break;
599 default:
600 SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit. Found: %d-bit\n",
601 8*precision);
602 return false;
603 }
604
605 uint32_t numEntries = SkColorLookUpTable::kOutputChannels;
606 for (uint32_t i = 0; i < inputChannels; i++) {
607 if (1 >= gridPoints[i]) {
608 SkColorSpacePrintf("Each input channel must have at least two grid points.");
609 return false;
610 }
611
612 if (!safe_mul(numEntries, gridPoints[i], &numEntries)) {
613 SkColorSpacePrintf("Too many entries in Color LUT.");
614 return false;
615 }
616 }
617
618 uint32_t clutBytes;
619 if (!safe_mul(numEntries, precision, &clutBytes)) {
620 SkColorSpacePrintf("Too many entries in Color LUT.\n");
621 return false;
622 }
623
624 if (len < clutBytes) {
625 SkColorSpacePrintf("Color LUT tag is too small (%d / %d bytes).\n", len, clutBytes);
626 return false;
627 }
628
629 // Movable struct colorLUT has ownership of fTable.
630 void* memory = sk_malloc_throw(sizeof(SkColorLookUpTable) + sizeof(float) * numEntries);
631 *colorLUT = sk_sp<SkColorLookUpTable>(new (memory) SkColorLookUpTable(inputChannels,
632 gridPoints));
633
634 float* table = SkTAddOffset<float>(memory, sizeof(SkColorLookUpTable));
635 const uint8_t* ptr = src;
636 for (uint32_t i = 0; i < numEntries; i++, ptr += precision) {
637 if (1 == precision) {
638 table[i] = ((float) *ptr) / 255.0f;
639 } else {
640 table[i] = ((float) read_big_endian_u16(ptr)) / 65535.0f;
641 }
642 }
643
644 return true;
645 }
646
647 /**
648 * Reads a matrix out of an A2B tag of an ICC profile.
649 * If |translate| is true, it will load a 3x4 matrix out that corresponds to a XYZ
650 * transform as well as a translation, and if |translate| is false it only loads a
651 * 3x3 matrix with no translation
652 *
653 * @param matrix The matrix to store the result in
654 * @param src Data to load the matrix out of.
655 * @param len The length of |src|.
656 * Must have 48 bytes if |translate| is set and 36 bytes otherwise.
657 * @param translate Whether to read the translation column or not
658 * @param pcs The profile connection space of the profile this matrix is for
659 *
660 * @return false on failure, true on success
661 */
load_matrix(SkMatrix44 * matrix,const uint8_t * src,size_t len,bool translate,SkColorSpace_A2B::PCS pcs)662 static bool load_matrix(SkMatrix44* matrix, const uint8_t* src, size_t len, bool translate,
663 SkColorSpace_A2B::PCS pcs) {
664 const size_t minLen = translate ? 48 : 36;
665 if (len < minLen) {
666 SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len);
667 return false;
668 }
669
670 float encodingFactor;
671 switch (pcs) {
672 case SkColorSpace_A2B::PCS::kLAB:
673 encodingFactor = 1.f;
674 break;
675 case SkColorSpace_A2B::PCS::kXYZ:
676 encodingFactor = 65535 / 32768.f;
677 break;
678 default:
679 encodingFactor = 1.f;
680 SkASSERT(false);
681 break;
682 }
683 float array[16];
684 array[ 0] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src));
685 array[ 1] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 4));
686 array[ 2] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 8));
687
688 array[ 4] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 12));
689 array[ 5] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 16));
690 array[ 6] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 20));
691
692 array[ 8] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 24));
693 array[ 9] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 28));
694 array[10] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 32));
695
696 if (translate) {
697 array[ 3] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 36)); // translate R
698 array[ 7] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 40)); // translate G
699 array[11] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 44)); // translate B
700 } else {
701 array[ 3] = 0.0f;
702 array[ 7] = 0.0f;
703 array[11] = 0.0f;
704 }
705
706 array[12] = 0.0f;
707 array[13] = 0.0f;
708 array[14] = 0.0f;
709 array[15] = 1.0f;
710 matrix->setRowMajorf(array);
711 SkColorSpacePrintf("A2B0 matrix loaded:\n");
712 for (int r = 0; r < 4; ++r) {
713 SkColorSpacePrintf("|");
714 for (int c = 0; c < 4; ++c) {
715 SkColorSpacePrintf(" %f ", matrix->get(r, c));
716 }
717 SkColorSpacePrintf("|\n");
718 }
719 return true;
720 }
721
is_named(const sk_sp<SkGammas> & gammas)722 static inline SkGammaNamed is_named(const sk_sp<SkGammas>& gammas) {
723 for (uint8_t i = 0; i < gammas->channels(); ++i) {
724 if (!gammas->isNamed(i) || gammas->data(i).fNamed != gammas->data(0).fNamed) {
725 return kNonStandard_SkGammaNamed;
726 }
727 }
728 return gammas->data(0).fNamed;
729 }
730
731 /**
732 * Parse and load an entire stored curve. Handles invalid gammas as well.
733 *
734 * There's nothing to do for the simple cases, but for table gammas we need to actually
735 * read the table into heap memory. And for parametric gammas, we need to copy over the
736 * parameter values.
737 *
738 * @param gammaNamed Out-variable. The named gamma curve.
739 * @param gammas Out-variable. The stored gamma curve information. Can be null if
740 * gammaNamed is a named curve
741 * @param inputChannels The number of gamma input channels
742 * @param rTagPtr Pointer to start of the gamma tag.
743 * @param taglen The size in bytes of the tag
744 *
745 * @return false on failure, true on success
746 */
parse_and_load_gamma(SkGammaNamed * gammaNamed,sk_sp<SkGammas> * gammas,uint8_t inputChannels,const uint8_t * tagSrc,size_t tagLen)747 static bool parse_and_load_gamma(SkGammaNamed* gammaNamed, sk_sp<SkGammas>* gammas,
748 uint8_t inputChannels, const uint8_t* tagSrc, size_t tagLen) {
749 SkGammas::Data data[kMaxColorChannels];
750 SkColorSpaceTransferFn params[kMaxColorChannels];
751 SkGammas::Type type[kMaxColorChannels];
752 const uint8_t* tagPtr[kMaxColorChannels];
753
754 tagPtr[0] = tagSrc;
755
756 *gammaNamed = kNonStandard_SkGammaNamed;
757
758 // On an invalid first gamma, tagBytes remains set as zero. This causes the two
759 // subsequent to be treated as identical (which is what we want).
760 size_t tagBytes = 0;
761 type[0] = parse_gamma(&data[0], ¶ms[0], &tagBytes, tagPtr[0], tagLen);
762 handle_invalid_gamma(&type[0], &data[0]);
763 size_t alignedTagBytes = SkAlign4(tagBytes);
764
765 bool allChannelsSame = false;
766 if (inputChannels * alignedTagBytes <= tagLen) {
767 allChannelsSame = true;
768 for (uint8_t i = 1; i < inputChannels; ++i) {
769 if (0 != memcmp(tagSrc, tagSrc + i * alignedTagBytes, tagBytes)) {
770 allChannelsSame = false;
771 break;
772 }
773 }
774 }
775 if (allChannelsSame) {
776 if (SkGammas::Type::kNamed_Type == type[0]) {
777 *gammaNamed = data[0].fNamed;
778 } else {
779 size_t allocSize = sizeof(SkGammas);
780 return_if_false(safe_add(allocSize, gamma_alloc_size(type[0], data[0]), &allocSize),
781 "SkGammas struct is too large to allocate");
782 void* memory = sk_malloc_throw(allocSize);
783 *gammas = sk_sp<SkGammas>(new (memory) SkGammas(inputChannels));
784 load_gammas(memory, 0, type[0], &data[0], params[0], tagPtr[0]);
785
786 for (uint8_t channel = 0; channel < inputChannels; ++channel) {
787 (*gammas)->fType[channel] = type[0];
788 (*gammas)->fData[channel] = data[0];
789 }
790 }
791 } else {
792 for (uint8_t channel = 1; channel < inputChannels; ++channel) {
793 tagPtr[channel] = tagPtr[channel - 1] + alignedTagBytes;
794 tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0;
795 tagBytes = 0;
796 type[channel] = parse_gamma(&data[channel], ¶ms[channel], &tagBytes,
797 tagPtr[channel], tagLen);
798 handle_invalid_gamma(&type[channel], &data[channel]);
799 alignedTagBytes = SkAlign4(tagBytes);
800 }
801
802 size_t allocSize = sizeof(SkGammas);
803 for (uint8_t channel = 0; channel < inputChannels; ++channel) {
804 return_if_false(safe_add(allocSize, gamma_alloc_size(type[channel], data[channel]),
805 &allocSize),
806 "SkGammas struct is too large to allocate");
807 }
808 void* memory = sk_malloc_throw(allocSize);
809 *gammas = sk_sp<SkGammas>(new (memory) SkGammas(inputChannels));
810
811 uint32_t offset = 0;
812 for (uint8_t channel = 0; channel < inputChannels; ++channel) {
813 (*gammas)->fType[channel] = type[channel];
814 offset += load_gammas(memory,offset, type[channel], &data[channel], params[channel],
815 tagPtr[channel]);
816 (*gammas)->fData[channel] = data[channel];
817
818 }
819 }
820
821 if (kNonStandard_SkGammaNamed == *gammaNamed) {
822 *gammaNamed = is_named(*gammas);
823 if (kNonStandard_SkGammaNamed != *gammaNamed) {
824 // No need to keep the gammas struct, the enum is enough.
825 *gammas = nullptr;
826 }
827 }
828 return true;
829 }
830
is_lut_gamma_linear(const uint8_t * src,size_t count,size_t precision)831 static bool is_lut_gamma_linear(const uint8_t* src, size_t count, size_t precision) {
832 // check for linear gamma (this is very common in lut gammas, as they aren't optional)
833 const float normalizeX = 1.f / (count - 1);
834 for (uint32_t x = 0; x < count; ++x) {
835 const float y = precision == 1 ? (src[x] / 255.f)
836 : (read_big_endian_u16(src + 2*x) / 65535.f);
837 if (!color_space_almost_equal(x * normalizeX, y)) {
838 return false;
839 }
840 }
841 return true;
842 }
843
load_lut_gammas(sk_sp<SkGammas> * gammas,SkGammaNamed * gammaNamed,size_t numTables,size_t entriesPerTable,size_t precision,const uint8_t * src,size_t len)844 static bool load_lut_gammas(sk_sp<SkGammas>* gammas, SkGammaNamed* gammaNamed, size_t numTables,
845 size_t entriesPerTable, size_t precision, const uint8_t* src,
846 size_t len) {
847 if (precision != 1 && precision != 2) {
848 SkColorSpacePrintf("Invalid gamma table precision %d\n", precision);
849 return false;
850 }
851 uint32_t totalEntries;
852 return_if_false(safe_mul(entriesPerTable, numTables, &totalEntries),
853 "Too many entries in gamma table.");
854 uint32_t readBytes;
855 return_if_false(safe_mul(precision, totalEntries, &readBytes),
856 "SkGammas struct is too large to read");
857 if (len < readBytes) {
858 SkColorSpacePrintf("Gamma table is too small. Provided: %d. Required: %d\n",
859 len, readBytes);
860 return false;
861 }
862
863 uint32_t writeBytesPerChannel;
864 return_if_false(safe_mul(sizeof(float), entriesPerTable, &writeBytesPerChannel),
865 "SkGammas struct is too large to allocate");
866 const size_t readBytesPerChannel = precision * entriesPerTable;
867 size_t numTablesToUse = 1;
868 for (size_t tableIndex = 1; tableIndex < numTables; ++tableIndex) {
869 if (0 != memcmp(src, src + readBytesPerChannel * tableIndex, readBytesPerChannel)) {
870 numTablesToUse = numTables;
871 break;
872 }
873 }
874
875 if (1 == numTablesToUse) {
876 if (is_lut_gamma_linear(src, entriesPerTable, precision)) {
877 *gammaNamed = kLinear_SkGammaNamed;
878 return true;
879 }
880 }
881 *gammaNamed = kNonStandard_SkGammaNamed;
882
883 uint32_t writetableBytes;
884 return_if_false(safe_mul(numTablesToUse, writeBytesPerChannel, &writetableBytes),
885 "SkGammas struct is too large to allocate");
886 size_t allocSize = sizeof(SkGammas);
887 return_if_false(safe_add(allocSize, (size_t)writetableBytes, &allocSize),
888 "SkGammas struct is too large to allocate");
889
890 void* memory = sk_malloc_throw(allocSize);
891 *gammas = sk_sp<SkGammas>(new (memory) SkGammas(numTables));
892
893 for (size_t tableIndex = 0; tableIndex < numTablesToUse; ++tableIndex) {
894 const uint8_t* ptr = src + readBytesPerChannel * tableIndex;
895 const size_t offset = sizeof(SkGammas) + tableIndex * writeBytesPerChannel;
896 float* table = SkTAddOffset<float>(memory, offset);
897 if (1 == precision) {
898 for (uint32_t i = 0; i < entriesPerTable; ++i, ptr += 1) {
899 table[i] = ((float) *ptr) / 255.0f;
900 }
901 } else if (2 == precision) {
902 for (uint32_t i = 0; i < entriesPerTable; ++i, ptr += 2) {
903 table[i] = ((float) read_big_endian_u16(ptr)) / 65535.0f;
904 }
905 }
906 }
907
908 SkASSERT(1 == numTablesToUse|| numTables == numTablesToUse);
909
910 size_t tableOffset = 0;
911 for (size_t tableIndex = 0; tableIndex < numTables; ++tableIndex) {
912 (*gammas)->fType[tableIndex] = SkGammas::Type::kTable_Type;
913 (*gammas)->fData[tableIndex].fTable.fOffset = tableOffset;
914 (*gammas)->fData[tableIndex].fTable.fSize = entriesPerTable;
915 if (numTablesToUse > 1) {
916 tableOffset += writeBytesPerChannel;
917 }
918 }
919
920 return true;
921 }
922
load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element> * elements,const uint8_t * src,size_t len,SkColorSpace_A2B::PCS pcs)923 bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, const uint8_t* src,
924 size_t len, SkColorSpace_A2B::PCS pcs) {
925 SkASSERT(len >= 32);
926 // Read the number of channels. The four bytes (4-7) that we skipped are reserved and
927 // must be zero.
928 const uint8_t inputChannels = src[8];
929 const uint8_t outputChannels = src[9];
930 if (SkColorLookUpTable::kOutputChannels != outputChannels) {
931 // We only handle RGB outputs. The number of output channels must be 3.
932 SkColorSpacePrintf("Output channels (%d) must equal 3 in A to B tag.\n", outputChannels);
933 return false;
934 }
935 if (inputChannels == 0 || inputChannels > 4) {
936 // And we only support 4 input channels.
937 // ICC says up to 16 but our decode can only handle 4.
938 // It could easily be extended to support up to 8, but we only allow CMYK/RGB
939 // input color spaces which are 3 and 4 so let's restrict it to 4 instead of 8.
940 // We can always change this check when we support bigger input spaces.
941 SkColorSpacePrintf("Input channels (%d) must be between 1 and 4 in A to B tag.\n",
942 inputChannels);
943 return false;
944 }
945
946
947 // It is important that these are loaded in the order of application, as the
948 // order you construct an A2B color space's elements is the order it is applied
949
950 // If the offset is non-zero it indicates that the element is present.
951 const uint32_t offsetToACurves = read_big_endian_i32(src + 28);
952 if (0 != offsetToACurves && offsetToACurves < len) {
953 const size_t tagLen = len - offsetToACurves;
954 SkGammaNamed gammaNamed;
955 sk_sp<SkGammas> gammas;
956 if (!parse_and_load_gamma(&gammaNamed, &gammas, inputChannels, src + offsetToACurves,
957 tagLen)) {
958 return false;
959 }
960 if (gammas) {
961 elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
962 } else if (kLinear_SkGammaNamed != gammaNamed) {
963 elements->push_back(SkColorSpace_A2B::Element(gammaNamed, inputChannels));
964 }
965 }
966
967 const uint32_t offsetToColorLUT = read_big_endian_i32(src + 24);
968 if (0 != offsetToColorLUT && offsetToColorLUT < len) {
969 sk_sp<SkColorLookUpTable> colorLUT;
970 const uint8_t* clutSrc = src + offsetToColorLUT;
971 const size_t clutLen = len - offsetToColorLUT;
972 // 16 bytes reserved for grid points, 1 for precision, 3 for padding.
973 // The color LUT data follows after this header.
974 static constexpr uint32_t kColorLUTHeaderSize = 20;
975 if (clutLen < kColorLUTHeaderSize) {
976 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", clutLen);
977 return false;
978 }
979
980 SkASSERT(inputChannels <= kMaxColorChannels);
981 uint8_t gridPoints[kMaxColorChannels];
982 for (uint32_t i = 0; i < inputChannels; ++i) {
983 gridPoints[i] = clutSrc[i];
984 }
985 // Space is provided for a maximum of 16 input channels.
986 // Now we determine the precision of the table values.
987 const uint8_t precision = clutSrc[16];
988 if (!load_color_lut(&colorLUT, inputChannels, precision, gridPoints,
989 clutSrc + kColorLUTHeaderSize, clutLen - kColorLUTHeaderSize)) {
990 SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n");
991 return false;
992 }
993 elements->push_back(SkColorSpace_A2B::Element(std::move(colorLUT)));
994 }
995
996 const uint32_t offsetToMCurves = read_big_endian_i32(src + 20);
997 if (0 != offsetToMCurves && offsetToMCurves < len) {
998 const size_t tagLen = len - offsetToMCurves;
999 SkGammaNamed gammaNamed;
1000 sk_sp<SkGammas> gammas;
1001 if (!parse_and_load_gamma(&gammaNamed, &gammas, outputChannels, src + offsetToMCurves,
1002 tagLen)) {
1003 return false;
1004 }
1005 if (gammas) {
1006 elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
1007 } else if (kLinear_SkGammaNamed != gammaNamed) {
1008 elements->push_back(SkColorSpace_A2B::Element(gammaNamed, outputChannels));
1009 }
1010 }
1011
1012 const uint32_t offsetToMatrix = read_big_endian_i32(src + 16);
1013 if (0 != offsetToMatrix && offsetToMatrix < len) {
1014 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
1015 if (!load_matrix(&matrix, src + offsetToMatrix, len - offsetToMatrix, true, pcs)) {
1016 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n");
1017 } else if (!matrix.isIdentity()) {
1018 elements->push_back(SkColorSpace_A2B::Element(matrix));
1019 }
1020 }
1021
1022 const uint32_t offsetToBCurves = read_big_endian_i32(src + 12);
1023 if (0 != offsetToBCurves && offsetToBCurves < len) {
1024 const size_t tagLen = len - offsetToBCurves;
1025 SkGammaNamed gammaNamed;
1026 sk_sp<SkGammas> gammas;
1027 if (!parse_and_load_gamma(&gammaNamed, &gammas, outputChannels, src + offsetToBCurves,
1028 tagLen)) {
1029 return false;
1030 }
1031 if (gammas) {
1032 elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
1033 } else if (kLinear_SkGammaNamed != gammaNamed) {
1034 elements->push_back(SkColorSpace_A2B::Element(gammaNamed, outputChannels));
1035 }
1036 }
1037
1038 return true;
1039 }
1040
load_a2b0_lutn_type(std::vector<SkColorSpace_A2B::Element> * elements,const uint8_t * src,size_t len,SkColorSpace_A2B::PCS pcs)1041 bool load_a2b0_lutn_type(std::vector<SkColorSpace_A2B::Element>* elements, const uint8_t* src,
1042 size_t len, SkColorSpace_A2B::PCS pcs) {
1043 const uint32_t type = read_big_endian_u32(src);
1044 switch (type) {
1045 case kTAG_lut8Type:
1046 SkASSERT(len >= 48);
1047 break;
1048 case kTAG_lut16Type:
1049 SkASSERT(len >= 52);
1050 break;
1051 default:
1052 SkASSERT(false);
1053 return false;
1054 }
1055 // Read the number of channels.
1056 // The four bytes (4-7) that we skipped are reserved and must be zero.
1057 const uint8_t inputChannels = src[8];
1058 const uint8_t outputChannels = src[9];
1059 if (SkColorLookUpTable::kOutputChannels != outputChannels) {
1060 // We only handle RGB outputs. The number of output channels must be 3.
1061 SkColorSpacePrintf("Output channels (%d) must equal 3 in A to B tag.\n", outputChannels);
1062 return false;
1063 }
1064 if (inputChannels == 0 || inputChannels > 4) {
1065 // And we only support 4 input channels.
1066 // ICC says up to 16 but our decode can only handle 4.
1067 // It could easily be extended to support up to 8, but we only allow CMYK/RGB
1068 // input color spaces which are 3 and 4 so let's restrict it to 4 instead of 8.
1069 // We can always change this check when we support bigger input spaces.
1070 SkColorSpacePrintf("Input channels (%d) must be between 1 and 4 in A to B tag.\n",
1071 inputChannels);
1072 return false;
1073 }
1074
1075 const uint8_t clutGridPoints = src[10];
1076 // 11th byte reserved for padding (required to be zero)
1077
1078 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
1079 load_matrix(&matrix, &src[12], len - 12, false, pcs);
1080 if (!matrix.isIdentity()) {
1081 // ICC specs (10.8/10.9) say lut8/16Type profiles must have identity matrices
1082 // if the input color space is not PCSXYZ, and we do not support PCSXYZ input color spaces
1083 // so we should never encounter a non-identity matrix here.
1084 // However, 2 test images from the ICC website have RGB input spaces and non-identity
1085 // matrices so we're not going to fail here, despite being against the spec.
1086 SkColorSpacePrintf("Warning: non-Identity matrix found in non-XYZ input color space"
1087 "lut profile");
1088 elements->push_back(SkColorSpace_A2B::Element(matrix));
1089 }
1090
1091 size_t dataOffset = 48;
1092 // # of input table entries
1093 size_t inTableEntries = 256;
1094 // # of output table entries
1095 size_t outTableEntries = 256;
1096 size_t precision = 1;
1097 if (kTAG_lut16Type == type) {
1098 dataOffset = 52;
1099 inTableEntries = read_big_endian_u16(src + 48);
1100 outTableEntries = read_big_endian_u16(src + 50);
1101 precision = 2;
1102
1103 constexpr size_t kMaxLut16GammaEntries = 4096;
1104 if (inTableEntries < 2) {
1105 SkColorSpacePrintf("Too few (%d) input gamma table entries. Must have at least 2.\n",
1106 inTableEntries);
1107 return false;
1108 } else if (inTableEntries > kMaxLut16GammaEntries) {
1109 SkColorSpacePrintf("Too many (%d) input gamma table entries. Must have at most %d.\n",
1110 inTableEntries, kMaxLut16GammaEntries);
1111 return false;
1112 }
1113
1114 if (outTableEntries < 2) {
1115 SkColorSpacePrintf("Too few (%d) output gamma table entries. Must have at least 2.\n",
1116 outTableEntries);
1117 return false;
1118 } else if (outTableEntries > kMaxLut16GammaEntries) {
1119 SkColorSpacePrintf("Too many (%d) output gamma table entries. Must have at most %d.\n",
1120 outTableEntries, kMaxLut16GammaEntries);
1121 return false;
1122 }
1123 }
1124
1125 const size_t inputOffset = dataOffset;
1126 return_if_false(len >= inputOffset, "A2B0 lutnType tag too small for input gamma table");
1127 sk_sp<SkGammas> inputGammas;
1128 SkGammaNamed inputGammaNamed;
1129 if (!load_lut_gammas(&inputGammas, &inputGammaNamed, inputChannels, inTableEntries, precision,
1130 src + inputOffset, len - inputOffset)) {
1131 SkColorSpacePrintf("Failed to read input gammas from lutnType tag.\n");
1132 return false;
1133 }
1134 SkASSERT(inputGammas || inputGammaNamed != kNonStandard_SkGammaNamed);
1135 if (kLinear_SkGammaNamed != inputGammaNamed) {
1136 if (kNonStandard_SkGammaNamed != inputGammaNamed) {
1137 elements->push_back(SkColorSpace_A2B::Element(inputGammaNamed, inputChannels));
1138 } else {
1139 elements->push_back(SkColorSpace_A2B::Element(std::move(inputGammas)));
1140 }
1141 }
1142
1143 const size_t clutOffset = inputOffset + precision*inTableEntries*inputChannels;
1144 return_if_false(len >= clutOffset, "A2B0 lutnType tag too small for CLUT");
1145 sk_sp<SkColorLookUpTable> colorLUT;
1146 const uint8_t gridPoints[kMaxColorChannels] = {
1147 clutGridPoints, clutGridPoints, clutGridPoints, clutGridPoints
1148 };
1149 if (!load_color_lut(&colorLUT, inputChannels, precision, gridPoints, src + clutOffset,
1150 len - clutOffset)) {
1151 SkColorSpacePrintf("Failed to read color LUT from lutnType tag.\n");
1152 return false;
1153 }
1154 SkASSERT(colorLUT);
1155 elements->push_back(SkColorSpace_A2B::Element(std::move(colorLUT)));
1156
1157 size_t clutSize = precision * outputChannels;
1158 for (int i = 0; i < inputChannels; ++i) {
1159 clutSize *= clutGridPoints;
1160 }
1161 const size_t outputOffset = clutOffset + clutSize;
1162 return_if_false(len >= outputOffset, "A2B0 lutnType tag too small for output gamma table");
1163 sk_sp<SkGammas> outputGammas;
1164 SkGammaNamed outputGammaNamed;
1165 if (!load_lut_gammas(&outputGammas, &outputGammaNamed, outputChannels, outTableEntries,
1166 precision, src + outputOffset, len - outputOffset)) {
1167 SkColorSpacePrintf("Failed to read output gammas from lutnType tag.\n");
1168 return false;
1169 }
1170 SkASSERT(outputGammas || outputGammaNamed != kNonStandard_SkGammaNamed);
1171 if (kLinear_SkGammaNamed != outputGammaNamed) {
1172 if (kNonStandard_SkGammaNamed != outputGammaNamed) {
1173 elements->push_back(SkColorSpace_A2B::Element(outputGammaNamed, outputChannels));
1174 } else {
1175 elements->push_back(SkColorSpace_A2B::Element(std::move(outputGammas)));
1176 }
1177 }
1178
1179 return true;
1180 }
1181
icf_channels(SkColorSpace::Type iccType)1182 static inline int icf_channels(SkColorSpace::Type iccType) {
1183 switch (iccType) {
1184 case SkColorSpace::kRGB_Type:
1185 return 3;
1186 case SkColorSpace::kCMYK_Type:
1187 return 4;
1188 default:
1189 SkASSERT(false);
1190 return 0;
1191 }
1192 }
1193
load_a2b0(std::vector<SkColorSpace_A2B::Element> * elements,const uint8_t * src,size_t len,SkColorSpace_A2B::PCS pcs,SkColorSpace::Type iccType)1194 static bool load_a2b0(std::vector<SkColorSpace_A2B::Element>* elements, const uint8_t* src,
1195 size_t len, SkColorSpace_A2B::PCS pcs,
1196 SkColorSpace::Type iccType) {
1197 if (len < 4) {
1198 return false;
1199 }
1200 const uint32_t type = read_big_endian_u32(src);
1201
1202 switch (type) {
1203 case kTAG_AtoBType:
1204 if (len < 32) {
1205 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len);
1206 return false;
1207 }
1208 SkColorSpacePrintf("A2B0 tag is of type lutAtoBType\n");
1209 if (!load_a2b0_a_to_b_type(elements, src, len, pcs)) {
1210 return false;
1211 }
1212 break;
1213 case kTAG_lut8Type:
1214 if (len < 48) {
1215 SkColorSpacePrintf("lut8 tag is too small (%d bytes).", len);
1216 return false;
1217 }
1218 SkColorSpacePrintf("A2B0 tag of type lut8Type\n");
1219 if (!load_a2b0_lutn_type(elements, src, len, pcs)) {
1220 return false;
1221 }
1222 break;
1223 case kTAG_lut16Type:
1224 if (len < 52) {
1225 SkColorSpacePrintf("lut16 tag is too small (%d bytes).", len);
1226 return false;
1227 }
1228 SkColorSpacePrintf("A2B0 tag of type lut16Type\n");
1229 if (!load_a2b0_lutn_type(elements, src, len, pcs)) {
1230 return false;
1231 }
1232 break;
1233 default:
1234 SkColorSpacePrintf("Unsupported A to B tag type: %c%c%c%c\n", (type>>24)&0xFF,
1235 (type>>16)&0xFF, (type>>8)&0xFF, type&0xFF);
1236 return false;
1237 }
1238 SkASSERT(SkColorSpace_A2B::PCS::kLAB == pcs || SkColorSpace_A2B::PCS::kXYZ == pcs);
1239 static constexpr int kPCSChannels = 3; // must be PCSLAB or PCSXYZ
1240 if (elements->empty()) {
1241 return kPCSChannels == icf_channels(iccType);
1242 }
1243 // now let's verify that the input/output channels of each A2B element actually match up
1244 if (icf_channels(iccType) != elements->front().inputChannels()) {
1245 SkColorSpacePrintf("Input channel count does not match first A2B element's input count");
1246 return false;
1247 }
1248 for (size_t i = 1; i < elements->size(); ++i) {
1249 if ((*elements)[i - 1].outputChannels() != (*elements)[i].inputChannels()) {
1250 SkColorSpacePrintf("A2B elements don't agree in input/output channel counts");
1251 return false;
1252 }
1253 }
1254 if (kPCSChannels != elements->back().outputChannels()) {
1255 SkColorSpacePrintf("PCS channel count doesn't match last A2B element's output count");
1256 return false;
1257 }
1258 return true;
1259 }
1260
tag_equals(const ICCTag * a,const ICCTag * b,const uint8_t * base)1261 static bool tag_equals(const ICCTag* a, const ICCTag* b, const uint8_t* base) {
1262 if (!a || !b) {
1263 return a == b;
1264 }
1265
1266 if (a->fLength != b->fLength) {
1267 return false;
1268 }
1269
1270 if (a->fOffset == b->fOffset) {
1271 return true;
1272 }
1273
1274 return !memcmp(a->addr(base), b->addr(base), a->fLength);
1275 }
1276
is_close_to_d50(const SkMatrix44 & matrix)1277 static inline bool is_close_to_d50(const SkMatrix44& matrix) {
1278 // rX + gX + bX
1279 float X = matrix.getFloat(0, 0) + matrix.getFloat(0, 1) + matrix.getFloat(0, 2);
1280
1281 // rY + gY + bY
1282 float Y = matrix.getFloat(1, 0) + matrix.getFloat(1, 1) + matrix.getFloat(1, 2);
1283
1284 // rZ + gZ + bZ
1285 float Z = matrix.getFloat(2, 0) + matrix.getFloat(2, 1) + matrix.getFloat(2, 2);
1286
1287 static const float kD50_WhitePoint[3] = { 0.96420f, 1.00000f, 0.82491f };
1288
1289 // This is a bit more lenient than QCMS and Adobe. Is there a reason to be stricter here?
1290 return (SkTAbs(X - kD50_WhitePoint[0]) <= 0.04f) &&
1291 (SkTAbs(Y - kD50_WhitePoint[1]) <= 0.04f) &&
1292 (SkTAbs(Z - kD50_WhitePoint[2]) <= 0.04f);
1293 }
1294
make_xyz(const ICCProfileHeader & header,ICCTag * tags,int tagCount,const uint8_t * base,sk_sp<SkData> profileData)1295 static sk_sp<SkColorSpace> make_xyz(const ICCProfileHeader& header, ICCTag* tags, int tagCount,
1296 const uint8_t* base, sk_sp<SkData> profileData) {
1297 if (kLAB_PCSSpace == header.fPCS) {
1298 return nullptr;
1299 }
1300
1301 // Recognize the rXYZ, gXYZ, and bXYZ tags.
1302 const ICCTag* r = ICCTag::Find(tags, tagCount, kTAG_rXYZ);
1303 const ICCTag* g = ICCTag::Find(tags, tagCount, kTAG_gXYZ);
1304 const ICCTag* b = ICCTag::Find(tags, tagCount, kTAG_bXYZ);
1305 if (!r || !g || !b) {
1306 return nullptr;
1307 }
1308
1309 float toXYZ[9];
1310 if (!load_xyz(&toXYZ[0], r->addr(base), r->fLength) ||
1311 !load_xyz(&toXYZ[3], g->addr(base), g->fLength) ||
1312 !load_xyz(&toXYZ[6], b->addr(base), b->fLength))
1313 {
1314 return_null("Need valid rgb tags for XYZ space");
1315 }
1316 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
1317 mat.set3x3(toXYZ[0], toXYZ[1], toXYZ[2],
1318 toXYZ[3], toXYZ[4], toXYZ[5],
1319 toXYZ[6], toXYZ[7], toXYZ[8]);
1320 if (!is_close_to_d50(mat)) {
1321 return_null("XYZ matrix is not D50");
1322 }
1323
1324 // If some, but not all, of the gamma tags are missing, assume that all
1325 // gammas are meant to be the same.
1326 r = ICCTag::Find(tags, tagCount, kTAG_rTRC);
1327 g = ICCTag::Find(tags, tagCount, kTAG_gTRC);
1328 b = ICCTag::Find(tags, tagCount, kTAG_bTRC);
1329 if ((!r || !g || !b)) {
1330 if (!r) {
1331 r = g ? g : b;
1332 }
1333 if (!g) {
1334 g = r ? r : b;
1335 }
1336 if (!b) {
1337 b = r ? r : g;
1338 }
1339 }
1340
1341 SkGammaNamed gammaNamed = kNonStandard_SkGammaNamed;
1342 sk_sp<SkGammas> gammas = nullptr;
1343 size_t tagBytes;
1344 if (r && g && b) {
1345 if (tag_equals(r, g, base) && tag_equals(g, b, base)) {
1346 SkGammas::Data data;
1347 SkColorSpaceTransferFn params;
1348 SkGammas::Type type =
1349 parse_gamma(&data, ¶ms, &tagBytes, r->addr(base), r->fLength);
1350 handle_invalid_gamma(&type, &data);
1351
1352 if (SkGammas::Type::kNamed_Type == type) {
1353 gammaNamed = data.fNamed;
1354 } else {
1355 size_t allocSize = sizeof(SkGammas);
1356 if (!safe_add(allocSize, gamma_alloc_size(type, data), &allocSize)) {
1357 return_null("SkGammas struct is too large to allocate");
1358 }
1359 void* memory = sk_malloc_throw(allocSize);
1360 gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
1361 load_gammas(memory, 0, type, &data, params, r->addr(base));
1362
1363 for (int i = 0; i < 3; ++i) {
1364 gammas->fType[i] = type;
1365 gammas->fData[i] = data;
1366 }
1367 }
1368 } else {
1369 SkGammas::Data rData;
1370 SkColorSpaceTransferFn rParams;
1371 SkGammas::Type rType =
1372 parse_gamma(&rData, &rParams, &tagBytes, r->addr(base), r->fLength);
1373 handle_invalid_gamma(&rType, &rData);
1374
1375 SkGammas::Data gData;
1376 SkColorSpaceTransferFn gParams;
1377 SkGammas::Type gType =
1378 parse_gamma(&gData, &gParams, &tagBytes, g->addr(base), g->fLength);
1379 handle_invalid_gamma(&gType, &gData);
1380
1381 SkGammas::Data bData;
1382 SkColorSpaceTransferFn bParams;
1383 SkGammas::Type bType =
1384 parse_gamma(&bData, &bParams, &tagBytes, b->addr(base), b->fLength);
1385 handle_invalid_gamma(&bType, &bData);
1386
1387 size_t allocSize = sizeof(SkGammas);
1388 if (!safe_add(allocSize, gamma_alloc_size(rType, rData), &allocSize) ||
1389 !safe_add(allocSize, gamma_alloc_size(gType, gData), &allocSize) ||
1390 !safe_add(allocSize, gamma_alloc_size(bType, bData), &allocSize)) {
1391 return_null("SkGammas struct is too large to allocate");
1392 }
1393 void* memory = sk_malloc_throw(allocSize);
1394 gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
1395
1396 uint32_t offset = 0;
1397 gammas->fType[0] = rType;
1398 offset += load_gammas(memory, offset, rType, &rData, rParams,
1399 r->addr(base));
1400
1401 gammas->fType[1] = gType;
1402 offset += load_gammas(memory, offset, gType, &gData, gParams,
1403 g->addr(base));
1404
1405 gammas->fType[2] = bType;
1406 load_gammas(memory, offset, bType, &bData, bParams, b->addr(base));
1407
1408 gammas->fData[0] = rData;
1409 gammas->fData[1] = gData;
1410 gammas->fData[2] = bData;
1411 }
1412 } else {
1413 // Guess sRGB if the profile is missing transfer functions.
1414 gammaNamed = kSRGB_SkGammaNamed;
1415 }
1416
1417 if (kNonStandard_SkGammaNamed == gammaNamed) {
1418 // It's possible that we'll initially detect non-matching gammas, only for
1419 // them to evaluate to the same named gamma curve.
1420 gammaNamed = is_named(gammas);
1421 }
1422
1423 if (kNonStandard_SkGammaNamed == gammaNamed) {
1424 return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed,
1425 std::move(gammas),
1426 mat, std::move(profileData)));
1427 }
1428
1429 return SkColorSpace::MakeRGB(gammaNamed, mat);
1430 }
1431
make_gray(const ICCProfileHeader & header,ICCTag * tags,int tagCount,const uint8_t * base,sk_sp<SkData> profileData)1432 static sk_sp<SkColorSpace> make_gray(const ICCProfileHeader& header, ICCTag* tags, int tagCount,
1433 const uint8_t* base, sk_sp<SkData> profileData) {
1434 if (kLAB_PCSSpace == header.fPCS) {
1435 return nullptr;
1436 }
1437
1438 const ICCTag* grayTRC = ICCTag::Find(tags, tagCount, kTAG_kTRC);
1439 if (!grayTRC) {
1440 return_null("grayTRC tag required for monochrome profiles.");
1441 }
1442 SkGammas::Data data;
1443 SkColorSpaceTransferFn params;
1444 size_t tagBytes;
1445 SkGammas::Type type =
1446 parse_gamma(&data, ¶ms, &tagBytes, grayTRC->addr(base), grayTRC->fLength);
1447 handle_invalid_gamma(&type, &data);
1448
1449 SkMatrix44 toXYZD50(SkMatrix44::kIdentity_Constructor);
1450 toXYZD50.setFloat(0, 0, kWhitePointD50[0]);
1451 toXYZD50.setFloat(1, 1, kWhitePointD50[1]);
1452 toXYZD50.setFloat(2, 2, kWhitePointD50[2]);
1453 if (SkGammas::Type::kNamed_Type == type) {
1454 return SkColorSpace::MakeRGB(data.fNamed, toXYZD50);
1455 }
1456
1457 size_t allocSize = sizeof(SkGammas);
1458 if (!safe_add(allocSize, gamma_alloc_size(type, data), &allocSize)) {
1459 return_null("SkGammas struct is too large to allocate");
1460 }
1461 void* memory = sk_malloc_throw(allocSize);
1462 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
1463 load_gammas(memory, 0, type, &data, params, grayTRC->addr(base));
1464 for (int i = 0; i < 3; ++i) {
1465 gammas->fType[i] = type;
1466 gammas->fData[i] = data;
1467 }
1468
1469 return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(kNonStandard_SkGammaNamed,
1470 std::move(gammas),
1471 toXYZD50, std::move(profileData)));
1472 }
1473
make_a2b(SkColorSpace::Type iccType,const ICCProfileHeader & header,ICCTag * tags,int tagCount,const uint8_t * base,sk_sp<SkData> profileData)1474 static sk_sp<SkColorSpace> make_a2b(SkColorSpace::Type iccType,
1475 const ICCProfileHeader& header, ICCTag* tags, int tagCount,
1476 const uint8_t* base, sk_sp<SkData> profileData) {
1477 const ICCTag* a2b0 = ICCTag::Find(tags, tagCount, kTAG_A2B0);
1478 if (a2b0) {
1479 const SkColorSpace_A2B::PCS pcs = kXYZ_PCSSpace == header.fPCS
1480 ? SkColorSpace_A2B::PCS::kXYZ
1481 : SkColorSpace_A2B::PCS::kLAB;
1482 std::vector<SkColorSpace_A2B::Element> elements;
1483 if (load_a2b0(&elements, a2b0->addr(base), a2b0->fLength, pcs, iccType)) {
1484 return sk_sp<SkColorSpace>(new SkColorSpace_A2B(iccType, std::move(elements),
1485 pcs, std::move(profileData)));
1486 }
1487 }
1488
1489 return nullptr;
1490 }
1491
MakeICC(const void * input,size_t len)1492 sk_sp<SkColorSpace> SkColorSpace::MakeICC(const void* input, size_t len) {
1493 if (!input || len < kICCHeaderSize) {
1494 return_null("Data is null or not large enough to contain an ICC profile");
1495 }
1496
1497 // Create our own copy of the input.
1498 void* memory = sk_malloc_throw(len);
1499 memcpy(memory, input, len);
1500 sk_sp<SkData> profileData = SkData::MakeFromMalloc(memory, len);
1501 const uint8_t* base = profileData->bytes();
1502 const uint8_t* ptr = base;
1503
1504 // Read the ICC profile header and check to make sure that it is valid.
1505 ICCProfileHeader header;
1506 header.init(ptr, len);
1507 if (!header.valid()) {
1508 return nullptr;
1509 }
1510
1511 // Adjust ptr and len before reading the tags.
1512 if (len < header.fSize) {
1513 SkColorSpacePrintf("ICC profile might be truncated.\n");
1514 } else if (len > header.fSize) {
1515 SkColorSpacePrintf("Caller provided extra data beyond the end of the ICC profile.\n");
1516 len = header.fSize;
1517 }
1518 ptr += kICCHeaderSize;
1519 len -= kICCHeaderSize;
1520
1521 // Parse tag headers.
1522 uint32_t tagCount = header.fTagCount;
1523 SkColorSpacePrintf("ICC profile contains %d tags.\n", tagCount);
1524 if (len < kICCTagTableEntrySize * tagCount) {
1525 return_null("Not enough input data to read tag table entries");
1526 }
1527
1528 SkAutoTArray<ICCTag> tags(tagCount);
1529 for (uint32_t i = 0; i < tagCount; i++) {
1530 ptr = tags[i].init(ptr);
1531 SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24) & 0xFF,
1532 (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >> 8) & 0xFF,
1533 (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLength);
1534
1535 if (!tags[i].valid(kICCHeaderSize + len)) {
1536 return_null("Tag is too large to fit in ICC profile");
1537 }
1538 }
1539
1540 Type a2b_type = kRGB_Type;
1541 switch (header.fInputColorSpace) {
1542 case kRGB_ColorSpace: {
1543 sk_sp<SkColorSpace> colorSpace =
1544 make_xyz(header, tags.get(), tagCount, base, profileData);
1545 if (colorSpace) {
1546 return colorSpace;
1547 }
1548 break;
1549 }
1550 case kGray_ColorSpace: {
1551 return make_gray(header, tags.get(), tagCount, base, profileData);
1552 }
1553 case kCMYK_ColorSpace:
1554 a2b_type = kCMYK_Type;
1555 break;
1556 default:
1557 return_null("ICC profile contains unsupported colorspace");
1558 }
1559
1560 return make_a2b(a2b_type, header, tags.get(), tagCount, base, profileData);
1561 }
1562