Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(48)

Side by Side Diff: src/core/SkColorSpace.cpp

Issue 2101823003: Move SkColorSpace ICC parsing/writing code to its own file (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « gyp/core.gypi ('k') | src/core/SkColorSpacePriv.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2016 Google Inc. 2 * Copyright 2016 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkColorSpace.h" 8 #include "SkColorSpace.h"
9 #include "SkColorSpace_Base.h" 9 #include "SkColorSpace_Base.h"
10 #include "SkEndian.h" 10 #include "SkColorSpacePriv.h"
11 #include "SkOnce.h" 11 #include "SkOnce.h"
12 #include "SkReadBuffer.h"
13 #include "SkWriteBuffer.h"
14
15 #define SkColorSpacePrintf(...)
16
17 static bool color_space_almost_equal(float a, float b) {
18 return SkTAbs(a - b) < 0.01f;
19 }
20
21 //////////////////////////////////////////////////////////////////////////////// //////////////////
22 12
23 SkColorSpace::SkColorSpace(GammaNamed gammaNamed, const SkMatrix44& toXYZD50, Na med named) 13 SkColorSpace::SkColorSpace(GammaNamed gammaNamed, const SkMatrix44& toXYZD50, Na med named)
24 : fGammaNamed(gammaNamed) 14 : fGammaNamed(gammaNamed)
25 , fToXYZD50(toXYZD50) 15 , fToXYZD50(toXYZD50)
26 , fNamed(named) 16 , fNamed(named)
27 {} 17 {}
28 18
29 SkColorSpace_Base::SkColorSpace_Base(GammaNamed gammaNamed, const SkMatrix44& to XYZD50, Named named) 19 SkColorSpace_Base::SkColorSpace_Base(GammaNamed gammaNamed, const SkMatrix44& to XYZD50, Named named)
30 : INHERITED(gammaNamed, toXYZD50, named) 20 : INHERITED(gammaNamed, toXYZD50, named)
31 , fGammas(nullptr) 21 , fGammas(nullptr)
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
70 color_space_almost_equal(toXYZD50.getFloat(2, 2), standard[8]) && 60 color_space_almost_equal(toXYZD50.getFloat(2, 2), standard[8]) &&
71 color_space_almost_equal(toXYZD50.getFloat(0, 3), 0.0f) && 61 color_space_almost_equal(toXYZD50.getFloat(0, 3), 0.0f) &&
72 color_space_almost_equal(toXYZD50.getFloat(1, 3), 0.0f) && 62 color_space_almost_equal(toXYZD50.getFloat(1, 3), 0.0f) &&
73 color_space_almost_equal(toXYZD50.getFloat(2, 3), 0.0f) && 63 color_space_almost_equal(toXYZD50.getFloat(2, 3), 0.0f) &&
74 color_space_almost_equal(toXYZD50.getFloat(3, 0), 0.0f) && 64 color_space_almost_equal(toXYZD50.getFloat(3, 0), 0.0f) &&
75 color_space_almost_equal(toXYZD50.getFloat(3, 1), 0.0f) && 65 color_space_almost_equal(toXYZD50.getFloat(3, 1), 0.0f) &&
76 color_space_almost_equal(toXYZD50.getFloat(3, 2), 0.0f) && 66 color_space_almost_equal(toXYZD50.getFloat(3, 2), 0.0f) &&
77 color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f); 67 color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f);
78 } 68 }
79 69
80 static void set_gamma_value(SkGammaCurve* gamma, float value) {
81 if (color_space_almost_equal(2.2f, value)) {
82 gamma->fNamed = SkColorSpace::k2Dot2Curve_GammaNamed;
83 } else if (color_space_almost_equal(1.0f, value)) {
84 gamma->fNamed = SkColorSpace::kLinear_GammaNamed;
85 } else if (color_space_almost_equal(0.0f, value)) {
86 SkColorSpacePrintf("Treating invalid zero gamma as linear.");
87 gamma->fNamed = SkColorSpace::kLinear_GammaNamed;
88 } else {
89 gamma->fValue = value;
90 }
91 }
92
93 sk_sp<SkColorSpace> SkColorSpace_Base::NewRGB(float values[3], const SkMatrix44& toXYZD50) { 70 sk_sp<SkColorSpace> SkColorSpace_Base::NewRGB(float values[3], const SkMatrix44& toXYZD50) {
94 SkGammaCurve curves[3]; 71 SkGammaCurve curves[3];
95 set_gamma_value(&curves[0], values[0]); 72 set_gamma_value(&curves[0], values[0]);
96 set_gamma_value(&curves[1], values[1]); 73 set_gamma_value(&curves[1], values[1]);
97 set_gamma_value(&curves[2], values[2]); 74 set_gamma_value(&curves[2], values[2]);
98 75
99 GammaNamed gammaNamed = SkGammas::Named(curves); 76 GammaNamed gammaNamed = SkGammas::Named(curves);
100 if (kNonStandard_GammaNamed == gammaNamed) { 77 if (kNonStandard_GammaNamed == gammaNamed) {
101 sk_sp<SkGammas> gammas(new SkGammas(std::move(curves[0]), std::move(curv es[1]), 78 sk_sp<SkGammas> gammas(new SkGammas(std::move(curves[0]), std::move(curv es[1]),
102 std::move(curves[2]))); 79 std::move(curves[2])));
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 return sk_ref_sp(adobeRGB); 134 return sk_ref_sp(adobeRGB);
158 } 135 }
159 default: 136 default:
160 break; 137 break;
161 } 138 }
162 return nullptr; 139 return nullptr;
163 } 140 }
164 141
165 //////////////////////////////////////////////////////////////////////////////// /////////////////// 142 //////////////////////////////////////////////////////////////////////////////// ///////////////////
166 143
167 #include "SkFixed.h"
168 #include "SkTemplates.h"
169
170 #define return_if_false(pred, msg) \
171 do { \
172 if (!(pred)) { \
173 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \
174 return false; \
175 } \
176 } while (0)
177
178 #define return_null(msg) \
179 do { \
180 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \
181 return nullptr; \
182 } while (0)
183
184 static uint16_t read_big_endian_short(const uint8_t* ptr) {
185 return ptr[0] << 8 | ptr[1];
186 }
187
188 static uint32_t read_big_endian_uint(const uint8_t* ptr) {
189 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
190 }
191
192 static int32_t read_big_endian_int(const uint8_t* ptr) {
193 return (int32_t) read_big_endian_uint(ptr);
194 }
195
196 // This is equal to the header size according to the ICC specification (128)
197 // plus the size of the tag count (4). We include the tag count since we
198 // always require it to be present anyway.
199 static constexpr size_t kICCHeaderSize = 132;
200
201 // Contains a signature (4), offset (4), and size (4).
202 static constexpr size_t kICCTagTableEntrySize = 12;
203
204 static constexpr uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' ' );
205 static constexpr uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r' );
206 static constexpr uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r' );
207 static constexpr uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r' );
208 static constexpr uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' ' );
209 static constexpr uint32_t kACSP_Signature = SkSetFourByteTag('a', 'c', 's', 'p' );
210
211 struct ICCProfileHeader {
212 uint32_t fSize;
213
214 // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.).
215 // We're always going to use this one.
216 uint32_t fCMMType_ignored;
217
218 uint32_t fVersion;
219 uint32_t fProfileClass;
220 uint32_t fInputColorSpace;
221 uint32_t fPCS;
222 uint32_t fDateTime_ignored[3];
223 uint32_t fSignature;
224
225 // Indicates the platform that this profile was created for (ex: Apple, Micr osoft). This
226 // doesn't really matter to us.
227 uint32_t fPlatformTarget_ignored;
228
229 // Flags can indicate:
230 // (1) Whether this profile was embedded in a file. This flag is consistent ly wrong.
231 // Ex: The profile came from a file but indicates that it did not.
232 // (2) Whether we are allowed to use the profile independently of the color data. If set,
233 // this may allow us to use the embedded profile for testing separate fr om the original
234 // image.
235 uint32_t fFlags_ignored;
236
237 // We support many output devices. It doesn't make sense to think about the attributes of
238 // the device in the context of the image profile.
239 uint32_t fDeviceManufacturer_ignored;
240 uint32_t fDeviceModel_ignored;
241 uint32_t fDeviceAttributes_ignored[2];
242
243 uint32_t fRenderingIntent;
244 int32_t fIlluminantXYZ[3];
245
246 // We don't care who created the profile.
247 uint32_t fCreator_ignored;
248
249 // This is an MD5 checksum. Could be useful for checking if profiles are eq ual.
250 uint32_t fProfileId_ignored[4];
251
252 // Reserved for future use.
253 uint32_t fReserved_ignored[7];
254
255 uint32_t fTagCount;
256
257 void init(const uint8_t* src, size_t len) {
258 SkASSERT(kICCHeaderSize == sizeof(*this));
259
260 uint32_t* dst = (uint32_t*) this;
261 for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) {
262 dst[i] = read_big_endian_uint(src);
263 }
264 }
265
266 bool valid() const {
267 return_if_false(fSize >= kICCHeaderSize, "Size is too small");
268
269 uint8_t majorVersion = fVersion >> 24;
270 return_if_false(majorVersion <= 4, "Unsupported version");
271
272 // These are the three basic classes of profiles that we might expect to see embedded
273 // in images. Four additional classes exist, but they generally are use d as a convenient
274 // way for CMMs to store calculated transforms.
275 return_if_false(fProfileClass == kDisplay_Profile ||
276 fProfileClass == kInput_Profile ||
277 fProfileClass == kOutput_Profile,
278 "Unsupported profile");
279
280 // TODO (msarett):
281 // All the profiles we've tested so far use RGB as the input color space .
282 return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space");
283
284 // TODO (msarett):
285 // All the profiles we've tested so far use XYZ as the profile connectio n space.
286 return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space");
287
288 return_if_false(fSignature == kACSP_Signature, "Bad signature");
289
290 // TODO (msarett):
291 // Should we treat different rendering intents differently?
292 // Valid rendering intents include kPerceptual (0), kRelative (1),
293 // kSaturation (2), and kAbsolute (3).
294 return_if_false(fRenderingIntent <= 3, "Bad rendering intent");
295
296 return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0 ]), 0.96420f) &&
297 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1 ]), 1.00000f) &&
298 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2 ]), 0.82491f),
299 "Illuminant must be D50");
300
301 return_if_false(fTagCount <= 100, "Too many tags");
302
303 return true;
304 }
305 };
306
307 template <class T>
308 static bool safe_add(T arg1, T arg2, size_t* result) {
309 SkASSERT(arg1 >= 0);
310 SkASSERT(arg2 >= 0);
311 if (arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) {
312 T sum = arg1 + arg2;
313 if (sum <= std::numeric_limits<size_t>::max()) {
314 *result = static_cast<size_t>(sum);
315 return true;
316 }
317 }
318 return false;
319 }
320
321 static bool safe_mul(uint32_t arg1, uint32_t arg2, uint32_t* result) {
322 uint64_t product64 = (uint64_t) arg1 * (uint64_t) arg2;
323 uint32_t product32 = (uint32_t) product64;
324 if (product32 != product64) {
325 return false;
326 }
327
328 *result = product32;
329 return true;
330 }
331
332 struct ICCTag {
333 uint32_t fSignature;
334 uint32_t fOffset;
335 uint32_t fLength;
336
337 const uint8_t* init(const uint8_t* src) {
338 fSignature = read_big_endian_uint(src);
339 fOffset = read_big_endian_uint(src + 4);
340 fLength = read_big_endian_uint(src + 8);
341 return src + 12;
342 }
343
344 bool valid(size_t len) {
345 size_t tagEnd;
346 return_if_false(safe_add(fOffset, fLength, &tagEnd),
347 "Tag too large, overflows integer addition");
348 return_if_false(tagEnd <= len, "Tag too large for ICC profile");
349 return true;
350 }
351
352 const uint8_t* addr(const uint8_t* src) const {
353 return src + fOffset;
354 }
355
356 static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature ) {
357 for (int i = 0; i < count; ++i) {
358 if (tags[i].fSignature == signature) {
359 return &tags[i];
360 }
361 }
362 return nullptr;
363 }
364 };
365
366 static constexpr uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z');
367 static constexpr uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z');
368 static constexpr uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z');
369 static constexpr uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C');
370 static constexpr uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C');
371 static constexpr uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C');
372 static constexpr uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0');
373
374 static bool load_xyz(float dst[3], const uint8_t* src, size_t len) {
375 if (len < 20) {
376 SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len);
377 return false;
378 }
379
380 dst[0] = SkFixedToFloat(read_big_endian_int(src + 8));
381 dst[1] = SkFixedToFloat(read_big_endian_int(src + 12));
382 dst[2] = SkFixedToFloat(read_big_endian_int(src + 16));
383 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]);
384 return true;
385 }
386
387 static constexpr uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', ' v');
388 static constexpr uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', ' a');
389
390 static bool load_gammas(SkGammaCurve* gammas, uint32_t numGammas, const uint8_t* src, size_t len) {
391 for (uint32_t i = 0; i < numGammas; i++) {
392 if (len < 12) {
393 // FIXME (msarett):
394 // We could potentially return false here after correctly parsing *s ome* of the
395 // gammas correctly. Should we somehow try to indicate a partial su ccess?
396 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
397 return false;
398 }
399
400 // We need to count the number of bytes in the tag, so we are able to mo ve to the
401 // next tag on the next loop iteration.
402 size_t tagBytes;
403
404 uint32_t type = read_big_endian_uint(src);
405 switch (type) {
406 case kTAG_CurveType: {
407 uint32_t count = read_big_endian_uint(src + 8);
408
409 // tagBytes = 12 + 2 * count
410 // We need to do safe addition here to avoid integer overflow.
411 if (!safe_add(count, count, &tagBytes) ||
412 !safe_add((size_t) 12, tagBytes, &tagBytes))
413 {
414 SkColorSpacePrintf("Invalid gamma count");
415 return false;
416 }
417
418 if (0 == count) {
419 // Some tags require a gamma curve, but the author doesn't a ctually want
420 // to transform the data. In this case, it is common to see a curve with
421 // a count of 0.
422 gammas[i].fNamed = SkColorSpace::kLinear_GammaNamed;
423 break;
424 } else if (len < tagBytes) {
425 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len) ;
426 return false;
427 }
428
429 const uint16_t* table = (const uint16_t*) (src + 12);
430 if (1 == count) {
431 // The table entry is the gamma (with a bias of 256).
432 float value = (read_big_endian_short((const uint8_t*) table) ) / 256.0f;
433 set_gamma_value(&gammas[i], value);
434 SkColorSpacePrintf("gamma %g\n", value);
435 break;
436 }
437
438 // Check for frequently occurring sRGB curves.
439 // We do this by sampling a few values and see if they match our expectation.
440 // A more robust solution would be to compare each value in this curve against
441 // an sRGB curve to see if we remain below an error threshold. At this time,
442 // we haven't seen any images in the wild that make this kind of
443 // calculation necessary. We encounter identical gamma curves o ver and
444 // over again, but relatively few variations.
445 if (1024 == count) {
446 // The magic values were chosen because they match a very co mmon sRGB
447 // gamma table and the less common Canon sRGB gamma table (w hich use
448 // different rounding rules).
449 if (0 == read_big_endian_short((const uint8_t*) &table[0]) & &
450 3366 == read_big_endian_short((const uint8_t*) &tabl e[257]) &&
451 14116 == read_big_endian_short((const uint8_t*) &tab le[513]) &&
452 34318 == read_big_endian_short((const uint8_t*) &tab le[768]) &&
453 65535 == read_big_endian_short((const uint8_t*) &tab le[1023])) {
454 gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed;
455 break;
456 }
457 } else if (26 == count) {
458 // The magic values were chosen because they match a very co mmon sRGB
459 // gamma table.
460 if (0 == read_big_endian_short((const uint8_t*) &table[0]) & &
461 3062 == read_big_endian_short((const uint8_t*) &tabl e[6]) &&
462 12824 == read_big_endian_short((const uint8_t*) &tab le[12]) &&
463 31237 == read_big_endian_short((const uint8_t*) &tab le[18]) &&
464 65535 == read_big_endian_short((const uint8_t*) &tab le[25])) {
465 gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed;
466 break;
467 }
468 } else if (4096 == count) {
469 // The magic values were chosen because they match Nikon, Ep son, and
470 // LCMS sRGB gamma tables (all of which use different roundi ng rules).
471 if (0 == read_big_endian_short((const uint8_t*) &table[0]) & &
472 950 == read_big_endian_short((const uint8_t*) &table [515]) &&
473 3342 == read_big_endian_short((const uint8_t*) &tabl e[1025]) &&
474 14079 == read_big_endian_short((const uint8_t*) &tab le[2051]) &&
475 65535 == read_big_endian_short((const uint8_t*) &tab le[4095])) {
476 gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed;
477 break;
478 }
479 }
480
481 // Otherwise, fill in the interpolation table.
482 gammas[i].fTableSize = count;
483 gammas[i].fTable = std::unique_ptr<float[]>(new float[count]);
484 for (uint32_t j = 0; j < count; j++) {
485 gammas[i].fTable[j] =
486 (read_big_endian_short((const uint8_t*) &table[j])) / 65535.0f;
487 }
488 break;
489 }
490 case kTAG_ParaCurveType: {
491 enum ParaCurveType {
492 kExponential_ParaCurveType = 0,
493 kGAB_ParaCurveType = 1,
494 kGABC_ParaCurveType = 2,
495 kGABDE_ParaCurveType = 3,
496 kGABCDEF_ParaCurveType = 4,
497 };
498
499 // Determine the format of the parametric curve tag.
500 uint16_t format = read_big_endian_short(src + 8);
501 if (kExponential_ParaCurveType == format) {
502 tagBytes = 12 + 4;
503 if (len < tagBytes) {
504 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
505 return false;
506 }
507
508 // Y = X^g
509 int32_t g = read_big_endian_int(src + 12);
510 set_gamma_value(&gammas[i], SkFixedToFloat(g));
511 } else {
512 // Here's where the real parametric gammas start. There are many
513 // permutations of the same equations.
514 //
515 // Y = (aX + b)^g + c for X >= d
516 // Y = eX + f otherwise
517 //
518 // We will fill in with zeros as necessary to always match t he above form.
519 float g = 0.0f, a = 0.0f, b = 0.0f, c = 0.0f, d = 0.0f, e = 0.0f, f = 0.0f;
520 switch(format) {
521 case kGAB_ParaCurveType: {
522 tagBytes = 12 + 12;
523 if (len < tagBytes) {
524 SkColorSpacePrintf("gamma tag is too small (%d b ytes)", len);
525 return false;
526 }
527
528 // Y = (aX + b)^g for X >= -b/a
529 // Y = 0 otherwise
530 g = SkFixedToFloat(read_big_endian_int(src + 12));
531 a = SkFixedToFloat(read_big_endian_int(src + 16));
532 if (0.0f == a) {
533 return false;
534 }
535
536 b = SkFixedToFloat(read_big_endian_int(src + 20));
537 d = -b / a;
538 break;
539 }
540 case kGABC_ParaCurveType:
541 tagBytes = 12 + 16;
542 if (len < tagBytes) {
543 SkColorSpacePrintf("gamma tag is too small (%d b ytes)", len);
544 return false;
545 }
546
547 // Y = (aX + b)^g + c for X >= -b/a
548 // Y = c otherwise
549 g = SkFixedToFloat(read_big_endian_int(src + 12));
550 a = SkFixedToFloat(read_big_endian_int(src + 16));
551 if (0.0f == a) {
552 return false;
553 }
554
555 b = SkFixedToFloat(read_big_endian_int(src + 20));
556 c = SkFixedToFloat(read_big_endian_int(src + 24));
557 d = -b / a;
558 f = c;
559 break;
560 case kGABDE_ParaCurveType:
561 tagBytes = 12 + 20;
562 if (len < tagBytes) {
563 SkColorSpacePrintf("gamma tag is too small (%d b ytes)", len);
564 return false;
565 }
566
567 // Y = (aX + b)^g for X >= d
568 // Y = cX otherwise
569 g = SkFixedToFloat(read_big_endian_int(src + 12));
570 a = SkFixedToFloat(read_big_endian_int(src + 16));
571 b = SkFixedToFloat(read_big_endian_int(src + 20));
572 d = SkFixedToFloat(read_big_endian_int(src + 28));
573 e = SkFixedToFloat(read_big_endian_int(src + 24));
574 break;
575 case kGABCDEF_ParaCurveType:
576 tagBytes = 12 + 28;
577 if (len < tagBytes) {
578 SkColorSpacePrintf("gamma tag is too small (%d b ytes)", len);
579 return false;
580 }
581
582 // Y = (aX + b)^g + c for X >= d
583 // Y = eX + f otherwise
584 // NOTE: The ICC spec writes "cX" in place of "eX" b ut I think
585 // it's a typo.
586 g = SkFixedToFloat(read_big_endian_int(src + 12));
587 a = SkFixedToFloat(read_big_endian_int(src + 16));
588 b = SkFixedToFloat(read_big_endian_int(src + 20));
589 c = SkFixedToFloat(read_big_endian_int(src + 24));
590 d = SkFixedToFloat(read_big_endian_int(src + 28));
591 e = SkFixedToFloat(read_big_endian_int(src + 32));
592 f = SkFixedToFloat(read_big_endian_int(src + 36));
593 break;
594 default:
595 SkColorSpacePrintf("Invalid parametric curve type\n" );
596 return false;
597 }
598
599 // Recognize and simplify a very common parametric represent ation of sRGB gamma.
600 if (color_space_almost_equal(0.9479f, a) &&
601 color_space_almost_equal(0.0521f, b) &&
602 color_space_almost_equal(0.0000f, c) &&
603 color_space_almost_equal(0.0405f, d) &&
604 color_space_almost_equal(0.0774f, e) &&
605 color_space_almost_equal(0.0000f, f) &&
606 color_space_almost_equal(2.4000f, g)) {
607 gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed;
608 } else {
609 // Fail on invalid gammas.
610 if (d <= 0.0f) {
611 // Y = (aX + b)^g + c for always
612 if (0.0f == a || 0.0f == g) {
613 SkColorSpacePrintf("A or G is zero, constant gam ma function "
614 "is nonsense");
615 return false;
616 }
617 } else if (d >= 1.0f) {
618 // Y = eX + f for always
619 if (0.0f == e) {
620 SkColorSpacePrintf("E is zero, constant gamma fu nction is "
621 "nonsense");
622 return false;
623 }
624 } else if ((0.0f == a || 0.0f == g) && 0.0f == e) {
625 SkColorSpacePrintf("A or G, and E are zero, constant gamma function "
626 "is nonsense");
627 return false;
628 }
629
630 gammas[i].fG = g;
631 gammas[i].fA = a;
632 gammas[i].fB = b;
633 gammas[i].fC = c;
634 gammas[i].fD = d;
635 gammas[i].fE = e;
636 gammas[i].fF = f;
637 }
638 }
639
640 break;
641 }
642 default:
643 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
644 return false;
645 }
646
647 // Ensure that we have successfully read a gamma representation.
648 SkASSERT(gammas[i].isNamed() || gammas[i].isValue() || gammas[i].isTable () ||
649 gammas[i].isParametric());
650
651 // Adjust src and len if there is another gamma curve to load.
652 if (i != numGammas - 1) {
653 // Each curve is padded to 4-byte alignment.
654 tagBytes = SkAlign4(tagBytes);
655 if (len < tagBytes) {
656 return false;
657 }
658
659 src += tagBytes;
660 len -= tagBytes;
661 }
662 }
663
664 return true;
665 }
666
667 static constexpr uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' ');
668
669 bool load_color_lut(SkColorLookUpTable* colorLUT, uint32_t inputChannels, uint32 _t outputChannels,
670 const uint8_t* src, size_t len) {
671 // 16 bytes reserved for grid points, 2 for precision, 2 for padding.
672 // The color LUT data follows after this header.
673 static constexpr uint32_t kColorLUTHeaderSize = 20;
674 if (len < kColorLUTHeaderSize) {
675 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len);
676 return false;
677 }
678 size_t dataLen = len - kColorLUTHeaderSize;
679
680 SkASSERT(3 == inputChannels && 3 == outputChannels);
681 colorLUT->fInputChannels = inputChannels;
682 colorLUT->fOutputChannels = outputChannels;
683 uint32_t numEntries = 1;
684 for (uint32_t i = 0; i < inputChannels; i++) {
685 colorLUT->fGridPoints[i] = src[i];
686 if (0 == src[i]) {
687 SkColorSpacePrintf("Each input channel must have at least one grid p oint.");
688 return false;
689 }
690
691 if (!safe_mul(numEntries, src[i], &numEntries)) {
692 SkColorSpacePrintf("Too many entries in Color LUT.");
693 return false;
694 }
695 }
696
697 if (!safe_mul(numEntries, outputChannels, &numEntries)) {
698 SkColorSpacePrintf("Too many entries in Color LUT.");
699 return false;
700 }
701
702 // Space is provided for a maximum of the 16 input channels. Now we determi ne the precision
703 // of the table values.
704 uint8_t precision = src[16];
705 switch (precision) {
706 case 1: // 8-bit data
707 case 2: // 16-bit data
708 break;
709 default:
710 SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit.\n") ;
711 return false;
712 }
713
714 uint32_t clutBytes;
715 if (!safe_mul(numEntries, precision, &clutBytes)) {
716 SkColorSpacePrintf("Too many entries in Color LUT.");
717 return false;
718 }
719
720 if (dataLen < clutBytes) {
721 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len);
722 return false;
723 }
724
725 // Movable struct colorLUT has ownership of fTable.
726 colorLUT->fTable = std::unique_ptr<float[]>(new float[numEntries]);
727 const uint8_t* ptr = src + kColorLUTHeaderSize;
728 for (uint32_t i = 0; i < numEntries; i++, ptr += precision) {
729 if (1 == precision) {
730 colorLUT->fTable[i] = ((float) ptr[i]) / 255.0f;
731 } else {
732 colorLUT->fTable[i] = ((float) read_big_endian_short(ptr)) / 65535.0 f;
733 }
734 }
735
736 return true;
737 }
738
739 bool load_matrix(SkMatrix44* toXYZ, const uint8_t* src, size_t len) {
740 if (len < 48) {
741 SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len);
742 return false;
743 }
744
745 // For this matrix to behave like our "to XYZ D50" matrices, it needs to be scaled.
746 constexpr float scale = 65535.0 / 32768.0;
747 float array[16];
748 array[ 0] = scale * SkFixedToFloat(read_big_endian_int(src));
749 array[ 1] = scale * SkFixedToFloat(read_big_endian_int(src + 4));
750 array[ 2] = scale * SkFixedToFloat(read_big_endian_int(src + 8));
751 array[ 3] = scale * SkFixedToFloat(read_big_endian_int(src + 36)); // transl ate R
752 array[ 4] = scale * SkFixedToFloat(read_big_endian_int(src + 12));
753 array[ 5] = scale * SkFixedToFloat(read_big_endian_int(src + 16));
754 array[ 6] = scale * SkFixedToFloat(read_big_endian_int(src + 20));
755 array[ 7] = scale * SkFixedToFloat(read_big_endian_int(src + 40)); // transl ate G
756 array[ 8] = scale * SkFixedToFloat(read_big_endian_int(src + 24));
757 array[ 9] = scale * SkFixedToFloat(read_big_endian_int(src + 28));
758 array[10] = scale * SkFixedToFloat(read_big_endian_int(src + 32));
759 array[11] = scale * SkFixedToFloat(read_big_endian_int(src + 44)); // transl ate B
760 array[12] = 0.0f;
761 array[13] = 0.0f;
762 array[14] = 0.0f;
763 array[15] = 1.0f;
764 toXYZ->setColMajorf(array);
765 return true;
766 }
767
768 bool load_a2b0(SkColorLookUpTable* colorLUT, SkGammaCurve* gammas, SkMatrix44* t oXYZ,
769 const uint8_t* src, size_t len) {
770 if (len < 32) {
771 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len);
772 return false;
773 }
774
775 uint32_t type = read_big_endian_uint(src);
776 if (kTAG_AtoBType != type) {
777 // FIXME (msarett): Need to support lut8Type and lut16Type.
778 SkColorSpacePrintf("Unsupported A to B tag type.\n");
779 return false;
780 }
781
782 // Read the number of channels. The four bytes that we skipped are reserved and
783 // must be zero.
784 uint8_t inputChannels = src[8];
785 uint8_t outputChannels = src[9];
786 if (3 != inputChannels || 3 != outputChannels) {
787 // We only handle (supposedly) RGB inputs and RGB outputs. The numbers of input
788 // channels and output channels both must be 3.
789 SkColorSpacePrintf("Input and output channels must equal 3 in A to B tag .\n");
790 return false;
791 }
792
793 // Read the offsets of each element in the A to B tag. With the exception o f A curves and
794 // B curves (which we do not yet support), we will handle these elements in the order in
795 // which they should be applied (rather than the order in which they occur i n the tag).
796 // If the offset is non-zero it indicates that the element is present.
797 uint32_t offsetToACurves = read_big_endian_int(src + 28);
798 uint32_t offsetToBCurves = read_big_endian_int(src + 12);
799 if ((0 != offsetToACurves) || (0 != offsetToBCurves)) {
800 // FIXME (msarett): Handle A and B curves.
801 // Note that the A curve is technically required in order to have a colo r LUT.
802 // However, all the A curves I have seen so far have are just placeholde rs that
803 // don't actually transform the data.
804 SkColorSpacePrintf("Ignoring A and/or B curve. Output may be wrong.\n") ;
805 }
806
807 uint32_t offsetToColorLUT = read_big_endian_int(src + 24);
808 if (0 != offsetToColorLUT && offsetToColorLUT < len) {
809 if (!load_color_lut(colorLUT, inputChannels, outputChannels, src + offse tToColorLUT,
810 len - offsetToColorLUT)) {
811 SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n");
812 }
813 }
814
815 uint32_t offsetToMCurves = read_big_endian_int(src + 20);
816 if (0 != offsetToMCurves && offsetToMCurves < len) {
817 if (!load_gammas(gammas, outputChannels, src + offsetToMCurves, len - of fsetToMCurves)) {
818 SkColorSpacePrintf("Failed to read M curves from A to B tag. Using linear gamma.\n");
819 gammas[0].fNamed = SkColorSpace::kLinear_GammaNamed;
820 gammas[1].fNamed = SkColorSpace::kLinear_GammaNamed;
821 gammas[2].fNamed = SkColorSpace::kLinear_GammaNamed;
822 }
823 }
824
825 uint32_t offsetToMatrix = read_big_endian_int(src + 16);
826 if (0 != offsetToMatrix && offsetToMatrix < len) {
827 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) {
828 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n");
829 toXYZ->setIdentity();
830 }
831 }
832
833 return true;
834 }
835
836 sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* input, size_t len) {
837 if (!input || len < kICCHeaderSize) {
838 return_null("Data is null or not large enough to contain an ICC profile" );
839 }
840
841 // Create our own copy of the input.
842 void* memory = sk_malloc_throw(len);
843 memcpy(memory, input, len);
844 sk_sp<SkData> data = SkData::MakeFromMalloc(memory, len);
845 const void* base = data->data();
846 const uint8_t* ptr = (const uint8_t*) base;
847
848 // Read the ICC profile header and check to make sure that it is valid.
849 ICCProfileHeader header;
850 header.init(ptr, len);
851 if (!header.valid()) {
852 return nullptr;
853 }
854
855 // Adjust ptr and len before reading the tags.
856 if (len < header.fSize) {
857 SkColorSpacePrintf("ICC profile might be truncated.\n");
858 } else if (len > header.fSize) {
859 SkColorSpacePrintf("Caller provided extra data beyond the end of the ICC profile.\n");
860 len = header.fSize;
861 }
862 ptr += kICCHeaderSize;
863 len -= kICCHeaderSize;
864
865 // Parse tag headers.
866 uint32_t tagCount = header.fTagCount;
867 SkColorSpacePrintf("ICC profile contains %d tags.\n", tagCount);
868 if (len < kICCTagTableEntrySize * tagCount) {
869 return_null("Not enough input data to read tag table entries");
870 }
871
872 SkAutoTArray<ICCTag> tags(tagCount);
873 for (uint32_t i = 0; i < tagCount; i++) {
874 ptr = tags[i].init(ptr);
875 SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24 ) & 0xFF,
876 (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >> 8) & 0xFF,
877 (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLen gth);
878
879 if (!tags[i].valid(kICCHeaderSize + len)) {
880 return_null("Tag is too large to fit in ICC profile");
881 }
882 }
883
884 switch (header.fInputColorSpace) {
885 case kRGB_ColorSpace: {
886 // Recognize the rXYZ, gXYZ, and bXYZ tags.
887 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ);
888 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ);
889 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ);
890 if (r && g && b) {
891 float toXYZ[9];
892 if (!load_xyz(&toXYZ[0], r->addr((const uint8_t*) base), r->fLen gth) ||
893 !load_xyz(&toXYZ[3], g->addr((const uint8_t*) base), g->fLen gth) ||
894 !load_xyz(&toXYZ[6], b->addr((const uint8_t*) base), b->fLen gth))
895 {
896 return_null("Need valid rgb tags for XYZ space");
897 }
898 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
899 mat.set3x3RowMajorf(toXYZ);
900
901 // It is not uncommon to see missing or empty gamma tags. This indicates
902 // that we should use unit gamma.
903 SkGammaCurve curves[3];
904 r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC);
905 g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC);
906 b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC);
907 if (!r || !load_gammas(&curves[0], 1, r->addr((const uint8_t*) b ase), r->fLength))
908 {
909 SkColorSpacePrintf("Failed to read R gamma tag.\n");
910 curves[0].fNamed = SkColorSpace::kLinear_GammaNamed;
911 }
912 if (!g || !load_gammas(&curves[1], 1, g->addr((const uint8_t*) b ase), g->fLength))
913 {
914 SkColorSpacePrintf("Failed to read G gamma tag.\n");
915 curves[1].fNamed = SkColorSpace::kLinear_GammaNamed;
916 }
917 if (!b || !load_gammas(&curves[2], 1, b->addr((const uint8_t*) b ase), b->fLength))
918 {
919 SkColorSpacePrintf("Failed to read B gamma tag.\n");
920 curves[2].fNamed = SkColorSpace::kLinear_GammaNamed;
921 }
922
923 GammaNamed gammaNamed = SkGammas::Named(curves);
924 if (kNonStandard_GammaNamed == gammaNamed) {
925 sk_sp<SkGammas> gammas = sk_make_sp<SkGammas>(std::move(curv es[0]),
926 std::move(curv es[1]),
927 std::move(curv es[2]));
928 return sk_sp<SkColorSpace>(new SkColorSpace_Base(nullptr, st d::move(gammas),
929 mat, std::m ove(data)));
930 } else {
931 return SkColorSpace_Base::NewRGB(gammaNamed, mat);
932 }
933 }
934
935 // Recognize color profile specified by A2B0 tag.
936 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0);
937 if (a2b0) {
938 sk_sp<SkColorLookUpTable> colorLUT = sk_make_sp<SkColorLookUpTab le>();
939 SkGammaCurve curves[3];
940 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor);
941 if (!load_a2b0(colorLUT.get(), curves, &toXYZ, a2b0->addr((const uint8_t*) base),
942 a2b0->fLength)) {
943 return_null("Failed to parse A2B0 tag");
944 }
945
946 GammaNamed gammaNamed = SkGammas::Named(curves);
947 colorLUT = colorLUT->fTable ? colorLUT : nullptr;
948 if (colorLUT || kNonStandard_GammaNamed == gammaNamed) {
949 sk_sp<SkGammas> gammas = sk_make_sp<SkGammas>(std::move(curv es[0]),
950 std::move(curv es[1]),
951 std::move(curv es[2]));
952
953 return sk_sp<SkColorSpace>(new SkColorSpace_Base(std::move(c olorLUT),
954 std::move(g ammas), toXYZ,
955 std::move(d ata)));
956 } else {
957 return SkColorSpace_Base::NewRGB(gammaNamed, toXYZ);
958 }
959 }
960 }
961 default:
962 break;
963 }
964
965 return_null("ICC profile contains unsupported colorspace");
966 }
967
968 //////////////////////////////////////////////////////////////////////////////// ///////////////////
969
970 // We will write a profile with the minimum nine required tags.
971 static constexpr uint32_t kICCNumEntries = 9;
972
973 static constexpr uint32_t kTAG_desc = SkSetFourByteTag('d', 'e', 's', 'c');
974 static constexpr uint32_t kTAG_desc_Bytes = 12;
975 static constexpr uint32_t kTAG_desc_Offset = kICCHeaderSize + kICCNumEntries*kIC CTagTableEntrySize;
976
977 static constexpr uint32_t kTAG_XYZ_Bytes = 20;
978 static constexpr uint32_t kTAG_rXYZ_Offset = kTAG_desc_Offset + kTAG_desc_Bytes;
979 static constexpr uint32_t kTAG_gXYZ_Offset = kTAG_rXYZ_Offset + kTAG_XYZ_Bytes;
980 static constexpr uint32_t kTAG_bXYZ_Offset = kTAG_gXYZ_Offset + kTAG_XYZ_Bytes;
981
982 static constexpr uint32_t kTAG_TRC_Bytes = 14;
983 static constexpr uint32_t kTAG_rTRC_Offset = kTAG_bXYZ_Offset + kTAG_XYZ_Bytes;
984 static constexpr uint32_t kTAG_gTRC_Offset = kTAG_rTRC_Offset + SkAlign4(kTAG_TR C_Bytes);
985 static constexpr uint32_t kTAG_bTRC_Offset = kTAG_gTRC_Offset + SkAlign4(kTAG_TR C_Bytes);
986
987 static constexpr uint32_t kTAG_wtpt = SkSetFourByteTag('w', 't', 'p', 't');
988 static constexpr uint32_t kTAG_wtpt_Offset = kTAG_bTRC_Offset + SkAlign4(kTAG_TR C_Bytes);
989
990 static constexpr uint32_t kTAG_cprt = SkSetFourByteTag('c', 'p', 'r', 't');
991 static constexpr uint32_t kTAG_cprt_Bytes = 12;
992 static constexpr uint32_t kTAG_cprt_Offset = kTAG_wtpt_Offset + kTAG_XYZ_Bytes;
993
994 static constexpr uint32_t kICCProfileSize = kTAG_cprt_Offset + kTAG_cprt_Bytes;
995
996 static constexpr uint32_t gICCHeader[kICCHeaderSize / 4] {
997 SkEndian_SwapBE32(kICCProfileSize), // Size of the profile
998 0, // Preferred CMM type (ignored)
999 SkEndian_SwapBE32(0x02100000), // Version 2.1
1000 SkEndian_SwapBE32(kDisplay_Profile), // Display device profile
1001 SkEndian_SwapBE32(kRGB_ColorSpace), // RGB input color space
1002 SkEndian_SwapBE32(kXYZ_PCSSpace), // XYZ profile connection space
1003 0, 0, 0, // Date and time (ignored)
1004 SkEndian_SwapBE32(kACSP_Signature), // Profile signature
1005 0, // Platform target (ignored)
1006 0x00000000, // Flags: not embedded, can be used ind ependently
1007 0, // Device manufacturer (ignored)
1008 0, // Device model (ignored)
1009 0, 0, // Device attributes (ignored)
1010 SkEndian_SwapBE32(1), // Relative colorimetric rendering inte nt
1011 SkEndian_SwapBE32(0x0000f6d6), // D50 standard illuminant (X)
1012 SkEndian_SwapBE32(0x00010000), // D50 standard illuminant (Y)
1013 SkEndian_SwapBE32(0x0000d32d), // D50 standard illuminant (Z)
1014 0, // Profile creator (ignored)
1015 0, 0, 0, 0, // Profile id checksum (ignored)
1016 0, 0, 0, 0, 0, 0, 0, // Reserved (ignored)
1017 SkEndian_SwapBE32(kICCNumEntries), // Number of tags
1018 };
1019
1020 static constexpr uint32_t gICCTagTable[3 * kICCNumEntries] {
1021 // Profile description
1022 SkEndian_SwapBE32(kTAG_desc),
1023 SkEndian_SwapBE32(kTAG_desc_Offset),
1024 SkEndian_SwapBE32(kTAG_desc_Bytes),
1025
1026 // rXYZ
1027 SkEndian_SwapBE32(kTAG_rXYZ),
1028 SkEndian_SwapBE32(kTAG_rXYZ_Offset),
1029 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
1030
1031 // gXYZ
1032 SkEndian_SwapBE32(kTAG_gXYZ),
1033 SkEndian_SwapBE32(kTAG_gXYZ_Offset),
1034 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
1035
1036 // bXYZ
1037 SkEndian_SwapBE32(kTAG_bXYZ),
1038 SkEndian_SwapBE32(kTAG_bXYZ_Offset),
1039 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
1040
1041 // rTRC
1042 SkEndian_SwapBE32(kTAG_rTRC),
1043 SkEndian_SwapBE32(kTAG_rTRC_Offset),
1044 SkEndian_SwapBE32(kTAG_TRC_Bytes),
1045
1046 // gTRC
1047 SkEndian_SwapBE32(kTAG_gTRC),
1048 SkEndian_SwapBE32(kTAG_gTRC_Offset),
1049 SkEndian_SwapBE32(kTAG_TRC_Bytes),
1050
1051 // bTRC
1052 SkEndian_SwapBE32(kTAG_bTRC),
1053 SkEndian_SwapBE32(kTAG_bTRC_Offset),
1054 SkEndian_SwapBE32(kTAG_TRC_Bytes),
1055
1056 // White point
1057 SkEndian_SwapBE32(kTAG_wtpt),
1058 SkEndian_SwapBE32(kTAG_wtpt_Offset),
1059 SkEndian_SwapBE32(kTAG_XYZ_Bytes),
1060
1061 // Copyright
1062 SkEndian_SwapBE32(kTAG_cprt),
1063 SkEndian_SwapBE32(kTAG_cprt_Offset),
1064 SkEndian_SwapBE32(kTAG_cprt_Bytes),
1065 };
1066
1067 static constexpr uint32_t kTAG_TextType = SkSetFourByteTag('m', 'l', 'u', 'c');
1068 static constexpr uint32_t gEmptyTextTag[3] {
1069 SkEndian_SwapBE32(kTAG_TextType), // Type signature
1070 0, // Reserved
1071 0, // Zero records
1072 };
1073
1074 static void write_xyz_tag(uint32_t* ptr, const SkMatrix44& toXYZ, int row) {
1075 ptr[0] = SkEndian_SwapBE32(kXYZ_PCSSpace);
1076 ptr[1] = 0;
1077 ptr[2] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 0)));
1078 ptr[3] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 1)));
1079 ptr[4] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 2)));
1080 }
1081
1082 static void write_trc_tag(uint32_t* ptr, float value) {
1083 ptr[0] = SkEndian_SwapBE32(kTAG_CurveType);
1084 ptr[1] = 0;
1085
1086 // Gamma will be specified with a single value.
1087 ptr[2] = SkEndian_SwapBE32(1);
1088
1089 // Convert gamma to 16-bit fixed point.
1090 uint16_t* ptr16 = (uint16_t*) (ptr + 3);
1091 ptr16[0] = SkEndian_SwapBE16((uint16_t) (value * 256.0f));
1092
1093 // Pad tag with zero.
1094 ptr16[1] = 0;
1095 }
1096
1097 static float get_gamma_value(const SkGammaCurve* curve) {
1098 switch (curve->fNamed) {
1099 case SkColorSpace::kSRGB_GammaNamed:
1100 // FIXME (msarett):
1101 // kSRGB cannot be represented by a value. Here we fall through to 2.2f,
1102 // which is a close guess. To be more accurate, we need to represen t sRGB
1103 // gamma with a parametric curve.
1104 case SkColorSpace::k2Dot2Curve_GammaNamed:
1105 return 2.2f;
1106 case SkColorSpace::kLinear_GammaNamed:
1107 return 1.0f;
1108 default:
1109 SkASSERT(curve->isValue());
1110 return curve->fValue;
1111 }
1112 }
1113
1114 sk_sp<SkData> SkColorSpace_Base::writeToICC() const {
1115 // Return if this object was created from a profile, or if we have already s erialized
1116 // the profile.
1117 if (fProfileData) {
1118 return fProfileData;
1119 }
1120
1121 // The client may create an SkColorSpace using an SkMatrix44, but currently we only
1122 // support writing profiles with 3x3 matrices.
1123 // TODO (msarett): Fix this!
1124 if (0.0f != fToXYZD50.getFloat(3, 0) || 0.0f != fToXYZD50.getFloat(3, 1) ||
1125 0.0f != fToXYZD50.getFloat(3, 2) || 0.0f != fToXYZD50.getFloat(0, 3) ||
1126 0.0f != fToXYZD50.getFloat(1, 3) || 0.0f != fToXYZD50.getFloat(2, 3))
1127 {
1128 return nullptr;
1129 }
1130
1131 SkAutoMalloc profile(kICCProfileSize);
1132 uint8_t* ptr = (uint8_t*) profile.get();
1133
1134 // Write profile header
1135 memcpy(ptr, gICCHeader, sizeof(gICCHeader));
1136 ptr += sizeof(gICCHeader);
1137
1138 // Write tag table
1139 memcpy(ptr, gICCTagTable, sizeof(gICCTagTable));
1140 ptr += sizeof(gICCTagTable);
1141
1142 // Write profile description tag
1143 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag));
1144 ptr += sizeof(gEmptyTextTag);
1145
1146 // Write XYZ tags
1147 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 0);
1148 ptr += kTAG_XYZ_Bytes;
1149 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 1);
1150 ptr += kTAG_XYZ_Bytes;
1151 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 2);
1152 ptr += kTAG_XYZ_Bytes;
1153
1154 // Write TRC tags
1155 GammaNamed gammaNamed = this->gammaNamed();
1156 if (kNonStandard_GammaNamed == gammaNamed) {
1157 write_trc_tag((uint32_t*) ptr, get_gamma_value(&as_CSB(this)->fGammas->f Red));
1158 ptr += SkAlign4(kTAG_TRC_Bytes);
1159 write_trc_tag((uint32_t*) ptr, get_gamma_value(&as_CSB(this)->fGammas->f Green));
1160 ptr += SkAlign4(kTAG_TRC_Bytes);
1161 write_trc_tag((uint32_t*) ptr, get_gamma_value(&as_CSB(this)->fGammas->f Blue));
1162 ptr += SkAlign4(kTAG_TRC_Bytes);
1163 } else {
1164 switch (gammaNamed) {
1165 case SkColorSpace::kSRGB_GammaNamed:
1166 // FIXME (msarett):
1167 // kSRGB cannot be represented by a value. Here we fall through to 2.2f,
1168 // which is a close guess. To be more accurate, we need to repr esent sRGB
1169 // gamma with a parametric curve.
1170 case SkColorSpace::k2Dot2Curve_GammaNamed:
1171 write_trc_tag((uint32_t*) ptr, 2.2f);
1172 ptr += SkAlign4(kTAG_TRC_Bytes);
1173 write_trc_tag((uint32_t*) ptr, 2.2f);
1174 ptr += SkAlign4(kTAG_TRC_Bytes);
1175 write_trc_tag((uint32_t*) ptr, 2.2f);
1176 ptr += SkAlign4(kTAG_TRC_Bytes);
1177 break;
1178 case SkColorSpace::kLinear_GammaNamed:
1179 write_trc_tag((uint32_t*) ptr, 1.0f);
1180 ptr += SkAlign4(kTAG_TRC_Bytes);
1181 write_trc_tag((uint32_t*) ptr, 1.0f);
1182 ptr += SkAlign4(kTAG_TRC_Bytes);
1183 write_trc_tag((uint32_t*) ptr, 1.0f);
1184 ptr += SkAlign4(kTAG_TRC_Bytes);
1185 break;
1186 default:
1187 SkASSERT(false);
1188 break;
1189 }
1190 }
1191
1192 // Write white point tag
1193 uint32_t* ptr32 = (uint32_t*) ptr;
1194 ptr32[0] = SkEndian_SwapBE32(kXYZ_PCSSpace);
1195 ptr32[1] = 0;
1196 // TODO (msarett): These values correspond to the D65 white point. This may not always be
1197 // correct.
1198 ptr32[2] = SkEndian_SwapBE32(0x0000f351);
1199 ptr32[3] = SkEndian_SwapBE32(0x00010000);
1200 ptr32[4] = SkEndian_SwapBE32(0x000116cc);
1201 ptr += kTAG_XYZ_Bytes;
1202
1203 // Write copyright tag
1204 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag));
1205
1206 // TODO (msarett): Should we try to hold onto the data so we can return imme diately if
1207 // the client calls again?
1208 return SkData::MakeFromMalloc(profile.release(), kICCProfileSize);
1209 }
1210
1211 //////////////////////////////////////////////////////////////////////////////// ///////////////////
1212
1213 enum Version { 144 enum Version {
1214 k0_Version, // Initial version, header + flags for matrix and profile 145 k0_Version, // Initial version, header + flags for matrix and profile
1215 }; 146 };
1216 147
1217 struct ColorSpaceHeader { 148 struct ColorSpaceHeader {
1218 /** 149 /**
1219 * If kMatrix_Flag is set, we will write 12 floats after the header. 150 * If kMatrix_Flag is set, we will write 12 floats after the header.
1220 * Should not be set at the same time as the kICC_Flag. 151 * Should not be set at the same time as the kICC_Flag.
1221 */ 152 */
1222 static constexpr uint8_t kMatrix_Flag = 1 << 0; 153 static constexpr uint8_t kMatrix_Flag = 1 << 0;
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
1350 281
1351 uint32_t profileSize = *((uint32_t*) data); 282 uint32_t profileSize = *((uint32_t*) data);
1352 data = SkTAddOffset<const void>(data, sizeof(uint32_t)); 283 data = SkTAddOffset<const void>(data, sizeof(uint32_t));
1353 length -= sizeof(uint32_t); 284 length -= sizeof(uint32_t);
1354 if (length < profileSize) { 285 if (length < profileSize) {
1355 return nullptr; 286 return nullptr;
1356 } 287 }
1357 288
1358 return NewICC(data, profileSize); 289 return NewICC(data, profileSize);
1359 } 290 }
OLDNEW
« no previous file with comments | « gyp/core.gypi ('k') | src/core/SkColorSpacePriv.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698