• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkAutoMalloc.h"
9 #include "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 
SkICC(sk_sp<SkColorSpace> colorSpace)20 SkICC::SkICC(sk_sp<SkColorSpace> colorSpace)
21     : fColorSpace(std::move(colorSpace))
22 {}
23 
Make(const void * ptr,size_t len)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 
toXYZD50(SkMatrix44 * toXYZD50) const33 bool SkICC::toXYZD50(SkMatrix44* toXYZD50) const {
34     return fColorSpace->toXYZD50(toXYZD50);
35 }
36 
isNumericalTransferFn(SkColorSpaceTransferFn * coeffs) const37 bool SkICC::isNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const {
38     return fColorSpace->isNumericalTransferFn(coeffs);
39 }
40 
41 static const int kDefaultTableSize = 512; // Arbitrary
42 
fn_to_table(float * tablePtr,const SkColorSpaceTransferFn & fn)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 
copy_to_table(float * tablePtr,const SkGammas * gammas,int index)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 
rawTransferFnData(Tables * tables) const63 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.
float_round_to_fixed(float x)287 static SkFixed float_round_to_fixed(float x) {
288     return sk_float_saturate2int((float)floor((double)x * SK_Fixed1 + 0.5));
289 }
290 
write_xyz_tag(uint32_t * ptr,const SkMatrix44 & toXYZ,int col)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 
write_trc_tag(uint32_t * ptr,const SkColorSpaceTransferFn & fn)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 
is_3x3(const SkMatrix44 & toXYZD50)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 
nearly_equal(float x,float y)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 
nearly_equal(const SkColorSpaceTransferFn & u,const SkColorSpaceTransferFn & v)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 
nearly_equal(const SkMatrix44 & toXYZD50,const float standard[9])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.
get_color_profile_description(const SkColorSpaceTransferFn & fn,const SkMatrix44 & toXYZD50)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 
get_color_profile_tag(char dst[kICCDescriptionTagSize],const SkColorSpaceTransferFn & fn,const SkMatrix44 & toXYZD50)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 
SkICCGetColorProfileTag(const SkColorSpaceTransferFn & fn,const SkMatrix44 & toXYZD50)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.
string_copy_ascii_to_utf16be(uint8_t * dst,const char * src,size_t count)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 
WriteToICC(const SkColorSpaceTransferFn & fn,const SkMatrix44 & toXYZD50)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