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

Unified Diff: src/core/SkColorSpace.cpp

Issue 1707033002: parse icc profiles Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 4 years, 10 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/core/SkColorSpace.h ('k') | tests/ColorSpaceTest.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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) {
« no previous file with comments | « src/core/SkColorSpace.h ('k') | tests/ColorSpaceTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698