LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkColorSpace_ICC.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 721 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 36 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      33           0 : static uint16_t read_big_endian_u16(const uint8_t* ptr) {
      34           0 :     return ptr[0] << 8 | ptr[1];
      35             : }
      36             : 
      37           0 : static uint32_t read_big_endian_u32(const uint8_t* ptr) {
      38           0 :     return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
      39             : }
      40             : 
      41           0 : static int32_t read_big_endian_i32(const uint8_t* ptr) {
      42           0 :     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             : 
      93           0 :     void init(const uint8_t* src, size_t len) {
      94             :         SkASSERT(kICCHeaderSize == sizeof(*this));
      95             : 
      96           0 :         uint32_t* dst = (uint32_t*) this;
      97           0 :         for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) {
      98           0 :             dst[i] = read_big_endian_u32(src);
      99             :         }
     100           0 :     }
     101             : 
     102           0 :     bool valid() const {
     103           0 :         return_if_false(fSize >= kICCHeaderSize, "Size is too small");
     104             : 
     105           0 :         uint8_t majorVersion = fVersion >> 24;
     106           0 :         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           0 :         return_if_false(fProfileClass == kDisplay_Profile ||
     112             :                         fProfileClass == kInput_Profile ||
     113             :                         fProfileClass == kOutput_Profile ||
     114             :                         fProfileClass == kColorSpace_Profile,
     115             :                         "Unsupported profile");
     116             : 
     117           0 :         switch (fInputColorSpace) {
     118             :             case kRGB_ColorSpace:
     119             :                 SkColorSpacePrintf("RGB Input Color Space");
     120           0 :                 break;
     121             :             case kCMYK_ColorSpace:
     122             :                 SkColorSpacePrintf("CMYK Input Color Space\n");
     123           0 :                 break;
     124             :             case kGray_ColorSpace:
     125             :                 SkColorSpacePrintf("Gray Input Color Space\n");
     126           0 :                 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           0 :                 return false;
     132             :         }
     133             : 
     134           0 :         switch (fPCS) {
     135             :             case kXYZ_PCSSpace:
     136             :                 SkColorSpacePrintf("XYZ PCS\n");
     137           0 :                 break;
     138             :             case kLAB_PCSSpace:
     139             :                 SkColorSpacePrintf("Lab PCS\n");
     140           0 :                 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           0 :                 return false;
     147             :         }
     148             : 
     149           0 :         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           0 :         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           0 :         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           0 :         return_if_false(fTagCount <= 100, "Too many tags");
     168             : 
     169           0 :         return true;
     170             :     }
     171             : };
     172             : 
     173             : template <class T>
     174           0 : static bool safe_add(T arg1, T arg2, size_t* result) {
     175             :     SkASSERT(arg1 >= 0);
     176             :     SkASSERT(arg2 >= 0);
     177           0 :     if (arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) {
     178           0 :         T sum = arg1 + arg2;
     179           0 :         if (sum <= std::numeric_limits<size_t>::max()) {
     180           0 :             *result = static_cast<size_t>(sum);
     181           0 :             return true;
     182             :         }
     183             :     }
     184           0 :     return false;
     185             : }
     186             : 
     187           0 : static bool safe_mul(uint32_t arg1, uint32_t arg2, uint32_t* result) {
     188           0 :     uint64_t product64 = (uint64_t) arg1 * (uint64_t) arg2;
     189           0 :     uint32_t product32 = (uint32_t) product64;
     190           0 :     if (product32 != product64) {
     191           0 :         return false;
     192             :     }
     193             : 
     194           0 :     *result = product32;
     195           0 :     return true;
     196             : }
     197             : 
     198             : struct ICCTag {
     199             :     uint32_t fSignature;
     200             :     uint32_t fOffset;
     201             :     uint32_t fLength;
     202             : 
     203           0 :     const uint8_t* init(const uint8_t* src) {
     204           0 :         fSignature = read_big_endian_u32(src);
     205           0 :         fOffset = read_big_endian_u32(src + 4);
     206           0 :         fLength = read_big_endian_u32(src + 8);
     207           0 :         return src + 12;
     208             :     }
     209             : 
     210           0 :     bool valid(size_t len) {
     211             :         size_t tagEnd;
     212           0 :         return_if_false(safe_add(fOffset, fLength, &tagEnd),
     213             :                         "Tag too large, overflows integer addition");
     214           0 :         return_if_false(tagEnd <= len, "Tag too large for ICC profile");
     215           0 :         return true;
     216             :     }
     217             : 
     218           0 :     const uint8_t* addr(const uint8_t* src) const {
     219           0 :         return src + fOffset;
     220             :     }
     221             : 
     222           0 :     static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature) {
     223           0 :         for (int i = 0; i < count; ++i) {
     224           0 :             if (tags[i].fSignature == signature) {
     225           0 :                 return &tags[i];
     226             :             }
     227             :         }
     228           0 :         return nullptr;
     229             :     }
     230             : };
     231             : 
     232           0 : static bool load_xyz(float dst[3], const uint8_t* src, size_t len) {
     233           0 :     if (len < 20) {
     234             :         SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len);
     235           0 :         return false;
     236             :     }
     237             : 
     238           0 :     dst[0] = SkFixedToFloat(read_big_endian_i32(src + 8));
     239           0 :     dst[1] = SkFixedToFloat(read_big_endian_i32(src + 12));
     240           0 :     dst[2] = SkFixedToFloat(read_big_endian_i32(src + 16));
     241             :     SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]);
     242           0 :     return true;
     243             : }
     244             : 
     245           0 : static SkGammas::Type set_gamma_value(SkGammas::Data* data, float value) {
     246           0 :     if (color_space_almost_equal(2.2f, value)) {
     247           0 :         data->fNamed = k2Dot2Curve_SkGammaNamed;
     248           0 :         return SkGammas::Type::kNamed_Type;
     249             :     }
     250             : 
     251           0 :     if (color_space_almost_equal(1.0f, value)) {
     252           0 :         data->fNamed = kLinear_SkGammaNamed;
     253           0 :         return SkGammas::Type::kNamed_Type;
     254             :     }
     255             : 
     256           0 :     if (color_space_almost_equal(0.0f, value)) {
     257           0 :         return SkGammas::Type::kNone_Type;
     258             :     }
     259             : 
     260           0 :     data->fValue = value;
     261           0 :     return SkGammas::Type::kValue_Type;
     262             : }
     263             : 
     264           0 : 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           0 :     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             :  */
     281           0 : static SkGammas::Type parse_gamma(SkGammas::Data* outData, SkColorSpaceTransferFn* outParams,
     282             :                                   size_t* outTagBytes, const uint8_t* src, size_t len) {
     283           0 :     if (len < 12) {
     284             :         SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
     285           0 :         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           0 :     uint32_t type = read_big_endian_u32(src);
     293             :     // Bytes 4-7 are reserved and should be set to zero.
     294           0 :     switch (type) {
     295             :         case kTAG_CurveType: {
     296           0 :             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           0 :             if (!safe_add(count, count, &tagBytes) ||
     301           0 :                 !safe_add((size_t) 12, tagBytes, &tagBytes))
     302             :             {
     303             :                 SkColorSpacePrintf("Invalid gamma count");
     304           0 :                 return SkGammas::Type::kNone_Type;
     305             :             }
     306             : 
     307           0 :             if (len < tagBytes) {
     308             :                 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
     309           0 :                 return SkGammas::Type::kNone_Type;
     310             :             }
     311           0 :             *outTagBytes = tagBytes;
     312             : 
     313           0 :             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           0 :                 outData->fNamed = kLinear_SkGammaNamed;
     318           0 :                 return SkGammas::Type::kNamed_Type;
     319             :             }
     320             : 
     321           0 :             const uint16_t* table = (const uint16_t*) (src + 12);
     322           0 :             if (1 == count) {
     323             :                 // The table entry is the gamma (with a bias of 256).
     324           0 :                 float value = (read_big_endian_u16((const uint8_t*) table)) / 256.0f;
     325             :                 SkColorSpacePrintf("gamma %g\n", value);
     326             : 
     327           0 :                 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           0 :             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           0 :                 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
     342           0 :                         3366 == read_big_endian_u16((const uint8_t*) &table[257]) &&
     343           0 :                         14116 == read_big_endian_u16((const uint8_t*) &table[513]) &&
     344           0 :                         34318 == read_big_endian_u16((const uint8_t*) &table[768]) &&
     345           0 :                         65535 == read_big_endian_u16((const uint8_t*) &table[1023])) {
     346           0 :                     outData->fNamed = kSRGB_SkGammaNamed;
     347           0 :                     return SkGammas::Type::kNamed_Type;
     348             :                 }
     349             :             }
     350             : 
     351           0 :             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           0 :                 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
     355           0 :                         3062 == read_big_endian_u16((const uint8_t*) &table[6]) &&
     356           0 :                         12824 == read_big_endian_u16((const uint8_t*) &table[12]) &&
     357           0 :                         31237 == read_big_endian_u16((const uint8_t*) &table[18]) &&
     358           0 :                         65535 == read_big_endian_u16((const uint8_t*) &table[25])) {
     359           0 :                     outData->fNamed = kSRGB_SkGammaNamed;
     360           0 :                     return SkGammas::Type::kNamed_Type;
     361             :                 }
     362             :             }
     363             : 
     364           0 :             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           0 :                 if (0 == read_big_endian_u16((const uint8_t*) &table[0]) &&
     368           0 :                         950 == read_big_endian_u16((const uint8_t*) &table[515]) &&
     369           0 :                         3342 == read_big_endian_u16((const uint8_t*) &table[1025]) &&
     370           0 :                         14079 == read_big_endian_u16((const uint8_t*) &table[2051]) &&
     371           0 :                         65535 == read_big_endian_u16((const uint8_t*) &table[4095])) {
     372           0 :                     outData->fNamed = kSRGB_SkGammaNamed;
     373           0 :                     return SkGammas::Type::kNamed_Type;
     374             :                 }
     375             :             }
     376             : 
     377             :             // Otherwise, we will represent gamma with a table.
     378           0 :             outData->fTable.fSize = count;
     379           0 :             return SkGammas::Type::kTable_Type;
     380             :         }
     381             :         case kTAG_ParaCurveType: {
     382             :             // Determine the format of the parametric curve tag.
     383           0 :             uint16_t format = read_big_endian_u16(src + 8);
     384           0 :             if (format > kGABCDEF_ParaCurveType) {
     385             :                 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
     386           0 :                 return SkGammas::Type::kNone_Type;
     387             :             }
     388             : 
     389           0 :             if (kExponential_ParaCurveType == format) {
     390           0 :                 tagBytes = 12 + 4;
     391           0 :                 if (len < tagBytes) {
     392             :                     SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
     393           0 :                     return SkGammas::Type::kNone_Type;
     394             :                 }
     395             : 
     396             :                 // Y = X^g
     397           0 :                 float g = read_big_endian_16_dot_16(src + 12);
     398             : 
     399           0 :                 *outTagBytes = tagBytes;
     400           0 :                 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           0 :             if (len < 24) {
     411             :                 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
     412           0 :                 return SkGammas::Type::kNone_Type;
     413             :             }
     414           0 :             float g = read_big_endian_16_dot_16(src + 12);
     415           0 :             float a = read_big_endian_16_dot_16(src + 16);
     416           0 :             float b = read_big_endian_16_dot_16(src + 20);
     417           0 :             float c = 0.0f, d = 0.0f, e = 0.0f, f = 0.0f;
     418           0 :             switch(format) {
     419             :                 case kGAB_ParaCurveType:
     420           0 :                     tagBytes = 12 + 12;
     421             : 
     422             :                     // Y = (aX + b)^g  for X >= -b/a
     423             :                     // Y = 0           otherwise
     424           0 :                     d = -b / a;
     425           0 :                     break;
     426             :                 case kGABC_ParaCurveType:
     427           0 :                     tagBytes = 12 + 16;
     428           0 :                     if (len < tagBytes) {
     429             :                         SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
     430           0 :                         return SkGammas::Type::kNone_Type;
     431             :                     }
     432             : 
     433             :                     // Y = (aX + b)^g + e  for X >= -b/a
     434             :                     // Y = e               otherwise
     435           0 :                     e = read_big_endian_16_dot_16(src + 24);
     436           0 :                     d = -b / a;
     437           0 :                     f = e;
     438           0 :                     break;
     439             :                 case kGABDE_ParaCurveType:
     440           0 :                     tagBytes = 12 + 20;
     441           0 :                     if (len < tagBytes) {
     442             :                         SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
     443           0 :                         return SkGammas::Type::kNone_Type;
     444             :                     }
     445             : 
     446             :                     // Y = (aX + b)^g  for X >= d
     447             :                     // Y = cX          otherwise
     448           0 :                     c = read_big_endian_16_dot_16(src + 24);
     449           0 :                     d = read_big_endian_16_dot_16(src + 28);
     450           0 :                     break;
     451             :                 case kGABCDEF_ParaCurveType:
     452           0 :                     tagBytes = 12 + 28;
     453           0 :                     if (len < tagBytes) {
     454             :                         SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
     455           0 :                         return SkGammas::Type::kNone_Type;
     456             :                     }
     457             : 
     458             :                     // Y = (aX + b)^g + e  for X >= d
     459             :                     // Y = cX + f          otherwise
     460           0 :                     c = read_big_endian_16_dot_16(src + 24);
     461           0 :                     d = read_big_endian_16_dot_16(src + 28);
     462           0 :                     e = read_big_endian_16_dot_16(src + 32);
     463           0 :                     f = read_big_endian_16_dot_16(src + 36);
     464           0 :                     break;
     465             :                 default:
     466           0 :                     SkASSERT(false);
     467           0 :                     return SkGammas::Type::kNone_Type;
     468             :             }
     469             : 
     470           0 :             outParams->fG = g;
     471           0 :             outParams->fA = a;
     472           0 :             outParams->fB = b;
     473           0 :             outParams->fC = c;
     474           0 :             outParams->fD = d;
     475           0 :             outParams->fE = e;
     476           0 :             outParams->fF = f;
     477             : 
     478           0 :             if (!is_valid_transfer_fn(*outParams)) {
     479           0 :                 return SkGammas::Type::kNone_Type;
     480             :             }
     481             : 
     482           0 :             if (is_almost_srgb(*outParams)) {
     483           0 :                 outData->fNamed = kSRGB_SkGammaNamed;
     484           0 :                 return SkGammas::Type::kNamed_Type;
     485             :             }
     486             : 
     487           0 :             if (is_almost_2dot2(*outParams)) {
     488           0 :                 outData->fNamed = k2Dot2Curve_SkGammaNamed;
     489           0 :                 return SkGammas::Type::kNamed_Type;
     490             :             }
     491             : 
     492           0 :             *outTagBytes = tagBytes;
     493           0 :             return SkGammas::Type::kParam_Type;
     494             :         }
     495             :         default:
     496             :             SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
     497           0 :             return SkGammas::Type::kNone_Type;
     498             :     }
     499             : }
     500             : 
     501             : /**
     502             :  *  Returns the additional size in bytes needed to store the gamma tag.
     503             :  */
     504           0 : static size_t gamma_alloc_size(SkGammas::Type type, const SkGammas::Data& data) {
     505           0 :     switch (type) {
     506             :         case SkGammas::Type::kNamed_Type:
     507             :         case SkGammas::Type::kValue_Type:
     508           0 :             return 0;
     509             :         case SkGammas::Type::kTable_Type:
     510           0 :             return sizeof(float) * data.fTable.fSize;
     511             :         case SkGammas::Type::kParam_Type:
     512           0 :             return sizeof(SkColorSpaceTransferFn);
     513             :         default:
     514           0 :             SkASSERT(false);
     515           0 :             return 0;
     516             :     }
     517             : }
     518             : 
     519             : /**
     520             :  *  Sets invalid gamma to the default value.
     521             :  */
     522           0 : static void handle_invalid_gamma(SkGammas::Type* type, SkGammas::Data* data) {
     523           0 :     if (SkGammas::Type::kNone_Type == *type) {
     524           0 :         *type = SkGammas::Type::kNamed_Type;
     525             : 
     526             :         // Guess sRGB in the case of a malformed transfer function.
     527           0 :         data->fNamed = kSRGB_SkGammaNamed;
     528             :     }
     529           0 : }
     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             :  */
     548           0 : 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           0 :     void* storage = SkTAddOffset<void>(memory, offset + sizeof(SkGammas));
     552             : 
     553           0 :     switch (type) {
     554             :         case SkGammas::Type::kNamed_Type:
     555             :         case SkGammas::Type::kValue_Type:
     556             :             // Nothing to do here.
     557           0 :             return 0;
     558             :         case SkGammas::Type::kTable_Type: {
     559           0 :             data->fTable.fOffset = offset;
     560             : 
     561           0 :             float* outTable = (float*) storage;
     562           0 :             const uint16_t* inTable = (const uint16_t*) (src + 12);
     563           0 :             for (int i = 0; i < data->fTable.fSize; i++) {
     564           0 :                 outTable[i] = (read_big_endian_u16((const uint8_t*) &inTable[i])) / 65535.0f;
     565             :             }
     566             : 
     567           0 :             return sizeof(float) * data->fTable.fSize;
     568             :         }
     569             :         case SkGammas::Type::kParam_Type:
     570           0 :             data->fTable.fOffset = offset;
     571           0 :             memcpy(storage, &params, sizeof(SkColorSpaceTransferFn));
     572           0 :             return sizeof(SkColorSpaceTransferFn);
     573             :         default:
     574           0 :             SkASSERT(false);
     575           0 :             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             : 
     583           0 : 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           0 :     switch (precision) {
     587             :         case 1: //  8-bit data
     588             :         case 2: // 16-bit data
     589           0 :             break;
     590             :         default:
     591             :             SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit. Found: %d-bit\n",
     592             :                                8*precision);
     593           0 :             return false;
     594             :     }
     595             : 
     596           0 :     uint32_t numEntries = SkColorLookUpTable::kOutputChannels;
     597           0 :     for (uint32_t i = 0; i < inputChannels; i++) {
     598           0 :         if (0 == gridPoints[i]) {
     599             :             SkColorSpacePrintf("Each input channel must have at least one grid point.");
     600           0 :             return false;
     601             :         }
     602             : 
     603           0 :         if (!safe_mul(numEntries, gridPoints[i], &numEntries)) {
     604             :             SkColorSpacePrintf("Too many entries in Color LUT.");
     605           0 :             return false;
     606             :         }
     607             :     }
     608             : 
     609             :     uint32_t clutBytes;
     610           0 :     if (!safe_mul(numEntries, precision, &clutBytes)) {
     611             :         SkColorSpacePrintf("Too many entries in Color LUT.\n");
     612           0 :         return false;
     613             :     }
     614             : 
     615           0 :     if (len < clutBytes) {
     616             :         SkColorSpacePrintf("Color LUT tag is too small (%d / %d bytes).\n", len, clutBytes);
     617           0 :         return false;
     618             :     }
     619             : 
     620             :     // Movable struct colorLUT has ownership of fTable.
     621           0 :     void* memory = sk_malloc_throw(sizeof(SkColorLookUpTable) + sizeof(float) * numEntries);
     622           0 :     *colorLUT = sk_sp<SkColorLookUpTable>(new (memory) SkColorLookUpTable(inputChannels,
     623           0 :                                                                           gridPoints));
     624             : 
     625           0 :     float* table = SkTAddOffset<float>(memory, sizeof(SkColorLookUpTable));
     626           0 :     const uint8_t* ptr = src;
     627           0 :     for (uint32_t i = 0; i < numEntries; i++, ptr += precision) {
     628           0 :         if (1 == precision) {
     629           0 :             table[i] = ((float) *ptr) / 255.0f;
     630             :         } else {
     631           0 :             table[i] = ((float) read_big_endian_u16(ptr)) / 65535.0f;
     632             :         }
     633             :     }
     634             : 
     635           0 :     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             :  */
     653           0 : static bool load_matrix(SkMatrix44* matrix, const uint8_t* src, size_t len, bool translate,
     654             :                         SkColorSpace_A2B::PCS pcs) {
     655           0 :     const size_t minLen = translate ? 48 : 36;
     656           0 :     if (len < minLen) {
     657             :         SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len);
     658           0 :         return false;
     659             :     }
     660             : 
     661             :     float encodingFactor;
     662           0 :     switch (pcs) {
     663             :         case SkColorSpace_A2B::PCS::kLAB:
     664           0 :             encodingFactor = 1.f;
     665           0 :             break;
     666             :         case SkColorSpace_A2B::PCS::kXYZ:
     667           0 :             encodingFactor = 65535 / 32768.f;
     668           0 :             break;
     669             :         default:
     670           0 :             encodingFactor = 1.f;
     671           0 :             SkASSERT(false);
     672           0 :             break; 
     673             :     }
     674             :     float array[16];
     675           0 :     array[ 0] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src));
     676           0 :     array[ 1] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 4));
     677           0 :     array[ 2] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 8));
     678             : 
     679           0 :     array[ 4] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 12));
     680           0 :     array[ 5] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 16));
     681           0 :     array[ 6] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 20));
     682             : 
     683           0 :     array[ 8] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 24));
     684           0 :     array[ 9] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 28));
     685           0 :     array[10] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 32));
     686             : 
     687           0 :     if (translate) {
     688           0 :         array[ 3] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 36)); // translate R
     689           0 :         array[ 7] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 40)); // translate G
     690           0 :         array[11] = encodingFactor * SkFixedToFloat(read_big_endian_i32(src + 44)); // translate B
     691             :     } else {
     692           0 :         array[ 3] = 0.0f;
     693           0 :         array[ 7] = 0.0f;
     694           0 :         array[11] = 0.0f;
     695             :     }
     696             : 
     697           0 :     array[12] = 0.0f;
     698           0 :     array[13] = 0.0f;
     699           0 :     array[14] = 0.0f;
     700           0 :     array[15] = 1.0f;
     701           0 :     matrix->setRowMajorf(array);
     702             :     SkColorSpacePrintf("A2B0 matrix loaded:\n");
     703           0 :     for (int r = 0; r < 4; ++r) {
     704             :         SkColorSpacePrintf("|");
     705           0 :         for (int c = 0; c < 4; ++c) {
     706             :             SkColorSpacePrintf(" %f ", matrix->get(r, c));
     707             :         }
     708             :         SkColorSpacePrintf("|\n");
     709             :     }
     710           0 :     return true;
     711             : }
     712             : 
     713           0 : static inline SkGammaNamed is_named(const sk_sp<SkGammas>& gammas) {
     714           0 :     for (uint8_t i = 0; i < gammas->channels(); ++i) {
     715           0 :         if (!gammas->isNamed(i) || gammas->data(i).fNamed != gammas->data(0).fNamed) {
     716           0 :             return kNonStandard_SkGammaNamed;
     717             :         }
     718             :     }
     719           0 :     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             :  */
     738           0 : static bool parse_and_load_gamma(SkGammaNamed* gammaNamed, sk_sp<SkGammas>* gammas,
     739             :                                  uint8_t inputChannels, const uint8_t* tagSrc, size_t tagLen) {
     740           0 :     SkGammas::Data data[kMaxColorChannels];
     741             :     SkColorSpaceTransferFn params[kMaxColorChannels];
     742             :     SkGammas::Type type[kMaxColorChannels];
     743             :     const uint8_t* tagPtr[kMaxColorChannels];
     744             : 
     745           0 :     tagPtr[0] = tagSrc;
     746             : 
     747           0 :     *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           0 :     size_t tagBytes = 0;
     752           0 :     type[0] = parse_gamma(&data[0], &params[0], &tagBytes, tagPtr[0], tagLen);
     753           0 :     handle_invalid_gamma(&type[0], &data[0]);
     754           0 :     size_t alignedTagBytes = SkAlign4(tagBytes);
     755             : 
     756           0 :     bool allChannelsSame = false;
     757           0 :     if (inputChannels * alignedTagBytes <= tagLen) {
     758           0 :         allChannelsSame = true;
     759           0 :         for (uint8_t i = 1; i < inputChannels; ++i) {
     760           0 :             if (0 != memcmp(tagSrc, tagSrc + i * alignedTagBytes, tagBytes)) {
     761           0 :                 allChannelsSame = false;
     762           0 :                 break;
     763             :             }
     764             :         }
     765             :     }
     766           0 :     if (allChannelsSame) {
     767           0 :         if (SkGammas::Type::kNamed_Type == type[0]) {
     768           0 :             *gammaNamed = data[0].fNamed;
     769             :         } else {
     770           0 :             size_t allocSize = sizeof(SkGammas);
     771           0 :             return_if_false(safe_add(allocSize, gamma_alloc_size(type[0], data[0]), &allocSize),
     772             :                             "SkGammas struct is too large to allocate");
     773           0 :             void* memory = sk_malloc_throw(allocSize);
     774           0 :             *gammas = sk_sp<SkGammas>(new (memory) SkGammas(inputChannels));
     775           0 :             load_gammas(memory, 0, type[0], &data[0], params[0], tagPtr[0]);
     776             : 
     777           0 :             for (uint8_t channel = 0; channel < inputChannels; ++channel) {
     778           0 :                 (*gammas)->fType[channel] = type[0];
     779           0 :                 (*gammas)->fData[channel] = data[0];
     780             :             }
     781             :         }
     782             :     } else {
     783           0 :         for (uint8_t channel = 1; channel < inputChannels; ++channel) {
     784           0 :             tagPtr[channel] = tagPtr[channel - 1] + alignedTagBytes;
     785           0 :             tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0;
     786           0 :             tagBytes = 0;
     787           0 :             type[channel] = parse_gamma(&data[channel], &params[channel], &tagBytes,
     788             :                                         tagPtr[channel], tagLen);
     789           0 :             handle_invalid_gamma(&type[channel], &data[channel]);
     790           0 :             alignedTagBytes = SkAlign4(tagBytes);
     791             :         }
     792             : 
     793           0 :         size_t allocSize = sizeof(SkGammas);
     794           0 :         for (uint8_t channel = 0; channel < inputChannels; ++channel) {
     795           0 :             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           0 :         void* memory = sk_malloc_throw(allocSize);
     800           0 :         *gammas = sk_sp<SkGammas>(new (memory) SkGammas(inputChannels));
     801             : 
     802           0 :         uint32_t offset = 0;
     803           0 :         for (uint8_t channel = 0; channel < inputChannels; ++channel) {
     804           0 :             (*gammas)->fType[channel] = type[channel];
     805           0 :             offset += load_gammas(memory,offset, type[channel], &data[channel], params[channel],
     806           0 :                                   tagPtr[channel]);
     807           0 :             (*gammas)->fData[channel] = data[channel];
     808             : 
     809             :         }
     810             :     }
     811             : 
     812           0 :     if (kNonStandard_SkGammaNamed == *gammaNamed) {
     813           0 :         *gammaNamed = is_named(*gammas);
     814           0 :         if (kNonStandard_SkGammaNamed != *gammaNamed) {
     815             :             // No need to keep the gammas struct, the enum is enough.
     816           0 :             *gammas = nullptr;
     817             :         }
     818             :     }
     819           0 :     return true;
     820             : }
     821             : 
     822           0 : 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           0 :     const float normalizeX = 1.f / (count - 1);
     825           0 :     for (uint32_t x = 0; x < count; ++x) {
     826           0 :         const float y = precision == 1 ? (src[x] / 255.f)
     827           0 :                                        : (read_big_endian_u16(src + 2*x) / 65535.f);
     828           0 :         if (!color_space_almost_equal(x * normalizeX, y)) {
     829           0 :             return false;
     830             :         }
     831             :     }
     832           0 :     return true;
     833             : }
     834             : 
     835           0 : 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           0 :     if (precision != 1 && precision != 2) {
     839             :         SkColorSpacePrintf("Invalid gamma table precision %d\n", precision);
     840           0 :         return false;
     841             :     }
     842             :     uint32_t totalEntries;
     843           0 :     return_if_false(safe_mul(entriesPerTable, numTables, &totalEntries),
     844             :                     "Too many entries in gamma table.");
     845             :     uint32_t readBytes;
     846           0 :     return_if_false(safe_mul(precision, totalEntries, &readBytes),
     847             :                     "SkGammas struct is too large to read");
     848           0 :     if (len < readBytes) {
     849             :         SkColorSpacePrintf("Gamma table is too small. Provided: %d. Required: %d\n",
     850             :                            len, readBytes);
     851           0 :         return false;
     852             :     }
     853             : 
     854             :     uint32_t writeBytesPerChannel;
     855           0 :     return_if_false(safe_mul(sizeof(float), entriesPerTable, &writeBytesPerChannel),
     856             :                     "SkGammas struct is too large to allocate");
     857           0 :     const size_t readBytesPerChannel = precision * entriesPerTable;
     858           0 :     size_t numTablesToUse = 1;
     859           0 :     for (size_t tableIndex = 1; tableIndex < numTables; ++tableIndex) {
     860           0 :         if (0 != memcmp(src, src + readBytesPerChannel * tableIndex, readBytesPerChannel)) {
     861           0 :             numTablesToUse = numTables;
     862           0 :             break;
     863             :         }
     864             :     }
     865             : 
     866           0 :     if (1 == numTablesToUse) {
     867           0 :         if (is_lut_gamma_linear(src, entriesPerTable, precision)) {
     868           0 :             *gammaNamed = kLinear_SkGammaNamed;
     869           0 :             return true;
     870             :         }
     871             :     }
     872           0 :     *gammaNamed = kNonStandard_SkGammaNamed;
     873             : 
     874             :     uint32_t writetableBytes;
     875           0 :     return_if_false(safe_mul(numTablesToUse, writeBytesPerChannel, &writetableBytes),
     876             :                     "SkGammas struct is too large to allocate");
     877           0 :     size_t allocSize = sizeof(SkGammas);
     878           0 :     return_if_false(safe_add(allocSize, (size_t)writetableBytes, &allocSize),
     879             :                     "SkGammas struct is too large to allocate");
     880             : 
     881           0 :     void* memory = sk_malloc_throw(allocSize);
     882           0 :     *gammas = sk_sp<SkGammas>(new (memory) SkGammas(numTables));
     883             : 
     884           0 :     for (size_t tableIndex = 0; tableIndex < numTablesToUse; ++tableIndex) {
     885           0 :         const uint8_t* ptr = src + readBytesPerChannel * tableIndex;
     886           0 :         const size_t offset = sizeof(SkGammas) + tableIndex * writeBytesPerChannel;
     887           0 :         float* table = SkTAddOffset<float>(memory, offset);
     888           0 :         if (1 == precision) {
     889           0 :             for (uint32_t i = 0; i < entriesPerTable; ++i, ptr += 1) {
     890           0 :                 table[i] = ((float) *ptr) / 255.0f;
     891             :             }
     892           0 :         } else if (2 == precision) {
     893           0 :             for (uint32_t i = 0; i < entriesPerTable; ++i, ptr += 2) {
     894           0 :                 table[i] = ((float) read_big_endian_u16(ptr)) / 65535.0f;
     895             :             }
     896             :         }
     897             :     }
     898             : 
     899           0 :     SkASSERT(1 == numTablesToUse|| numTables == numTablesToUse);
     900             : 
     901           0 :     size_t tableOffset = 0;
     902           0 :     for (size_t tableIndex = 0; tableIndex < numTables; ++tableIndex) {
     903           0 :         (*gammas)->fType[tableIndex]                = SkGammas::Type::kTable_Type;
     904           0 :         (*gammas)->fData[tableIndex].fTable.fOffset = tableOffset;
     905           0 :         (*gammas)->fData[tableIndex].fTable.fSize   = entriesPerTable;
     906           0 :         if (numTablesToUse > 1) {
     907           0 :             tableOffset += writeBytesPerChannel;
     908             :         }
     909             :     }
     910             : 
     911           0 :     return true;
     912             : }
     913             : 
     914           0 : 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           0 :     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           0 :     const uint8_t inputChannels = src[8];
     920           0 :     const uint8_t outputChannels = src[9];
     921           0 :     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           0 :         return false;
     925             :     }
     926           0 :     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           0 :         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           0 :     const uint32_t offsetToACurves = read_big_endian_i32(src + 28);
     943           0 :     if (0 != offsetToACurves && offsetToACurves < len) {
     944           0 :         const size_t tagLen = len - offsetToACurves;
     945             :         SkGammaNamed gammaNamed;
     946           0 :         sk_sp<SkGammas> gammas;
     947           0 :         if (!parse_and_load_gamma(&gammaNamed, &gammas, inputChannels, src + offsetToACurves,
     948             :                                   tagLen)) {
     949           0 :             return false;
     950             :         }
     951           0 :         if (gammas) {
     952           0 :             elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
     953           0 :         } else if (kLinear_SkGammaNamed != gammaNamed) {
     954           0 :             elements->push_back(SkColorSpace_A2B::Element(gammaNamed, inputChannels));
     955             :         }
     956             :     }
     957             : 
     958           0 :     const uint32_t offsetToColorLUT = read_big_endian_i32(src + 24);
     959           0 :     if (0 != offsetToColorLUT && offsetToColorLUT < len) {
     960           0 :         sk_sp<SkColorLookUpTable> colorLUT;
     961           0 :         const uint8_t* clutSrc = src + offsetToColorLUT;
     962           0 :         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           0 :         if (clutLen < kColorLUTHeaderSize) {
     967             :             SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", clutLen);
     968           0 :             return false;
     969             :         }
     970             : 
     971           0 :         SkASSERT(inputChannels <= kMaxColorChannels);
     972             :         uint8_t gridPoints[kMaxColorChannels];
     973           0 :         for (uint32_t i = 0; i < inputChannels; ++i) {
     974           0 :             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           0 :         const uint8_t precision = clutSrc[16];
     979           0 :         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           0 :             return false;
     983             :         }
     984           0 :         elements->push_back(SkColorSpace_A2B::Element(std::move(colorLUT)));
     985             :     }
     986             : 
     987           0 :     const uint32_t offsetToMCurves = read_big_endian_i32(src + 20);
     988           0 :     if (0 != offsetToMCurves && offsetToMCurves < len) {
     989           0 :         const size_t tagLen = len - offsetToMCurves;
     990             :         SkGammaNamed gammaNamed;
     991           0 :         sk_sp<SkGammas> gammas;
     992           0 :         if (!parse_and_load_gamma(&gammaNamed, &gammas, outputChannels, src + offsetToMCurves,
     993             :                                   tagLen)) {
     994           0 :             return false;
     995             :         }
     996           0 :         if (gammas) {
     997           0 :             elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
     998           0 :         } else if (kLinear_SkGammaNamed != gammaNamed) {
     999           0 :             elements->push_back(SkColorSpace_A2B::Element(gammaNamed, outputChannels));
    1000             :         }
    1001             :     }
    1002             : 
    1003           0 :     const uint32_t offsetToMatrix = read_big_endian_i32(src + 16);
    1004           0 :     if (0 != offsetToMatrix && offsetToMatrix < len) {
    1005           0 :         SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
    1006           0 :         if (!load_matrix(&matrix, src + offsetToMatrix, len - offsetToMatrix, true, pcs)) {
    1007             :             SkColorSpacePrintf("Failed to read matrix from A to B tag.\n");
    1008           0 :         } else if (!matrix.isIdentity()) {
    1009           0 :             elements->push_back(SkColorSpace_A2B::Element(matrix));
    1010             :         }
    1011             :     }
    1012             : 
    1013           0 :     const uint32_t offsetToBCurves = read_big_endian_i32(src + 12);
    1014           0 :     if (0 != offsetToBCurves && offsetToBCurves < len) {
    1015           0 :         const size_t tagLen = len - offsetToBCurves;
    1016             :         SkGammaNamed gammaNamed;
    1017           0 :         sk_sp<SkGammas> gammas;
    1018           0 :         if (!parse_and_load_gamma(&gammaNamed, &gammas, outputChannels, src + offsetToBCurves,
    1019             :                                   tagLen)) {
    1020           0 :             return false;
    1021             :         }
    1022           0 :         if (gammas) {
    1023           0 :             elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
    1024           0 :         } else if (kLinear_SkGammaNamed != gammaNamed) {
    1025           0 :             elements->push_back(SkColorSpace_A2B::Element(gammaNamed, outputChannels));
    1026             :         }
    1027             :     }
    1028             : 
    1029           0 :     return true;
    1030             : }
    1031             : 
    1032           0 : bool load_a2b0_lutn_type(std::vector<SkColorSpace_A2B::Element>* elements, const uint8_t* src,
    1033             :                          size_t len, SkColorSpace_A2B::PCS pcs) {
    1034           0 :     const uint32_t type = read_big_endian_u32(src);
    1035           0 :     switch (type) {
    1036             :         case kTAG_lut8Type:
    1037           0 :             SkASSERT(len >= 48);
    1038           0 :             break;
    1039             :         case kTAG_lut16Type:
    1040           0 :             SkASSERT(len >= 52);
    1041           0 :             break;
    1042             :         default:
    1043           0 :             SkASSERT(false);
    1044           0 :             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           0 :     const uint8_t inputChannels = src[8];
    1049           0 :     const uint8_t outputChannels = src[9];
    1050           0 :     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           0 :         return false;
    1054             :     }
    1055           0 :     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           0 :         return false;
    1064             :     }
    1065             : 
    1066           0 :     const uint8_t clutGridPoints = src[10];
    1067             :     // 11th byte reserved for padding (required to be zero)
    1068             : 
    1069           0 :     SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
    1070           0 :     load_matrix(&matrix, &src[12], len - 12, false, pcs);
    1071           0 :     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           0 :         elements->push_back(SkColorSpace_A2B::Element(matrix));
    1080             :     }
    1081             : 
    1082           0 :     size_t dataOffset      = 48;
    1083             :     // # of input table entries
    1084           0 :     size_t inTableEntries  = 256;
    1085             :     // # of output table entries
    1086           0 :     size_t outTableEntries = 256;
    1087           0 :     size_t precision       = 1;
    1088           0 :     if (kTAG_lut16Type == type) {
    1089           0 :         dataOffset      = 52;
    1090           0 :         inTableEntries  = read_big_endian_u16(src + 48);
    1091           0 :         outTableEntries = read_big_endian_u16(src + 50);
    1092           0 :         precision       = 2;
    1093             :         
    1094           0 :         constexpr size_t kMaxLut16GammaEntries = 4096;
    1095           0 :         if (inTableEntries < 2) {
    1096             :             SkColorSpacePrintf("Too few (%d) input gamma table entries. Must have at least 2.\n",
    1097             :                                inTableEntries);
    1098           0 :             return false;
    1099           0 :         } else if (inTableEntries > kMaxLut16GammaEntries) {
    1100             :             SkColorSpacePrintf("Too many (%d) input gamma table entries. Must have at most %d.\n",
    1101             :                                inTableEntries, kMaxLut16GammaEntries);
    1102           0 :             return false;
    1103             :         }
    1104             :         
    1105           0 :         if (outTableEntries < 2) {
    1106             :             SkColorSpacePrintf("Too few (%d) output gamma table entries. Must have at least 2.\n",
    1107             :                                outTableEntries);
    1108           0 :             return false;
    1109           0 :         } else if (outTableEntries > kMaxLut16GammaEntries) {
    1110             :             SkColorSpacePrintf("Too many (%d) output gamma table entries. Must have at most %d.\n",
    1111             :                                outTableEntries, kMaxLut16GammaEntries);
    1112           0 :             return false;
    1113             :         }
    1114             :     }
    1115             : 
    1116           0 :     const size_t inputOffset = dataOffset;
    1117           0 :     return_if_false(len >= inputOffset, "A2B0 lutnType tag too small for input gamma table");
    1118           0 :     sk_sp<SkGammas> inputGammas;
    1119             :     SkGammaNamed inputGammaNamed;
    1120           0 :     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           0 :         return false;
    1124             :     }
    1125           0 :     SkASSERT(inputGammas || inputGammaNamed != kNonStandard_SkGammaNamed);
    1126           0 :     if (kLinear_SkGammaNamed != inputGammaNamed) {
    1127           0 :         if (kNonStandard_SkGammaNamed != inputGammaNamed) {
    1128           0 :             elements->push_back(SkColorSpace_A2B::Element(inputGammaNamed, inputChannels));
    1129             :         } else {
    1130           0 :             elements->push_back(SkColorSpace_A2B::Element(std::move(inputGammas)));
    1131             :         }
    1132             :     }
    1133             : 
    1134           0 :     const size_t clutOffset = inputOffset + precision*inTableEntries*inputChannels;
    1135           0 :     return_if_false(len >= clutOffset, "A2B0 lutnType tag too small for CLUT");
    1136           0 :     sk_sp<SkColorLookUpTable> colorLUT;
    1137             :     const uint8_t gridPoints[kMaxColorChannels] = {
    1138             :         clutGridPoints, clutGridPoints, clutGridPoints, clutGridPoints
    1139           0 :     };
    1140           0 :     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           0 :         return false;
    1144             :     }
    1145           0 :     SkASSERT(colorLUT);
    1146           0 :     elements->push_back(SkColorSpace_A2B::Element(std::move(colorLUT)));
    1147             : 
    1148           0 :     size_t clutSize = precision * outputChannels;
    1149           0 :     for (int i = 0; i < inputChannels; ++i) {
    1150           0 :         clutSize *= clutGridPoints;
    1151             :     }
    1152           0 :     const size_t outputOffset = clutOffset + clutSize;
    1153           0 :     return_if_false(len >= outputOffset, "A2B0 lutnType tag too small for output gamma table");
    1154           0 :     sk_sp<SkGammas> outputGammas;
    1155             :     SkGammaNamed outputGammaNamed;
    1156           0 :     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           0 :         return false;
    1160             :     }
    1161           0 :     SkASSERT(outputGammas || outputGammaNamed != kNonStandard_SkGammaNamed);
    1162           0 :     if (kLinear_SkGammaNamed != outputGammaNamed) {
    1163           0 :         if (kNonStandard_SkGammaNamed != outputGammaNamed) {
    1164           0 :             elements->push_back(SkColorSpace_A2B::Element(outputGammaNamed, outputChannels));
    1165             :         } else {
    1166           0 :             elements->push_back(SkColorSpace_A2B::Element(std::move(outputGammas)));
    1167             :         }
    1168             :     }
    1169             : 
    1170           0 :     return true;
    1171             : }
    1172             : 
    1173           0 : static inline int icf_channels(SkColorSpace_Base::ICCTypeFlag iccType) {
    1174           0 :     switch (iccType) {
    1175             :         case SkColorSpace_Base::kRGB_ICCTypeFlag:
    1176           0 :             return 3;
    1177             :         case SkColorSpace_Base::kCMYK_ICCTypeFlag:
    1178           0 :             return 4;
    1179             :         default:
    1180           0 :             SkASSERT(false);
    1181           0 :             return 0;
    1182             :     }
    1183             : }
    1184             : 
    1185           0 : 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           0 :     if (len < 4) {
    1189           0 :         return false;
    1190             :     }
    1191           0 :     const uint32_t type = read_big_endian_u32(src);
    1192             : 
    1193           0 :     switch (type) {
    1194             :         case kTAG_AtoBType:
    1195           0 :             if (len < 32) {
    1196             :                 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len);
    1197           0 :                 return false;
    1198             :             }
    1199             :             SkColorSpacePrintf("A2B0 tag is of type lutAtoBType\n");
    1200           0 :             if (!load_a2b0_a_to_b_type(elements, src, len, pcs)) {
    1201           0 :                 return false;
    1202             :             }
    1203           0 :             break;
    1204             :         case kTAG_lut8Type:
    1205           0 :             if (len < 48) {
    1206             :                 SkColorSpacePrintf("lut8 tag is too small (%d bytes).", len);
    1207           0 :                 return false;
    1208             :             }
    1209             :             SkColorSpacePrintf("A2B0 tag of type lut8Type\n");
    1210           0 :             if (!load_a2b0_lutn_type(elements, src, len, pcs)) {
    1211           0 :                 return false;
    1212             :             }
    1213           0 :             break;
    1214             :         case kTAG_lut16Type:
    1215           0 :             if (len < 52) {
    1216             :                 SkColorSpacePrintf("lut16 tag is too small (%d bytes).", len);
    1217           0 :                 return false;
    1218             :             }
    1219             :             SkColorSpacePrintf("A2B0 tag of type lut16Type\n");
    1220           0 :             if (!load_a2b0_lutn_type(elements, src, len, pcs)) {
    1221           0 :                 return false;
    1222             :             }
    1223           0 :             break;
    1224             :         default:
    1225             :             SkColorSpacePrintf("Unsupported A to B tag type: %c%c%c%c\n", (type>>24)&0xFF,
    1226             :                                (type>>16)&0xFF, (type>>8)&0xFF, type&0xFF);
    1227           0 :             return false;
    1228             :     }
    1229           0 :     SkASSERT(SkColorSpace_A2B::PCS::kLAB == pcs || SkColorSpace_A2B::PCS::kXYZ == pcs);
    1230             :     static constexpr int kPCSChannels = 3; // must be PCSLAB or PCSXYZ
    1231           0 :     if (elements->empty()) {
    1232           0 :         return kPCSChannels == icf_channels(iccType);
    1233             :     }
    1234             :     // now let's verify that the input/output channels of each A2B element actually match up
    1235           0 :     if (icf_channels(iccType) != elements->front().inputChannels()) {
    1236             :         SkColorSpacePrintf("Input channel count does not match first A2B element's input count");
    1237           0 :         return false;
    1238             :     }
    1239           0 :     for (size_t i = 1; i < elements->size(); ++i) {
    1240           0 :         if ((*elements)[i - 1].outputChannels() != (*elements)[i].inputChannels()) {
    1241             :             SkColorSpacePrintf("A2B elements don't agree in input/output channel counts");
    1242           0 :             return false;
    1243             :         }
    1244             :     }
    1245           0 :     if (kPCSChannels != elements->back().outputChannels()) {
    1246             :         SkColorSpacePrintf("PCS channel count doesn't match last A2B element's output count");
    1247           0 :         return false;
    1248             :     }
    1249           0 :     return true;
    1250             : }
    1251             : 
    1252           0 : static bool tag_equals(const ICCTag* a, const ICCTag* b, const uint8_t* base) {
    1253           0 :     if (!a || !b) {
    1254           0 :         return a == b;
    1255             :     }
    1256             : 
    1257           0 :     if (a->fLength != b->fLength) {
    1258           0 :         return false;
    1259             :     }
    1260             : 
    1261           0 :     if (a->fOffset == b->fOffset) {
    1262           0 :         return true;
    1263             :     }
    1264             : 
    1265           0 :     return !memcmp(a->addr(base), b->addr(base), a->fLength);
    1266             : }
    1267             : 
    1268           0 : static inline bool is_close_to_d50(const SkMatrix44& matrix) {
    1269             :     // rX + gX + bX
    1270           0 :     float X = matrix.getFloat(0, 0) + matrix.getFloat(0, 1) + matrix.getFloat(0, 2);
    1271             : 
    1272             :     // rY + gY + bY
    1273           0 :     float Y = matrix.getFloat(1, 0) + matrix.getFloat(1, 1) + matrix.getFloat(1, 2);
    1274             : 
    1275             :     // rZ + gZ + bZ
    1276           0 :     float Z = matrix.getFloat(2, 0) + matrix.getFloat(2, 1) + matrix.getFloat(2, 2);
    1277             : 
    1278             :     static const float kD50_WhitePoint[3] = { 0.96420f, 1.00000f, 0.82491f };
    1279             : 
    1280             :     // This is a bit more lenient than QCMS and Adobe.  Is there a reason to be stricter here?
    1281           0 :     return (SkTAbs(X - kD50_WhitePoint[0]) <= 0.04f) &&
    1282           0 :            (SkTAbs(Y - kD50_WhitePoint[1]) <= 0.04f) &&
    1283           0 :            (SkTAbs(Z - kD50_WhitePoint[2]) <= 0.04f);
    1284             : }
    1285             : 
    1286           0 : static sk_sp<SkColorSpace> make_xyz(const ICCProfileHeader& header, ICCTag* tags, int tagCount,
    1287             :                                     const uint8_t* base, sk_sp<SkData> profileData) {
    1288           0 :     if (kLAB_PCSSpace == header.fPCS) {
    1289           0 :         return nullptr;
    1290             :     }
    1291             : 
    1292             :     // Recognize the rXYZ, gXYZ, and bXYZ tags.
    1293           0 :     const ICCTag* r = ICCTag::Find(tags, tagCount, kTAG_rXYZ);
    1294           0 :     const ICCTag* g = ICCTag::Find(tags, tagCount, kTAG_gXYZ);
    1295           0 :     const ICCTag* b = ICCTag::Find(tags, tagCount, kTAG_bXYZ);
    1296           0 :     if (!r || !g || !b) {
    1297           0 :         return nullptr;
    1298             :     }
    1299             : 
    1300             :     float toXYZ[9];
    1301           0 :     if (!load_xyz(&toXYZ[0], r->addr(base), r->fLength) ||
    1302           0 :         !load_xyz(&toXYZ[3], g->addr(base), g->fLength) ||
    1303           0 :         !load_xyz(&toXYZ[6], b->addr(base), b->fLength))
    1304             :     {
    1305           0 :         return_null("Need valid rgb tags for XYZ space");
    1306             :     }
    1307           0 :     SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
    1308           0 :     mat.set3x3(toXYZ[0], toXYZ[1], toXYZ[2],
    1309             :                toXYZ[3], toXYZ[4], toXYZ[5],
    1310           0 :                toXYZ[6], toXYZ[7], toXYZ[8]);
    1311           0 :     if (!is_close_to_d50(mat)) {
    1312           0 :         return_null("XYZ matrix is not D50");
    1313             :     }
    1314             : 
    1315             :     // If some, but not all, of the gamma tags are missing, assume that all
    1316             :     // gammas are meant to be the same.
    1317           0 :     r = ICCTag::Find(tags, tagCount, kTAG_rTRC);
    1318           0 :     g = ICCTag::Find(tags, tagCount, kTAG_gTRC);
    1319           0 :     b = ICCTag::Find(tags, tagCount, kTAG_bTRC);
    1320           0 :     if ((!r || !g || !b)) {
    1321           0 :         if (!r) {
    1322           0 :             r = g ? g : b;
    1323             :         }
    1324           0 :         if (!g) {
    1325           0 :             g = r ? r : b;
    1326             :         }
    1327           0 :         if (!b) {
    1328           0 :             b = r ? r : g;
    1329             :         }
    1330             :     }
    1331             : 
    1332           0 :     SkGammaNamed gammaNamed = kNonStandard_SkGammaNamed;
    1333           0 :     sk_sp<SkGammas> gammas = nullptr;
    1334             :     size_t tagBytes;
    1335           0 :     if (r && g && b) {
    1336           0 :         if (tag_equals(r, g, base) && tag_equals(g, b, base)) {
    1337           0 :             SkGammas::Data data;
    1338             :             SkColorSpaceTransferFn params;
    1339             :             SkGammas::Type type =
    1340           0 :                     parse_gamma(&data, &params, &tagBytes, r->addr(base), r->fLength);
    1341           0 :             handle_invalid_gamma(&type, &data);
    1342             : 
    1343           0 :             if (SkGammas::Type::kNamed_Type == type) {
    1344           0 :                 gammaNamed = data.fNamed;
    1345             :             } else {
    1346           0 :                 size_t allocSize = sizeof(SkGammas);
    1347           0 :                 if (!safe_add(allocSize, gamma_alloc_size(type, data), &allocSize)) {
    1348           0 :                     return_null("SkGammas struct is too large to allocate");
    1349             :                 }
    1350           0 :                 void* memory = sk_malloc_throw(allocSize);
    1351           0 :                 gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
    1352           0 :                 load_gammas(memory, 0, type, &data, params, r->addr(base));
    1353             : 
    1354           0 :                 for (int i = 0; i < 3; ++i) {
    1355           0 :                     gammas->fType[i] = type;
    1356           0 :                     gammas->fData[i] = data;
    1357             :                 }
    1358             :             }
    1359             :         } else {
    1360           0 :             SkGammas::Data rData;
    1361             :             SkColorSpaceTransferFn rParams;
    1362             :             SkGammas::Type rType =
    1363           0 :                     parse_gamma(&rData, &rParams, &tagBytes, r->addr(base), r->fLength);
    1364           0 :             handle_invalid_gamma(&rType, &rData);
    1365             : 
    1366           0 :             SkGammas::Data gData;
    1367             :             SkColorSpaceTransferFn gParams;
    1368             :             SkGammas::Type gType =
    1369           0 :                     parse_gamma(&gData, &gParams, &tagBytes, g->addr(base), g->fLength);
    1370           0 :             handle_invalid_gamma(&gType, &gData);
    1371             : 
    1372           0 :             SkGammas::Data bData;
    1373             :             SkColorSpaceTransferFn bParams;
    1374             :             SkGammas::Type bType =
    1375           0 :                     parse_gamma(&bData, &bParams, &tagBytes, b->addr(base), b->fLength);
    1376           0 :             handle_invalid_gamma(&bType, &bData);
    1377             : 
    1378           0 :             size_t allocSize = sizeof(SkGammas);
    1379           0 :             if (!safe_add(allocSize, gamma_alloc_size(rType, rData), &allocSize) ||
    1380           0 :                 !safe_add(allocSize, gamma_alloc_size(gType, gData), &allocSize) ||
    1381           0 :                 !safe_add(allocSize, gamma_alloc_size(bType, bData), &allocSize)) {
    1382           0 :                 return_null("SkGammas struct is too large to allocate");
    1383             :             }
    1384           0 :             void* memory = sk_malloc_throw(allocSize);
    1385           0 :             gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
    1386             : 
    1387           0 :             uint32_t offset = 0;
    1388           0 :             gammas->fType[0] = rType;
    1389           0 :             offset += load_gammas(memory, offset, rType, &rData, rParams,
    1390           0 :                                   r->addr(base));
    1391             : 
    1392           0 :             gammas->fType[1] = gType;
    1393           0 :             offset += load_gammas(memory, offset, gType, &gData, gParams,
    1394           0 :                                   g->addr(base));
    1395             : 
    1396           0 :             gammas->fType[2] = bType;
    1397           0 :             load_gammas(memory, offset, bType, &bData, bParams, b->addr(base));
    1398             : 
    1399           0 :             gammas->fData[0] = rData;
    1400           0 :             gammas->fData[1] = gData;
    1401           0 :             gammas->fData[2] = bData;
    1402           0 :         }
    1403             :     } else {
    1404             :         // Guess sRGB if the profile is missing transfer functions.
    1405           0 :         gammaNamed = kSRGB_SkGammaNamed;
    1406             :     }
    1407             : 
    1408           0 :     if (kNonStandard_SkGammaNamed == gammaNamed) {
    1409             :         // It's possible that we'll initially detect non-matching gammas, only for
    1410             :         // them to evaluate to the same named gamma curve.
    1411           0 :         gammaNamed = is_named(gammas);
    1412             :     }
    1413             : 
    1414           0 :     if (kNonStandard_SkGammaNamed == gammaNamed) {
    1415             :         return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed,
    1416           0 :                                                         std::move(gammas),
    1417           0 :                                                         mat, std::move(profileData)));
    1418             :     }
    1419             : 
    1420           0 :     return SkColorSpace_Base::MakeRGB(gammaNamed, mat);
    1421             : }
    1422             : 
    1423           0 : static sk_sp<SkColorSpace> make_gray(const ICCProfileHeader& header, ICCTag* tags, int tagCount,
    1424             :                                      const uint8_t* base, sk_sp<SkData> profileData) {
    1425           0 :     if (kLAB_PCSSpace == header.fPCS) {
    1426           0 :         return nullptr;
    1427             :     }
    1428             : 
    1429           0 :     const ICCTag* grayTRC = ICCTag::Find(tags, tagCount, kTAG_kTRC);
    1430           0 :     if (!grayTRC) {
    1431           0 :         return_null("grayTRC tag required for monochrome profiles.");
    1432             :     }
    1433           0 :     SkGammas::Data data;
    1434             :     SkColorSpaceTransferFn params;
    1435             :     size_t tagBytes;
    1436             :     SkGammas::Type type =
    1437           0 :             parse_gamma(&data, &params, &tagBytes, grayTRC->addr(base), grayTRC->fLength);
    1438           0 :     handle_invalid_gamma(&type, &data);
    1439             : 
    1440           0 :     SkMatrix44 toXYZD50(SkMatrix44::kIdentity_Constructor);
    1441           0 :     toXYZD50.setFloat(0, 0, kWhitePointD50[0]);
    1442           0 :     toXYZD50.setFloat(1, 1, kWhitePointD50[1]);
    1443           0 :     toXYZD50.setFloat(2, 2, kWhitePointD50[2]);
    1444           0 :     if (SkGammas::Type::kNamed_Type == type) {
    1445           0 :         return SkColorSpace_Base::MakeRGB(data.fNamed, toXYZD50);
    1446             :     }
    1447             : 
    1448           0 :     size_t allocSize = sizeof(SkGammas);
    1449           0 :     if (!safe_add(allocSize, gamma_alloc_size(type, data), &allocSize)) {
    1450           0 :         return_null("SkGammas struct is too large to allocate");
    1451             :     }
    1452           0 :     void* memory = sk_malloc_throw(allocSize);
    1453           0 :     sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
    1454           0 :     load_gammas(memory, 0, type, &data, params, grayTRC->addr(base));
    1455           0 :     for (int i = 0; i < 3; ++i) {
    1456           0 :         gammas->fType[i] = type;
    1457           0 :         gammas->fData[i] = data;
    1458             :     }
    1459             : 
    1460             :     return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(kNonStandard_SkGammaNamed,
    1461           0 :                                                     std::move(gammas),
    1462           0 :                                                     toXYZD50, std::move(profileData)));
    1463             : }
    1464             : 
    1465           0 : static sk_sp<SkColorSpace> make_a2b(SkColorSpace_Base::ICCTypeFlag iccType,
    1466             :                                     const ICCProfileHeader& header, ICCTag* tags, int tagCount,
    1467             :                                     const uint8_t* base, sk_sp<SkData> profileData) {
    1468           0 :     const ICCTag* a2b0 = ICCTag::Find(tags, tagCount, kTAG_A2B0);
    1469           0 :     if (a2b0) {
    1470           0 :         const SkColorSpace_A2B::PCS pcs = kXYZ_PCSSpace == header.fPCS
    1471           0 :                                         ? SkColorSpace_A2B::PCS::kXYZ
    1472           0 :                                         : SkColorSpace_A2B::PCS::kLAB;
    1473           0 :         std::vector<SkColorSpace_A2B::Element> elements;
    1474           0 :         if (load_a2b0(&elements, a2b0->addr(base), a2b0->fLength, pcs, iccType)) {
    1475           0 :             return sk_sp<SkColorSpace>(new SkColorSpace_A2B(iccType, std::move(elements),
    1476           0 :                                                             pcs, std::move(profileData)));
    1477             :         }
    1478             :     }
    1479             : 
    1480           0 :     return nullptr;
    1481             : }
    1482             : 
    1483           0 : sk_sp<SkColorSpace> SkColorSpace::MakeICC(const void* input, size_t len) {
    1484           0 :     return SkColorSpace_Base::MakeICC(input, len, SkColorSpace_Base::kRGB_ICCTypeFlag);
    1485             : }
    1486             : 
    1487           0 : sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len,
    1488             :                                                ICCTypeFlag desiredType) {
    1489           0 :     if (!input || len < kICCHeaderSize) {
    1490           0 :         return_null("Data is null or not large enough to contain an ICC profile");
    1491             :     }
    1492             : 
    1493             :     // Create our own copy of the input.
    1494           0 :     void* memory = sk_malloc_throw(len);
    1495           0 :     memcpy(memory, input, len);
    1496           0 :     sk_sp<SkData> profileData = SkData::MakeFromMalloc(memory, len);
    1497           0 :     const uint8_t* base = profileData->bytes();
    1498           0 :     const uint8_t* ptr = base;
    1499             : 
    1500             :     // Read the ICC profile header and check to make sure that it is valid.
    1501             :     ICCProfileHeader header;
    1502           0 :     header.init(ptr, len);
    1503           0 :     if (!header.valid()) {
    1504           0 :         return nullptr;
    1505             :     }
    1506             : 
    1507             :     // Adjust ptr and len before reading the tags.
    1508           0 :     if (len < header.fSize) {
    1509             :         SkColorSpacePrintf("ICC profile might be truncated.\n");
    1510           0 :     } else if (len > header.fSize) {
    1511             :         SkColorSpacePrintf("Caller provided extra data beyond the end of the ICC profile.\n");
    1512           0 :         len = header.fSize;
    1513             :     }
    1514           0 :     ptr += kICCHeaderSize;
    1515           0 :     len -= kICCHeaderSize;
    1516             : 
    1517             :     // Parse tag headers.
    1518           0 :     uint32_t tagCount = header.fTagCount;
    1519             :     SkColorSpacePrintf("ICC profile contains %d tags.\n", tagCount);
    1520           0 :     if (len < kICCTagTableEntrySize * tagCount) {
    1521           0 :         return_null("Not enough input data to read tag table entries");
    1522             :     }
    1523             : 
    1524           0 :     SkAutoTArray<ICCTag> tags(tagCount);
    1525           0 :     for (uint32_t i = 0; i < tagCount; i++) {
    1526           0 :         ptr = tags[i].init(ptr);
    1527             :         SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24) & 0xFF,
    1528             :                 (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >>  8) & 0xFF,
    1529             :                 (tags[i].fSignature >>  0) & 0xFF, tags[i].fOffset, tags[i].fLength);
    1530             : 
    1531           0 :         if (!tags[i].valid(kICCHeaderSize + len)) {
    1532           0 :             return_null("Tag is too large to fit in ICC profile");
    1533             :         }
    1534             :     }
    1535             : 
    1536           0 :     switch (header.fInputColorSpace) {
    1537             :         case kRGB_ColorSpace: {
    1538           0 :             if (!(kRGB_ICCTypeFlag & desiredType)) {
    1539           0 :                 return_null("Provided input color format (RGB) does not match profile.");
    1540             :             }
    1541             : 
    1542             :             sk_sp<SkColorSpace> colorSpace =
    1543           0 :                     make_xyz(header, tags.get(), tagCount, base, profileData);
    1544           0 :             if (colorSpace) {
    1545           0 :                 return colorSpace;
    1546             :             }
    1547             : 
    1548           0 :             desiredType = kRGB_ICCTypeFlag;
    1549           0 :             break;
    1550             :         }
    1551             :         case kGray_ColorSpace: {
    1552           0 :             if (!(kGray_ICCTypeFlag & desiredType)) {
    1553           0 :                 return_null("Provided input color format (Gray) does not match profile.");
    1554             :             }
    1555             : 
    1556           0 :             return make_gray(header, tags.get(), tagCount, base, profileData);
    1557             :         }
    1558             :         case kCMYK_ColorSpace:
    1559           0 :             if (!(kCMYK_ICCTypeFlag & desiredType)) {
    1560           0 :                 return_null("Provided input color format (CMYK) does not match profile.");
    1561             :             }
    1562             : 
    1563           0 :             desiredType = kCMYK_ICCTypeFlag;
    1564           0 :             break;
    1565             :         default:
    1566           0 :             return_null("ICC profile contains unsupported colorspace");
    1567             :     }
    1568             : 
    1569           0 :     return make_a2b(desiredType, header, tags.get(), tagCount, base, profileData);
    1570             : }

Generated by: LCOV version 1.13