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');