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>
diff --git a/src/core/SkColorSpace_ICC.cpp b/src/core/SkColorSpace_ICC.cpp
index 6d7462c..b76a981 100644
--- a/src/core/SkColorSpace_ICC.cpp
+++ b/src/core/SkColorSpace_ICC.cpp
@@ -12,6 +12,7 @@
#include "SkColorSpacePriv.h"
#include "SkEndian.h"
#include "SkFixed.h"
+#include "SkICCPriv.h"
#include "SkTemplates.h"
#define return_if_false(pred, msg) \
@@ -40,25 +41,6 @@
return (int32_t) read_big_endian_u32(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 kCMYK_ColorSpace = SkSetFourByteTag('C', 'M', 'Y', 'K');
-static constexpr uint32_t kGray_ColorSpace = SkSetFourByteTag('G', 'R', 'A', 'Y');
-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 kColorSpace_Profile = SkSetFourByteTag('s', 'p', 'a', 'c');
-static constexpr uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' ');
-static constexpr uint32_t kLAB_PCSSpace = SkSetFourByteTag('L', 'a', 'b', ' ');
-static constexpr uint32_t kACSP_Signature = SkSetFourByteTag('a', 'c', 's', 'p');
-
struct ICCProfileHeader {
uint32_t fSize;
@@ -243,15 +225,6 @@
}
};
-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_kTRC = SkSetFourByteTag('k', '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);
@@ -265,9 +238,6 @@
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 SkGammas::Type set_gamma_value(SkGammas::Data* data, float value) {
if (color_space_almost_equal(2.2f, value)) {
data->fNamed = k2Dot2Curve_SkGammaNamed;
@@ -1497,7 +1467,9 @@
std::move(profileData)));
}
-static sk_sp<SkColorSpace> make_a2b(SkColorSpace_Base::InputColorFormat inputColorFormat, const ICCProfileHeader& header, ICCTag* tags, int tagCount, const uint8_t* base, sk_sp<SkData> profileData) {
+static sk_sp<SkColorSpace> make_a2b(SkColorSpace_Base::InputColorFormat inputColorFormat,
+ const ICCProfileHeader& header, ICCTag* tags, int tagCount,
+ const uint8_t* base, sk_sp<SkData> profileData) {
const ICCTag* a2b0 = ICCTag::Find(tags, tagCount, kTAG_A2B0);
if (a2b0) {
const SkColorSpace_A2B::PCS pcs = kXYZ_PCSSpace == header.fPCS
@@ -1605,235 +1577,3 @@
return make_a2b(inputColorFormat, header, tags.get(), tagCount, base, profileData);
}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-// 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);
-}
diff --git a/src/core/SkICC.cpp b/src/core/SkICC.cpp
index 9a1a3c6..7b8ae9f 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::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);
+}
diff --git a/src/core/SkICCPriv.h b/src/core/SkICCPriv.h
new file mode 100644
index 0000000..ac85667
--- /dev/null
+++ b/src/core/SkICCPriv.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// 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 kCMYK_ColorSpace = SkSetFourByteTag('C', 'M', 'Y', 'K');
+static constexpr uint32_t kGray_ColorSpace = SkSetFourByteTag('G', 'R', 'A', 'Y');
+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 kColorSpace_Profile = SkSetFourByteTag('s', 'p', 'a', 'c');
+static constexpr uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' ');
+static constexpr uint32_t kLAB_PCSSpace = SkSetFourByteTag('L', 'a', 'b', ' ');
+static constexpr uint32_t kACSP_Signature = SkSetFourByteTag('a', 'c', 's', 'p');
+
+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_kTRC = SkSetFourByteTag('k', 'T', 'R', 'C');
+static constexpr uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0');
+
+static constexpr uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v');
+static constexpr uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a');