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