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