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