Reland "Add SkColorSpace factory from 3x3 row-major gamut and transfer function"

Moved named common transfer functions and gamuts to constexpr values in
SkColorSpace.h, in SkNamedTransferFn and SkNamedGamut namespaces.

Converted nearly all SkColorSpace::MakeRGB calls within Skia to use the
new factory with the named values. Multiple clients want a way to
extract named transfer function and gamut - this still doesn't provide
that, but this may be a better path forward for honestly advertising how
SkColorSpace works internally.

Originally landed as:
https://skia.googlesource.com/skia/+/a9549ab31630fc244094e6f1692371cbaf87f666

Re-landing with a new serialization format, but maintaining ability to
load old serialized color spaces, for SKP compatibility.

Bug: skia:
Change-Id: Ib84a6e1cd5d7d9816175773fdbaff2ca32658667
Reviewed-on: https://skia-review.googlesource.com/c/181176
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/bench/ColorCanvasDrawBitmapBench.cpp b/bench/ColorCanvasDrawBitmapBench.cpp
index c150e79..a7877f4 100644
--- a/bench/ColorCanvasDrawBitmapBench.cpp
+++ b/bench/ColorCanvasDrawBitmapBench.cpp
@@ -54,5 +54,5 @@
 DEF_BENCH(return new ColorCanvasDrawBitmap(SkColorSpace::MakeSRGB(), SkColorSpace::MakeSRGB(),
         "sRGB_to_sRGB");)
 DEF_BENCH(return new ColorCanvasDrawBitmap(
-        SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kAdobeRGB_Gamut),
+        SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kAdobeRGB),
         SkColorSpace::MakeSRGB(), "AdobeRGB_to_sRGB");)
diff --git a/bench/ColorSpaceXformBench.cpp b/bench/ColorSpaceXformBench.cpp
index fc95e8e..b7b6b7d 100644
--- a/bench/ColorSpaceXformBench.cpp
+++ b/bench/ColorSpaceXformBench.cpp
@@ -34,8 +34,8 @@
 
     void onDelayedSetup() override {
         sk_sp<SkColorSpace> src = SkColorSpace::MakeSRGB(),
-                            dst = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                                        SkColorSpace::kDCIP3_D65_Gamut);
+                            dst = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
+                                                        SkNamedGamut::kDCIP3);
 
         fSteps = skstd::make_unique<SkColorSpaceXformSteps>(src.get(), kOpaque_SkAlphaType,
                                                             dst.get(), kPremul_SkAlphaType);
diff --git a/bench/VertexColorSpaceBench.cpp b/bench/VertexColorSpaceBench.cpp
index 129d0ff..164a2b4 100644
--- a/bench/VertexColorSpaceBench.cpp
+++ b/bench/VertexColorSpaceBench.cpp
@@ -257,8 +257,8 @@
 
         GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
 
-        auto p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                        SkColorSpace::kDCIP3_D65_Gamut);
+        auto p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
+                                        SkNamedGamut::kDCIP3);
         auto xform = GrColorSpaceXform::Make(sk_srgb_singleton(), kUnpremul_SkAlphaType,
                                              p3.get(),            kUnpremul_SkAlphaType);
 
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
index 21bed94..35fcefa 100644
--- a/bench/nanobench.cpp
+++ b/bench/nanobench.cpp
@@ -478,10 +478,7 @@
     CPU_CONFIG(565,  kRaster_Backend, kRGB_565_SkColorType, kOpaque_SkAlphaType, nullptr)
 
     // 'narrow' has a gamut narrower than sRGB, and different transfer function.
-    SkMatrix44 narrow_gamut;
-    narrow_gamut.set3x3RowMajorf(gNarrow_toXYZD50);
-
-    auto narrow = SkColorSpace::MakeRGB(k2Dot2Curve_SkGammaNamed, narrow_gamut),
+    auto narrow = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, gNarrow_toXYZD50),
            srgb = SkColorSpace::MakeSRGB(),
      srgbLinear = SkColorSpace::MakeSRGBLinear();
 
diff --git a/dm/DM.cpp b/dm/DM.cpp
index 1694676..6f63b36 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -833,7 +833,7 @@
 
 static sk_sp<SkColorSpace> rec2020() {
     return SkColorSpace::MakeRGB({2.22222f, 0.909672f, 0.0903276f, 0.222222f, 0.0812429f, 0, 0},
-                                 SkColorSpace::kRec2020_Gamut);
+                                 SkNamedGamut::kRec2020);
 }
 
 static void push_sink(const SkCommandLineConfig& config, Sink* s) {
@@ -925,14 +925,10 @@
         // Configs relevant to color management testing (and 8888 for reference).
 
         // 'narrow' has a gamut narrower than sRGB, and different transfer function.
-        SkMatrix44 narrow_gamut;
-        narrow_gamut.set3x3RowMajorf(gNarrow_toXYZD50);
-
-        auto narrow = SkColorSpace::MakeRGB(k2Dot2Curve_SkGammaNamed, narrow_gamut),
+        auto narrow = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, gNarrow_toXYZD50),
                srgb = SkColorSpace::MakeSRGB(),
          srgbLinear = SkColorSpace::MakeSRGBLinear(),
-                 p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                            SkColorSpace::kDCIP3_D65_Gamut);
+                 p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
 
         SINK(     "f16",  RasterSink,  kRGBA_F16_SkColorType, srgbLinear);
         SINK(    "srgb",  RasterSink, kRGBA_8888_SkColorType, srgb      );
@@ -958,8 +954,7 @@
 #define VIA(t, via, ...) if (tag.equals(t)) return new via(__VA_ARGS__)
     VIA("gbr",       ViaCSXform,           wrapped, rgb_to_gbr(), true);
     VIA("p3",        ViaCSXform,           wrapped,
-                     SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                           SkColorSpace::kDCIP3_D65_Gamut), false);
+                     SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3), false);
     VIA("lite",      ViaLite,              wrapped);
 #ifdef TEST_VIA_SVG
     VIA("svg",       ViaSVG,               wrapped);
diff --git a/gm/makecolorspace.cpp b/gm/makecolorspace.cpp
index c7e603d..2f5acd6 100644
--- a/gm/makecolorspace.cpp
+++ b/gm/makecolorspace.cpp
@@ -48,8 +48,8 @@
     }
 
     void onDraw(SkCanvas* canvas) override {
-        sk_sp<SkColorSpace> wideGamut = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                                              SkColorSpace::kAdobeRGB_Gamut);
+        sk_sp<SkColorSpace> wideGamut = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
+                                                              SkNamedGamut::kAdobeRGB);
         sk_sp<SkColorSpace> wideGamutLinear = wideGamut->makeLinearGamma();
 
         // Lazy images
diff --git a/gm/p3.cpp b/gm/p3.cpp
index 28e8644..a67ab72 100644
--- a/gm/p3.cpp
+++ b/gm/p3.cpp
@@ -94,8 +94,7 @@
 }
 
 DEF_SIMPLE_GM(p3, canvas, 450, 1300) {
-    auto p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                    SkColorSpace::kDCIP3_D65_Gamut);
+    auto p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
     auto srgb = SkColorSpace::MakeSRGB();
 
     auto p3_to_srgb = [&](SkColor4f c) {
@@ -356,8 +355,7 @@
 }
 
 DEF_SIMPLE_GM(p3_ovals, canvas, 450, 320) {
-    auto p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                    SkColorSpace::kDCIP3_D65_Gamut);
+    auto p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
 
     // Test cases that exercise each Op in GrOvalOpFactory.cpp
 
diff --git a/gm/pictureshadercache.cpp b/gm/pictureshadercache.cpp
index b3e2c26..e3badd8 100644
--- a/gm/pictureshadercache.cpp
+++ b/gm/pictureshadercache.cpp
@@ -60,9 +60,12 @@
 
         {
             // Render in a funny color space that converts green to yellow.
-            SkMatrix44 greenToYellow;
-            greenToYellow.setFloat(0, 1, 1.0f);
-            sk_sp<SkColorSpace> gty = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+            skcms_Matrix3x3 greenToYellow = {{
+                { 1, 1, 0 },
+                { 0, 1, 0 },
+                { 0, 0, 1 },
+            }};
+            sk_sp<SkColorSpace> gty = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
                                                             greenToYellow);
             SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100, std::move(gty));
             sk_sp<SkSurface> surface(SkSurface::MakeRaster(info));
diff --git a/gm/readpixels.cpp b/gm/readpixels.cpp
index 8250860..e69803b 100644
--- a/gm/readpixels.cpp
+++ b/gm/readpixels.cpp
@@ -62,10 +62,9 @@
 }
 
 static sk_sp<SkColorSpace> make_parametric_transfer_fn(const SkColorSpacePrimaries& primaries) {
-    SkMatrix44 toXYZD50;
+    skcms_Matrix3x3 toXYZD50;
     SkAssertResult(primaries.toXYZD50(&toXYZD50));
-    SkColorSpaceTransferFn fn;
-    fn.fA = 1.f; fn.fB = 0.f; fn.fC = 0.f; fn.fD = 0.f; fn.fE = 0.f; fn.fF = 0.f; fn.fG = 1.8f;
+    skcms_TransferFunction fn = { 1.8f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f };
     return SkColorSpace::MakeRGB(fn, toXYZD50);
 }
 
diff --git a/gm/tosrgb_colorfilter.cpp b/gm/tosrgb_colorfilter.cpp
index d9c7890..8637386 100644
--- a/gm/tosrgb_colorfilter.cpp
+++ b/gm/tosrgb_colorfilter.cpp
@@ -26,20 +26,17 @@
     canvas->drawBitmapRect(bmp, SkRect::MakeXYWH(10, 10, 50, 50), nullptr);
 
     auto srgb = SkColorSpace::MakeSRGB();
-    auto rec2020 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                         SkColorSpace::kRec2020_Gamut);
+    auto rec2020 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kRec2020);
 
     // NarrowGamut RGB (an artifically smaller than sRGB gamut)
-    SkColorSpacePrimaries narrowPrimaries = {
+    skcms_Matrix3x3 narrowGamutRGBMatrix;
+    SkAssertResult(skcms_PrimariesToXYZD50(
         0.54f, 0.33f,     // Rx, Ry
         0.33f, 0.50f,     // Gx, Gy
         0.25f, 0.20f,     // Bx, By
         0.3127f, 0.3290f, // Wx, Wy
-    };
-    SkMatrix44 narrowGamutRGBMatrix;
-    narrowPrimaries.toXYZD50(&narrowGamutRGBMatrix);
-    auto narrow = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                        narrowGamutRGBMatrix);
+        &narrowGamutRGBMatrix));
+    auto narrow = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, narrowGamutRGBMatrix);
 
     SkPaint paint;
 
diff --git a/include/core/SkColorSpace.h b/include/core/SkColorSpace.h
index 26771f3..6013c2a 100644
--- a/include/core/SkColorSpace.h
+++ b/include/core/SkColorSpace.h
@@ -8,13 +8,14 @@
 #ifndef SkColorSpace_DEFINED
 #define SkColorSpace_DEFINED
 
+#include "../private/SkFixed.h"
 #include "../private/SkOnce.h"
+#include "../../third_party/skcms/skcms.h"
 #include "SkMatrix44.h"
 #include "SkRefCnt.h"
 #include <memory>
 
 class SkData;
-struct skcms_ICCProfile;
 
 enum SkGammaNamed {
     kLinear_SkGammaNamed,
@@ -41,6 +42,8 @@
      *  representation of SkColorSpace.
      */
     bool toXYZD50(SkMatrix44* toXYZD50) const;
+
+    bool toXYZD50(skcms_Matrix3x3* toXYZD50) const;
 };
 
 /**
@@ -62,6 +65,56 @@
     float fF;
 };
 
+namespace SkNamedTransferFn {
+
+// Like SkNamedGamut::kSRGB, keeping this bitwise exactly the same as skcms makes things fastest.
+static constexpr skcms_TransferFunction kSRGB =
+    { 2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0.0f, 0.0f };
+
+static constexpr skcms_TransferFunction k2Dot2 =
+    { 2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+
+static constexpr skcms_TransferFunction kLinear =
+    { 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+
+}
+
+namespace SkNamedGamut {
+
+static constexpr skcms_Matrix3x3 kSRGB = {{
+    // ICC fixed-point (16.16) representation, taken from skcms. Please keep them exactly in sync.
+    // 0.436065674f, 0.385147095f, 0.143066406f,
+    // 0.222488403f, 0.716873169f, 0.060607910f,
+    // 0.013916016f, 0.097076416f, 0.714096069f,
+    { SkFixedToFloat(0x6FA2), SkFixedToFloat(0x6299), SkFixedToFloat(0x24A0) },
+    { SkFixedToFloat(0x38F5), SkFixedToFloat(0xB785), SkFixedToFloat(0x0F84) },
+    { SkFixedToFloat(0x0390), SkFixedToFloat(0x18DA), SkFixedToFloat(0xB6CF) },
+}};
+
+static constexpr skcms_Matrix3x3 kAdobeRGB = {{
+    // ICC fixed-point (16.16) repesentation of:
+    // 0.60974, 0.20528, 0.14919,
+    // 0.31111, 0.62567, 0.06322,
+    // 0.01947, 0.06087, 0.74457,
+    { SkFixedToFloat(0x9c18), SkFixedToFloat(0x348d), SkFixedToFloat(0x2631) },
+    { SkFixedToFloat(0x4fa5), SkFixedToFloat(0xa02c), SkFixedToFloat(0x102f) },
+    { SkFixedToFloat(0x04fc), SkFixedToFloat(0x0f95), SkFixedToFloat(0xbe9c) },
+}};
+
+static constexpr skcms_Matrix3x3 kDCIP3 = {{
+    {  0.515102f,   0.291965f,  0.157153f  },
+    {  0.241182f,   0.692236f,  0.0665819f },
+    { -0.00104941f, 0.0418818f, 0.784378f  },
+}};
+
+static constexpr skcms_Matrix3x3 kRec2020 = {{
+    {  0.673459f,   0.165661f,  0.125100f, },
+    {  0.279033f,   0.675338f,  0.0456288f },
+    { -0.00193139f, 0.0299794f, 0.797162f  },
+}};
+
+}
+
 class SK_API SkColorSpace : public SkNVRefCnt<SkColorSpace> {
 public:
     /**
@@ -107,6 +160,12 @@
     static sk_sp<SkColorSpace> MakeRGB(SkGammaNamed gammaNamed, const SkMatrix44& toXYZD50);
 
     /**
+     *  Create an SkColorSpace from a transfer function and a row-major 3x3 transformation to XYZ.
+     */
+    static sk_sp<SkColorSpace> MakeRGB(const skcms_TransferFunction& transferFn,
+                                       const skcms_Matrix3x3& toXYZ);
+
+    /**
      *  Create an SkColorSpace from a parsed (skcms) ICC profile.
      */
     static sk_sp<SkColorSpace> Make(const skcms_ICCProfile&);
@@ -136,12 +195,16 @@
      */
     bool isNumericalTransferFn(SkColorSpaceTransferFn* fn) const;
 
+    bool isNumericalTransferFn(skcms_TransferFunction* fn) const;
+
     /**
      *  Returns true and sets |toXYZD50| if the color gamut can be described as a matrix.
      *  Returns false otherwise.
      */
     bool toXYZD50(SkMatrix44* toXYZD50) const;
 
+    bool toXYZD50(skcms_Matrix3x3* toXYZD50) const;
+
     /**
      *  Returns a hash of the gamut transformation to XYZ D50. Allows for fast equality checking
      *  of gamuts, at the (very small) risk of collision.
diff --git a/src/codec/SkAndroidCodec.cpp b/src/codec/SkAndroidCodec.cpp
index 581640f..d741d6d 100644
--- a/src/codec/SkAndroidCodec.cpp
+++ b/src/codec/SkAndroidCodec.cpp
@@ -164,9 +164,8 @@
     switch (outputColorType) {
         case kRGBA_8888_SkColorType:
         case kBGRA_8888_SkColorType: {
-            // If |prefColorSpace| is supported, choose it.
-            SkColorSpaceTransferFn fn;
-            if (prefColorSpace && prefColorSpace->isNumericalTransferFn(&fn)) {
+            // If |prefColorSpace| is supplied, choose it.
+            if (prefColorSpace) {
                 return prefColorSpace;
             }
 
@@ -179,8 +178,7 @@
                 }
 
                 if (is_wide_gamut(*encodedProfile)) {
-                    return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                                 SkColorSpace::kDCIP3_D65_Gamut);
+                    return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
                 }
             }
 
diff --git a/src/codec/SkRawCodec.cpp b/src/codec/SkRawCodec.cpp
index fb7efb9..a8ec40b 100644
--- a/src/codec/SkRawCodec.cpp
+++ b/src/codec/SkRawCodec.cpp
@@ -612,17 +612,6 @@
     bool fIsXtransImage;
 };
 
-static constexpr skcms_Matrix3x3 gAdobe_RGB_to_XYZD50 = {{
-    // ICC fixed-point (16.16) repesentation of:
-    // 0.60974, 0.20528, 0.14919,
-    // 0.31111, 0.62567, 0.06322,
-    // 0.01947, 0.06087, 0.74457,
-    { SkFixedToFloat(0x9c18), SkFixedToFloat(0x348d), SkFixedToFloat(0x2631) }, // Rx, Gx, Bx
-    { SkFixedToFloat(0x4fa5), SkFixedToFloat(0xa02c), SkFixedToFloat(0x102f) }, // Ry, Gy, By
-    { SkFixedToFloat(0x04fc), SkFixedToFloat(0x0f95), SkFixedToFloat(0xbe9c) }, // Rz, Gz, Bz
-}};
-
-
 /*
  * Tries to handle the image with PIEX. If PIEX returns kOk and finds the preview image, create a
  * SkJpegCodec. If PIEX returns kFail, then the file is invalid, return nullptr. In other cases,
@@ -649,12 +638,10 @@
 
         std::unique_ptr<SkEncodedInfo::ICCProfile> profile;
         if (imageData.color_space == ::piex::PreviewImageData::kAdobeRgb) {
-            constexpr skcms_TransferFunction twoDotTwo =
-                    { 2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
             skcms_ICCProfile skcmsProfile;
             skcms_Init(&skcmsProfile);
-            skcms_SetTransferFunction(&skcmsProfile, &twoDotTwo);
-            skcms_SetXYZD50(&skcmsProfile, &gAdobe_RGB_to_XYZD50);
+            skcms_SetTransferFunction(&skcmsProfile, &SkNamedTransferFn::k2Dot2);
+            skcms_SetXYZD50(&skcmsProfile, &SkNamedGamut::kAdobeRGB);
             profile = SkEncodedInfo::ICCProfile::Make(skcmsProfile);
         }
 
diff --git a/src/core/SkColorSpace.cpp b/src/core/SkColorSpace.cpp
index 66ddaf4..f54f2ec 100644
--- a/src/core/SkColorSpace.cpp
+++ b/src/core/SkColorSpace.cpp
@@ -20,6 +20,10 @@
     return true;
 }
 
+bool SkColorSpacePrimaries::toXYZD50(skcms_Matrix3x3* toXYZ_D50) const {
+    return skcms_PrimariesToXYZD50(fRX, fRY, fGX, fGY, fBX, fBY, fWX, fWY, toXYZ_D50);
+}
+
 static bool is_3x3(const SkMatrix44& m44) {
     return m44.getFloat(0, 3) == 0.0f
         && m44.getFloat(1, 3) == 0.0f
@@ -57,9 +61,9 @@
     fToXYZD50Hash = SkOpts::hash_fn(fToXYZD50_3x3, 9*sizeof(float), 0);
 
     switch (fGammaNamed) {
-        case kSRGB_SkGammaNamed:        transferFn = &  gSRGB_TransferFn.fG; break;
-        case k2Dot2Curve_SkGammaNamed:  transferFn = & g2Dot2_TransferFn.fG; break;
-        case kLinear_SkGammaNamed:      transferFn = &gLinear_TransferFn.fG; break;
+        case kSRGB_SkGammaNamed:        transferFn = &  SkNamedTransferFn::kSRGB.g; break;
+        case k2Dot2Curve_SkGammaNamed:  transferFn = & SkNamedTransferFn::k2Dot2.g; break;
+        case kLinear_SkGammaNamed:      transferFn = &SkNamedTransferFn::kLinear.g; break;
         case kNonStandard_SkGammaNamed:                                      break;
     }
     memcpy(fTransferFn, transferFn, 7*sizeof(float));
@@ -73,12 +77,12 @@
     }
     switch (gammaNamed) {
         case kSRGB_SkGammaNamed:
-            if (xyz_almost_equal(toXYZD50, gSRGB_toXYZD50)) {
+            if (xyz_almost_equal(toXYZD50, &SkNamedGamut::kSRGB.vals[0][0])) {
                 return SkColorSpace::MakeSRGB();
             }
             break;
         case kLinear_SkGammaNamed:
-            if (xyz_almost_equal(toXYZD50, gSRGB_toXYZD50)) {
+            if (xyz_almost_equal(toXYZD50, &SkNamedGamut::kSRGB.vals[0][0])) {
                 return SkColorSpace::MakeSRGBLinear();
             }
             break;
@@ -136,6 +140,16 @@
     return SkColorSpace::MakeRGB(coeffs, toXYZD50);
 }
 
+sk_sp<SkColorSpace> SkColorSpace::MakeRGB(const skcms_TransferFunction& transferFn,
+                                          const skcms_Matrix3x3& toXYZ) {
+    SkMatrix44 toXYZD50;
+    toXYZD50.set3x3RowMajorf(&toXYZ.vals[0][0]);
+    SkColorSpaceTransferFn tf;
+    memcpy(&tf, &transferFn, sizeof(tf));
+    // Going through this old path makes sure we classify transferFn as an SkGammaNamed
+    return SkColorSpace::MakeRGB(tf, toXYZD50);
+}
+
 class SkColorSpaceSingletonFactory {
 public:
     static SkColorSpace* Make(SkGammaNamed gamma, const float to_xyz[9]) {
@@ -148,12 +162,12 @@
 
 SkColorSpace* sk_srgb_singleton() {
     static SkColorSpace* cs = SkColorSpaceSingletonFactory::Make(kSRGB_SkGammaNamed,
-                                                                 gSRGB_toXYZD50);
+                                                                 &SkNamedGamut::kSRGB.vals[0][0]);
     return cs;
 }
 SkColorSpace* sk_srgb_linear_singleton() {
     static SkColorSpace* cs = SkColorSpaceSingletonFactory::Make(kLinear_SkGammaNamed,
-                                                                 gSRGB_toXYZD50);
+                                                                 &SkNamedGamut::kSRGB.vals[0][0]);
     return cs;
 }
 
@@ -196,6 +210,11 @@
     return true;
 }
 
+bool SkColorSpace::isNumericalTransferFn(skcms_TransferFunction* coeffs) const {
+    this->transferFn(&coeffs->g);
+    return true;
+}
+
 void SkColorSpace::transferFn(float gabcdef[7]) const {
     memcpy(gabcdef, &fTransferFn, 7*sizeof(float));
 }
@@ -210,6 +229,10 @@
     return true;
 }
 
+bool SkColorSpace::toXYZD50(skcms_Matrix3x3* toXYZD50) const {
+    memcpy(toXYZD50, fToXYZD50_3x3, 9*sizeof(float));
+    return true;
+}
 
 void SkColorSpace::gamutTransformTo(const SkColorSpace* dst, float src_to_dst[9]) const {
     dst->computeLazyDstFields();
@@ -314,134 +337,47 @@
 
 enum Version {
     k0_Version, // Initial version, header + flags for matrix and profile
+    k1_Version, // Simple header (version tag) + 16 floats
+
+    kCurrent_Version = k1_Version,
 };
 
 enum NamedColorSpace {
     kSRGB_NamedColorSpace,
-    // No longer a singleton, preserved to support reading data from branches m65 and older
     kAdobeRGB_NamedColorSpace,
     kSRGBLinear_NamedColorSpace,
 };
 
 struct ColorSpaceHeader {
-    /**
-     *  It is only valid to set zero or one flags.
-     *  Setting multiple flags is invalid.
-     */
-
-    /**
-     *  If kMatrix_Flag is set, we will write 12 floats after the header.
-     */
+    // Flag values, only used by old (k0_Version) serialization
     static constexpr uint8_t kMatrix_Flag     = 1 << 0;
-
-    /**
-     *  If kICC_Flag is set, we will write an ICC profile after the header.
-     *  The ICC profile will be written as a uint32 size, followed immediately
-     *  by the data (padded to 4 bytes).
-     *  DEPRECATED / UNUSED
-     */
     static constexpr uint8_t kICC_Flag        = 1 << 1;
-
-    /**
-     *  If kTransferFn_Flag is set, we will write 19 floats after the header.
-     *  The first seven represent the transfer fn, and the next twelve are the
-     *  matrix.
-     */
     static constexpr uint8_t kTransferFn_Flag = 1 << 3;
 
-    static ColorSpaceHeader Pack(Version version, uint8_t named, uint8_t gammaNamed, uint8_t flags)
-    {
-        ColorSpaceHeader header;
+    uint8_t fVersion = kCurrent_Version;
 
-        SkASSERT(k0_Version == version);
-        header.fVersion = (uint8_t) version;
-
-        SkASSERT(named <= kSRGBLinear_NamedColorSpace);
-        header.fNamed = (uint8_t) named;
-
-        SkASSERT(gammaNamed <= kNonStandard_SkGammaNamed);
-        header.fGammaNamed = (uint8_t) gammaNamed;
-
-        SkASSERT(flags <= kTransferFn_Flag);
-        header.fFlags = flags;
-        return header;
-    }
-
-    uint8_t fVersion;            // Always zero
-    uint8_t fNamed;              // Must be a SkColorSpace::Named
-    uint8_t fGammaNamed;         // Must be a SkGammaNamed
-    uint8_t fFlags;
+    // Other fields are only used by k0_Version. Could be re-purposed in future versions.
+    uint8_t fNamed      = 0;
+    uint8_t fGammaNamed = 0;
+    uint8_t fFlags      = 0;
 };
 
 size_t SkColorSpace::writeToMemory(void* memory) const {
-    // If we have a named profile, only write the enum.
-    const SkGammaNamed gammaNamed = this->gammaNamed();
-    if (this == sk_srgb_singleton()) {
-        if (memory) {
-            *((ColorSpaceHeader*) memory) = ColorSpaceHeader::Pack(
-                    k0_Version, kSRGB_NamedColorSpace, gammaNamed, 0);
-        }
-        return sizeof(ColorSpaceHeader);
-    } else if (this == sk_srgb_linear_singleton()) {
-        if (memory) {
-            *((ColorSpaceHeader*) memory) = ColorSpaceHeader::Pack(
-                    k0_Version, kSRGBLinear_NamedColorSpace, gammaNamed, 0);
-        }
-        return sizeof(ColorSpaceHeader);
+    if (memory) {
+        *((ColorSpaceHeader*) memory) = ColorSpaceHeader();
+        memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader));
+
+        memcpy(memory, fTransferFn, 7 * sizeof(float));
+        memory = SkTAddOffset<void>(memory, 7 * sizeof(float));
+
+        memcpy(memory, fToXYZD50_3x3, 9 * sizeof(float));
     }
 
-    // If we have a named gamma, write the enum and the matrix.
-    switch (gammaNamed) {
-        case kSRGB_SkGammaNamed:
-        case k2Dot2Curve_SkGammaNamed:
-        case kLinear_SkGammaNamed: {
-            if (memory) {
-                *((ColorSpaceHeader*) memory) =
-                        ColorSpaceHeader::Pack(k0_Version, 0, gammaNamed,
-                                                ColorSpaceHeader::kMatrix_Flag);
-                memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader));
-                SkMatrix44 m44;
-                this->toXYZD50(&m44);
-                m44.as3x4RowMajorf((float*) memory);
-            }
-            return sizeof(ColorSpaceHeader) + 12 * sizeof(float);
-        }
-        default: {
-            SkColorSpaceTransferFn transferFn;
-            SkAssertResult(this->isNumericalTransferFn(&transferFn));
-
-            if (memory) {
-                *((ColorSpaceHeader*) memory) =
-                        ColorSpaceHeader::Pack(k0_Version, 0, gammaNamed,
-                                                ColorSpaceHeader::kTransferFn_Flag);
-                memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader));
-
-                *(((float*) memory) + 0) = transferFn.fA;
-                *(((float*) memory) + 1) = transferFn.fB;
-                *(((float*) memory) + 2) = transferFn.fC;
-                *(((float*) memory) + 3) = transferFn.fD;
-                *(((float*) memory) + 4) = transferFn.fE;
-                *(((float*) memory) + 5) = transferFn.fF;
-                *(((float*) memory) + 6) = transferFn.fG;
-                memory = SkTAddOffset<void>(memory, 7 * sizeof(float));
-
-                SkMatrix44 m44;
-                this->toXYZD50(&m44);
-                m44.as3x4RowMajorf((float*) memory);
-            }
-
-            return sizeof(ColorSpaceHeader) + 19 * sizeof(float);
-        }
-    }
+    return sizeof(ColorSpaceHeader) + 16 * sizeof(float);
 }
 
 sk_sp<SkData> SkColorSpace::serialize() const {
-    size_t size = this->writeToMemory(nullptr);
-    if (0 == size) {
-        return nullptr;
-    }
-
-    sk_sp<SkData> data = SkData::MakeUninitialized(size);
+    sk_sp<SkData> data = SkData::MakeUninitialized(this->writeToMemory(nullptr));
     this->writeToMemory(data->writable_data());
     return data;
 }
@@ -454,59 +390,81 @@
     ColorSpaceHeader header = *((const ColorSpaceHeader*) data);
     data = SkTAddOffset<const void>(data, sizeof(ColorSpaceHeader));
     length -= sizeof(ColorSpaceHeader);
-    if (0 == header.fFlags) {
-        switch ((NamedColorSpace)header.fNamed) {
-            case kSRGB_NamedColorSpace:
-                return SkColorSpace::MakeSRGB();
-            case kSRGBLinear_NamedColorSpace:
-                return SkColorSpace::MakeSRGBLinear();
-            case kAdobeRGB_NamedColorSpace:
-                return SkColorSpace::MakeRGB(g2Dot2_TransferFn, SkColorSpace::kAdobeRGB_Gamut);
-        }
-    }
-
-    switch ((SkGammaNamed) header.fGammaNamed) {
-        case kSRGB_SkGammaNamed:
-        case k2Dot2Curve_SkGammaNamed:
-        case kLinear_SkGammaNamed: {
-            if (ColorSpaceHeader::kMatrix_Flag != header.fFlags || length < 12 * sizeof(float)) {
-                return nullptr;
-            }
-
-            SkMatrix44 toXYZ;
-            toXYZ.set3x4RowMajorf((const float*) data);
-            return SkColorSpace::MakeRGB((SkGammaNamed) header.fGammaNamed, toXYZ);
-        }
-        default:
-            break;
-    }
-
-    switch (header.fFlags) {
-        case ColorSpaceHeader::kICC_Flag: {
-            // Deprecated and unsupported code path
+    if (k1_Version == header.fVersion) {
+        if (length < 16 * sizeof(float)) {
             return nullptr;
         }
-        case ColorSpaceHeader::kTransferFn_Flag: {
-            if (length < 19 * sizeof(float)) {
+
+        skcms_TransferFunction transferFn;
+        memcpy(&transferFn, data, 7 * sizeof(float));
+        data = SkTAddOffset<const void>(data, 7 * sizeof(float));
+
+        skcms_Matrix3x3 toXYZ;
+        memcpy(&toXYZ, data, 9 * sizeof(float));
+        return SkColorSpace::MakeRGB(transferFn, toXYZ);
+    } else if (k0_Version == header.fVersion) {
+        if (0 == header.fFlags) {
+            switch ((NamedColorSpace)header.fNamed) {
+                case kSRGB_NamedColorSpace:
+                    return SkColorSpace::MakeSRGB();
+                case kSRGBLinear_NamedColorSpace:
+                    return SkColorSpace::MakeSRGBLinear();
+                case kAdobeRGB_NamedColorSpace:
+                    return SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2,
+                                                 SkNamedGamut::kAdobeRGB);
+            }
+        }
+
+        switch ((SkGammaNamed) header.fGammaNamed) {
+            case kSRGB_SkGammaNamed:
+            case k2Dot2Curve_SkGammaNamed:
+            case kLinear_SkGammaNamed: {
+                if (ColorSpaceHeader::kMatrix_Flag != header.fFlags ||
+                    length < 12 * sizeof(float)) {
+                    return nullptr;
+                }
+
+                SkMatrix44 toXYZ;
+                toXYZ.set3x4RowMajorf((const float*) data);
+                return SkColorSpace::MakeRGB((SkGammaNamed) header.fGammaNamed, toXYZ);
+            }
+            default:
+                break;
+        }
+
+        switch (header.fFlags) {
+            case ColorSpaceHeader::kICC_Flag: {
+                // Deprecated and unsupported code path
                 return nullptr;
             }
+            case ColorSpaceHeader::kTransferFn_Flag: {
+                if (length < 19 * sizeof(float)) {
+                    return nullptr;
+                }
 
-            SkColorSpaceTransferFn transferFn;
-            transferFn.fA = *(((const float*) data) + 0);
-            transferFn.fB = *(((const float*) data) + 1);
-            transferFn.fC = *(((const float*) data) + 2);
-            transferFn.fD = *(((const float*) data) + 3);
-            transferFn.fE = *(((const float*) data) + 4);
-            transferFn.fF = *(((const float*) data) + 5);
-            transferFn.fG = *(((const float*) data) + 6);
-            data = SkTAddOffset<const void>(data, 7 * sizeof(float));
+                // Version 0 TF is in abcdefg order
+                skcms_TransferFunction transferFn;
+                transferFn.a = *(((const float*) data) + 0);
+                transferFn.b = *(((const float*) data) + 1);
+                transferFn.c = *(((const float*) data) + 2);
+                transferFn.d = *(((const float*) data) + 3);
+                transferFn.e = *(((const float*) data) + 4);
+                transferFn.f = *(((const float*) data) + 5);
+                transferFn.g = *(((const float*) data) + 6);
+                data = SkTAddOffset<const void>(data, 7 * sizeof(float));
 
-            SkMatrix44 toXYZ;
-            toXYZ.set3x4RowMajorf((const float*) data);
-            return SkColorSpace::MakeRGB(transferFn, toXYZ);
+                // Version 0 matrix is row-major 3x4
+                skcms_Matrix3x3 toXYZ;
+                memcpy(&toXYZ.vals[0][0], (const float*)data + 0, 3 * sizeof(float));
+                memcpy(&toXYZ.vals[1][0], (const float*)data + 4, 3 * sizeof(float));
+                memcpy(&toXYZ.vals[2][0], (const float*)data + 8, 3 * sizeof(float));
+                return SkColorSpace::MakeRGB(transferFn, toXYZ);
+            }
+            default:
+                return nullptr;
         }
-        default:
-            return nullptr;
+    } else {
+        return nullptr;
     }
 }
 
diff --git a/src/core/SkColorSpacePriv.h b/src/core/SkColorSpacePriv.h
index 4d93d59..298b932 100644
--- a/src/core/SkColorSpacePriv.h
+++ b/src/core/SkColorSpacePriv.h
@@ -14,70 +14,26 @@
 
 #define SkColorSpacePrintf(...)
 
-static constexpr float gSRGB_toXYZD50[] {
-    // These are taken from skcms, and there originally from 16-bit fixed point.
-    // For best results, please keep them exactly in sync with skcms.
-    0.436065674f, 0.385147095f, 0.143066406f,
-    0.222488403f, 0.716873169f, 0.060607910f,
-    0.013916016f, 0.097076416f, 0.714096069f,
-};
-
-static constexpr float gAdobeRGB_toXYZD50[] {
-    // ICC fixed-point (16.16) repesentation of:
-    // 0.60974, 0.20528, 0.14919,
-    // 0.31111, 0.62567, 0.06322,
-    // 0.01947, 0.06087, 0.74457,
-    SkFixedToFloat(0x9c18), SkFixedToFloat(0x348d), SkFixedToFloat(0x2631), // Rx, Gx, Bx
-    SkFixedToFloat(0x4fa5), SkFixedToFloat(0xa02c), SkFixedToFloat(0x102f), // Ry, Gy, By
-    SkFixedToFloat(0x04fc), SkFixedToFloat(0x0f95), SkFixedToFloat(0xbe9c), // Rz, Gz, Bz
-};
-
-static constexpr float gDCIP3_toXYZD50[] {
-    0.515102f,   0.291965f,  0.157153f,  // Rx, Gx, Bx
-    0.241182f,   0.692236f,  0.0665819f, // Ry, Gy, By
-   -0.00104941f, 0.0418818f, 0.784378f,  // Rz, Gz, Bz
-};
-
-static constexpr float gRec2020_toXYZD50[] {
-    0.673459f,   0.165661f,  0.125100f,  // Rx, Gx, Bx
-    0.279033f,   0.675338f,  0.0456288f, // Ry, Gy, By
-   -0.00193139f, 0.0299794f, 0.797162f,  // Rz, Gz, Bz
-};
-
 // A gamut narrower than sRGB, useful for testing.
-static constexpr float gNarrow_toXYZD50[] {
-    0.190974f,  0.404865f,  0.368380f,
-    0.114746f,  0.582937f,  0.302318f,
-    0.032925f,  0.153615f,  0.638669f,
-};
-
-// Like gSRGB_toXYZD50, keeping this bitwise exactly the same as skcms makes things fastest.
-static constexpr SkColorSpaceTransferFn gSRGB_TransferFn =
-#ifdef SK_LEGACY_SRGB_TRANSFER_FUNCTION
-        { 2.4f, 1.0f / 1.055f, 0.055f / 1.055f, 1.0f / 12.92f, 0.04045f, 0.0f, 0.0f };
-#else
-        { 2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0.0f, 0.0f };
-#endif
-
-static constexpr SkColorSpaceTransferFn g2Dot2_TransferFn =
-        { 2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
-
-static constexpr SkColorSpaceTransferFn gLinear_TransferFn =
-        { 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+static constexpr skcms_Matrix3x3 gNarrow_toXYZD50 = {{
+    { 0.190974f,  0.404865f,  0.368380f },
+    { 0.114746f,  0.582937f,  0.302318f },
+    { 0.032925f,  0.153615f,  0.638669f },
+}};
 
 static inline void to_xyz_d50(SkMatrix44* toXYZD50, SkColorSpace::Gamut gamut) {
     switch (gamut) {
         case SkColorSpace::kSRGB_Gamut:
-            toXYZD50->set3x3RowMajorf(gSRGB_toXYZD50);
+            toXYZD50->set3x3RowMajorf(&SkNamedGamut::kSRGB.vals[0][0]);
             break;
         case SkColorSpace::kAdobeRGB_Gamut:
-            toXYZD50->set3x3RowMajorf(gAdobeRGB_toXYZD50);
+            toXYZD50->set3x3RowMajorf(&SkNamedGamut::kAdobeRGB.vals[0][0]);
             break;
         case SkColorSpace::kDCIP3_D65_Gamut:
-            toXYZD50->set3x3RowMajorf(gDCIP3_toXYZD50);
+            toXYZD50->set3x3RowMajorf(&SkNamedGamut::kDCIP3.vals[0][0]);
             break;
         case SkColorSpace::kRec2020_Gamut:
-            toXYZD50->set3x3RowMajorf(gRec2020_toXYZD50);
+            toXYZD50->set3x3RowMajorf(&SkNamedGamut::kRec2020.vals[0][0]);
             break;
     }
 }
@@ -149,13 +105,13 @@
 }
 
 static inline bool is_almost_srgb(const SkColorSpaceTransferFn& coeffs) {
-    return transfer_fn_almost_equal(gSRGB_TransferFn.fA, coeffs.fA) &&
-           transfer_fn_almost_equal(gSRGB_TransferFn.fB, coeffs.fB) &&
-           transfer_fn_almost_equal(gSRGB_TransferFn.fC, coeffs.fC) &&
-           transfer_fn_almost_equal(gSRGB_TransferFn.fD, coeffs.fD) &&
-           transfer_fn_almost_equal(gSRGB_TransferFn.fE, coeffs.fE) &&
-           transfer_fn_almost_equal(gSRGB_TransferFn.fF, coeffs.fF) &&
-           transfer_fn_almost_equal(gSRGB_TransferFn.fG, coeffs.fG);
+    return transfer_fn_almost_equal(SkNamedTransferFn::kSRGB.a, coeffs.fA) &&
+           transfer_fn_almost_equal(SkNamedTransferFn::kSRGB.b, coeffs.fB) &&
+           transfer_fn_almost_equal(SkNamedTransferFn::kSRGB.c, coeffs.fC) &&
+           transfer_fn_almost_equal(SkNamedTransferFn::kSRGB.d, coeffs.fD) &&
+           transfer_fn_almost_equal(SkNamedTransferFn::kSRGB.e, coeffs.fE) &&
+           transfer_fn_almost_equal(SkNamedTransferFn::kSRGB.f, coeffs.fF) &&
+           transfer_fn_almost_equal(SkNamedTransferFn::kSRGB.g, coeffs.fG);
 }
 
 static inline bool is_almost_2dot2(const SkColorSpaceTransferFn& coeffs) {
diff --git a/src/core/SkColorSpaceXformSteps.cpp b/src/core/SkColorSpaceXformSteps.cpp
index a2f73fc..5fff910 100644
--- a/src/core/SkColorSpaceXformSteps.cpp
+++ b/src/core/SkColorSpaceXformSteps.cpp
@@ -53,10 +53,10 @@
         this->src_to_dst_matrix[8] = row_major[8];
     } else {
     #ifdef SK_DEBUG
-        SkMatrix44 srcM, dstM;
+        skcms_Matrix3x3 srcM, dstM;
         src->toXYZD50(&srcM);
         dst->toXYZD50(&dstM);
-        SkASSERT(0 == memcmp(&srcM, &dstM, 16*sizeof(SkMScalar)) && "Hash collision");
+        SkASSERT(0 == memcmp(&srcM, &dstM, 9*sizeof(float)) && "Hash collision");
     #endif
     }
 
diff --git a/src/core/SkICC.cpp b/src/core/SkICC.cpp
index a9df5b0..d1db49e 100644
--- a/src/core/SkICC.cpp
+++ b/src/core/SkICC.cpp
@@ -206,14 +206,14 @@
 }
 
 static bool nearly_equal(const SkColorSpaceTransferFn& u,
-                         const SkColorSpaceTransferFn& v) {
-    return nearly_equal(u.fG, v.fG)
-        && nearly_equal(u.fA, v.fA)
-        && nearly_equal(u.fB, v.fB)
-        && nearly_equal(u.fC, v.fC)
-        && nearly_equal(u.fD, v.fD)
-        && nearly_equal(u.fE, v.fE)
-        && nearly_equal(u.fF, v.fF);
+                         const skcms_TransferFunction& v) {
+    return nearly_equal(u.fG, v.g)
+        && nearly_equal(u.fA, v.a)
+        && nearly_equal(u.fB, v.b)
+        && nearly_equal(u.fC, v.c)
+        && nearly_equal(u.fD, v.d)
+        && nearly_equal(u.fE, v.e)
+        && nearly_equal(u.fF, v.f);
 }
 
 static bool nearly_equal(const float u[9], const float v[9]) {
@@ -228,23 +228,23 @@
 // Return nullptr if the color profile doen't have a special name.
 const char* get_color_profile_description(const SkColorSpaceTransferFn& fn,
                                           const float toXYZD50[9]) {
-    bool srgb_xfer = nearly_equal(fn, gSRGB_TransferFn);
-    bool srgb_gamut = nearly_equal(toXYZD50, gSRGB_toXYZD50);
+    bool srgb_xfer = nearly_equal(fn, SkNamedTransferFn::kSRGB);
+    bool srgb_gamut = nearly_equal(toXYZD50, &SkNamedGamut::kSRGB.vals[0][0]);
     if (srgb_xfer && srgb_gamut) {
         return "sRGB";
     }
-    bool line_xfer = nearly_equal(fn, gLinear_TransferFn);
+    bool line_xfer = nearly_equal(fn, SkNamedTransferFn::kLinear);
     if (line_xfer && srgb_gamut) {
         return "Linear Transfer with sRGB Gamut";
     }
-    bool twoDotTwo = nearly_equal(fn, g2Dot2_TransferFn);
+    bool twoDotTwo = nearly_equal(fn, SkNamedTransferFn::k2Dot2);
     if (twoDotTwo && srgb_gamut) {
         return "2.2 Transfer with sRGB Gamut";
     }
-    if (twoDotTwo && nearly_equal(toXYZD50, gAdobeRGB_toXYZD50)) {
+    if (twoDotTwo && nearly_equal(toXYZD50, &SkNamedGamut::kAdobeRGB.vals[0][0])) {
         return "AdobeRGB";
     }
-    bool dcip3_gamut = nearly_equal(toXYZD50, gDCIP3_toXYZD50);
+    bool dcip3_gamut = nearly_equal(toXYZD50, &SkNamedGamut::kDCIP3.vals[0][0]);
     if (srgb_xfer || line_xfer) {
         if (srgb_xfer && dcip3_gamut) {
             return "sRGB Transfer with DCI-P3 Gamut";
@@ -252,7 +252,7 @@
         if (line_xfer && dcip3_gamut) {
             return "Linear Transfer with DCI-P3 Gamut";
         }
-        bool rec2020 = nearly_equal(toXYZD50, gRec2020_toXYZD50);
+        bool rec2020 = nearly_equal(toXYZD50, &SkNamedGamut::kRec2020.vals[0][0]);
         if (srgb_xfer && rec2020) {
             return "sRGB Transfer with Rec-BT-2020 Gamut";
         }
diff --git a/src/gpu/GrColorSpaceXform.cpp b/src/gpu/GrColorSpaceXform.cpp
index e8cb662..2cd5e5a 100644
--- a/src/gpu/GrColorSpaceXform.cpp
+++ b/src/gpu/GrColorSpaceXform.cpp
@@ -8,7 +8,6 @@
 #include "GrColorSpaceXform.h"
 #include "SkColorSpace.h"
 #include "SkColorSpacePriv.h"
-#include "SkMatrix44.h"
 #include "glsl/GrGLSLColorSpaceXformHelper.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
diff --git a/tests/AndroidCodecTest.cpp b/tests/AndroidCodecTest.cpp
index 3381d4f..49415f8 100644
--- a/tests/AndroidCodecTest.cpp
+++ b/tests/AndroidCodecTest.cpp
@@ -15,7 +15,6 @@
 #include "SkEncodedImageFormat.h"
 #include "SkImageGenerator.h"
 #include "SkImageInfo.h"
-#include "SkMatrix44.h"
 #include "SkPixmapPriv.h"
 #include "SkRefCnt.h"
 #include "SkSize.h"
@@ -157,8 +156,7 @@
         return;
     }
 
-    auto expected = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                          SkColorSpace::kDCIP3_D65_Gamut);
+    auto expected = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
     REPORTER_ASSERT(r, SkColorSpace::Equals(cs.get(), expected.get()));
 }
 
@@ -190,17 +188,15 @@
     REPORTER_ASSERT(r, !cs->isSRGB());
     REPORTER_ASSERT(r, cs->gammaCloseToSRGB());
 
-    SkMatrix44 matrix;
+    skcms_Matrix3x3 matrix;
     cs->toXYZD50(&matrix);
 
-    SkMatrix44 expected;
-    static constexpr float kExpected[] = {
-        0.426254272f,  0.369018555f,  0.168914795f,
-        0.226013184f,  0.685974121f,  0.0880126953f,
-        0.0116729736f, 0.0950927734f, 0.71812439f,
-    };
-    expected.set3x3RowMajorf(kExpected);
-    REPORTER_ASSERT(r, matrix == expected);
+    static constexpr skcms_Matrix3x3 kExpected = {{
+        { 0.426254272f,  0.369018555f,  0.168914795f  },
+        { 0.226013184f,  0.685974121f,  0.0880126953f },
+        { 0.0116729736f, 0.0950927734f, 0.71812439f   },
+    }};
+    REPORTER_ASSERT(r, 0 == memcmp(&matrix, &kExpected, sizeof(skcms_Matrix3x3)));
 }
 
 DEF_TEST(AndroidCodec_orientation, r) {
diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp
index bf30dd9..8aeb0c4 100644
--- a/tests/CanvasTest.cpp
+++ b/tests/CanvasTest.cpp
@@ -813,8 +813,8 @@
 
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
 DEF_TEST(Canvas_LegacyColorBehavior, r) {
-    sk_sp<SkColorSpace> cs = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                                   SkColorSpace::kAdobeRGB_Gamut);
+    sk_sp<SkColorSpace> cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
+                                                   SkNamedGamut::kAdobeRGB);
 
     // Make a Adobe RGB bitmap.
     SkBitmap bitmap;
diff --git a/tests/CodecTest.cpp b/tests/CodecTest.cpp
index bd988f6..8c918a8 100644
--- a/tests/CodecTest.cpp
+++ b/tests/CodecTest.cpp
@@ -26,7 +26,6 @@
 #include "SkMD5.h"
 #include "SkMakeUnique.h"
 #include "SkMalloc.h"
-#include "SkMatrix44.h"
 #include "SkPixmap.h"
 #include "SkPngChunkReader.h"
 #include "SkPngEncoder.h"
@@ -1025,7 +1024,7 @@
 
     const int dstWidth = subsetWidth / opts.fSampleSize;
     const int dstHeight = subsetHeight / opts.fSampleSize;
-    auto colorSpace = SkColorSpace::MakeRGB(g2Dot2_TransferFn, SkColorSpace::kAdobeRGB_Gamut);
+    auto colorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB);
     SkImageInfo dstInfo = codec->getInfo().makeWH(dstWidth, dstHeight)
                                           .makeColorType(kN32_SkColorType)
                                           .makeColorSpace(colorSpace);
@@ -1591,22 +1590,21 @@
 
     // Test with P3 color space.
     SkDynamicMemoryWStream p3Buf;
-    sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                                   SkColorSpace::kDCIP3_D65_Gamut);
+    sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
     pixmap.setColorSpace(p3);
     encode_format(&p3Buf, pixmap, format);
     sk_sp<SkData> p3Data = p3Buf.detachAsData();
     std::unique_ptr<SkCodec> p3Codec(SkCodec::MakeFromData(p3Data));
     REPORTER_ASSERT(r, p3Codec->getInfo().colorSpace()->gammaCloseToSRGB());
-    SkMatrix44 mat0, mat1;
+    skcms_Matrix3x3 mat0, mat1;
     bool success = p3->toXYZD50(&mat0);
     REPORTER_ASSERT(r, success);
     success = p3Codec->getInfo().colorSpace()->toXYZD50(&mat1);
     REPORTER_ASSERT(r, success);
 
-    for (int i = 0; i < 4; i++) {
-        for (int j = 0; j < 4; j++) {
-            REPORTER_ASSERT(r, color_space_almost_equal(mat0.get(i, j), mat1.get(i, j)));
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            REPORTER_ASSERT(r, color_space_almost_equal(mat0.vals[i][j], mat1.vals[i][j]));
         }
     }
 }
diff --git a/tests/ColorSpaceTest.cpp b/tests/ColorSpaceTest.cpp
index 42986e9..1deb8ee 100644
--- a/tests/ColorSpaceTest.cpp
+++ b/tests/ColorSpaceTest.cpp
@@ -11,7 +11,6 @@
 #include "SkColorSpacePriv.h"
 #include "SkData.h"
 #include "SkImageInfo.h"
-#include "SkMatrix44.h"
 #include "SkRefCnt.h"
 #include "SkStream.h"
 #include "SkTypes.h"
@@ -34,20 +33,13 @@
     REPORTER_ASSERT(r, nullptr != space);
     REPORTER_ASSERT(r, expectedGamma == space->gammaNamed());
 
-    SkMatrix44 mat;
+    skcms_Matrix3x3 mat;
     space->toXYZD50(&mat);
-    const float src[] = {
-        1, 0, 0, 1,
-        0, 1, 0, 1,
-        0, 0, 1, 1,
-    };
     const float* ref[3] = { red, green, blue };
-    float dst[4];
     for (int i = 0; i < 3; ++i) {
-        mat.mapScalars(&src[i*4], dst);
-        REPORTER_ASSERT(r, almost_equal(ref[i][0], dst[0]));
-        REPORTER_ASSERT(r, almost_equal(ref[i][1], dst[1]));
-        REPORTER_ASSERT(r, almost_equal(ref[i][2], dst[2]));
+        REPORTER_ASSERT(r, almost_equal(ref[i][0], mat.vals[0][i]));
+        REPORTER_ASSERT(r, almost_equal(ref[i][1], mat.vals[1][i]));
+        REPORTER_ASSERT(r, almost_equal(ref[i][2], mat.vals[2][i]));
     }
 }
 
@@ -70,12 +62,6 @@
     test_space(r, colorSpace.get(), red, green, blue, expectedGamma);
 }
 
-static constexpr float g_sRGB_XYZ[]{
-    0.4358f, 0.3853f, 0.1430f,    // Rx, Gx, Bx
-    0.2224f, 0.7170f, 0.0606f,    // Ry, Gy, Gz
-    0.0139f, 0.0971f, 0.7139f,    // Rz, Gz, Bz
-};
-
 static constexpr float g_sRGB_R[]{ 0.4358f, 0.2224f, 0.0139f };
 static constexpr float g_sRGB_G[]{ 0.3853f, 0.7170f, 0.0971f };
 static constexpr float g_sRGB_B[]{ 0.1430f, 0.0606f, 0.7139f };
@@ -93,9 +79,9 @@
               kSRGB_SkGammaNamed);
 #endif
 
-    const float red[] = { 0.385117f, 0.716904f, 0.0970612f };
+    const float red[]   = { 0.385117f, 0.716904f, 0.0970612f };
     const float green[] = { 0.143051f, 0.0606079f, 0.713913f };
-    const float blue[] = { 0.436035f, 0.222488f, 0.013916f };
+    const float blue[]  = { 0.436035f, 0.222488f, 0.013916f };
     test_path(r, "images/icc-v2-gbr.jpg", red, green, blue, k2Dot2Curve_SkGammaNamed);
 
     test_path(r, "images/webp-color-profile-crash.webp",
@@ -108,80 +94,6 @@
             red, green, blue, kNonStandard_SkGammaNamed);
 }
 
-DEF_TEST(ColorSpaceSRGBCompare, r) {
-    // Create an sRGB color space by name
-    sk_sp<SkColorSpace> namedColorSpace = SkColorSpace::MakeSRGB();
-
-    // Create an sRGB color space by value
-    SkMatrix44 srgbToxyzD50;
-    srgbToxyzD50.set3x3RowMajorf(g_sRGB_XYZ);
-    sk_sp<SkColorSpace> rgbColorSpace =
-            SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, srgbToxyzD50);
-    REPORTER_ASSERT(r, rgbColorSpace == namedColorSpace);
-
-    SkColorSpaceTransferFn srgbFn;
-    srgbFn.fA = (1.0f / 1.055f);
-    srgbFn.fB = (0.055f / 1.055f);
-    srgbFn.fC = (1.0f / 12.92f);
-    srgbFn.fD = 0.04045f;
-    srgbFn.fE = 0.0f;
-    srgbFn.fF = 0.0f;
-    srgbFn.fG = 2.4f;
-    sk_sp<SkColorSpace> rgbColorSpace2 = SkColorSpace::MakeRGB(srgbFn, srgbToxyzD50);
-    REPORTER_ASSERT(r, rgbColorSpace2 == namedColorSpace);
-
-    // Change a single value from the sRGB matrix
-    srgbToxyzD50.set(2, 2, 0.5f);
-    sk_sp<SkColorSpace> strangeColorSpace =
-            SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, srgbToxyzD50);
-    REPORTER_ASSERT(r, strangeColorSpace != namedColorSpace);
-}
-
-DEF_TEST(ColorSpaceSRGBLinearCompare, r) {
-    // Create the linear sRGB color space by name
-    sk_sp<SkColorSpace> namedColorSpace = SkColorSpace::MakeSRGBLinear();
-
-    // Create the linear sRGB color space via the sRGB color space's makeLinearGamma()
-    auto srgb = SkColorSpace::MakeSRGB();
-    sk_sp<SkColorSpace> viaSrgbColorSpace = srgb->makeLinearGamma();
-    REPORTER_ASSERT(r, namedColorSpace == viaSrgbColorSpace);
-
-    // Create a linear sRGB color space by value
-    SkMatrix44 srgbToxyzD50;
-    srgbToxyzD50.set3x3RowMajorf(g_sRGB_XYZ);
-    sk_sp<SkColorSpace> rgbColorSpace =
-        SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, srgbToxyzD50);
-    REPORTER_ASSERT(r, rgbColorSpace == namedColorSpace);
-
-    SkColorSpaceTransferFn linearExpFn;
-    linearExpFn.fA = 1.0f;
-    linearExpFn.fB = 0.0f;
-    linearExpFn.fC = 0.0f;
-    linearExpFn.fD = 0.0f;
-    linearExpFn.fE = 0.0f;
-    linearExpFn.fF = 0.0f;
-    linearExpFn.fG = 1.0f;
-    sk_sp<SkColorSpace> rgbColorSpace2 = SkColorSpace::MakeRGB(linearExpFn, srgbToxyzD50);
-    REPORTER_ASSERT(r, rgbColorSpace2 == namedColorSpace);
-
-    SkColorSpaceTransferFn linearFn;
-    linearFn.fA = 0.0f;
-    linearFn.fB = 0.0f;
-    linearFn.fC = 1.0f;
-    linearFn.fD = 1.0f;
-    linearFn.fE = 0.0f;
-    linearFn.fF = 0.0f;
-    linearFn.fG = 0.0f;
-    sk_sp<SkColorSpace> rgbColorSpace3 = SkColorSpace::MakeRGB(linearFn, srgbToxyzD50);
-    REPORTER_ASSERT(r, rgbColorSpace3 == namedColorSpace);
-
-    // Change a single value from the sRGB matrix
-    srgbToxyzD50.set(2, 2, 0.5f);
-    sk_sp<SkColorSpace> strangeColorSpace =
-        SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, srgbToxyzD50);
-    REPORTER_ASSERT(r, strangeColorSpace != namedColorSpace);
-}
-
 static void test_serialize(skiatest::Reporter* r, sk_sp<SkColorSpace> space, bool isNamed) {
     sk_sp<SkData> data1 = space->serialize();
 
@@ -219,15 +131,19 @@
     test("icc_profiles/HP_ZR30w.icc");
     test("icc_profiles/HP_Z32x.icc");
 
-    SkColorSpaceTransferFn fn;
-    fn.fA = 1.0f;
-    fn.fB = 0.0f;
-    fn.fC = 1.0f;
-    fn.fD = 0.5f;
-    fn.fE = 0.0f;
-    fn.fF = 0.0f;
-    fn.fG = 1.0f;
-    SkMatrix44 toXYZ(SkMatrix44::kIdentity_Constructor);
+    skcms_TransferFunction fn;
+    fn.a = 1.0f;
+    fn.b = 0.0f;
+    fn.c = 1.0f;
+    fn.d = 0.5f;
+    fn.e = 0.0f;
+    fn.f = 0.0f;
+    fn.g = 1.0f;
+    skcms_Matrix3x3 toXYZ = {{
+        { 1, 0, 0 },
+        { 0, 1, 0 },
+        { 0, 0, 1 },
+    }};
     test_serialize(r, SkColorSpace::MakeRGB(fn, toXYZ), false);
 }
 
@@ -248,15 +164,19 @@
     sk_sp<SkColorSpace> z30 = parse("icc_profiles/HP_ZR30w.icc");
     sk_sp<SkColorSpace> z32 = parse("icc_profiles/HP_Z32x.icc");
 
-    SkColorSpaceTransferFn fn;
-    fn.fA = 1.0f;
-    fn.fB = 0.0f;
-    fn.fC = 1.0f;
-    fn.fD = 0.5f;
-    fn.fE = 0.0f;
-    fn.fF = 0.0f;
-    fn.fG = 1.0f;
-    SkMatrix44 toXYZ(SkMatrix44::kIdentity_Constructor);
+    skcms_TransferFunction fn;
+    fn.a = 1.0f;
+    fn.b = 0.0f;
+    fn.c = 1.0f;
+    fn.d = 0.5f;
+    fn.e = 0.0f;
+    fn.f = 0.0f;
+    fn.g = 1.0f;
+    skcms_Matrix3x3 toXYZ = {{
+        { 1, 0, 0 },
+        { 0, 1, 0 },
+        { 0, 0, 1 },
+    }};
     sk_sp<SkColorSpace> rgb4 = SkColorSpace::MakeRGB(fn, toXYZ);
 
     REPORTER_ASSERT(r, SkColorSpace::Equals(nullptr, nullptr));
@@ -273,28 +193,20 @@
     REPORTER_ASSERT(r, !SkColorSpace::Equals(srgb.get(), rgb4.get()));
 }
 
-static inline bool matrix_almost_equal(const SkMatrix44& a, const SkMatrix44& b) {
-    return almost_equal(a.get(0, 0), b.get(0, 0)) &&
-           almost_equal(a.get(0, 1), b.get(0, 1)) &&
-           almost_equal(a.get(0, 2), b.get(0, 2)) &&
-           almost_equal(a.get(0, 3), b.get(0, 3)) &&
-           almost_equal(a.get(1, 0), b.get(1, 0)) &&
-           almost_equal(a.get(1, 1), b.get(1, 1)) &&
-           almost_equal(a.get(1, 2), b.get(1, 2)) &&
-           almost_equal(a.get(1, 3), b.get(1, 3)) &&
-           almost_equal(a.get(2, 0), b.get(2, 0)) &&
-           almost_equal(a.get(2, 1), b.get(2, 1)) &&
-           almost_equal(a.get(2, 2), b.get(2, 2)) &&
-           almost_equal(a.get(2, 3), b.get(2, 3)) &&
-           almost_equal(a.get(3, 0), b.get(3, 0)) &&
-           almost_equal(a.get(3, 1), b.get(3, 1)) &&
-           almost_equal(a.get(3, 2), b.get(3, 2)) &&
-           almost_equal(a.get(3, 3), b.get(3, 3));
+static inline bool matrix_almost_equal(const skcms_Matrix3x3& a, const skcms_Matrix3x3& b) {
+    for (int r = 0; r < 3; ++r) {
+        for (int c = 0; c < 3; ++c) {
+            if (!almost_equal(a.vals[r][c], b.vals[r][c])) {
+                return false;
+            }
+        }
+    }
+    return true;
 }
 
 static inline void check_primaries(skiatest::Reporter* r, const SkColorSpacePrimaries& primaries,
-                                   const SkMatrix44& reference) {
-    SkMatrix44 toXYZ;
+                                   const skcms_Matrix3x3& reference) {
+    skcms_Matrix3x3 toXYZ;
     bool result = primaries.toXYZD50(&toXYZ);
     REPORTER_ASSERT(r, result);
     REPORTER_ASSERT(r, matrix_almost_equal(toXYZ, reference));
@@ -302,21 +214,16 @@
 
 DEF_TEST(ColorSpace_Primaries, r) {
     // sRGB primaries (D65)
-    SkColorSpacePrimaries srgb;
-    srgb.fRX = 0.64f;
-    srgb.fRY = 0.33f;
-    srgb.fGX = 0.30f;
-    srgb.fGY = 0.60f;
-    srgb.fBX = 0.15f;
-    srgb.fBY = 0.06f;
-    srgb.fWX = 0.3127f;
-    srgb.fWY = 0.3290f;
-    SkMatrix44 srgbToXYZ;
-    bool result = srgb.toXYZD50(&srgbToXYZ);
+    skcms_Matrix3x3 srgbToXYZ;
+    bool result = skcms_PrimariesToXYZD50(
+        0.64f, 0.33f,
+        0.30f, 0.60f,
+        0.15f, 0.06f,
+        0.3127f, 0.3290f,
+        &srgbToXYZ);
     REPORTER_ASSERT(r, result);
 
-    sk_sp<SkColorSpace> space = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                                      srgbToXYZ);
+    sk_sp<SkColorSpace> space = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, srgbToXYZ);
     REPORTER_ASSERT(r, SkColorSpace::MakeSRGB() == space);
 
     // ProPhoto (D50)
@@ -329,10 +236,11 @@
     proPhoto.fBY = 0.0001f;
     proPhoto.fWX = 0.34567f;
     proPhoto.fWY = 0.35850f;
-    SkMatrix44 proToXYZ;
-    proToXYZ.set3x3(0.7976749f, 0.2880402f, 0.0000000f,
-                    0.1351917f, 0.7118741f, 0.0000000f,
-                    0.0313534f, 0.0000857f, 0.8252100f);
+    skcms_Matrix3x3 proToXYZ = {{
+        { 0.7976749f, 0.1351917f, 0.0313534f },
+        { 0.2880402f, 0.7118741f, 0.0000857f },
+        { 0.0000000f, 0.0000000f, 0.8252100f },
+    }};
     check_primaries(r, proPhoto, proToXYZ);
 
     // NTSC (C)
@@ -345,10 +253,11 @@
     ntsc.fBY = 0.08f;
     ntsc.fWX = 0.31006f;
     ntsc.fWY = 0.31616f;
-    SkMatrix44 ntscToXYZ;
-    ntscToXYZ.set3x3(0.6343706f, 0.3109496f, -0.0011817f,
-                     0.1852204f, 0.5915984f, 0.0555518f,
-                     0.1446290f, 0.0974520f, 0.7708399f);
+    skcms_Matrix3x3 ntscToXYZ = {{
+        {  0.6343706f, 0.1852204f, 0.1446290f },
+        {  0.3109496f, 0.5915984f, 0.0974520f },
+        { -0.0011817f, 0.0555518f, 0.7708399f }
+    }};
     check_primaries(r, ntsc, ntscToXYZ);
 
     // DCI P3 (D65)
@@ -361,9 +270,8 @@
     p3.fBY = 0.060f;
     p3.fWX = 0.3127f;
     p3.fWY = 0.3290f;
-    space = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                  SkColorSpace::kDCIP3_D65_Gamut);
-    SkMatrix44 reference;
+    space = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
+    skcms_Matrix3x3 reference;
     SkAssertResult(space->toXYZD50(&reference));
     check_primaries(r, p3, reference);
 
@@ -377,8 +285,7 @@
     rec2020.fBY = 0.046f;
     rec2020.fWX = 0.3127f;
     rec2020.fWY = 0.3290f;
-    space = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                  SkColorSpace::kRec2020_Gamut);
+    space = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kRec2020);
     SkAssertResult(space->toXYZD50(&reference));
     check_primaries(r, rec2020, reference);
 }
@@ -386,18 +293,16 @@
 DEF_TEST(ColorSpace_MatrixHash, r) {
     sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
 
-    SkColorSpaceTransferFn fn;
-    fn.fA = 1.0f;
-    fn.fB = 0.0f;
-    fn.fC = 0.0f;
-    fn.fD = 0.0f;
-    fn.fE = 0.0f;
-    fn.fF = 0.0f;
-    fn.fG = 3.0f;
+    skcms_TransferFunction fn;
+    fn.a = 1.0f;
+    fn.b = 0.0f;
+    fn.c = 0.0f;
+    fn.d = 0.0f;
+    fn.e = 0.0f;
+    fn.f = 0.0f;
+    fn.g = 3.0f;
 
-    SkMatrix44 srgbMat;
-    srgbMat.set3x3RowMajorf(gSRGB_toXYZD50);
-    sk_sp<SkColorSpace> strange = SkColorSpace::MakeRGB(fn, srgbMat);
+    sk_sp<SkColorSpace> strange = SkColorSpace::MakeRGB(fn, SkNamedGamut::kSRGB);
 
     REPORTER_ASSERT(r, srgb->toXYZD50Hash() == strange->toXYZD50Hash());
 }
@@ -405,15 +310,15 @@
 DEF_TEST(ColorSpace_IsSRGB, r) {
     sk_sp<SkColorSpace> srgb0 = SkColorSpace::MakeSRGB();
 
-    SkColorSpaceTransferFn fn;
-    fn.fA = 1.0f;
-    fn.fB = 0.0f;
-    fn.fC = 0.0f;
-    fn.fD = 0.0f;
-    fn.fE = 0.0f;
-    fn.fF = 0.0f;
-    fn.fG = 2.2f;
-    sk_sp<SkColorSpace> twoDotTwo = SkColorSpace::MakeRGB(fn, SkColorSpace::kSRGB_Gamut);
+    skcms_TransferFunction fn;
+    fn.a = 1.0f;
+    fn.b = 0.0f;
+    fn.c = 0.0f;
+    fn.d = 0.0f;
+    fn.e = 0.0f;
+    fn.f = 0.0f;
+    fn.g = 2.2f;
+    sk_sp<SkColorSpace> twoDotTwo = SkColorSpace::MakeRGB(fn, SkNamedGamut::kSRGB);
 
     REPORTER_ASSERT(r, srgb0->isSRGB());
     REPORTER_ASSERT(r, !twoDotTwo->isSRGB());
diff --git a/tests/DeferredDisplayListTest.cpp b/tests/DeferredDisplayListTest.cpp
index 3f03ae1..8e49446 100644
--- a/tests/DeferredDisplayListTest.cpp
+++ b/tests/DeferredDisplayListTest.cpp
@@ -296,8 +296,7 @@
         case 4:
             // This just needs to be a colorSpace different from that returned by MakeSRGB().
             // In this case we just change the gamut.
-            fColorSpace = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                                SkColorSpace::kAdobeRGB_Gamut);
+            fColorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kAdobeRGB);
             break;
         case kSampleCount:
             fSampleCount = 4;
diff --git a/tests/ICCTest.cpp b/tests/ICCTest.cpp
index f82bfa4..efe39a4 100644
--- a/tests/ICCTest.cpp
+++ b/tests/ICCTest.cpp
@@ -15,56 +15,13 @@
 
 #include "../third_party/skcms/skcms.h"
 
-DEF_TEST(WriteICCProfile, r) {
-    auto adobeRGB = SkColorSpace::MakeRGB(g2Dot2_TransferFn, SkColorSpace::kAdobeRGB_Gamut);
-
-    struct {
-        SkColorSpaceTransferFn fn;
-        const float*           toXYZD50;
-        const char*            desc;
-        sk_sp<SkColorSpace>    want;
-    } tests[] = {
-        {g2Dot2_TransferFn, gAdobeRGB_toXYZD50, "AdobeRGB", adobeRGB},
-        { gSRGB_TransferFn,     gSRGB_toXYZD50,     "sRGB", SkColorSpace::MakeSRGB()},
-    };
-
-    for (auto test : tests) {
-        sk_sp<SkData> profile = SkWriteICCProfile(test.fn, test.toXYZD50);
-        REPORTER_ASSERT(r, profile);
-
-        skcms_ICCProfile parsed;
-        REPORTER_ASSERT(r, skcms_Parse(profile->data(), profile->size(), &parsed));
-
-        sk_sp<SkColorSpace> got = SkColorSpace::Make(parsed);
-        REPORTER_ASSERT(r, got);
-        REPORTER_ASSERT(r, SkColorSpace::Equals(got.get(), test.want.get()));
-
-        skcms_ICCTag desc;
-        REPORTER_ASSERT(r, skcms_GetTagBySignature(&parsed,
-                                                   SkSetFourByteTag('d','e','s','c'),
-                                                   &desc));
-
-        // Rather than really carefully break down the 'desc' tag,
-        // just check our expected description is somewhere in there (as big-endian UTF-16).
-        uint8_t big_endian_utf16[16];
-        for (size_t i = 0; i < strlen(test.desc); i++) {
-            big_endian_utf16[2*i+0] = 0;
-            big_endian_utf16[2*i+1] = test.desc[i];
-        }
-
-        SkString haystack((const char*)desc.buf, desc.size),
-                 needle  ((const char*)big_endian_utf16, 2*strlen(test.desc));
-        REPORTER_ASSERT(r, haystack.contains(needle.c_str()));
-    }
-}
-
 DEF_TEST(AdobeRGB, r) {
     if (sk_sp<SkData> profile = GetResourceAsData("icc_profiles/AdobeRGB1998.icc")) {
         skcms_ICCProfile parsed;
         REPORTER_ASSERT(r, skcms_Parse(profile->data(), profile->size(), &parsed));
 
         auto got  = SkColorSpace::Make(parsed);
-        auto want = SkColorSpace::MakeRGB(g2Dot2_TransferFn, SkColorSpace::kAdobeRGB_Gamut);
+        auto want = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB);
         REPORTER_ASSERT(r, SkColorSpace::Equals(got.get(), want.get()));
     }
 }
diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp
index 9b78821..1ad49dd 100644
--- a/tests/ImageTest.cpp
+++ b/tests/ImageTest.cpp
@@ -1168,13 +1168,13 @@
     REPORTER_ASSERT(r, srgb.get() == image->colorSpace());
 
     image = GetResourceAsImage("images/webp-color-profile-lossy.webp");
-    SkColorSpaceTransferFn fn;
+    skcms_TransferFunction fn;
     bool success = image->colorSpace()->isNumericalTransferFn(&fn);
     REPORTER_ASSERT(r, success);
-    REPORTER_ASSERT(r, color_space_almost_equal(1.8f, fn.fG));
+    REPORTER_ASSERT(r, color_space_almost_equal(1.8f, fn.g));
 
-    sk_sp<SkColorSpace> rec2020 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                                        SkColorSpace::kRec2020_Gamut);
+    sk_sp<SkColorSpace> rec2020 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
+                                                        SkNamedGamut::kRec2020);
     image = create_picture_image(rec2020);
     REPORTER_ASSERT(r, SkColorSpace::Equals(rec2020.get(), image->colorSpace()));
 
@@ -1195,11 +1195,10 @@
 }
 
 DEF_TEST(Image_makeColorSpace, r) {
-    sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                                   SkColorSpace::kDCIP3_D65_Gamut);
-    SkColorSpaceTransferFn fn;
-    fn.fA = 1.f; fn.fB = 0.f; fn.fC = 0.f; fn.fD = 0.f; fn.fE = 0.f; fn.fF = 0.f; fn.fG = 1.8f;
-    sk_sp<SkColorSpace> adobeGamut = SkColorSpace::MakeRGB(fn, SkColorSpace::kAdobeRGB_Gamut);
+    sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
+    skcms_TransferFunction fn;
+    fn.a = 1.f; fn.b = 0.f; fn.c = 0.f; fn.d = 0.f; fn.e = 0.f; fn.f = 0.f; fn.g = 1.8f;
+    sk_sp<SkColorSpace> adobeGamut = SkColorSpace::MakeRGB(fn, SkNamedGamut::kAdobeRGB);
 
     SkBitmap srgbBitmap;
     srgbBitmap.allocPixels(SkImageInfo::MakeS32(1, 1, kOpaque_SkAlphaType));
diff --git a/tests/NonlinearBlendingTest.cpp b/tests/NonlinearBlendingTest.cpp
index e91dda8..768d964 100644
--- a/tests/NonlinearBlendingTest.cpp
+++ b/tests/NonlinearBlendingTest.cpp
@@ -12,8 +12,7 @@
 
 DEF_TEST(SkColorSpaceXformSteps_vs_skcms, r) {
     auto srgb = SkColorSpace::MakeSRGB();
-    auto dp3  = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                      SkColorSpace::kDCIP3_D65_Gamut);
+    auto dp3  = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
 
     skcms_ICCProfile srgb_profile;
     srgb->toProfile(&srgb_profile);
diff --git a/tests/SkColorSpaceXformStepsTest.cpp b/tests/SkColorSpaceXformStepsTest.cpp
index f0ae414..6b15385 100644
--- a/tests/SkColorSpaceXformStepsTest.cpp
+++ b/tests/SkColorSpaceXformStepsTest.cpp
@@ -11,8 +11,8 @@
 
 DEF_TEST(SkColorSpaceXformSteps, r) {
     auto srgb   = SkColorSpace::MakeSRGB(),
-         adobe  = SkColorSpace::MakeRGB(g2Dot2_TransferFn, SkColorSpace::kAdobeRGB_Gamut),
-         srgb22 = SkColorSpace::MakeRGB(g2Dot2_TransferFn, SkColorSpace::    kSRGB_Gamut),
+         adobe  = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB),
+         srgb22 = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kSRGB),
          srgb1  = srgb ->makeLinearGamma(),
          adobe1 = adobe->makeLinearGamma();
 
diff --git a/tests/TableColorFilterTest.cpp b/tests/TableColorFilterTest.cpp
index 857aa7e..bb5df60 100644
--- a/tests/TableColorFilterTest.cpp
+++ b/tests/TableColorFilterTest.cpp
@@ -17,8 +17,7 @@
 
 DEF_TEST(TableColorFilter, r) {
     // Using a wide source gamut will make saturated colors go well out of range of sRGB.
-    auto rec2020 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                         SkColorSpace::kRec2020_Gamut);
+    auto rec2020 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kRec2020);
     sk_sp<SkColorFilter> to_srgb = SkToSRGBColorFilter::Make(rec2020);
 
     // Any table will work fine here.  An identity table makes testing easy.
diff --git a/tests/ToSRGBColorFilter.cpp b/tests/ToSRGBColorFilter.cpp
index 01c5673..519ffb8 100644
--- a/tests/ToSRGBColorFilter.cpp
+++ b/tests/ToSRGBColorFilter.cpp
@@ -22,8 +22,7 @@
     REPORTER_ASSERT(r, nullptr == SkToSRGBColorFilter::Make(nullptr));
 
     // Here's a realistic conversion.
-    auto dci_p3 = SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma,
-                                        SkColorSpace::kDCIP3_D65_Gamut);
+    auto dci_p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, SkNamedGamut::kDCIP3);
     REPORTER_ASSERT(r, nullptr != SkToSRGBColorFilter::Make(dci_p3));
 
 }
diff --git a/tools/flags/SkCommonFlagsConfig.cpp b/tools/flags/SkCommonFlagsConfig.cpp
index 66a5b2a..44e79ac 100644
--- a/tools/flags/SkCommonFlagsConfig.cpp
+++ b/tools/flags/SkCommonFlagsConfig.cpp
@@ -309,16 +309,13 @@
         *outColorSpace = SkColorSpace::MakeSRGB();
     } else if (value.equals("p3")) {
         *outColorType = kRGBA_8888_SkColorType;
-        *outColorSpace = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                               SkColorSpace::kDCIP3_D65_Gamut);
+        *outColorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
     } else if (value.equals("esrgb")) {
         *outColorType = kRGBA_F16_SkColorType;
         *outColorSpace = SkColorSpace::MakeSRGB();
     } else if (value.equals("narrow") || value.equals("enarrow")) {
-        SkMatrix44 narrow_gamut;
-        narrow_gamut.set3x3RowMajorf(gNarrow_toXYZD50);
         *outColorType = value.equals("narrow") ? kRGBA_8888_SkColorType : kRGBA_F16_SkColorType;
-        *outColorSpace = SkColorSpace::MakeRGB(k2Dot2Curve_SkGammaNamed, narrow_gamut);
+        *outColorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, gNarrow_toXYZD50);
     } else if (value.equals("f16")) {
         *outColorType = kRGBA_F16_SkColorType;
         *outColorSpace = SkColorSpace::MakeSRGBLinear();
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index fcae406..7ec001b 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -185,7 +185,7 @@
     , fColorMode(ColorMode::kLegacy)
     , fColorSpacePrimaries(gSrgbPrimaries)
     // Our UI can only tweak gamma (currently), so start out gamma-only
-    , fColorSpaceTransferFn(g2Dot2_TransferFn)
+    , fColorSpaceTransferFn(SkNamedTransferFn::k2Dot2)
     , fZoomLevel(0.0f)
     , fRotation(0.0f)
     , fOffset{0.5f, 0.5f}
@@ -748,7 +748,7 @@
         }
         title.appendf(" %s Gamma %f",
                       curPrimaries >= 0 ? gNamedPrimaries[curPrimaries].fName : "Custom",
-                      fColorSpaceTransferFn.fG);
+                      fColorSpaceTransferFn.g);
     }
 
     const DisplayParams& params = fWindow->getRequestedDisplayParams();
@@ -1091,7 +1091,7 @@
     // If we're in any of the color managed modes, construct the color space we're going to use
     sk_sp<SkColorSpace> colorSpace = nullptr;
     if (ColorMode::kLegacy != fColorMode) {
-        SkMatrix44 toXYZ;
+        skcms_Matrix3x3 toXYZ;
         SkAssertResult(fColorSpacePrimaries.toXYZD50(&toXYZ));
         colorSpace = SkColorSpace::MakeRGB(fColorSpaceTransferFn, toXYZ);
     }
@@ -1835,7 +1835,7 @@
                 }
 
                 // Let user adjust the gamma
-                ImGui::SliderFloat("Gamma", &fColorSpaceTransferFn.fG, 0.5f, 3.5f);
+                ImGui::SliderFloat("Gamma", &fColorSpaceTransferFn.g, 0.5f, 3.5f);
 
                 if (ImGui::Combo("Primaries", &primariesIdx,
                                  "sRGB\0AdobeRGB\0P3\0Rec. 2020\0Custom\0\0")) {
diff --git a/tools/viewer/Viewer.h b/tools/viewer/Viewer.h
index e140eb4..2cff486 100644
--- a/tools/viewer/Viewer.h
+++ b/tools/viewer/Viewer.h
@@ -138,7 +138,7 @@
     // Color properties for slide rendering
     ColorMode              fColorMode;
     SkColorSpacePrimaries  fColorSpacePrimaries;
-    SkColorSpaceTransferFn fColorSpaceTransferFn;
+    skcms_TransferFunction fColorSpaceTransferFn;
 
     // transform data
     SkScalar               fZoomLevel;