Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2016 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkAutoMalloc.h"
      9 #include "SkColorSpacePriv.h"
     10 #include "SkColorSpaceXformPriv.h"
     11 #include "SkColorSpace_XYZ.h"
     12 #include "SkEndian.h"
     13 #include "SkFixed.h"
     14 #include "SkICC.h"
     15 #include "SkICCPriv.h"
     16 #include "SkMD5.h"
     17 #include "SkString.h"
     18 #include "SkUtils.h"
     19 
     20 SkICC::SkICC(sk_sp<SkColorSpace> colorSpace)
     21     : fColorSpace(std::move(colorSpace))
     22 {}
     23 
     24 sk_sp<SkICC> SkICC::Make(const void* ptr, size_t len) {
     25     sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeICC(ptr, len);
     26     if (!colorSpace) {
     27         return nullptr;
     28     }
     29 
     30     return sk_sp<SkICC>(new SkICC(std::move(colorSpace)));
     31 }
     32 
     33 bool SkICC::toXYZD50(SkMatrix44* toXYZD50) const {
     34     return fColorSpace->toXYZD50(toXYZD50);
     35 }
     36 
     37 bool SkICC::isNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const {
     38     return fColorSpace->isNumericalTransferFn(coeffs);
     39 }
     40 
     41 static const int kDefaultTableSize = 512; // Arbitrary
     42 
     43 void fn_to_table(float* tablePtr, const SkColorSpaceTransferFn& fn) {
     44     // Y = (aX + b)^g + e  for X >= d
     45     // Y = cX + f          otherwise
     46     for (int i = 0; i < kDefaultTableSize; i++) {
     47         float x = ((float) i) / ((float) (kDefaultTableSize - 1));
     48         if (x >= fn.fD) {
     49             tablePtr[i] = clamp_0_1(powf(fn.fA * x + fn.fB, fn.fG) + fn.fE);
     50         } else {
     51             tablePtr[i] = clamp_0_1(fn.fC * x + fn.fF);
     52         }
     53     }
     54 }
     55 
     56 void copy_to_table(float* tablePtr, const SkGammas* gammas, int index) {
     57     SkASSERT(gammas->isTable(index));
     58     const float* ptr = gammas->table(index);
     59     const size_t bytes = gammas->tableSize(index) * sizeof(float);
     60     memcpy(tablePtr, ptr, bytes);
     61 }
     62 
     63 bool SkICC::rawTransferFnData(Tables* tables) const {
     64     if (!fColorSpace->toXYZD50()) {
     65         return false;  // Can't even dream of handling A2B here...
     66     }
     67     SkColorSpace_XYZ* colorSpace = (SkColorSpace_XYZ*) fColorSpace.get();
     68 
     69     SkColorSpaceTransferFn fn;
     70     if (this->isNumericalTransferFn(&fn)) {
     71         tables->fStorage = SkData::MakeUninitialized(kDefaultTableSize * sizeof(float));
     72         fn_to_table((float*) tables->fStorage->writable_data(), fn);
     73         tables->fRed.fOffset = tables->fGreen.fOffset = tables->fBlue.fOffset = 0;
     74         tables->fRed.fCount = tables->fGreen.fCount = tables->fBlue.fCount = kDefaultTableSize;
     75         return true;
     76     }
     77 
     78     const SkGammas* gammas = colorSpace->gammas();
     79     SkASSERT(gammas);
     80     if (gammas->allChannelsSame()) {
     81         SkASSERT(gammas->isTable(0));
     82         tables->fStorage = SkData::MakeUninitialized(gammas->tableSize(0) * sizeof(float));
     83         copy_to_table((float*) tables->fStorage->writable_data(), gammas, 0);
     84         tables->fRed.fOffset = tables->fGreen.fOffset = tables->fBlue.fOffset = 0;
     85         tables->fRed.fCount = tables->fGreen.fCount = tables->fBlue.fCount = gammas->tableSize(0);
     86         return true;
     87     }
     88 
     89     // Determine the storage size.
     90     size_t storageSize = 0;
     91     for (int i = 0; i < 3; i++) {
     92         if (gammas->isTable(i)) {
     93             storageSize += gammas->tableSize(i) * sizeof(float);
     94         } else {
     95             storageSize += kDefaultTableSize * sizeof(float);
     96         }
     97     }
     98 
     99     // Fill in the tables.
    100     tables->fStorage = SkData::MakeUninitialized(storageSize);
    101     float* ptr = (float*) tables->fStorage->writable_data();
    102     size_t offset = 0;
    103     Channel rgb[3];
    104     for (int i = 0; i < 3; i++) {
    105         if (gammas->isTable(i)) {
    106             copy_to_table(ptr, gammas, i);
    107             rgb[i].fOffset = offset;
    108             rgb[i].fCount = gammas->tableSize(i);
    109             offset += rgb[i].fCount * sizeof(float);
    110             ptr += rgb[i].fCount;
    111             continue;
    112         }
    113 
    114         if (gammas->isNamed(i)) {
    115             SkAssertResult(named_to_parametric(&fn, gammas->data(i).fNamed));
    116         } else if (gammas->isValue(i)) {
    117             value_to_parametric(&fn, gammas->data(i).fValue);
    118         } else {
    119             SkASSERT(gammas->isParametric(i));
    120             fn = gammas->params(i);
    121         }
    122 
    123         fn_to_table(ptr, fn);
    124         rgb[i].fOffset = offset;
    125         rgb[i].fCount = kDefaultTableSize;
    126         offset += kDefaultTableSize * sizeof(float);
    127         ptr += kDefaultTableSize;
    128     }
    129 
    130     tables->fRed = rgb[0];
    131     tables->fGreen = rgb[1];
    132     tables->fBlue = rgb[2];
    133     return true;
    134 }
    135 
    136 ///////////////////////////////////////////////////////////////////////////////////////////////////
    137 
    138 static constexpr char kDescriptionTagBodyPrefix[12] =
    139         { 'G', 'o', 'o', 'g', 'l', 'e', '/', 'S', 'k', 'i', 'a' , '/'};
    140 
    141 static constexpr size_t kICCDescriptionTagSize = 44;
    142 
    143 static_assert(kICCDescriptionTagSize ==
    144               sizeof(kDescriptionTagBodyPrefix) + 2 * sizeof(SkMD5::Digest), "");
    145 static constexpr size_t kDescriptionTagBodySize = kICCDescriptionTagSize * 2;  // ascii->utf16be
    146 
    147 static_assert(SkIsAlign4(kDescriptionTagBodySize), "Description must be aligned to 4-bytes.");
    148 static constexpr uint32_t kDescriptionTagHeader[7] {
    149     SkEndian_SwapBE32(kTAG_TextType),                        // Type signature
    150     0,                                                       // Reserved
    151     SkEndian_SwapBE32(1),                                    // Number of records
    152     SkEndian_SwapBE32(12),                                   // Record size (must be 12)
    153     SkEndian_SwapBE32(SkSetFourByteTag('e', 'n', 'U', 'S')), // English USA
    154     SkEndian_SwapBE32(kDescriptionTagBodySize),              // Length of string
    155     SkEndian_SwapBE32(28),                                   // Offset of string
    156 };
    157 
    158 static constexpr uint32_t kWhitePointTag[5] {
    159     SkEndian_SwapBE32(kXYZ_PCSSpace),
    160     0,
    161     SkEndian_SwapBE32(0x0000f6d6), // X = 0.96420 (D50)
    162     SkEndian_SwapBE32(0x00010000), // Y = 1.00000 (D50)
    163     SkEndian_SwapBE32(0x0000d32d), // Z = 0.82491 (D50)
    164 };
    165 
    166 // Google Inc. 2016 (UTF-16)
    167 static constexpr uint8_t kCopyrightTagBody[] = {
    168         0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00,
    169         0x49, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x32, 0x00, 0x30, 0x00, 0x31,
    170         0x00, 0x36,
    171 };
    172 static_assert(SkIsAlign4(sizeof(kCopyrightTagBody)), "Copyright must be aligned to 4-bytes.");
    173 static constexpr uint32_t kCopyrightTagHeader[7] {
    174     SkEndian_SwapBE32(kTAG_TextType),                        // Type signature
    175     0,                                                       // Reserved
    176     SkEndian_SwapBE32(1),                                    // Number of records
    177     SkEndian_SwapBE32(12),                                   // Record size (must be 12)
    178     SkEndian_SwapBE32(SkSetFourByteTag('e', 'n', 'U', 'S')), // English USA
    179     SkEndian_SwapBE32(sizeof(kCopyrightTagBody)),            // Length of string
    180     SkEndian_SwapBE32(28),                                   // Offset of string
    181 };
    182 
    183 // We will write a profile with the minimum nine required tags.
    184 static constexpr uint32_t kICCNumEntries = 9;
    185 
    186 static constexpr uint32_t kTAG_desc = SkSetFourByteTag('d', 'e', 's', 'c');
    187 static constexpr uint32_t kTAG_desc_Bytes = sizeof(kDescriptionTagHeader) +
    188                                             kDescriptionTagBodySize;
    189 static constexpr uint32_t kTAG_desc_Offset = kICCHeaderSize +
    190                                              kICCNumEntries * kICCTagTableEntrySize;
    191 
    192 static constexpr uint32_t kTAG_XYZ_Bytes = 20;
    193 static constexpr uint32_t kTAG_rXYZ_Offset = kTAG_desc_Offset + kTAG_desc_Bytes;
    194 static constexpr uint32_t kTAG_gXYZ_Offset = kTAG_rXYZ_Offset + kTAG_XYZ_Bytes;
    195 static constexpr uint32_t kTAG_bXYZ_Offset = kTAG_gXYZ_Offset + kTAG_XYZ_Bytes;
    196 
    197 static constexpr uint32_t kTAG_TRC_Bytes = 40;
    198 static constexpr uint32_t kTAG_rTRC_Offset = kTAG_bXYZ_Offset + kTAG_XYZ_Bytes;
    199 static constexpr uint32_t kTAG_gTRC_Offset = kTAG_rTRC_Offset;
    200 static constexpr uint32_t kTAG_bTRC_Offset = kTAG_rTRC_Offset;
    201 
    202 static constexpr uint32_t kTAG_wtpt = SkSetFourByteTag('w', 't', 'p', 't');
    203 static constexpr uint32_t kTAG_wtpt_Offset = kTAG_bTRC_Offset + kTAG_TRC_Bytes;
    204 
    205 static constexpr uint32_t kTAG_cprt = SkSetFourByteTag('c', 'p', 'r', 't');
    206 static constexpr uint32_t kTAG_cprt_Bytes = sizeof(kCopyrightTagHeader) +
    207                                             sizeof(kCopyrightTagBody);
    208 static constexpr uint32_t kTAG_cprt_Offset = kTAG_wtpt_Offset + kTAG_XYZ_Bytes;
    209 
    210 static constexpr uint32_t kICCProfileSize = kTAG_cprt_Offset + kTAG_cprt_Bytes;
    211 
    212 static constexpr uint32_t kICCHeader[kICCHeaderSize / 4] {
    213     SkEndian_SwapBE32(kICCProfileSize),  // Size of the profile
    214     0,                                   // Preferred CMM type (ignored)
    215     SkEndian_SwapBE32(0x02100000),       // Version 2.1
    216     SkEndian_SwapBE32(kDisplay_Profile), // Display device profile
    217     SkEndian_SwapBE32(kRGB_ColorSpace),  // RGB input color space
    218     SkEndian_SwapBE32(kXYZ_PCSSpace),    // XYZ profile connection space
    219     0, 0, 0,                             // Date and time (ignored)
    220     SkEndian_SwapBE32(kACSP_Signature),  // Profile signature
    221     0,                                   // Platform target (ignored)
    222     0x00000000,                          // Flags: not embedded, can be used independently
    223     0,                                   // Device manufacturer (ignored)
    224     0,                                   // Device model (ignored)
    225     0, 0,                                // Device attributes (ignored)
    226     SkEndian_SwapBE32(1),                // Relative colorimetric rendering intent
    227     SkEndian_SwapBE32(0x0000f6d6),       // D50 standard illuminant (X)
    228     SkEndian_SwapBE32(0x00010000),       // D50 standard illuminant (Y)
    229     SkEndian_SwapBE32(0x0000d32d),       // D50 standard illuminant (Z)
    230     0,                                   // Profile creator (ignored)
    231     0, 0, 0, 0,                          // Profile id checksum (ignored)
    232     0, 0, 0, 0, 0, 0, 0,                 // Reserved (ignored)
    233     SkEndian_SwapBE32(kICCNumEntries),   // Number of tags
    234 };
    235 
    236 static constexpr uint32_t kICCTagTable[3 * kICCNumEntries] {
    237     // Profile description
    238     SkEndian_SwapBE32(kTAG_desc),
    239     SkEndian_SwapBE32(kTAG_desc_Offset),
    240     SkEndian_SwapBE32(kTAG_desc_Bytes),
    241 
    242     // rXYZ
    243     SkEndian_SwapBE32(kTAG_rXYZ),
    244     SkEndian_SwapBE32(kTAG_rXYZ_Offset),
    245     SkEndian_SwapBE32(kTAG_XYZ_Bytes),
    246 
    247     // gXYZ
    248     SkEndian_SwapBE32(kTAG_gXYZ),
    249     SkEndian_SwapBE32(kTAG_gXYZ_Offset),
    250     SkEndian_SwapBE32(kTAG_XYZ_Bytes),
    251 
    252     // bXYZ
    253     SkEndian_SwapBE32(kTAG_bXYZ),
    254     SkEndian_SwapBE32(kTAG_bXYZ_Offset),
    255     SkEndian_SwapBE32(kTAG_XYZ_Bytes),
    256 
    257     // rTRC
    258     SkEndian_SwapBE32(kTAG_rTRC),
    259     SkEndian_SwapBE32(kTAG_rTRC_Offset),
    260     SkEndian_SwapBE32(kTAG_TRC_Bytes),
    261 
    262     // gTRC
    263     SkEndian_SwapBE32(kTAG_gTRC),
    264     SkEndian_SwapBE32(kTAG_gTRC_Offset),
    265     SkEndian_SwapBE32(kTAG_TRC_Bytes),
    266 
    267     // bTRC
    268     SkEndian_SwapBE32(kTAG_bTRC),
    269     SkEndian_SwapBE32(kTAG_bTRC_Offset),
    270     SkEndian_SwapBE32(kTAG_TRC_Bytes),
    271 
    272     // White point
    273     SkEndian_SwapBE32(kTAG_wtpt),
    274     SkEndian_SwapBE32(kTAG_wtpt_Offset),
    275     SkEndian_SwapBE32(kTAG_XYZ_Bytes),
    276 
    277     // Copyright
    278     SkEndian_SwapBE32(kTAG_cprt),
    279     SkEndian_SwapBE32(kTAG_cprt_Offset),
    280     SkEndian_SwapBE32(kTAG_cprt_Bytes),
    281 };
    282 
    283 // This is like SkFloatToFixed, but rounds to nearest, preserving as much accuracy as possible
    284 // when going float -> fixed -> float (it has the same accuracy when going fixed -> float -> fixed).
    285 // The use of double is necessary to accomodate the full potential 32-bit mantissa of the 16.16
    286 // SkFixed value, and so avoiding rounding problems with float. Also, see the comment in SkFixed.h.
    287 static SkFixed float_round_to_fixed(float x) {
    288     return sk_float_saturate2int((float)floor((double)x * SK_Fixed1 + 0.5));
    289 }
    290 
    291 static void write_xyz_tag(uint32_t* ptr, const SkMatrix44& toXYZ, int col) {
    292     ptr[0] = SkEndian_SwapBE32(kXYZ_PCSSpace);
    293     ptr[1] = 0;
    294     ptr[2] = SkEndian_SwapBE32(float_round_to_fixed(toXYZ.getFloat(0, col)));
    295     ptr[3] = SkEndian_SwapBE32(float_round_to_fixed(toXYZ.getFloat(1, col)));
    296     ptr[4] = SkEndian_SwapBE32(float_round_to_fixed(toXYZ.getFloat(2, col)));
    297 }
    298 
    299 static void write_trc_tag(uint32_t* ptr, const SkColorSpaceTransferFn& fn) {
    300     ptr[0] = SkEndian_SwapBE32(kTAG_ParaCurveType);
    301     ptr[1] = 0;
    302     ptr[2] = (uint32_t) (SkEndian_SwapBE16(kGABCDEF_ParaCurveType));
    303     ptr[3] = SkEndian_SwapBE32(float_round_to_fixed(fn.fG));
    304     ptr[4] = SkEndian_SwapBE32(float_round_to_fixed(fn.fA));
    305     ptr[5] = SkEndian_SwapBE32(float_round_to_fixed(fn.fB));
    306     ptr[6] = SkEndian_SwapBE32(float_round_to_fixed(fn.fC));
    307     ptr[7] = SkEndian_SwapBE32(float_round_to_fixed(fn.fD));
    308     ptr[8] = SkEndian_SwapBE32(float_round_to_fixed(fn.fE));
    309     ptr[9] = SkEndian_SwapBE32(float_round_to_fixed(fn.fF));
    310 }
    311 
    312 static bool is_3x3(const SkMatrix44& toXYZD50) {
    313     return 0.0f == toXYZD50.get(3, 0) && 0.0f == toXYZD50.get(3, 1) && 0.0f == toXYZD50.get(3, 2) &&
    314            0.0f == toXYZD50.get(0, 3) && 0.0f == toXYZD50.get(1, 3) && 0.0f == toXYZD50.get(2, 3) &&
    315            1.0f == toXYZD50.get(3, 3);
    316 }
    317 
    318 static bool nearly_equal(float x, float y) {
    319     // A note on why I chose this tolerance:  transfer_fn_almost_equal() uses a
    320     // tolerance of 0.001f, which doesn't seem to be enough to distinguish
    321     // between similar transfer functions, for example: gamma2.2 and sRGB.
    322     //
    323     // If the tolerance is 0.0f, then this we can't distinguish between two
    324     // different encodings of what is clearly the same colorspace.  Some
    325     // experimentation with example files lead to this number:
    326     static constexpr float kTolerance = 1.0f / (1 << 11);
    327     return ::fabsf(x - y) <= kTolerance;
    328 }
    329 
    330 static bool nearly_equal(const SkColorSpaceTransferFn& u,
    331                          const SkColorSpaceTransferFn& v) {
    332     return nearly_equal(u.fG, v.fG)
    333         && nearly_equal(u.fA, v.fA)
    334         && nearly_equal(u.fB, v.fB)
    335         && nearly_equal(u.fC, v.fC)
    336         && nearly_equal(u.fD, v.fD)
    337         && nearly_equal(u.fE, v.fE)
    338         && nearly_equal(u.fF, v.fF);
    339 }
    340 
    341 static bool nearly_equal(const SkMatrix44& toXYZD50, const float standard[9]) {
    342     return nearly_equal(toXYZD50.getFloat(0, 0), standard[0])
    343         && nearly_equal(toXYZD50.getFloat(0, 1), standard[1])
    344         && nearly_equal(toXYZD50.getFloat(0, 2), standard[2])
    345         && nearly_equal(toXYZD50.getFloat(1, 0), standard[3])
    346         && nearly_equal(toXYZD50.getFloat(1, 1), standard[4])
    347         && nearly_equal(toXYZD50.getFloat(1, 2), standard[5])
    348         && nearly_equal(toXYZD50.getFloat(2, 0), standard[6])
    349         && nearly_equal(toXYZD50.getFloat(2, 1), standard[7])
    350         && nearly_equal(toXYZD50.getFloat(2, 2), standard[8])
    351         && nearly_equal(toXYZD50.getFloat(0, 3), 0.0f)
    352         && nearly_equal(toXYZD50.getFloat(1, 3), 0.0f)
    353         && nearly_equal(toXYZD50.getFloat(2, 3), 0.0f)
    354         && nearly_equal(toXYZD50.getFloat(3, 0), 0.0f)
    355         && nearly_equal(toXYZD50.getFloat(3, 1), 0.0f)
    356         && nearly_equal(toXYZD50.getFloat(3, 2), 0.0f)
    357         && nearly_equal(toXYZD50.getFloat(3, 3), 1.0f);
    358 }
    359 
    360 // Return nullptr if the color profile doen't have a special name.
    361 const char* get_color_profile_description(const SkColorSpaceTransferFn& fn,
    362                                           const SkMatrix44& toXYZD50) {
    363     bool srgb_xfer = nearly_equal(fn, gSRGB_TransferFn);
    364     bool srgb_gamut = nearly_equal(toXYZD50, gSRGB_toXYZD50);
    365     if (srgb_xfer && srgb_gamut) {
    366         return "sRGB";
    367     }
    368     bool line_xfer = nearly_equal(fn, gLinear_TransferFn);
    369     if (line_xfer && srgb_gamut) {
    370         return "Linear Transfer with sRGB Gamut";
    371     }
    372     bool twoDotTwo = nearly_equal(fn, g2Dot2_TransferFn);
    373     if (twoDotTwo && srgb_gamut) {
    374         return "2.2 Transfer with sRGB Gamut";
    375     }
    376     if (twoDotTwo && nearly_equal(toXYZD50, gAdobeRGB_toXYZD50)) {
    377         return "AdobeRGB";
    378     }
    379     bool dcip3_gamut = nearly_equal(toXYZD50, gDCIP3_toXYZD50);
    380     if (srgb_xfer || line_xfer) {
    381         if (srgb_xfer && dcip3_gamut) {
    382             return "sRGB Transfer with DCI-P3 Gamut";
    383         }
    384         if (line_xfer && dcip3_gamut) {
    385             return "Linear Transfer with DCI-P3 Gamut";
    386         }
    387         bool rec2020 = nearly_equal(toXYZD50, gRec2020_toXYZD50);
    388         if (srgb_xfer && rec2020) {
    389             return "sRGB Transfer with Rec-BT-2020 Gamut";
    390         }
    391         if (line_xfer && rec2020) {
    392             return "Linear Transfer with Rec-BT-2020 Gamut";
    393         }
    394     }
    395     if (dcip3_gamut && nearly_equal(fn, gDCIP3_TransferFn)) {
    396         return "DCI-P3";
    397     }
    398     return nullptr;
    399 }
    400 
    401 static void get_color_profile_tag(char dst[kICCDescriptionTagSize],
    402                                  const SkColorSpaceTransferFn& fn,
    403                                  const SkMatrix44& toXYZD50) {
    404     SkASSERT(dst);
    405     if (const char* description = get_color_profile_description(fn, toXYZD50)) {
    406         SkASSERT(strlen(description) < kICCDescriptionTagSize);
    407         strncpy(dst, description, kICCDescriptionTagSize);
    408         // "If the length of src is less than n, strncpy() writes additional
    409         // null bytes to dest to ensure that a total of n bytes are written."
    410     } else {
    411         strncpy(dst, kDescriptionTagBodyPrefix, sizeof(kDescriptionTagBodyPrefix));
    412         SkMD5 md5;
    413         for (int i = 0; i < 3; ++i) {
    414             for (int j = 0; j < 3; ++j) {
    415                 float value = toXYZD50.getFloat(i,j);
    416                 md5.write(&value, sizeof(value));
    417             }
    418         }
    419         static_assert(sizeof(fn) == sizeof(float) * 7, "packed");
    420         md5.write(&fn, sizeof(fn));
    421         SkMD5::Digest digest;
    422         md5.finish(digest);
    423         char* ptr = dst + sizeof(kDescriptionTagBodyPrefix);
    424         for (unsigned i = 0; i < sizeof(SkMD5::Digest); ++i) {
    425             uint8_t byte = digest.data[i];
    426             *ptr++ = SkHexadecimalDigits::gUpper[byte >> 4];
    427             *ptr++ = SkHexadecimalDigits::gUpper[byte & 0xF];
    428         }
    429         SkASSERT(ptr == dst + kICCDescriptionTagSize);
    430     }
    431 }
    432 
    433 SkString SkICCGetColorProfileTag(const SkColorSpaceTransferFn& fn,
    434                                  const SkMatrix44& toXYZD50) {
    435     char tag[kICCDescriptionTagSize];
    436     get_color_profile_tag(tag, fn, toXYZD50);
    437     size_t len = kICCDescriptionTagSize;
    438     while (len > 0 && tag[len - 1] == '\0') {
    439         --len;  // tag is padded out with zeros
    440     }
    441     SkASSERT(len != 0);
    442     return SkString(tag, len);
    443 }
    444 
    445 // returns pointer just beyond where we just wrote.
    446 static uint8_t* string_copy_ascii_to_utf16be(uint8_t* dst, const char* src, size_t count) {
    447     while (count-- > 0) {
    448         *dst++ = 0;
    449         *dst++ = (uint8_t)(*src++);
    450     }
    451     return dst;
    452 }
    453 
    454 sk_sp<SkData> SkICC::WriteToICC(const SkColorSpaceTransferFn& fn, const SkMatrix44& toXYZD50) {
    455     if (!is_3x3(toXYZD50) || !is_valid_transfer_fn(fn)) {
    456         return nullptr;
    457     }
    458 
    459     SkAutoMalloc profile(kICCProfileSize);
    460     uint8_t* ptr = (uint8_t*) profile.get();
    461 
    462     // Write profile header
    463     memcpy(ptr, kICCHeader, sizeof(kICCHeader));
    464     ptr += sizeof(kICCHeader);
    465 
    466     // Write tag table
    467     memcpy(ptr, kICCTagTable, sizeof(kICCTagTable));
    468     ptr += sizeof(kICCTagTable);
    469 
    470     // Write profile description tag
    471     memcpy(ptr, kDescriptionTagHeader, sizeof(kDescriptionTagHeader));
    472     ptr += sizeof(kDescriptionTagHeader);
    473     {
    474         char colorProfileTag[kICCDescriptionTagSize];
    475         get_color_profile_tag(colorProfileTag, fn, toXYZD50);
    476         ptr = string_copy_ascii_to_utf16be(ptr, colorProfileTag, kICCDescriptionTagSize);
    477     }
    478 
    479     // Write XYZ tags
    480     write_xyz_tag((uint32_t*) ptr, toXYZD50, 0);
    481     ptr += kTAG_XYZ_Bytes;
    482     write_xyz_tag((uint32_t*) ptr, toXYZD50, 1);
    483     ptr += kTAG_XYZ_Bytes;
    484     write_xyz_tag((uint32_t*) ptr, toXYZD50, 2);
    485     ptr += kTAG_XYZ_Bytes;
    486 
    487     // Write TRC tag
    488     write_trc_tag((uint32_t*) ptr, fn);
    489     ptr += kTAG_TRC_Bytes;
    490 
    491     // Write white point tag (must be D50)
    492     memcpy(ptr, kWhitePointTag, sizeof(kWhitePointTag));
    493     ptr += sizeof(kWhitePointTag);
    494 
    495     // Write copyright tag
    496     memcpy(ptr, kCopyrightTagHeader, sizeof(kCopyrightTagHeader));
    497     ptr += sizeof(kCopyrightTagHeader);
    498     memcpy(ptr, kCopyrightTagBody, sizeof(kCopyrightTagBody));
    499     ptr += sizeof(kCopyrightTagBody);
    500 
    501     SkASSERT(kICCProfileSize == ptr - (uint8_t*) profile.get());
    502     return SkData::MakeFromMalloc(profile.release(), kICCProfileSize);
    503 }
    504