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