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) { |