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, ¶ms, 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], ¶ms[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], ¶ms[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, ¶ms, &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, ¶ms, &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 : }
|