• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &params, 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], &params[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], &params[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, &params, &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, &params, &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