diff options
author | 2016-12-19 09:43:30 -0500 | |
---|---|---|
committer | 2016-12-19 15:24:01 +0000 | |
commit | 8e04286fa62d04767d39d820439fa4caf6223a46 (patch) | |
tree | 8ad64ebea3005936a67d9d2925612ad330a9590e /src/core/SkICC.cpp | |
parent | 879bdc9b40e615cc419855b58dd1108e63d2b17e (diff) |
Move existing writeICC() code into SkICC
BUG=skia:
Change-Id: Ifb7db7adcc69104fa9abe1765fd60b7f627bdbaf
Reviewed-on: https://skia-review.googlesource.com/6261
Commit-Queue: Matt Sarett <msarett@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Diffstat (limited to 'src/core/SkICC.cpp')
-rw-r--r-- | src/core/SkICC.cpp | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/src/core/SkICC.cpp b/src/core/SkICC.cpp index 9a1a3c6e33..7b8ae9f5a0 100644 --- a/src/core/SkICC.cpp +++ b/src/core/SkICC.cpp @@ -6,7 +6,11 @@ */ #include "SkColorSpace_Base.h" +#include "SkColorSpace_XYZ.h" +#include "SkEndian.h" +#include "SkFixed.h" #include "SkICC.h" +#include "SkICCPriv.h" SkICC::SkICC(sk_sp<SkColorSpace> colorSpace) : fColorSpace(std::move(colorSpace)) @@ -34,3 +38,235 @@ bool SkICC::toXYZD50(SkMatrix44* toXYZD50) const { bool SkICC::isNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const { return as_CSB(fColorSpace)->onIsNumericalTransferFn(coeffs); } + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// 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 col) { + ptr[0] = SkEndian_SwapBE32(kXYZ_PCSSpace); + ptr[1] = 0; + ptr[2] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(0, col))); + ptr[3] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(1, col))); + ptr[4] = SkEndian_SwapBE32(SkFloatToFixed(toXYZ.getFloat(2, col))); +} + +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; +} + +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; + } + // Profile Data is be mandatory for A2B0 Color Spaces + SkASSERT(type() == Type::kXYZ); + + // The client may create an SkColorSpace using an SkMatrix44, but currently we only + // support writing profiles with 3x3 matrices. + // TODO (msarett): Fix this! + const SkColorSpace_XYZ* thisXYZ = static_cast<const SkColorSpace_XYZ*>(this); + const SkMatrix44& toXYZD50 = *thisXYZ->toXYZD50(); + if (0.0f != toXYZD50.getFloat(3, 0) || 0.0f != toXYZD50.getFloat(3, 1) || + 0.0f != toXYZD50.getFloat(3, 2) || 0.0f != toXYZD50.getFloat(0, 3) || + 0.0f != toXYZD50.getFloat(1, 3) || 0.0f != toXYZD50.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, toXYZD50, 0); + ptr += kTAG_XYZ_Bytes; + write_xyz_tag((uint32_t*) ptr, toXYZD50, 1); + ptr += kTAG_XYZ_Bytes; + write_xyz_tag((uint32_t*) ptr, toXYZD50, 2); + ptr += kTAG_XYZ_Bytes; + + // Write TRC tags + SkGammaNamed gammaNamed = thisXYZ->gammaNamed(); + if (kNonStandard_SkGammaNamed == gammaNamed) { + // FIXME (msarett): + // Write the correct gamma representation rather than 2.2f. + 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); + } else { + switch (gammaNamed) { + case kSRGB_SkGammaNamed: + // 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 k2Dot2Curve_SkGammaNamed: + 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 kLinear_SkGammaNamed: + 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); +} |