Index: src/core/SkColorSpace.cpp |
diff --git a/src/core/SkColorSpace.cpp b/src/core/SkColorSpace.cpp |
index f988c7d3232e3468a2cb6831be9ecdb285c327db..894c4b99b7564c0040be2b8826c47d3efcdf5c30 100644 |
--- a/src/core/SkColorSpace.cpp |
+++ b/src/core/SkColorSpace.cpp |
@@ -7,18 +7,8 @@ |
#include "SkColorSpace.h" |
#include "SkColorSpace_Base.h" |
-#include "SkEndian.h" |
+#include "SkColorSpacePriv.h" |
#include "SkOnce.h" |
-#include "SkReadBuffer.h" |
-#include "SkWriteBuffer.h" |
- |
-#define SkColorSpacePrintf(...) |
- |
-static bool color_space_almost_equal(float a, float b) { |
- return SkTAbs(a - b) < 0.01f; |
-} |
- |
-////////////////////////////////////////////////////////////////////////////////////////////////// |
SkColorSpace::SkColorSpace(GammaNamed gammaNamed, const SkMatrix44& toXYZD50, Named named) |
: fGammaNamed(gammaNamed) |
@@ -77,19 +67,6 @@ static bool xyz_almost_equal(const SkMatrix44& toXYZD50, const float* standard) |
color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f); |
} |
-static void set_gamma_value(SkGammaCurve* gamma, float value) { |
- if (color_space_almost_equal(2.2f, value)) { |
- gamma->fNamed = SkColorSpace::k2Dot2Curve_GammaNamed; |
- } else if (color_space_almost_equal(1.0f, value)) { |
- gamma->fNamed = SkColorSpace::kLinear_GammaNamed; |
- } else if (color_space_almost_equal(0.0f, value)) { |
- SkColorSpacePrintf("Treating invalid zero gamma as linear."); |
- gamma->fNamed = SkColorSpace::kLinear_GammaNamed; |
- } else { |
- gamma->fValue = value; |
- } |
-} |
- |
sk_sp<SkColorSpace> SkColorSpace_Base::NewRGB(float values[3], const SkMatrix44& toXYZD50) { |
SkGammaCurve curves[3]; |
set_gamma_value(&curves[0], values[0]); |
@@ -164,1052 +141,6 @@ sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) { |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
-#include "SkFixed.h" |
-#include "SkTemplates.h" |
- |
-#define return_if_false(pred, msg) \ |
- do { \ |
- if (!(pred)) { \ |
- SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ |
- return false; \ |
- } \ |
- } while (0) |
- |
-#define return_null(msg) \ |
- do { \ |
- SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ |
- return nullptr; \ |
- } while (0) |
- |
-static uint16_t read_big_endian_short(const uint8_t* ptr) { |
- return ptr[0] << 8 | ptr[1]; |
-} |
- |
-static uint32_t read_big_endian_uint(const uint8_t* ptr) { |
- return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; |
-} |
- |
-static int32_t read_big_endian_int(const uint8_t* ptr) { |
- return (int32_t) read_big_endian_uint(ptr); |
-} |
- |
-// This is equal to the header size according to the ICC specification (128) |
-// plus the size of the tag count (4). We include the tag count since we |
-// always require it to be present anyway. |
-static constexpr size_t kICCHeaderSize = 132; |
- |
-// Contains a signature (4), offset (4), and size (4). |
-static constexpr size_t kICCTagTableEntrySize = 12; |
- |
-static constexpr uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' '); |
-static constexpr uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r'); |
-static constexpr uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r'); |
-static constexpr uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r'); |
-static constexpr uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' '); |
-static constexpr uint32_t kACSP_Signature = SkSetFourByteTag('a', 'c', 's', 'p'); |
- |
-struct ICCProfileHeader { |
- uint32_t fSize; |
- |
- // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.). |
- // We're always going to use this one. |
- uint32_t fCMMType_ignored; |
- |
- uint32_t fVersion; |
- uint32_t fProfileClass; |
- uint32_t fInputColorSpace; |
- uint32_t fPCS; |
- uint32_t fDateTime_ignored[3]; |
- uint32_t fSignature; |
- |
- // Indicates the platform that this profile was created for (ex: Apple, Microsoft). This |
- // doesn't really matter to us. |
- uint32_t fPlatformTarget_ignored; |
- |
- // Flags can indicate: |
- // (1) Whether this profile was embedded in a file. This flag is consistently wrong. |
- // Ex: The profile came from a file but indicates that it did not. |
- // (2) Whether we are allowed to use the profile independently of the color data. If set, |
- // this may allow us to use the embedded profile for testing separate from the original |
- // image. |
- uint32_t fFlags_ignored; |
- |
- // We support many output devices. It doesn't make sense to think about the attributes of |
- // the device in the context of the image profile. |
- uint32_t fDeviceManufacturer_ignored; |
- uint32_t fDeviceModel_ignored; |
- uint32_t fDeviceAttributes_ignored[2]; |
- |
- uint32_t fRenderingIntent; |
- int32_t fIlluminantXYZ[3]; |
- |
- // We don't care who created the profile. |
- uint32_t fCreator_ignored; |
- |
- // This is an MD5 checksum. Could be useful for checking if profiles are equal. |
- uint32_t fProfileId_ignored[4]; |
- |
- // Reserved for future use. |
- uint32_t fReserved_ignored[7]; |
- |
- uint32_t fTagCount; |
- |
- void init(const uint8_t* src, size_t len) { |
- SkASSERT(kICCHeaderSize == sizeof(*this)); |
- |
- uint32_t* dst = (uint32_t*) this; |
- for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) { |
- dst[i] = read_big_endian_uint(src); |
- } |
- } |
- |
- bool valid() const { |
- return_if_false(fSize >= kICCHeaderSize, "Size is too small"); |
- |
- uint8_t majorVersion = fVersion >> 24; |
- return_if_false(majorVersion <= 4, "Unsupported version"); |
- |
- // These are the three basic classes of profiles that we might expect to see embedded |
- // in images. Four additional classes exist, but they generally are used as a convenient |
- // way for CMMs to store calculated transforms. |
- return_if_false(fProfileClass == kDisplay_Profile || |
- fProfileClass == kInput_Profile || |
- fProfileClass == kOutput_Profile, |
- "Unsupported profile"); |
- |
- // TODO (msarett): |
- // All the profiles we've tested so far use RGB as the input color space. |
- return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space"); |
- |
- // TODO (msarett): |
- // All the profiles we've tested so far use XYZ as the profile connection space. |
- return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space"); |
- |
- return_if_false(fSignature == kACSP_Signature, "Bad signature"); |
- |
- // TODO (msarett): |
- // Should we treat different rendering intents differently? |
- // Valid rendering intents include kPerceptual (0), kRelative (1), |
- // kSaturation (2), and kAbsolute (3). |
- return_if_false(fRenderingIntent <= 3, "Bad rendering intent"); |
- |
- return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0]), 0.96420f) && |
- color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1]), 1.00000f) && |
- color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2]), 0.82491f), |
- "Illuminant must be D50"); |
- |
- return_if_false(fTagCount <= 100, "Too many tags"); |
- |
- return true; |
- } |
-}; |
- |
-template <class T> |
-static bool safe_add(T arg1, T arg2, size_t* result) { |
- SkASSERT(arg1 >= 0); |
- SkASSERT(arg2 >= 0); |
- if (arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) { |
- T sum = arg1 + arg2; |
- if (sum <= std::numeric_limits<size_t>::max()) { |
- *result = static_cast<size_t>(sum); |
- return true; |
- } |
- } |
- return false; |
-} |
- |
-static bool safe_mul(uint32_t arg1, uint32_t arg2, uint32_t* result) { |
- uint64_t product64 = (uint64_t) arg1 * (uint64_t) arg2; |
- uint32_t product32 = (uint32_t) product64; |
- if (product32 != product64) { |
- return false; |
- } |
- |
- *result = product32; |
- return true; |
-} |
- |
-struct ICCTag { |
- uint32_t fSignature; |
- uint32_t fOffset; |
- uint32_t fLength; |
- |
- const uint8_t* init(const uint8_t* src) { |
- fSignature = read_big_endian_uint(src); |
- fOffset = read_big_endian_uint(src + 4); |
- fLength = read_big_endian_uint(src + 8); |
- return src + 12; |
- } |
- |
- bool valid(size_t len) { |
- size_t tagEnd; |
- return_if_false(safe_add(fOffset, fLength, &tagEnd), |
- "Tag too large, overflows integer addition"); |
- return_if_false(tagEnd <= len, "Tag too large for ICC profile"); |
- return true; |
- } |
- |
- const uint8_t* addr(const uint8_t* src) const { |
- return src + fOffset; |
- } |
- |
- 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; |
- } |
-}; |
- |
-static constexpr uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z'); |
-static constexpr uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z'); |
-static constexpr uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z'); |
-static constexpr uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C'); |
-static constexpr uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C'); |
-static constexpr uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C'); |
-static constexpr uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0'); |
- |
-static bool load_xyz(float dst[3], const uint8_t* src, size_t len) { |
- if (len < 20) { |
- SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len); |
- return false; |
- } |
- |
- dst[0] = SkFixedToFloat(read_big_endian_int(src + 8)); |
- dst[1] = SkFixedToFloat(read_big_endian_int(src + 12)); |
- dst[2] = SkFixedToFloat(read_big_endian_int(src + 16)); |
- SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]); |
- return true; |
-} |
- |
-static constexpr uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v'); |
-static constexpr uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a'); |
- |
-static bool load_gammas(SkGammaCurve* gammas, uint32_t numGammas, const uint8_t* src, size_t len) { |
- for (uint32_t i = 0; i < numGammas; i++) { |
- if (len < 12) { |
- // FIXME (msarett): |
- // We could potentially return false here after correctly parsing *some* of the |
- // gammas correctly. Should we somehow try to indicate a partial success? |
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
- return false; |
- } |
- |
- // We need to count the number of bytes in the tag, so we are able to move to the |
- // next tag on the next loop iteration. |
- size_t tagBytes; |
- |
- uint32_t type = read_big_endian_uint(src); |
- switch (type) { |
- case kTAG_CurveType: { |
- uint32_t count = read_big_endian_uint(src + 8); |
- |
- // tagBytes = 12 + 2 * count |
- // We need to do safe addition here to avoid integer overflow. |
- if (!safe_add(count, count, &tagBytes) || |
- !safe_add((size_t) 12, tagBytes, &tagBytes)) |
- { |
- SkColorSpacePrintf("Invalid gamma count"); |
- return false; |
- } |
- |
- if (0 == count) { |
- // Some tags require a gamma curve, but the author doesn't actually want |
- // to transform the data. In this case, it is common to see a curve with |
- // a count of 0. |
- gammas[i].fNamed = SkColorSpace::kLinear_GammaNamed; |
- break; |
- } else if (len < tagBytes) { |
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
- return false; |
- } |
- |
- const uint16_t* table = (const uint16_t*) (src + 12); |
- if (1 == count) { |
- // The table entry is the gamma (with a bias of 256). |
- float value = (read_big_endian_short((const uint8_t*) table)) / 256.0f; |
- set_gamma_value(&gammas[i], value); |
- SkColorSpacePrintf("gamma %g\n", value); |
- break; |
- } |
- |
- // Check for frequently occurring sRGB curves. |
- // We do this by sampling a few values and see if they match our expectation. |
- // A more robust solution would be to compare each value in this curve against |
- // an sRGB curve to see if we remain below an error threshold. At this time, |
- // we haven't seen any images in the wild that make this kind of |
- // calculation necessary. We encounter identical gamma curves over and |
- // over again, but relatively few variations. |
- if (1024 == count) { |
- // The magic values were chosen because they match a very common sRGB |
- // gamma table and the less common Canon sRGB gamma table (which use |
- // different rounding rules). |
- if (0 == read_big_endian_short((const uint8_t*) &table[0]) && |
- 3366 == read_big_endian_short((const uint8_t*) &table[257]) && |
- 14116 == read_big_endian_short((const uint8_t*) &table[513]) && |
- 34318 == read_big_endian_short((const uint8_t*) &table[768]) && |
- 65535 == read_big_endian_short((const uint8_t*) &table[1023])) { |
- gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed; |
- break; |
- } |
- } else if (26 == count) { |
- // The magic values were chosen because they match a very common sRGB |
- // gamma table. |
- if (0 == read_big_endian_short((const uint8_t*) &table[0]) && |
- 3062 == read_big_endian_short((const uint8_t*) &table[6]) && |
- 12824 == read_big_endian_short((const uint8_t*) &table[12]) && |
- 31237 == read_big_endian_short((const uint8_t*) &table[18]) && |
- 65535 == read_big_endian_short((const uint8_t*) &table[25])) { |
- gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed; |
- break; |
- } |
- } else if (4096 == count) { |
- // The magic values were chosen because they match Nikon, Epson, and |
- // LCMS sRGB gamma tables (all of which use different rounding rules). |
- if (0 == read_big_endian_short((const uint8_t*) &table[0]) && |
- 950 == read_big_endian_short((const uint8_t*) &table[515]) && |
- 3342 == read_big_endian_short((const uint8_t*) &table[1025]) && |
- 14079 == read_big_endian_short((const uint8_t*) &table[2051]) && |
- 65535 == read_big_endian_short((const uint8_t*) &table[4095])) { |
- gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed; |
- break; |
- } |
- } |
- |
- // Otherwise, fill in the interpolation table. |
- gammas[i].fTableSize = count; |
- gammas[i].fTable = std::unique_ptr<float[]>(new float[count]); |
- for (uint32_t j = 0; j < count; j++) { |
- gammas[i].fTable[j] = |
- (read_big_endian_short((const uint8_t*) &table[j])) / 65535.0f; |
- } |
- break; |
- } |
- case kTAG_ParaCurveType: { |
- enum ParaCurveType { |
- kExponential_ParaCurveType = 0, |
- kGAB_ParaCurveType = 1, |
- kGABC_ParaCurveType = 2, |
- kGABDE_ParaCurveType = 3, |
- kGABCDEF_ParaCurveType = 4, |
- }; |
- |
- // Determine the format of the parametric curve tag. |
- uint16_t format = read_big_endian_short(src + 8); |
- if (kExponential_ParaCurveType == format) { |
- tagBytes = 12 + 4; |
- if (len < tagBytes) { |
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
- return false; |
- } |
- |
- // Y = X^g |
- int32_t g = read_big_endian_int(src + 12); |
- set_gamma_value(&gammas[i], SkFixedToFloat(g)); |
- } else { |
- // Here's where the real parametric gammas start. There are many |
- // permutations of the same equations. |
- // |
- // Y = (aX + b)^g + c for X >= d |
- // Y = eX + f otherwise |
- // |
- // We will fill in with zeros as necessary to always match the above form. |
- float g = 0.0f, a = 0.0f, b = 0.0f, c = 0.0f, d = 0.0f, e = 0.0f, f = 0.0f; |
- switch(format) { |
- case kGAB_ParaCurveType: { |
- tagBytes = 12 + 12; |
- if (len < tagBytes) { |
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
- return false; |
- } |
- |
- // Y = (aX + b)^g for X >= -b/a |
- // Y = 0 otherwise |
- g = SkFixedToFloat(read_big_endian_int(src + 12)); |
- a = SkFixedToFloat(read_big_endian_int(src + 16)); |
- if (0.0f == a) { |
- return false; |
- } |
- |
- b = SkFixedToFloat(read_big_endian_int(src + 20)); |
- d = -b / a; |
- break; |
- } |
- case kGABC_ParaCurveType: |
- tagBytes = 12 + 16; |
- if (len < tagBytes) { |
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
- return false; |
- } |
- |
- // Y = (aX + b)^g + c for X >= -b/a |
- // Y = c otherwise |
- g = SkFixedToFloat(read_big_endian_int(src + 12)); |
- a = SkFixedToFloat(read_big_endian_int(src + 16)); |
- if (0.0f == a) { |
- return false; |
- } |
- |
- b = SkFixedToFloat(read_big_endian_int(src + 20)); |
- c = SkFixedToFloat(read_big_endian_int(src + 24)); |
- d = -b / a; |
- f = c; |
- break; |
- case kGABDE_ParaCurveType: |
- tagBytes = 12 + 20; |
- if (len < tagBytes) { |
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
- return false; |
- } |
- |
- // Y = (aX + b)^g for X >= d |
- // Y = cX otherwise |
- g = SkFixedToFloat(read_big_endian_int(src + 12)); |
- a = SkFixedToFloat(read_big_endian_int(src + 16)); |
- b = SkFixedToFloat(read_big_endian_int(src + 20)); |
- d = SkFixedToFloat(read_big_endian_int(src + 28)); |
- e = SkFixedToFloat(read_big_endian_int(src + 24)); |
- break; |
- case kGABCDEF_ParaCurveType: |
- tagBytes = 12 + 28; |
- if (len < tagBytes) { |
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
- return false; |
- } |
- |
- // Y = (aX + b)^g + c for X >= d |
- // Y = eX + f otherwise |
- // NOTE: The ICC spec writes "cX" in place of "eX" but I think |
- // it's a typo. |
- g = SkFixedToFloat(read_big_endian_int(src + 12)); |
- a = SkFixedToFloat(read_big_endian_int(src + 16)); |
- b = SkFixedToFloat(read_big_endian_int(src + 20)); |
- c = SkFixedToFloat(read_big_endian_int(src + 24)); |
- d = SkFixedToFloat(read_big_endian_int(src + 28)); |
- e = SkFixedToFloat(read_big_endian_int(src + 32)); |
- f = SkFixedToFloat(read_big_endian_int(src + 36)); |
- break; |
- default: |
- SkColorSpacePrintf("Invalid parametric curve type\n"); |
- return false; |
- } |
- |
- // Recognize and simplify a very common parametric representation of sRGB gamma. |
- if (color_space_almost_equal(0.9479f, a) && |
- color_space_almost_equal(0.0521f, b) && |
- color_space_almost_equal(0.0000f, c) && |
- color_space_almost_equal(0.0405f, d) && |
- color_space_almost_equal(0.0774f, e) && |
- color_space_almost_equal(0.0000f, f) && |
- color_space_almost_equal(2.4000f, g)) { |
- gammas[i].fNamed = SkColorSpace::kSRGB_GammaNamed; |
- } else { |
- // Fail on invalid gammas. |
- if (d <= 0.0f) { |
- // Y = (aX + b)^g + c for always |
- if (0.0f == a || 0.0f == g) { |
- SkColorSpacePrintf("A or G is zero, constant gamma function " |
- "is nonsense"); |
- return false; |
- } |
- } else if (d >= 1.0f) { |
- // Y = eX + f for always |
- if (0.0f == e) { |
- SkColorSpacePrintf("E is zero, constant gamma function is " |
- "nonsense"); |
- return false; |
- } |
- } else if ((0.0f == a || 0.0f == g) && 0.0f == e) { |
- SkColorSpacePrintf("A or G, and E are zero, constant gamma function " |
- "is nonsense"); |
- return false; |
- } |
- |
- gammas[i].fG = g; |
- gammas[i].fA = a; |
- gammas[i].fB = b; |
- gammas[i].fC = c; |
- gammas[i].fD = d; |
- gammas[i].fE = e; |
- gammas[i].fF = f; |
- } |
- } |
- |
- break; |
- } |
- default: |
- SkColorSpacePrintf("Unsupported gamma tag type %d\n", type); |
- return false; |
- } |
- |
- // Ensure that we have successfully read a gamma representation. |
- SkASSERT(gammas[i].isNamed() || gammas[i].isValue() || gammas[i].isTable() || |
- gammas[i].isParametric()); |
- |
- // Adjust src and len if there is another gamma curve to load. |
- if (i != numGammas - 1) { |
- // Each curve is padded to 4-byte alignment. |
- tagBytes = SkAlign4(tagBytes); |
- if (len < tagBytes) { |
- return false; |
- } |
- |
- src += tagBytes; |
- len -= tagBytes; |
- } |
- } |
- |
- return true; |
-} |
- |
-static constexpr uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' '); |
- |
-bool load_color_lut(SkColorLookUpTable* colorLUT, uint32_t inputChannels, uint32_t outputChannels, |
- const uint8_t* src, size_t len) { |
- // 16 bytes reserved for grid points, 2 for precision, 2 for padding. |
- // The color LUT data follows after this header. |
- static constexpr uint32_t kColorLUTHeaderSize = 20; |
- if (len < kColorLUTHeaderSize) { |
- SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); |
- return false; |
- } |
- size_t dataLen = len - kColorLUTHeaderSize; |
- |
- SkASSERT(3 == inputChannels && 3 == outputChannels); |
- colorLUT->fInputChannels = inputChannels; |
- colorLUT->fOutputChannels = outputChannels; |
- uint32_t numEntries = 1; |
- for (uint32_t i = 0; i < inputChannels; i++) { |
- colorLUT->fGridPoints[i] = src[i]; |
- if (0 == src[i]) { |
- SkColorSpacePrintf("Each input channel must have at least one grid point."); |
- return false; |
- } |
- |
- if (!safe_mul(numEntries, src[i], &numEntries)) { |
- SkColorSpacePrintf("Too many entries in Color LUT."); |
- return false; |
- } |
- } |
- |
- if (!safe_mul(numEntries, outputChannels, &numEntries)) { |
- SkColorSpacePrintf("Too many entries in Color LUT."); |
- return false; |
- } |
- |
- // Space is provided for a maximum of the 16 input channels. Now we determine the precision |
- // of the table values. |
- uint8_t precision = src[16]; |
- switch (precision) { |
- case 1: // 8-bit data |
- case 2: // 16-bit data |
- break; |
- default: |
- SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit.\n"); |
- return false; |
- } |
- |
- uint32_t clutBytes; |
- if (!safe_mul(numEntries, precision, &clutBytes)) { |
- SkColorSpacePrintf("Too many entries in Color LUT."); |
- return false; |
- } |
- |
- if (dataLen < clutBytes) { |
- SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); |
- return false; |
- } |
- |
- // Movable struct colorLUT has ownership of fTable. |
- colorLUT->fTable = std::unique_ptr<float[]>(new float[numEntries]); |
- const uint8_t* ptr = src + kColorLUTHeaderSize; |
- for (uint32_t i = 0; i < numEntries; i++, ptr += precision) { |
- if (1 == precision) { |
- colorLUT->fTable[i] = ((float) ptr[i]) / 255.0f; |
- } else { |
- colorLUT->fTable[i] = ((float) read_big_endian_short(ptr)) / 65535.0f; |
- } |
- } |
- |
- return true; |
-} |
- |
-bool load_matrix(SkMatrix44* toXYZ, const uint8_t* src, size_t len) { |
- if (len < 48) { |
- SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len); |
- return false; |
- } |
- |
- // For this matrix to behave like our "to XYZ D50" matrices, it needs to be scaled. |
- constexpr float scale = 65535.0 / 32768.0; |
- float array[16]; |
- array[ 0] = scale * SkFixedToFloat(read_big_endian_int(src)); |
- array[ 1] = scale * SkFixedToFloat(read_big_endian_int(src + 4)); |
- array[ 2] = scale * SkFixedToFloat(read_big_endian_int(src + 8)); |
- array[ 3] = scale * SkFixedToFloat(read_big_endian_int(src + 36)); // translate R |
- array[ 4] = scale * SkFixedToFloat(read_big_endian_int(src + 12)); |
- array[ 5] = scale * SkFixedToFloat(read_big_endian_int(src + 16)); |
- array[ 6] = scale * SkFixedToFloat(read_big_endian_int(src + 20)); |
- array[ 7] = scale * SkFixedToFloat(read_big_endian_int(src + 40)); // translate G |
- array[ 8] = scale * SkFixedToFloat(read_big_endian_int(src + 24)); |
- array[ 9] = scale * SkFixedToFloat(read_big_endian_int(src + 28)); |
- array[10] = scale * SkFixedToFloat(read_big_endian_int(src + 32)); |
- array[11] = scale * SkFixedToFloat(read_big_endian_int(src + 44)); // translate B |
- array[12] = 0.0f; |
- array[13] = 0.0f; |
- array[14] = 0.0f; |
- array[15] = 1.0f; |
- toXYZ->setColMajorf(array); |
- return true; |
-} |
- |
-bool load_a2b0(SkColorLookUpTable* colorLUT, SkGammaCurve* gammas, SkMatrix44* toXYZ, |
- const uint8_t* src, size_t len) { |
- if (len < 32) { |
- SkColorSpacePrintf("A to B tag is too small (%d bytes).", len); |
- return false; |
- } |
- |
- uint32_t type = read_big_endian_uint(src); |
- if (kTAG_AtoBType != type) { |
- // FIXME (msarett): Need to support lut8Type and lut16Type. |
- SkColorSpacePrintf("Unsupported A to B tag type.\n"); |
- return false; |
- } |
- |
- // Read the number of channels. The four bytes that we skipped are reserved and |
- // must be zero. |
- uint8_t inputChannels = src[8]; |
- uint8_t outputChannels = src[9]; |
- if (3 != inputChannels || 3 != outputChannels) { |
- // We only handle (supposedly) RGB inputs and RGB outputs. The numbers of input |
- // channels and output channels both must be 3. |
- SkColorSpacePrintf("Input and output channels must equal 3 in A to B tag.\n"); |
- return false; |
- } |
- |
- // Read the offsets of each element in the A to B tag. With the exception of A curves and |
- // B curves (which we do not yet support), we will handle these elements in the order in |
- // which they should be applied (rather than the order in which they occur in the tag). |
- // If the offset is non-zero it indicates that the element is present. |
- uint32_t offsetToACurves = read_big_endian_int(src + 28); |
- uint32_t offsetToBCurves = read_big_endian_int(src + 12); |
- if ((0 != offsetToACurves) || (0 != offsetToBCurves)) { |
- // FIXME (msarett): Handle A and B curves. |
- // Note that the A curve is technically required in order to have a color LUT. |
- // However, all the A curves I have seen so far have are just placeholders that |
- // don't actually transform the data. |
- SkColorSpacePrintf("Ignoring A and/or B curve. Output may be wrong.\n"); |
- } |
- |
- uint32_t offsetToColorLUT = read_big_endian_int(src + 24); |
- if (0 != offsetToColorLUT && offsetToColorLUT < len) { |
- if (!load_color_lut(colorLUT, inputChannels, outputChannels, src + offsetToColorLUT, |
- len - offsetToColorLUT)) { |
- SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n"); |
- } |
- } |
- |
- uint32_t offsetToMCurves = read_big_endian_int(src + 20); |
- if (0 != offsetToMCurves && offsetToMCurves < len) { |
- if (!load_gammas(gammas, outputChannels, src + offsetToMCurves, len - offsetToMCurves)) { |
- SkColorSpacePrintf("Failed to read M curves from A to B tag. Using linear gamma.\n"); |
- gammas[0].fNamed = SkColorSpace::kLinear_GammaNamed; |
- gammas[1].fNamed = SkColorSpace::kLinear_GammaNamed; |
- gammas[2].fNamed = SkColorSpace::kLinear_GammaNamed; |
- } |
- } |
- |
- uint32_t offsetToMatrix = read_big_endian_int(src + 16); |
- if (0 != offsetToMatrix && offsetToMatrix < len) { |
- if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) { |
- SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); |
- toXYZ->setIdentity(); |
- } |
- } |
- |
- return true; |
-} |
- |
-sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* input, size_t len) { |
- if (!input || len < kICCHeaderSize) { |
- return_null("Data is null or not large enough to contain an ICC profile"); |
- } |
- |
- // Create our own copy of the input. |
- void* memory = sk_malloc_throw(len); |
- memcpy(memory, input, len); |
- sk_sp<SkData> data = SkData::MakeFromMalloc(memory, len); |
- const void* base = data->data(); |
- const uint8_t* ptr = (const uint8_t*) base; |
- |
- // Read the ICC profile header and check to make sure that it is valid. |
- ICCProfileHeader header; |
- header.init(ptr, len); |
- if (!header.valid()) { |
- return nullptr; |
- } |
- |
- // Adjust ptr and len before reading the tags. |
- if (len < header.fSize) { |
- SkColorSpacePrintf("ICC profile might be truncated.\n"); |
- } else if (len > header.fSize) { |
- SkColorSpacePrintf("Caller provided extra data beyond the end of the ICC profile.\n"); |
- len = header.fSize; |
- } |
- ptr += kICCHeaderSize; |
- len -= kICCHeaderSize; |
- |
- // Parse tag headers. |
- uint32_t tagCount = header.fTagCount; |
- SkColorSpacePrintf("ICC profile contains %d tags.\n", tagCount); |
- if (len < kICCTagTableEntrySize * tagCount) { |
- return_null("Not enough input data to read tag table entries"); |
- } |
- |
- SkAutoTArray<ICCTag> tags(tagCount); |
- for (uint32_t i = 0; i < tagCount; i++) { |
- ptr = tags[i].init(ptr); |
- SkColorSpacePrintf("[%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); |
- |
- if (!tags[i].valid(kICCHeaderSize + len)) { |
- return_null("Tag is too large to fit in ICC profile"); |
- } |
- } |
- |
- switch (header.fInputColorSpace) { |
- case kRGB_ColorSpace: { |
- // Recognize the rXYZ, gXYZ, and bXYZ tags. |
- 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) { |
- float toXYZ[9]; |
- if (!load_xyz(&toXYZ[0], r->addr((const uint8_t*) base), r->fLength) || |
- !load_xyz(&toXYZ[3], g->addr((const uint8_t*) base), g->fLength) || |
- !load_xyz(&toXYZ[6], b->addr((const uint8_t*) base), b->fLength)) |
- { |
- return_null("Need valid rgb tags for XYZ space"); |
- } |
- SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); |
- mat.set3x3RowMajorf(toXYZ); |
- |
- // It is not uncommon to see missing or empty gamma tags. This indicates |
- // that we should use unit gamma. |
- SkGammaCurve curves[3]; |
- 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 || !load_gammas(&curves[0], 1, r->addr((const uint8_t*) base), r->fLength)) |
- { |
- SkColorSpacePrintf("Failed to read R gamma tag.\n"); |
- curves[0].fNamed = SkColorSpace::kLinear_GammaNamed; |
- } |
- if (!g || !load_gammas(&curves[1], 1, g->addr((const uint8_t*) base), g->fLength)) |
- { |
- SkColorSpacePrintf("Failed to read G gamma tag.\n"); |
- curves[1].fNamed = SkColorSpace::kLinear_GammaNamed; |
- } |
- if (!b || !load_gammas(&curves[2], 1, b->addr((const uint8_t*) base), b->fLength)) |
- { |
- SkColorSpacePrintf("Failed to read B gamma tag.\n"); |
- curves[2].fNamed = SkColorSpace::kLinear_GammaNamed; |
- } |
- |
- GammaNamed gammaNamed = SkGammas::Named(curves); |
- if (kNonStandard_GammaNamed == gammaNamed) { |
- sk_sp<SkGammas> gammas = sk_make_sp<SkGammas>(std::move(curves[0]), |
- std::move(curves[1]), |
- std::move(curves[2])); |
- return sk_sp<SkColorSpace>(new SkColorSpace_Base(nullptr, std::move(gammas), |
- mat, std::move(data))); |
- } else { |
- return SkColorSpace_Base::NewRGB(gammaNamed, mat); |
- } |
- } |
- |
- // Recognize color profile specified by A2B0 tag. |
- const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); |
- if (a2b0) { |
- sk_sp<SkColorLookUpTable> colorLUT = sk_make_sp<SkColorLookUpTable>(); |
- SkGammaCurve curves[3]; |
- SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); |
- if (!load_a2b0(colorLUT.get(), curves, &toXYZ, a2b0->addr((const uint8_t*) base), |
- a2b0->fLength)) { |
- return_null("Failed to parse A2B0 tag"); |
- } |
- |
- GammaNamed gammaNamed = SkGammas::Named(curves); |
- colorLUT = colorLUT->fTable ? colorLUT : nullptr; |
- if (colorLUT || kNonStandard_GammaNamed == gammaNamed) { |
- sk_sp<SkGammas> gammas = sk_make_sp<SkGammas>(std::move(curves[0]), |
- std::move(curves[1]), |
- std::move(curves[2])); |
- |
- return sk_sp<SkColorSpace>(new SkColorSpace_Base(std::move(colorLUT), |
- std::move(gammas), toXYZ, |
- std::move(data))); |
- } else { |
- return SkColorSpace_Base::NewRGB(gammaNamed, toXYZ); |
- } |
- } |
- } |
- default: |
- break; |
- } |
- |
- return_null("ICC profile contains unsupported colorspace"); |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////////////////////////// |
- |
-// We will write a profile with the minimum nine required tags. |
-static constexpr uint32_t kICCNumEntries = 9; |
- |
-static constexpr uint32_t kTAG_desc = SkSetFourByteTag('d', 'e', 's', 'c'); |
-static constexpr uint32_t kTAG_desc_Bytes = 12; |
-static constexpr uint32_t kTAG_desc_Offset = kICCHeaderSize + kICCNumEntries*kICCTagTableEntrySize; |
- |
-static constexpr uint32_t kTAG_XYZ_Bytes = 20; |
-static constexpr uint32_t kTAG_rXYZ_Offset = kTAG_desc_Offset + kTAG_desc_Bytes; |
-static constexpr uint32_t kTAG_gXYZ_Offset = kTAG_rXYZ_Offset + kTAG_XYZ_Bytes; |
-static constexpr uint32_t kTAG_bXYZ_Offset = kTAG_gXYZ_Offset + kTAG_XYZ_Bytes; |
- |
-static constexpr uint32_t kTAG_TRC_Bytes = 14; |
-static constexpr uint32_t kTAG_rTRC_Offset = kTAG_bXYZ_Offset + kTAG_XYZ_Bytes; |
-static constexpr uint32_t kTAG_gTRC_Offset = kTAG_rTRC_Offset + SkAlign4(kTAG_TRC_Bytes); |
-static constexpr uint32_t kTAG_bTRC_Offset = kTAG_gTRC_Offset + SkAlign4(kTAG_TRC_Bytes); |
- |
-static constexpr uint32_t kTAG_wtpt = SkSetFourByteTag('w', 't', 'p', 't'); |
-static constexpr uint32_t kTAG_wtpt_Offset = kTAG_bTRC_Offset + SkAlign4(kTAG_TRC_Bytes); |
- |
-static constexpr uint32_t kTAG_cprt = SkSetFourByteTag('c', 'p', 'r', 't'); |
-static constexpr uint32_t kTAG_cprt_Bytes = 12; |
-static constexpr uint32_t kTAG_cprt_Offset = kTAG_wtpt_Offset + kTAG_XYZ_Bytes; |
- |
-static constexpr uint32_t kICCProfileSize = kTAG_cprt_Offset + kTAG_cprt_Bytes; |
- |
-static constexpr uint32_t gICCHeader[kICCHeaderSize / 4] { |
- SkEndian_SwapBE32(kICCProfileSize), // Size of the profile |
- 0, // Preferred CMM type (ignored) |
- SkEndian_SwapBE32(0x02100000), // Version 2.1 |
- SkEndian_SwapBE32(kDisplay_Profile), // Display device profile |
- SkEndian_SwapBE32(kRGB_ColorSpace), // RGB input color space |
- SkEndian_SwapBE32(kXYZ_PCSSpace), // XYZ profile connection space |
- 0, 0, 0, // Date and time (ignored) |
- SkEndian_SwapBE32(kACSP_Signature), // Profile signature |
- 0, // Platform target (ignored) |
- 0x00000000, // Flags: not embedded, can be used independently |
- 0, // Device manufacturer (ignored) |
- 0, // Device model (ignored) |
- 0, 0, // Device attributes (ignored) |
- SkEndian_SwapBE32(1), // Relative colorimetric rendering intent |
- SkEndian_SwapBE32(0x0000f6d6), // D50 standard illuminant (X) |
- SkEndian_SwapBE32(0x00010000), // D50 standard illuminant (Y) |
- SkEndian_SwapBE32(0x0000d32d), // D50 standard illuminant (Z) |
- 0, // Profile creator (ignored) |
- 0, 0, 0, 0, // Profile id checksum (ignored) |
- 0, 0, 0, 0, 0, 0, 0, // Reserved (ignored) |
- SkEndian_SwapBE32(kICCNumEntries), // Number of tags |
-}; |
- |
-static constexpr uint32_t gICCTagTable[3 * kICCNumEntries] { |
- // Profile description |
- SkEndian_SwapBE32(kTAG_desc), |
- SkEndian_SwapBE32(kTAG_desc_Offset), |
- SkEndian_SwapBE32(kTAG_desc_Bytes), |
- |
- // rXYZ |
- SkEndian_SwapBE32(kTAG_rXYZ), |
- SkEndian_SwapBE32(kTAG_rXYZ_Offset), |
- SkEndian_SwapBE32(kTAG_XYZ_Bytes), |
- |
- // gXYZ |
- SkEndian_SwapBE32(kTAG_gXYZ), |
- SkEndian_SwapBE32(kTAG_gXYZ_Offset), |
- SkEndian_SwapBE32(kTAG_XYZ_Bytes), |
- |
- // bXYZ |
- SkEndian_SwapBE32(kTAG_bXYZ), |
- SkEndian_SwapBE32(kTAG_bXYZ_Offset), |
- SkEndian_SwapBE32(kTAG_XYZ_Bytes), |
- |
- // rTRC |
- SkEndian_SwapBE32(kTAG_rTRC), |
- SkEndian_SwapBE32(kTAG_rTRC_Offset), |
- SkEndian_SwapBE32(kTAG_TRC_Bytes), |
- |
- // gTRC |
- SkEndian_SwapBE32(kTAG_gTRC), |
- SkEndian_SwapBE32(kTAG_gTRC_Offset), |
- SkEndian_SwapBE32(kTAG_TRC_Bytes), |
- |
- // bTRC |
- SkEndian_SwapBE32(kTAG_bTRC), |
- SkEndian_SwapBE32(kTAG_bTRC_Offset), |
- SkEndian_SwapBE32(kTAG_TRC_Bytes), |
- |
- // White point |
- SkEndian_SwapBE32(kTAG_wtpt), |
- SkEndian_SwapBE32(kTAG_wtpt_Offset), |
- SkEndian_SwapBE32(kTAG_XYZ_Bytes), |
- |
- // Copyright |
- SkEndian_SwapBE32(kTAG_cprt), |
- SkEndian_SwapBE32(kTAG_cprt_Offset), |
- SkEndian_SwapBE32(kTAG_cprt_Bytes), |
-}; |
- |
-static constexpr uint32_t kTAG_TextType = SkSetFourByteTag('m', 'l', 'u', 'c'); |
-static constexpr uint32_t gEmptyTextTag[3] { |
- SkEndian_SwapBE32(kTAG_TextType), // Type signature |
- 0, // Reserved |
- 0, // Zero records |
-}; |
- |
-static void write_xyz_tag(uint32_t* ptr, const SkMatrix44& toXYZ, int row) { |
- ptr[0] = SkEndian_SwapBE32(kXYZ_PCSSpace); |
- ptr[1] = 0; |
- ptr[2] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 0))); |
- ptr[3] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 1))); |
- ptr[4] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(row, 2))); |
-} |
- |
-static void write_trc_tag(uint32_t* ptr, float value) { |
- ptr[0] = SkEndian_SwapBE32(kTAG_CurveType); |
- ptr[1] = 0; |
- |
- // Gamma will be specified with a single value. |
- ptr[2] = SkEndian_SwapBE32(1); |
- |
- // Convert gamma to 16-bit fixed point. |
- uint16_t* ptr16 = (uint16_t*) (ptr + 3); |
- ptr16[0] = SkEndian_SwapBE16((uint16_t) (value * 256.0f)); |
- |
- // Pad tag with zero. |
- ptr16[1] = 0; |
-} |
- |
-static float get_gamma_value(const SkGammaCurve* curve) { |
- switch (curve->fNamed) { |
- case SkColorSpace::kSRGB_GammaNamed: |
- // FIXME (msarett): |
- // kSRGB cannot be represented by a value. Here we fall through to 2.2f, |
- // which is a close guess. To be more accurate, we need to represent sRGB |
- // gamma with a parametric curve. |
- case SkColorSpace::k2Dot2Curve_GammaNamed: |
- return 2.2f; |
- case SkColorSpace::kLinear_GammaNamed: |
- return 1.0f; |
- default: |
- SkASSERT(curve->isValue()); |
- return curve->fValue; |
- } |
-} |
- |
-sk_sp<SkData> SkColorSpace_Base::writeToICC() const { |
- // Return if this object was created from a profile, or if we have already serialized |
- // the profile. |
- if (fProfileData) { |
- return fProfileData; |
- } |
- |
- // The client may create an SkColorSpace using an SkMatrix44, but currently we only |
- // support writing profiles with 3x3 matrices. |
- // TODO (msarett): Fix this! |
- if (0.0f != fToXYZD50.getFloat(3, 0) || 0.0f != fToXYZD50.getFloat(3, 1) || |
- 0.0f != fToXYZD50.getFloat(3, 2) || 0.0f != fToXYZD50.getFloat(0, 3) || |
- 0.0f != fToXYZD50.getFloat(1, 3) || 0.0f != fToXYZD50.getFloat(2, 3)) |
- { |
- return nullptr; |
- } |
- |
- SkAutoMalloc profile(kICCProfileSize); |
- uint8_t* ptr = (uint8_t*) profile.get(); |
- |
- // Write profile header |
- memcpy(ptr, gICCHeader, sizeof(gICCHeader)); |
- ptr += sizeof(gICCHeader); |
- |
- // Write tag table |
- memcpy(ptr, gICCTagTable, sizeof(gICCTagTable)); |
- ptr += sizeof(gICCTagTable); |
- |
- // Write profile description tag |
- memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); |
- ptr += sizeof(gEmptyTextTag); |
- |
- // Write XYZ tags |
- write_xyz_tag((uint32_t*) ptr, fToXYZD50, 0); |
- ptr += kTAG_XYZ_Bytes; |
- write_xyz_tag((uint32_t*) ptr, fToXYZD50, 1); |
- ptr += kTAG_XYZ_Bytes; |
- write_xyz_tag((uint32_t*) ptr, fToXYZD50, 2); |
- ptr += kTAG_XYZ_Bytes; |
- |
- // Write TRC tags |
- GammaNamed gammaNamed = this->gammaNamed(); |
- if (kNonStandard_GammaNamed == gammaNamed) { |
- write_trc_tag((uint32_t*) ptr, get_gamma_value(&as_CSB(this)->fGammas->fRed)); |
- ptr += SkAlign4(kTAG_TRC_Bytes); |
- write_trc_tag((uint32_t*) ptr, get_gamma_value(&as_CSB(this)->fGammas->fGreen)); |
- ptr += SkAlign4(kTAG_TRC_Bytes); |
- write_trc_tag((uint32_t*) ptr, get_gamma_value(&as_CSB(this)->fGammas->fBlue)); |
- ptr += SkAlign4(kTAG_TRC_Bytes); |
- } else { |
- switch (gammaNamed) { |
- case SkColorSpace::kSRGB_GammaNamed: |
- // FIXME (msarett): |
- // kSRGB cannot be represented by a value. Here we fall through to 2.2f, |
- // which is a close guess. To be more accurate, we need to represent sRGB |
- // gamma with a parametric curve. |
- case SkColorSpace::k2Dot2Curve_GammaNamed: |
- write_trc_tag((uint32_t*) ptr, 2.2f); |
- ptr += SkAlign4(kTAG_TRC_Bytes); |
- write_trc_tag((uint32_t*) ptr, 2.2f); |
- ptr += SkAlign4(kTAG_TRC_Bytes); |
- write_trc_tag((uint32_t*) ptr, 2.2f); |
- ptr += SkAlign4(kTAG_TRC_Bytes); |
- break; |
- case SkColorSpace::kLinear_GammaNamed: |
- write_trc_tag((uint32_t*) ptr, 1.0f); |
- ptr += SkAlign4(kTAG_TRC_Bytes); |
- write_trc_tag((uint32_t*) ptr, 1.0f); |
- ptr += SkAlign4(kTAG_TRC_Bytes); |
- write_trc_tag((uint32_t*) ptr, 1.0f); |
- ptr += SkAlign4(kTAG_TRC_Bytes); |
- break; |
- default: |
- SkASSERT(false); |
- break; |
- } |
- } |
- |
- // Write white point tag |
- uint32_t* ptr32 = (uint32_t*) ptr; |
- ptr32[0] = SkEndian_SwapBE32(kXYZ_PCSSpace); |
- ptr32[1] = 0; |
- // TODO (msarett): These values correspond to the D65 white point. This may not always be |
- // correct. |
- ptr32[2] = SkEndian_SwapBE32(0x0000f351); |
- ptr32[3] = SkEndian_SwapBE32(0x00010000); |
- ptr32[4] = SkEndian_SwapBE32(0x000116cc); |
- ptr += kTAG_XYZ_Bytes; |
- |
- // Write copyright tag |
- memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); |
- |
- // TODO (msarett): Should we try to hold onto the data so we can return immediately if |
- // the client calls again? |
- return SkData::MakeFromMalloc(profile.release(), kICCProfileSize); |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////////////////////////// |
- |
enum Version { |
k0_Version, // Initial version, header + flags for matrix and profile |
}; |