SkColorSpace.cpp revision 264f88aff5c2303339c1dbcad02e12f70c62ae72
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 "SkAtomics.h"
9#include "SkColorSpace.h"
10#include "SkColorSpacePriv.h"
11#include "SkOnce.h"
12
13static bool color_space_almost_equal(float a, float b) {
14    return SkTAbs(a - b) < 0.01f;
15}
16
17//////////////////////////////////////////////////////////////////////////////////////////////////
18
19static int32_t gUniqueColorSpaceID;
20
21SkColorSpace::SkColorSpace(sk_sp<SkGammas> gammas, const SkMatrix44& toXYZD50, Named named)
22    : fGammas(gammas)
23    , fToXYZD50(toXYZD50)
24    , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID))
25    , fNamed(named)
26{}
27
28SkColorSpace::SkColorSpace(SkColorLookUpTable* colorLUT, sk_sp<SkGammas> gammas,
29                           const SkMatrix44& toXYZD50)
30    : fColorLUT(colorLUT)
31    , fGammas(gammas)
32    , fToXYZD50(toXYZD50)
33    , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID))
34    , fNamed(kUnknown_Named)
35{}
36
37const float gSRGB_toXYZD50[] {
38    0.4358f, 0.2224f, 0.0139f,    // * R
39    0.3853f, 0.7170f, 0.0971f,    // * G
40    0.1430f, 0.0606f, 0.7139f,    // * B
41};
42
43const float gAdobeRGB_toXYZD50[] {
44    0.6098f, 0.3111f, 0.0195f,    // * R
45    0.2052f, 0.6257f, 0.0609f,    // * G
46    0.1492f, 0.0632f, 0.7448f,    // * B
47};
48
49/**
50 *  Checks if our toXYZ matrix is a close match to a known color gamut.
51 *
52 *  @param toXYZD50 transformation matrix deduced from profile data
53 *  @param standard 3x3 canonical transformation matrix
54 */
55static bool xyz_almost_equal(const SkMatrix44& toXYZD50, const float* standard) {
56    return color_space_almost_equal(toXYZD50.getFloat(0, 0), standard[0]) &&
57           color_space_almost_equal(toXYZD50.getFloat(0, 1), standard[1]) &&
58           color_space_almost_equal(toXYZD50.getFloat(0, 2), standard[2]) &&
59           color_space_almost_equal(toXYZD50.getFloat(1, 0), standard[3]) &&
60           color_space_almost_equal(toXYZD50.getFloat(1, 1), standard[4]) &&
61           color_space_almost_equal(toXYZD50.getFloat(1, 2), standard[5]) &&
62           color_space_almost_equal(toXYZD50.getFloat(2, 0), standard[6]) &&
63           color_space_almost_equal(toXYZD50.getFloat(2, 1), standard[7]) &&
64           color_space_almost_equal(toXYZD50.getFloat(2, 2), standard[8]) &&
65           color_space_almost_equal(toXYZD50.getFloat(0, 3), 0.0f) &&
66           color_space_almost_equal(toXYZD50.getFloat(1, 3), 0.0f) &&
67           color_space_almost_equal(toXYZD50.getFloat(2, 3), 0.0f) &&
68           color_space_almost_equal(toXYZD50.getFloat(3, 0), 0.0f) &&
69           color_space_almost_equal(toXYZD50.getFloat(3, 1), 0.0f) &&
70           color_space_almost_equal(toXYZD50.getFloat(3, 2), 0.0f) &&
71           color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f);
72}
73
74static SkOnce gStandardGammasOnce;
75static SkGammas* gStandardGammas;
76
77sk_sp<SkColorSpace> SkColorSpace::NewRGB(float gammaVals[3], const SkMatrix44& toXYZD50) {
78    sk_sp<SkGammas> gammas = nullptr;
79
80    // Check if we really have sRGB or Adobe RGB
81    if (color_space_almost_equal(2.2f, gammaVals[0]) &&
82        color_space_almost_equal(2.2f, gammaVals[1]) &&
83        color_space_almost_equal(2.2f, gammaVals[2]))
84    {
85        gStandardGammasOnce([] {
86            gStandardGammas = new SkGammas(2.2f, 2.2f, 2.2f);
87        });
88        gammas = sk_ref_sp(gStandardGammas);
89
90        if (xyz_almost_equal(toXYZD50, gSRGB_toXYZD50)) {
91            return SkColorSpace::NewNamed(kSRGB_Named);
92        } else if (xyz_almost_equal(toXYZD50, gAdobeRGB_toXYZD50)) {
93            return SkColorSpace::NewNamed(kAdobeRGB_Named);
94        }
95    }
96
97    if (!gammas) {
98        gammas = sk_sp<SkGammas>(new SkGammas(gammaVals[0], gammaVals[1], gammaVals[2]));
99    }
100    return sk_sp<SkColorSpace>(new SkColorSpace(gammas, toXYZD50, kUnknown_Named));
101}
102
103sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) {
104    static SkOnce sRGBOnce;
105    static SkColorSpace* sRGB;
106    static SkOnce adobeRGBOnce;
107    static SkColorSpace* adobeRGB;
108
109    switch (named) {
110        case kSRGB_Named: {
111            gStandardGammasOnce([] {
112                gStandardGammas = new SkGammas(2.2f, 2.2f, 2.2f);
113            });
114
115            sRGBOnce([] {
116                SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor);
117                srgbToxyzD50.set3x3ColMajorf(gSRGB_toXYZD50);
118                sRGB = new SkColorSpace(sk_ref_sp(gStandardGammas), srgbToxyzD50, kSRGB_Named);
119            });
120            return sk_ref_sp(sRGB);
121        }
122        case kAdobeRGB_Named: {
123            gStandardGammasOnce([] {
124                gStandardGammas = new SkGammas(2.2f, 2.2f, 2.2f);
125            });
126
127            adobeRGBOnce([] {
128                SkMatrix44 adobergbToxyzD50(SkMatrix44::kUninitialized_Constructor);
129                adobergbToxyzD50.set3x3ColMajorf(gAdobeRGB_toXYZD50);
130                adobeRGB = new SkColorSpace(sk_ref_sp(gStandardGammas), adobergbToxyzD50,
131                                            kAdobeRGB_Named);
132            });
133            return sk_ref_sp(adobeRGB);
134        }
135        default:
136            break;
137    }
138    return nullptr;
139}
140
141///////////////////////////////////////////////////////////////////////////////////////////////////
142
143#include "SkFixed.h"
144#include "SkTemplates.h"
145
146#define SkColorSpacePrintf(...)
147
148#define return_if_false(pred, msg)                                   \
149    do {                                                             \
150        if (!(pred)) {                                               \
151            SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \
152            return false;                                            \
153        }                                                            \
154    } while (0)
155
156#define return_null(msg)                                             \
157    do {                                                             \
158        SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg));     \
159        return nullptr;                                              \
160    } while (0)
161
162static uint16_t read_big_endian_short(const uint8_t* ptr) {
163    return ptr[0] << 8 | ptr[1];
164}
165
166static uint32_t read_big_endian_uint(const uint8_t* ptr) {
167    return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
168}
169
170static int32_t read_big_endian_int(const uint8_t* ptr) {
171    return (int32_t) read_big_endian_uint(ptr);
172}
173
174// This is equal to the header size according to the ICC specification (128)
175// plus the size of the tag count (4).  We include the tag count since we
176// always require it to be present anyway.
177static const size_t kICCHeaderSize = 132;
178
179// Contains a signature (4), offset (4), and size (4).
180static const size_t kICCTagTableEntrySize = 12;
181
182static const uint32_t kRGB_ColorSpace  = SkSetFourByteTag('R', 'G', 'B', ' ');
183
184struct ICCProfileHeader {
185    uint32_t fSize;
186
187    // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.).
188    // We're always going to use this one.
189    uint32_t fCMMType_ignored;
190
191    uint32_t fVersion;
192    uint32_t fProfileClass;
193    uint32_t fInputColorSpace;
194    uint32_t fPCS;
195    uint32_t fDateTime_ignored[3];
196    uint32_t fSignature;
197
198    // Indicates the platform that this profile was created for (ex: Apple, Microsoft).  This
199    // doesn't really matter to us.
200    uint32_t fPlatformTarget_ignored;
201
202    // Flags can indicate:
203    // (1) Whether this profile was embedded in a file.  This flag is consistently wrong.
204    //     Ex: The profile came from a file but indicates that it did not.
205    // (2) Whether we are allowed to use the profile independently of the color data.  If set,
206    //     this may allow us to use the embedded profile for testing separate from the original
207    //     image.
208    uint32_t fFlags_ignored;
209
210    // We support many output devices.  It doesn't make sense to think about the attributes of
211    // the device in the context of the image profile.
212    uint32_t fDeviceManufacturer_ignored;
213    uint32_t fDeviceModel_ignored;
214    uint32_t fDeviceAttributes_ignored[2];
215
216    uint32_t fRenderingIntent;
217    int32_t  fIlluminantXYZ[3];
218
219    // We don't care who created the profile.
220    uint32_t fCreator_ignored;
221
222    // This is an MD5 checksum.  Could be useful for checking if profiles are equal.
223    uint32_t fProfileId_ignored[4];
224
225    // Reserved for future use.
226    uint32_t fReserved_ignored[7];
227
228    uint32_t fTagCount;
229
230    void init(const uint8_t* src, size_t len) {
231        SkASSERT(kICCHeaderSize == sizeof(*this));
232
233        uint32_t* dst = (uint32_t*) this;
234        for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) {
235            dst[i] = read_big_endian_uint(src);
236        }
237    }
238
239    bool valid() const {
240        return_if_false(fSize >= kICCHeaderSize, "Size is too small");
241
242        uint8_t majorVersion = fVersion >> 24;
243        return_if_false(majorVersion <= 4, "Unsupported version");
244
245        // These are the three basic classes of profiles that we might expect to see embedded
246        // in images.  Four additional classes exist, but they generally are used as a convenient
247        // way for CMMs to store calculated transforms.
248        const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r');
249        const uint32_t kInput_Profile   = SkSetFourByteTag('s', 'c', 'n', 'r');
250        const uint32_t kOutput_Profile  = SkSetFourByteTag('p', 'r', 't', 'r');
251        return_if_false(fProfileClass == kDisplay_Profile ||
252                        fProfileClass == kInput_Profile ||
253                        fProfileClass == kOutput_Profile,
254                        "Unsupported profile");
255
256        // TODO (msarett):
257        // All the profiles we've tested so far use RGB as the input color space.
258        return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space");
259
260        // TODO (msarett):
261        // All the profiles we've tested so far use XYZ as the profile connection space.
262        const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' ');
263        return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space");
264
265        return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "Bad signature");
266
267        // TODO (msarett):
268        // Should we treat different rendering intents differently?
269        // Valid rendering intents include kPerceptual (0), kRelative (1),
270        // kSaturation (2), and kAbsolute (3).
271        return_if_false(fRenderingIntent <= 3, "Bad rendering intent");
272
273        return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0]), 0.96420f) &&
274                        color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1]), 1.00000f) &&
275                        color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2]), 0.82491f),
276                        "Illuminant must be D50");
277
278        return_if_false(fTagCount <= 100, "Too many tags");
279
280        return true;
281    }
282};
283
284struct ICCTag {
285    uint32_t fSignature;
286    uint32_t fOffset;
287    uint32_t fLength;
288
289    const uint8_t* init(const uint8_t* src) {
290        fSignature = read_big_endian_uint(src);
291        fOffset = read_big_endian_uint(src + 4);
292        fLength = read_big_endian_uint(src + 8);
293        return src + 12;
294    }
295
296    bool valid(size_t len) {
297        return_if_false(fOffset + fLength <= len, "Tag too large for ICC profile");
298        return true;
299    }
300
301    const uint8_t* addr(const uint8_t* src) const {
302        return src + fOffset;
303    }
304
305    static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature) {
306        for (int i = 0; i < count; ++i) {
307            if (tags[i].fSignature == signature) {
308                return &tags[i];
309            }
310        }
311        return nullptr;
312    }
313};
314
315static const uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z');
316static const uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z');
317static const uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z');
318static const uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C');
319static const uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C');
320static const uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C');
321static const uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0');
322
323bool load_xyz(float dst[3], const uint8_t* src, size_t len) {
324    if (len < 20) {
325        SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len);
326        return false;
327    }
328
329    dst[0] = SkFixedToFloat(read_big_endian_int(src + 8));
330    dst[1] = SkFixedToFloat(read_big_endian_int(src + 12));
331    dst[2] = SkFixedToFloat(read_big_endian_int(src + 16));
332    SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]);
333    return true;
334}
335
336static const uint32_t kTAG_CurveType     = SkSetFourByteTag('c', 'u', 'r', 'v');
337static const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a');
338
339bool SkColorSpace::LoadGammas(SkGammaCurve* gammas, uint32_t numGammas, const uint8_t* src,
340                              size_t len) {
341    for (uint32_t i = 0; i < numGammas; i++) {
342        if (len < 12) {
343            // FIXME (msarett):
344            // We could potentially return false here after correctly parsing *some* of the
345            // gammas correctly.  Should we somehow try to indicate a partial success?
346            SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
347            return false;
348        }
349
350        // We need to count the number of bytes in the tag, so we are able to move to the
351        // next tag on the next loop iteration.
352        size_t tagBytes;
353
354        uint32_t type = read_big_endian_uint(src);
355        switch (type) {
356            case kTAG_CurveType: {
357                uint32_t count = read_big_endian_uint(src + 8);
358                tagBytes = 12 + count * 2;
359                if (0 == count) {
360                    // Some tags require a gamma curve, but the author doesn't actually want
361                    // to transform the data.  In this case, it is common to see a curve with
362                    // a count of 0.
363                    gammas[i].fValue = 1.0f;
364                    break;
365                } else if (len < 12 + 2 * count) {
366                    SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
367                    return false;
368                }
369
370                const uint16_t* table = (const uint16_t*) (src + 12);
371                if (1 == count) {
372                    // The table entry is the gamma (with a bias of 256).
373                    uint16_t value = read_big_endian_short((const uint8_t*) table);
374                    gammas[i].fValue = value / 256.0f;
375                    SkColorSpacePrintf("gamma %d %g\n", value, gammas[i].fValue);
376                    break;
377                }
378
379                // Check for frequently occurring curves and use a fast approximation.
380                // We do this by sampling a few values and see if they match our expectation.
381                // A more robust solution would be to compare each value in this curve against
382                // a 2.2f curve see if we remain below an error threshold.  At this time,
383                // we haven't seen any images in the wild that make this kind of
384                // calculation necessary.  We encounter identical gamma curves over and
385                // over again, but relatively few variations.
386                if (1024 == count) {
387                    // The magic values were chosen because they match a very common sRGB
388                    // gamma table and the less common Canon sRGB gamma table (which use
389                    // different rounding rules).
390                    if (0 == read_big_endian_short((const uint8_t*) &table[0]) &&
391                            3366 == read_big_endian_short((const uint8_t*) &table[257]) &&
392                            14116 == read_big_endian_short((const uint8_t*) &table[513]) &&
393                            34318 == read_big_endian_short((const uint8_t*) &table[768]) &&
394                            65535 == read_big_endian_short((const uint8_t*) &table[1023])) {
395                        gammas[i].fValue = 2.2f;
396                        break;
397                    }
398                } else if (26 == count) {
399                    // The magic values were chosen because they match a very common sRGB
400                    // gamma table.
401                    if (0 == read_big_endian_short((const uint8_t*) &table[0]) &&
402                            3062 == read_big_endian_short((const uint8_t*) &table[6]) &&
403                            12824 == read_big_endian_short((const uint8_t*) &table[12]) &&
404                            31237 == read_big_endian_short((const uint8_t*) &table[18]) &&
405                            65535 == read_big_endian_short((const uint8_t*) &table[25])) {
406                        gammas[i].fValue = 2.2f;
407                        break;
408                    }
409                } else if (4096 == count) {
410                    // The magic values were chosen because they match Nikon, Epson, and
411                    // LCMS sRGB gamma tables (all of which use different rounding rules).
412                    if (0 == read_big_endian_short((const uint8_t*) &table[0]) &&
413                            950 == read_big_endian_short((const uint8_t*) &table[515]) &&
414                            3342 == read_big_endian_short((const uint8_t*) &table[1025]) &&
415                            14079 == read_big_endian_short((const uint8_t*) &table[2051]) &&
416                            65535 == read_big_endian_short((const uint8_t*) &table[4095])) {
417                        gammas[i].fValue = 2.2f;
418                        break;
419                    }
420                }
421
422                // Otherwise, fill in the interpolation table.
423                gammas[i].fTableSize = count;
424                gammas[i].fTable = std::unique_ptr<float[]>(new float[count]);
425                for (uint32_t j = 0; j < count; j++) {
426                    gammas[i].fTable[j] =
427                            (read_big_endian_short((const uint8_t*) &table[j])) / 65535.0f;
428                }
429                break;
430            }
431            case kTAG_ParaCurveType:
432                // Guess 2.2f.
433                // FIXME (msarett): Handle parametric curves.
434                SkColorSpacePrintf("parametric curve\n");
435                gammas[i].fValue = 2.2f;
436
437                // Determine the size of the parametric curve tag.
438                switch(read_big_endian_short(src + 8)) {
439                    case 0:
440                        tagBytes = 12 + 4;
441                        break;
442                    case 1:
443                        tagBytes = 12 + 12;
444                        break;
445                    case 2:
446                        tagBytes = 12 + 16;
447                        break;
448                    case 3:
449                        tagBytes = 12 + 20;
450                        break;
451                    case 4:
452                        tagBytes = 12 + 28;
453                        break;
454                    default:
455                        SkColorSpacePrintf("Invalid parametric curve type\n");
456                        return false;
457                }
458                break;
459            default:
460                SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
461                return false;
462        }
463
464        // Adjust src and len if there is another gamma curve to load.
465        if (i != numGammas - 1) {
466            // Each curve is padded to 4-byte alignment.
467            tagBytes = SkAlign4(tagBytes);
468            if (len < tagBytes) {
469                return false;
470            }
471
472            src += tagBytes;
473            len -= tagBytes;
474        }
475    }
476
477    return true;
478}
479
480static const uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' ');
481
482bool SkColorSpace::LoadColorLUT(SkColorLookUpTable* colorLUT, uint32_t inputChannels,
483                                uint32_t outputChannels, const uint8_t* src, size_t len) {
484    if (len < 20) {
485        SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len);
486        return false;
487    }
488
489    SkASSERT(inputChannels <= SkColorLookUpTable::kMaxChannels && 3 == outputChannels);
490    colorLUT->fInputChannels = inputChannels;
491    colorLUT->fOutputChannels = outputChannels;
492    uint32_t numEntries = 1;
493    for (uint32_t i = 0; i < inputChannels; i++) {
494        colorLUT->fGridPoints[i] = src[i];
495        numEntries *= src[i];
496    }
497    numEntries *= outputChannels;
498
499    // Space is provided for a maximum of the 16 input channels.  Now we determine the precision
500    // of the table values.
501    uint8_t precision = src[16];
502    switch (precision) {
503        case 1: // 8-bit data
504        case 2: // 16-bit data
505            break;
506        default:
507            SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit.\n", len);
508            return false;
509    }
510
511    if (len < 20 + numEntries * precision) {
512        SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len);
513        return false;
514    }
515
516    // Movable struct colorLUT has ownership of fTable.
517    colorLUT->fTable = std::unique_ptr<float[]>(new float[numEntries]);
518    const uint8_t* ptr = src + 20;
519    for (uint32_t i = 0; i < numEntries; i++, ptr += precision) {
520        if (1 == precision) {
521            colorLUT->fTable[i] = ((float) ptr[i]) / 255.0f;
522        } else {
523            colorLUT->fTable[i] = ((float) read_big_endian_short(ptr)) / 65535.0f;
524        }
525    }
526
527    return true;
528}
529
530bool load_matrix(SkMatrix44* toXYZ, const uint8_t* src, size_t len) {
531    if (len < 48) {
532        SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len);
533        return false;
534    }
535
536    float array[16];
537    array[ 0] = SkFixedToFloat(read_big_endian_int(src));
538    array[ 1] = SkFixedToFloat(read_big_endian_int(src + 4));
539    array[ 2] = SkFixedToFloat(read_big_endian_int(src + 8));
540    array[ 3] = 0;
541    array[ 4] = SkFixedToFloat(read_big_endian_int(src + 12));
542    array[ 5] = SkFixedToFloat(read_big_endian_int(src + 16));
543    array[ 6] = SkFixedToFloat(read_big_endian_int(src + 20));
544    array[ 7] = 0;
545    array[ 8] = SkFixedToFloat(read_big_endian_int(src + 24));
546    array[ 9] = SkFixedToFloat(read_big_endian_int(src + 28));
547    array[10] = SkFixedToFloat(read_big_endian_int(src + 32));
548    array[11] = 0;
549    array[12] = SkFixedToFloat(read_big_endian_int(src + 36));  // translate R
550    array[13] = SkFixedToFloat(read_big_endian_int(src + 40));  // translate G
551    array[14] = SkFixedToFloat(read_big_endian_int(src + 44));
552    array[15] = 1;
553    toXYZ->setColMajorf(array);
554    return true;
555}
556
557bool SkColorSpace::LoadA2B0(SkColorLookUpTable* colorLUT, SkGammaCurve* gammas, SkMatrix44* toXYZ,
558                            const uint8_t* src, size_t len) {
559    if (len < 32) {
560        SkColorSpacePrintf("A to B tag is too small (%d bytes).", len);
561        return false;
562    }
563
564    uint32_t type = read_big_endian_uint(src);
565    if (kTAG_AtoBType != type) {
566        // FIXME (msarett): Need to support lut8Type and lut16Type.
567        SkColorSpacePrintf("Unsupported A to B tag type.\n");
568        return false;
569    }
570
571    // Read the number of channels.  The four bytes that we skipped are reserved and
572    // must be zero.
573    uint8_t inputChannels = src[8];
574    uint8_t outputChannels = src[9];
575    if (0 == inputChannels || inputChannels > SkColorLookUpTable::kMaxChannels ||
576            3 != outputChannels) {
577        // The color LUT assumes that there are at most 16 input channels.  For RGB
578        // profiles, output channels should be 3.
579        SkColorSpacePrintf("Too many input or output channels in A to B tag.\n");
580        return false;
581    }
582
583    // Read the offsets of each element in the A to B tag.  With the exception of A curves and
584    // B curves (which we do not yet support), we will handle these elements in the order in
585    // which they should be applied (rather than the order in which they occur in the tag).
586    // If the offset is non-zero it indicates that the element is present.
587    uint32_t offsetToACurves = read_big_endian_int(src + 28);
588    uint32_t offsetToBCurves = read_big_endian_int(src + 12);
589    if ((0 != offsetToACurves) || (0 != offsetToBCurves)) {
590        // FIXME (msarett): Handle A and B curves.
591        // Note that the A curve is technically required in order to have a color LUT.
592        // However, all the A curves I have seen so far have are just placeholders that
593        // don't actually transform the data.
594        SkColorSpacePrintf("Ignoring A and/or B curve.  Output may be wrong.\n");
595    }
596
597    uint32_t offsetToColorLUT = read_big_endian_int(src + 24);
598    if (0 != offsetToColorLUT && offsetToColorLUT < len) {
599        if (!SkColorSpace::LoadColorLUT(colorLUT, inputChannels, outputChannels,
600                                        src + offsetToColorLUT, len - offsetToColorLUT)) {
601            SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n");
602        }
603    }
604
605    uint32_t offsetToMCurves = read_big_endian_int(src + 20);
606    if (0 != offsetToMCurves && offsetToMCurves < len) {
607        if (!SkColorSpace::LoadGammas(gammas, outputChannels, src + offsetToMCurves,
608                                      len - offsetToMCurves)) {
609            SkColorSpacePrintf("Failed to read M curves from A to B tag.\n");
610        }
611    }
612
613    uint32_t offsetToMatrix = read_big_endian_int(src + 16);
614    if (0 != offsetToMatrix && offsetToMatrix < len) {
615        if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) {
616            SkColorSpacePrintf("Failed to read matrix from A to B tag.\n");
617        }
618    }
619
620    return true;
621}
622
623sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* base, size_t len) {
624    const uint8_t* ptr = (const uint8_t*) base;
625
626    if (len < kICCHeaderSize) {
627        return_null("Data is not large enough to contain an ICC profile");
628    }
629
630    // Read the ICC profile header and check to make sure that it is valid.
631    ICCProfileHeader header;
632    header.init(ptr, len);
633    if (!header.valid()) {
634        return nullptr;
635    }
636
637    // Adjust ptr and len before reading the tags.
638    if (len < header.fSize) {
639        SkColorSpacePrintf("ICC profile might be truncated.\n");
640    } else if (len > header.fSize) {
641        SkColorSpacePrintf("Caller provided extra data beyond the end of the ICC profile.\n");
642        len = header.fSize;
643    }
644    ptr += kICCHeaderSize;
645    len -= kICCHeaderSize;
646
647    // Parse tag headers.
648    uint32_t tagCount = header.fTagCount;
649    SkColorSpacePrintf("ICC profile contains %d tags.\n", tagCount);
650    if (len < kICCTagTableEntrySize * tagCount) {
651        return_null("Not enough input data to read tag table entries");
652    }
653
654    SkAutoTArray<ICCTag> tags(tagCount);
655    for (uint32_t i = 0; i < tagCount; i++) {
656        ptr = tags[i].init(ptr);
657        SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24) & 0xFF,
658                (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >>  8) & 0xFF,
659                (tags[i].fSignature >>  0) & 0xFF, tags[i].fOffset, tags[i].fLength);
660
661        if (!tags[i].valid(kICCHeaderSize + len)) {
662            return_null("Tag is too large to fit in ICC profile");
663        }
664    }
665
666    switch (header.fInputColorSpace) {
667        case kRGB_ColorSpace: {
668            // Recognize the rXYZ, gXYZ, and bXYZ tags.
669            const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ);
670            const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ);
671            const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ);
672            if (r && g && b) {
673                float toXYZ[9];
674                if (!load_xyz(&toXYZ[0], r->addr((const uint8_t*) base), r->fLength) ||
675                    !load_xyz(&toXYZ[3], g->addr((const uint8_t*) base), g->fLength) ||
676                    !load_xyz(&toXYZ[6], b->addr((const uint8_t*) base), b->fLength))
677                {
678                    return_null("Need valid rgb tags for XYZ space");
679                }
680
681                // It is not uncommon to see missing or empty gamma tags.  This indicates
682                // that we should use unit gamma.
683                SkGammaCurve curves[3];
684                r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC);
685                g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC);
686                b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC);
687                if (!r || !SkColorSpace::LoadGammas(&curves[0], 1,
688                                                    r->addr((const uint8_t*) base), r->fLength)) {
689                    SkColorSpacePrintf("Failed to read R gamma tag.\n");
690                }
691                if (!g || !SkColorSpace::LoadGammas(&curves[1], 1,
692                                                    g->addr((const uint8_t*) base), g->fLength)) {
693                    SkColorSpacePrintf("Failed to read G gamma tag.\n");
694                }
695                if (!b || !SkColorSpace::LoadGammas(&curves[2], 1,
696                                                    b->addr((const uint8_t*) base), b->fLength)) {
697                    SkColorSpacePrintf("Failed to read B gamma tag.\n");
698                }
699
700                sk_sp<SkGammas> gammas(new SkGammas(std::move(curves[0]), std::move(curves[1]),
701                                                    std::move(curves[2])));
702                SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
703                mat.set3x3ColMajorf(toXYZ);
704                if (gammas->isValues()) {
705                    // When we have values, take advantage of the NewFromRGB initializer.
706                    // This allows us to check for canonical sRGB and Adobe RGB.
707                    float gammaVals[3];
708                    gammaVals[0] = gammas->fRed.fValue;
709                    gammaVals[1] = gammas->fGreen.fValue;
710                    gammaVals[2] = gammas->fBlue.fValue;
711                    return SkColorSpace::NewRGB(gammaVals, mat);
712                } else {
713                    return sk_sp<SkColorSpace>(new SkColorSpace(gammas, mat, kUnknown_Named));
714                }
715            }
716
717            // Recognize color profile specified by A2B0 tag.
718            const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0);
719            if (a2b0) {
720                SkAutoTDelete<SkColorLookUpTable> colorLUT(new SkColorLookUpTable());
721                SkGammaCurve curves[3];
722                SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor);
723                if (!SkColorSpace::LoadA2B0(colorLUT, curves, &toXYZ,
724                                            a2b0->addr((const uint8_t*) base), a2b0->fLength)) {
725                    return_null("Failed to parse A2B0 tag");
726                }
727
728                sk_sp<SkGammas> gammas(new SkGammas(std::move(curves[0]), std::move(curves[1]),
729                                                    std::move(curves[2])));
730                if (colorLUT->fTable) {
731                    return sk_sp<SkColorSpace>(new SkColorSpace(colorLUT.release(), gammas, toXYZ));
732                } else if (gammas->isValues()) {
733                    // When we have values, take advantage of the NewFromRGB initializer.
734                    // This allows us to check for canonical sRGB and Adobe RGB.
735                    float gammaVals[3];
736                    gammaVals[0] = gammas->fRed.fValue;
737                    gammaVals[1] = gammas->fGreen.fValue;
738                    gammaVals[2] = gammas->fBlue.fValue;
739                    return SkColorSpace::NewRGB(gammaVals, toXYZ);
740                } else {
741                    return sk_sp<SkColorSpace>(new SkColorSpace(gammas, toXYZ, kUnknown_Named));
742                }
743            }
744
745        }
746        default:
747            break;
748    }
749
750    return_null("ICC profile contains unsupported colorspace");
751}
752