Add SkColorSpace_Base::makeColorSpin

This is a utility that creates a version of an existing XYZ color space
that performs our color spin operation. Assigning this to a source remaps
RGB to GBR. Assigning it to a destination does the opposite (RGB to BRG).

Bug: skia:
Change-Id: I3528698220bd32aa01dcd3db225e60f151a4b5bd
Reviewed-on: https://skia-review.googlesource.com/71280
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Mike Klein <mtklein@chromium.org>
diff --git a/dm/DM.cpp b/dm/DM.cpp
index 77538cf..2ef7dc9 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -919,19 +919,7 @@
 }
 
 static sk_sp<SkColorSpace> rgb_to_gbr() {
-    float gbr[9];
-    gbr[0] = gSRGB_toXYZD50[1];
-    gbr[1] = gSRGB_toXYZD50[2];
-    gbr[2] = gSRGB_toXYZD50[0];
-    gbr[3] = gSRGB_toXYZD50[4];
-    gbr[4] = gSRGB_toXYZD50[5];
-    gbr[5] = gSRGB_toXYZD50[3];
-    gbr[6] = gSRGB_toXYZD50[7];
-    gbr[7] = gSRGB_toXYZD50[8];
-    gbr[8] = gSRGB_toXYZD50[6];
-    SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
-    toXYZD50.set3x3RowMajorf(gbr);
-    return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, toXYZD50);
+    return as_CSB(SkColorSpace::MakeSRGB())->makeColorSpin();
 }
 
 static Sink* create_via(const SkString& tag, Sink* wrapped) {
diff --git a/gm/color4f.cpp b/gm/color4f.cpp
index 2ebe931..36850a1 100644
--- a/gm/color4f.cpp
+++ b/gm/color4f.cpp
@@ -96,11 +96,7 @@
     canvas->translate(10, 10);
 
     auto srgb = SkColorSpace::MakeSRGB();
-
-    SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
-    // red -> blue, green -> red, blue -> green (sRGB)
-    mat.set3x3(0, 0, 1, 1, 0, 0, 0, 1, 0);
-    mat.postConcat(*as_CSB(srgb)->toXYZD50());
+    auto spin = as_CSB(srgb)->makeColorSpin(); // RGB -> GBR
 
     const SkColor4f colors[] {
         { 1, 0, 0, 1 },
@@ -116,8 +112,7 @@
         sk_sp<SkShader> shaders[] {
             SkShader::MakeColorShader(c4, nullptr),
             SkShader::MakeColorShader(c4, srgb),
-            SkShader::MakeColorShader(c4,
-                    SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, mat)),
+            SkShader::MakeColorShader(c4, spin),
         };
 
         canvas->save();
diff --git a/src/core/SkColorSpace_Base.h b/src/core/SkColorSpace_Base.h
index 32e12b4..083c3eb 100644
--- a/src/core/SkColorSpace_Base.h
+++ b/src/core/SkColorSpace_Base.h
@@ -180,6 +180,16 @@
      */
     virtual sk_sp<SkColorSpace> makeSRGBGamma() const = 0;
 
+    /**
+     *  Returns a color space with the same transfer function as this one, but with the primary
+     *  colors rotated. For any XYZ space, this produces a new color space that maps RGB to GBR
+     *  (when applied to a source), and maps RGB to BRG (when applied to a destination). For other
+     *  types of color spaces, returns nullptr.
+     *
+     *  This is used for testing, to construct color spaces that have severe and testable behavior.
+     */
+    virtual sk_sp<SkColorSpace> makeColorSpin() const { return nullptr; }
+
     enum class Type : uint8_t {
         kXYZ,
         kA2B
diff --git a/src/core/SkColorSpace_XYZ.cpp b/src/core/SkColorSpace_XYZ.cpp
index 9b650b3..ed66a74 100644
--- a/src/core/SkColorSpace_XYZ.cpp
+++ b/src/core/SkColorSpace_XYZ.cpp
@@ -96,6 +96,13 @@
     return SkColorSpace_Base::MakeRGB(kSRGB_SkGammaNamed, fToXYZD50);
 }
 
+sk_sp<SkColorSpace> SkColorSpace_XYZ::makeColorSpin() const {
+    SkMatrix44 spin(SkMatrix44::kUninitialized_Constructor);
+    spin.set3x3(0, 1, 0, 0, 0, 1, 1, 0, 0);
+    spin.postConcat(fToXYZD50);
+    return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(fGammaNamed, fGammas, spin, fProfileData));
+}
+
 void SkColorSpace_XYZ::toDstGammaTables(const uint8_t* tables[3], sk_sp<SkData>* storage,
                                          int numTables) const {
     fToDstGammaOnce([this, numTables] {
diff --git a/src/core/SkColorSpace_XYZ.h b/src/core/SkColorSpace_XYZ.h
index 1dd3ef3..3ea2665 100644
--- a/src/core/SkColorSpace_XYZ.h
+++ b/src/core/SkColorSpace_XYZ.h
@@ -29,6 +29,7 @@
 
     sk_sp<SkColorSpace> makeLinearGamma() const override;
     sk_sp<SkColorSpace> makeSRGBGamma() const override;
+    sk_sp<SkColorSpace> makeColorSpin() const override;
 
     SkGammaNamed gammaNamed() const { return fGammaNamed; }
 
diff --git a/tools/create_flutter_test_images.cpp b/tools/create_flutter_test_images.cpp
index 69a0d11..2fa39f6 100644
--- a/tools/create_flutter_test_images.cpp
+++ b/tools/create_flutter_test_images.cpp
@@ -15,19 +15,7 @@
  *  Create a color space that swaps the red, green, and blue channels.
  */
 static sk_sp<SkColorSpace> gbr_color_space() {
-    float gbr[9];
-    gbr[0] = gSRGB_toXYZD50[1];
-    gbr[1] = gSRGB_toXYZD50[2];
-    gbr[2] = gSRGB_toXYZD50[0];
-    gbr[3] = gSRGB_toXYZD50[4];
-    gbr[4] = gSRGB_toXYZD50[5];
-    gbr[5] = gSRGB_toXYZD50[3];
-    gbr[6] = gSRGB_toXYZD50[7];
-    gbr[7] = gSRGB_toXYZD50[8];
-    gbr[8] = gSRGB_toXYZD50[6];
-    SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
-    toXYZD50.set3x3RowMajorf(gbr);
-    return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, toXYZD50);
+    return as_CSB(SkColorSpace::MakeSRGB())->makeColorSpin();
 }
 
 /**