| Index: src/core/SkColorSpace.cpp
|
| diff --git a/src/core/SkColorSpace.cpp b/src/core/SkColorSpace.cpp
|
| index 6651abb3fca864086418a36be5bfb066e42dd3b0..5dfb9d8da59061929389c9eef92723c7e7d80c16 100644
|
| --- a/src/core/SkColorSpace.cpp
|
| +++ b/src/core/SkColorSpace.cpp
|
| @@ -114,11 +114,13 @@ SkColorSpace* SkColorSpace::NewRGB(const SkFloat3x3& toXYZD50, const SkFloat3& g
|
| }
|
| }
|
|
|
| +#if 0
|
| // check the matrix for invertibility
|
| float d = det(toXYZD50);
|
| if (!SkFloatIsFinite(d) || !SkFloatIsFinite(1 / d)) {
|
| return nullptr;
|
| }
|
| +#endif
|
|
|
| return new SkColorSpace(toXYZD50, gamma, kUnknown_Named);
|
| }
|
| @@ -157,6 +159,243 @@ SkColorSpace* SkColorSpace::NewNamed(Named named) {
|
| }
|
|
|
| ///////////////////////////////////////////////////////////////////////////////////////////////////
|
| +#include "SkEndian.h"
|
| +#include "SkStream.h"
|
| +
|
| +#define return_if_false(pred, msg) \
|
| + do { if (!(pred)) { SkDebugf("parsing icc profile: %s\n", msg); return false; } } while (0)
|
| +
|
| +#define return_null(msg) do { SkDebugf("ICC Profile: %s\n", (msg)); return nullptr; } while (0)
|
| +
|
| +const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' ');
|
| +const uint32_t kGray_ColorSpace = SkSetFourByteTag('G', 'R', 'A', 'Y');
|
| +
|
| +const uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z');
|
| +const uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z');
|
| +const uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z');
|
| +
|
| +const uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C');
|
| +const uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C');
|
| +const uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C');
|
| +
|
| +struct ICCProfileHeader {
|
| + uint32_t fSize;
|
| + uint32_t fCMMType;
|
| + uint32_t fVersion;
|
| + uint32_t fClassProfile;
|
| + uint32_t fColorSpace;
|
| + uint32_t fPCS;
|
| + uint32_t fCreated[3];
|
| + uint32_t fSignature;
|
| + uint32_t fPlatformTarget_ignored;
|
| + uint32_t fFlags_ignored;
|
| + uint32_t fManufacturer_ignored;
|
| + uint32_t fDeviceModel_ignored;
|
| + uint32_t fDeviceAttributes_ignored[2];
|
| + uint32_t fRenderingIntent;
|
| + uint32_t fIlluminantXYZ_ignored[3];
|
| + uint32_t fCreator_ignored;
|
| +
|
| + uint32_t fReserved_ignored[11];
|
| +
|
| + bool init(const uint32_t data[], size_t length) {
|
| + SkASSERT(128 == sizeof(*this));
|
| + if (length < 128) {
|
| + return false;
|
| + }
|
| + uint32_t* ptr = &fSize;
|
| + for (int i = 0; i < 32; ++i) {
|
| + ptr[i] = SkEndian_SwapBE32(data[i]);
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + bool valid() const {
|
| + return_if_false(fSize > 128, "size too small");
|
| +
|
| + uint8_t version[4];
|
| + memcpy(version, &fVersion, sizeof(version));
|
| + return_if_false((version[3] == 2 || version[3] == 4) && version[0] == 0 && version[1] == 0,
|
| + "unsupported version");
|
| +
|
| + const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r');
|
| + const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r');
|
| + const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r');
|
| + return_if_false(fClassProfile == kDisplay_Profile ||
|
| + fClassProfile == kInput_Profile ||
|
| + fClassProfile == kOutput_Profile,
|
| + "unsupported class profile");
|
| +
|
| + return_if_false(fColorSpace == kRGB_ColorSpace ||
|
| + fColorSpace == kGray_ColorSpace,
|
| + "unsupported class profile");
|
| +
|
| + const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' ');
|
| + return_if_false(fPCS == kXYZ_PCSSpace,
|
| + "unsupported class profile");
|
| +
|
| + return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "bad signature");
|
| +
|
| + enum ICCIntents {
|
| + kPerceptual = 0,
|
| + kRelativeColormetric = 1,
|
| + kSaturation = 2,
|
| + kAbsoluteColorMetric = 3,
|
| + };
|
| + return_if_false(fRenderingIntent >= 0 || fRenderingIntent <= 3, "bad rendering intent");
|
| +
|
| + return true;
|
| + }
|
| +};
|
| +
|
| +struct ICCTag {
|
| + uint32_t fSignature;
|
| + uint32_t fOffset;
|
| + uint32_t fLength;
|
| +
|
| + const uint32_t* init(const uint32_t data[]) {
|
| + fSignature = SkEndian_SwapBE32(data[0]);
|
| + fOffset = SkEndian_SwapBE32(data[1]);
|
| + fLength = SkEndian_SwapBE32(data[2]);
|
| + return data + 3;
|
| + }
|
| +
|
| + const uint32_t* addr(const uint32_t* base) const {
|
| + SkASSERT(SkIsAlign4(fOffset));
|
| + return base + (fOffset >> 2);
|
| + }
|
| +
|
| + static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature) {
|
| + for (int i = 0; i < count; ++i) {
|
| + if (tags[i].fSignature == signature) {
|
| + return &tags[i];
|
| + }
|
| + }
|
| + return nullptr;
|
| + }
|
| +};
|
| +
|
| +void load_3x(float dst[3], const uint32_t data[]) {
|
| + uint32_t tag = SkEndian_SwapBE32(data[0]);
|
| + dst[0] = SkFixedToFloat(SkEndian_SwapBE32(data[2]));
|
| + dst[1] = SkFixedToFloat(SkEndian_SwapBE32(data[2]));
|
| + dst[2] = SkFixedToFloat(SkEndian_SwapBE32(data[2]));
|
| + SkDebugf("3x tag %08X %g %g %g\n", tag, dst[0], dst[1], dst[2]);
|
| +}
|
| +
|
| +const uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v');
|
| +const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a');
|
| +
|
| +static float load_gamma(const uint32_t data[]) {
|
| + uint32_t type = SkEndian_SwapBE32(data[0]);
|
| + switch (type) {
|
| + case kTAG_CurveType: {
|
| + int count = SkEndian_SwapBE32(data[2]);
|
| + const uint16_t* table = (const uint16_t*)&data[3];
|
| + if (1 == count) { // table entry is the exponent (bias 256)
|
| + unsigned value = SkEndian_SwapBE16(table[0]);
|
| + float gamma = value / 256.0f;
|
| + SkDebugf("gamma %d %g\n", value, gamma);
|
| + return gamma;
|
| + }
|
| + // general interp table (ingored for now)
|
| + for (int i = 0; i < count; ++i) {
|
| + unsigned value = SkEndian_SwapBE16(table[i]);
|
| + SkDebugf("curve[%d] %d\n", i, value);
|
| + }
|
| + return 2.2f;
|
| + } break;
|
| + case kTAG_ParaCurveType: {
|
| + SkDebugf("parametric curve\n");
|
| + return 2.2f;
|
| + } break;
|
| + default:
|
| + return 1;
|
| + }
|
| +}
|
| +
|
| +SkColorSpace* SkColorSpace::NewICCProfile32(const uint32_t base[], size_t length) {
|
| + const uint32_t storedSize = SkEndian_SwapBE32(base[0]);
|
| + if (storedSize < 128+4) {
|
| + return nullptr; // too small
|
| + }
|
| + if (length > storedSize) {
|
| + length = storedSize;
|
| + }
|
| +
|
| + ICCProfileHeader header;
|
| + if (!header.init(base, length) || !header.valid()) {
|
| + return nullptr;
|
| + }
|
| +
|
| + const uint32_t* data = base + (128/4);
|
| + int tagCount = SkEndian_SwapBE32(*data++);
|
| + if (tagCount < 0 || tagCount > 100) {
|
| + return nullptr;
|
| + }
|
| +
|
| + SkDebugf("%d tags\n", tagCount);
|
| + SkAutoTArray<ICCTag> tags(tagCount);
|
| + for (int i = 0; i < tagCount; ++i) {
|
| + data = tags[i].init(data);
|
| + SkDebugf("[%d] %c%c%c%c %d %d\n", i,
|
| + (tags[i].fSignature >> 24) & 0xFF, (tags[i].fSignature >> 16) & 0xFF,
|
| + (tags[i].fSignature >> 8) & 0xFF, (tags[i].fSignature >> 0) & 0xFF,
|
| + tags[i].fOffset, tags[i].fLength);
|
| + }
|
| +
|
| + SkFloat3x3 toXYZ;
|
| + SkFloat3 gamma {{ 1, 1, 1 }};
|
| +
|
| + // Load up our matrix
|
| + switch (header.fColorSpace) {
|
| + case kRGB_ColorSpace: {
|
| + const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ);
|
| + const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ);
|
| + const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ);
|
| + if (!r || !g || !b) {
|
| + return_null("needed rgb tags for XYZ space");
|
| + }
|
| + load_3x(&toXYZ.fMat[0], r->addr(base));
|
| + load_3x(&toXYZ.fMat[3], g->addr(base));
|
| + load_3x(&toXYZ.fMat[6], b->addr(base));
|
| +
|
| + r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC);
|
| + g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC);
|
| + b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC);
|
| + if (r) {
|
| + gamma.fVec[0] = load_gamma(r->addr(base));
|
| + }
|
| + if (g) {
|
| + gamma.fVec[1] = load_gamma(g->addr(base));
|
| + }
|
| + if (b) {
|
| + gamma.fVec[2] = load_gamma(b->addr(base));
|
| + }
|
| + return SkColorSpace::NewRGB(toXYZ, gamma);
|
| + }
|
| + default:
|
| + break;
|
| + }
|
| + return_null("unsupported colorspace");
|
| +}
|
| +
|
| +SkColorSpace* SkColorSpace::NewICCProfile(const void* ptr, size_t length) {
|
| + const uint32_t* data;
|
| + SkAutoTArray<uint32_t> storage;
|
| +
|
| + if ((intptr_t)ptr & 3) { // not 4-byte aligned
|
| + storage.reset(SkAlign4(length) >> 2);
|
| + memcpy(storage.get(), ptr, length);
|
| + data = storage.get();
|
| + } else {
|
| + data = (const uint32_t*)ptr;
|
| + }
|
| + return NewICCProfile32(data, length);
|
| +}
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////////////////////////
|
| +///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
| SkColorSpace::Result SkColorSpace::Concat(const SkColorSpace* src, const SkColorSpace* dst,
|
| SkFloat3x3* result) {
|
|
|