[Impeller] Provide an opaque interface for color sources in Aiks (#41315)

Part of https://github.com/flutter/flutter/issues/125017.

Pulls ColorSource construction out of the dispatcher and into Aiks.

Cleans up a few design warts we've accumulated over time:
- Removes special exceptions for solid colors in `Paint`.
- Makes `SolidColorContents` a `ColorSourceContents`.
- Removes paint ref capture for kImage.
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 128550b..d86db3f 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -1013,6 +1013,8 @@
 ORIGIN: ../../../flutter/impeller/aiks/aiks_playground.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/impeller/aiks/canvas.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/impeller/aiks/canvas.h + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/impeller/aiks/color_source.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/impeller/aiks/color_source.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/impeller/aiks/image.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/impeller/aiks/image.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/impeller/aiks/paint.cc + ../../../flutter/LICENSE
@@ -3606,6 +3608,8 @@
 FILE: ../../../flutter/impeller/aiks/aiks_playground.h
 FILE: ../../../flutter/impeller/aiks/canvas.cc
 FILE: ../../../flutter/impeller/aiks/canvas.h
+FILE: ../../../flutter/impeller/aiks/color_source.cc
+FILE: ../../../flutter/impeller/aiks/color_source.h
 FILE: ../../../flutter/impeller/aiks/image.cc
 FILE: ../../../flutter/impeller/aiks/image.h
 FILE: ../../../flutter/impeller/aiks/paint.cc
diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn
index b5b0d05..16df517 100644
--- a/impeller/aiks/BUILD.gn
+++ b/impeller/aiks/BUILD.gn
@@ -10,6 +10,8 @@
     "aiks_context.h",
     "canvas.cc",
     "canvas.h",
+    "color_source.cc",
+    "color_source.h",
     "image.cc",
     "image.h",
     "paint.cc",
diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc
index a2738b7..1531285 100644
--- a/impeller/aiks/aiks_unittests.cc
+++ b/impeller/aiks/aiks_unittests.cc
@@ -157,12 +157,8 @@
   canvas.Scale(aiks_test->GetContentScale());
   canvas.Translate({100.0f, 100.0f, 0});
   Paint paint;
-  paint.color_source = [texture, tile_mode]() {
-    auto contents = std::make_shared<TiledTextureContents>();
-    contents->SetTexture(texture);
-    contents->SetTileModes(tile_mode, tile_mode);
-    return contents;
-  };
+  paint.color_source =
+      ColorSource::MakeImage(texture, tile_mode, tile_mode, {}, {});
   paint.color = Color(1, 1, 1, 1);
   canvas.DrawRect({0, 0, 600, 600}, paint);
   ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
@@ -347,18 +343,14 @@
   canvas.Scale(aiks_test->GetContentScale());
   Paint paint;
   canvas.Translate({100.0f, 0, 0});
-  paint.color_source = [tile_mode]() {
-    std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
-                                 Color{0.1294, 0.5882, 0.9529, 0.0}};
-    std::vector<Scalar> stops = {0.0, 1.0};
 
-    auto contents = std::make_shared<LinearGradientContents>();
-    contents->SetEndPoints({0, 0}, {200, 200});
-    contents->SetColors(std::move(colors));
-    contents->SetStops(std::move(stops));
-    contents->SetTileMode(tile_mode);
-    return contents;
-  };
+  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
+                               Color{0.1294, 0.5882, 0.9529, 0.0}};
+  std::vector<Scalar> stops = {0.0, 1.0};
+
+  paint.color_source = ColorSource::MakeLinearGradient(
+      {0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {});
+
   paint.color = Color(1.0, 1.0, 1.0, 1.0);
   canvas.DrawRect({0, 0, 600, 600}, paint);
   ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
@@ -384,19 +376,15 @@
   Canvas canvas;
   Paint paint;
   canvas.Translate({100.0, 100.0, 0});
-  paint.color_source = [tile_mode]() {
-    std::vector<Color> colors = {
-        Color{0.9568, 0.2627, 0.2118, 1.0}, Color{0.9568, 0.2627, 0.2118, 1.0},
-        Color{0.1294, 0.5882, 0.9529, 1.0}, Color{0.1294, 0.5882, 0.9529, 1.0}};
-    std::vector<Scalar> stops = {0.0, 0.5, 0.5, 1.0};
 
-    auto contents = std::make_shared<LinearGradientContents>();
-    contents->SetEndPoints({0, 0}, {500, 500});
-    contents->SetColors(std::move(colors));
-    contents->SetStops(std::move(stops));
-    contents->SetTileMode(tile_mode);
-    return contents;
-  };
+  std::vector<Color> colors = {
+      Color{0.9568, 0.2627, 0.2118, 1.0}, Color{0.9568, 0.2627, 0.2118, 1.0},
+      Color{0.1294, 0.5882, 0.9529, 1.0}, Color{0.1294, 0.5882, 0.9529, 1.0}};
+  std::vector<Scalar> stops = {0.0, 0.5, 0.5, 1.0};
+
+  paint.color_source = ColorSource::MakeLinearGradient(
+      {0, 0}, {500, 500}, std::move(colors), std::move(stops), tile_mode, {});
+
   paint.color = Color(1.0, 1.0, 1.0, 1.0);
   canvas.DrawRect({0, 0, 500, 500}, paint);
   ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
@@ -415,32 +403,28 @@
   canvas.Scale(aiks_test->GetContentScale());
   Paint paint;
   canvas.Translate({100, 100, 0});
-  paint.color_source = [tile_mode]() {
-    std::vector<Color> colors = {
-        Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
-        Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
-        Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
-        Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
-        Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
-        Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
-        Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
-    std::vector<Scalar> stops = {
-        0.0,
-        (1.0 / 6.0) * 1,
-        (1.0 / 6.0) * 2,
-        (1.0 / 6.0) * 3,
-        (1.0 / 6.0) * 4,
-        (1.0 / 6.0) * 5,
-        1.0,
-    };
 
-    auto contents = std::make_shared<LinearGradientContents>();
-    contents->SetEndPoints({0, 0}, {200, 200});
-    contents->SetColors(std::move(colors));
-    contents->SetStops(std::move(stops));
-    contents->SetTileMode(tile_mode);
-    return contents;
+  std::vector<Color> colors = {
+      Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
+      Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
+      Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
+      Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
+      Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
+      Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
+      Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
+  std::vector<Scalar> stops = {
+      0.0,
+      (1.0 / 6.0) * 1,
+      (1.0 / 6.0) * 2,
+      (1.0 / 6.0) * 3,
+      (1.0 / 6.0) * 4,
+      (1.0 / 6.0) * 5,
+      1.0,
   };
+
+  paint.color_source = ColorSource::MakeLinearGradient(
+      {0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {});
+
   paint.color = Color(1.0, 1.0, 1.0, 1.0);
   canvas.DrawRect({0, 0, 600, 600}, paint);
   canvas.Restore();
@@ -477,15 +461,10 @@
     current_stop += 1 / 2000.0;
   }
   stops[2000 - 1] = 1.0;
-  paint.color_source = [tile_mode, stops = std::move(stops),
-                        colors = std::move(colors)]() {
-    auto contents = std::make_shared<LinearGradientContents>();
-    contents->SetEndPoints({0, 0}, {200, 200});
-    contents->SetColors(colors);
-    contents->SetStops(stops);
-    contents->SetTileMode(tile_mode);
-    return contents;
-  };
+
+  paint.color_source = ColorSource::MakeLinearGradient(
+      {0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {});
+
   canvas.DrawRect({0, 0, 600, 600}, paint);
   ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
 }
@@ -525,28 +504,22 @@
     Paint paint;
     canvas.Translate({100.0, 100.0, 0});
     auto tile_mode = tile_modes[selected_tile_mode];
-    paint.color_source = [tile_mode]() {
-      std::vector<Color> colors = {
-          Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
-          Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
-          Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
-          Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
-          Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
-          Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
-          Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
-      std::vector<Scalar> stops = {
-          0.0,         2.0 / 62.0,  4.0 / 62.0, 8.0 / 62.0,
-          16.0 / 62.0, 32.0 / 62.0, 1.0,
-      };
 
-      auto contents = std::make_shared<LinearGradientContents>();
-      contents->SetEndPoints({0, 0}, {200, 200});
-      contents->SetColors(std::move(colors));
-      contents->SetStops(std::move(stops));
-      contents->SetTileMode(tile_mode);
-      contents->SetEffectTransform(matrix);
-      return contents;
+    std::vector<Color> colors = {
+        Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
+        Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
+        Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
+        Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
+        Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
+        Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
+        Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
+    std::vector<Scalar> stops = {
+        0.0, 2.0 / 62.0, 4.0 / 62.0, 8.0 / 62.0, 16.0 / 62.0, 32.0 / 62.0, 1.0,
     };
+
+    paint.color_source = ColorSource::MakeLinearGradient(
+        {0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {});
+
     canvas.DrawRect({0, 0, 600, 600}, paint);
     return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
   };
@@ -582,19 +555,14 @@
     Paint paint;
     canvas.Translate({100.0, 100.0, 0});
     auto tile_mode = tile_modes[selected_tile_mode];
-    paint.color_source = [tile_mode]() {
-      std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
-                                   Color{0.1294, 0.5882, 0.9529, 1.0}};
-      std::vector<Scalar> stops = {0.0, 1.0};
 
-      auto contents = std::make_shared<RadialGradientContents>();
-      contents->SetCenterAndRadius({100, 100}, 100);
-      contents->SetColors(std::move(colors));
-      contents->SetStops(std::move(stops));
-      contents->SetTileMode(tile_mode);
-      contents->SetEffectTransform(matrix);
-      return contents;
-    };
+    std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
+                                 Color{0.1294, 0.5882, 0.9529, 1.0}};
+    std::vector<Scalar> stops = {0.0, 1.0};
+
+    paint.color_source = ColorSource::MakeRadialGradient(
+        {100, 100}, 100, std::move(colors), std::move(stops), tile_mode, {});
+
     canvas.DrawRect({0, 0, 600, 600}, paint);
     return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
   };
@@ -630,33 +598,28 @@
     Paint paint;
     canvas.Translate({100.0, 100.0, 0});
     auto tile_mode = tile_modes[selected_tile_mode];
-    paint.color_source = [tile_mode]() {
-      std::vector<Color> colors = {
-          Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
-          Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
-          Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
-          Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
-          Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
-          Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
-          Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
-      std::vector<Scalar> stops = {
-          0.0,
-          (1.0 / 6.0) * 1,
-          (1.0 / 6.0) * 2,
-          (1.0 / 6.0) * 3,
-          (1.0 / 6.0) * 4,
-          (1.0 / 6.0) * 5,
-          1.0,
-      };
 
-      auto contents = std::make_shared<RadialGradientContents>();
-      contents->SetCenterAndRadius({100, 100}, 100);
-      contents->SetColors(std::move(colors));
-      contents->SetStops(std::move(stops));
-      contents->SetTileMode(tile_mode);
-      contents->SetEffectTransform(matrix);
-      return contents;
+    std::vector<Color> colors = {
+        Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
+        Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
+        Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
+        Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
+        Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
+        Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
+        Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
+    std::vector<Scalar> stops = {
+        0.0,
+        (1.0 / 6.0) * 1,
+        (1.0 / 6.0) * 2,
+        (1.0 / 6.0) * 3,
+        (1.0 / 6.0) * 4,
+        (1.0 / 6.0) * 5,
+        1.0,
     };
+
+    paint.color_source = ColorSource::MakeRadialGradient(
+        {100, 100}, 100, std::move(colors), std::move(stops), tile_mode, {});
+
     canvas.DrawRect({0, 0, 600, 600}, paint);
     return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
   };
@@ -669,17 +632,15 @@
   canvas.Scale(aiks_test->GetContentScale());
   Paint paint;
   canvas.Translate({100, 100, 0});
-  paint.color_source = [tile_mode]() {
-    auto contents = std::make_shared<SweepGradientContents>();
-    contents->SetCenterAndAngles({100, 100}, Degrees(45), Degrees(135));
-    std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
-                                 Color{0.1294, 0.5882, 0.9529, 1.0}};
-    std::vector<Scalar> stops = {0.0, 1.0};
-    contents->SetColors(std::move(colors));
-    contents->SetStops(std::move(stops));
-    contents->SetTileMode(tile_mode);
-    return contents;
-  };
+
+  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
+                               Color{0.1294, 0.5882, 0.9529, 1.0}};
+  std::vector<Scalar> stops = {0.0, 1.0};
+
+  paint.color_source = ColorSource::MakeSweepGradient(
+      {100, 100}, Degrees(45), Degrees(135), std::move(colors),
+      std::move(stops), tile_mode, {});
+
   canvas.DrawRect({0, 0, 600, 600}, paint);
   ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
 }
@@ -704,32 +665,29 @@
   Canvas canvas;
   Paint paint;
   canvas.Translate({100.0, 100.0, 0});
-  paint.color_source = [tile_mode]() {
-    auto contents = std::make_shared<SweepGradientContents>();
-    contents->SetCenterAndAngles({100, 100}, Degrees(45), Degrees(135));
-    std::vector<Color> colors = {
-        Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
-        Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
-        Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
-        Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
-        Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
-        Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
-        Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
-    std::vector<Scalar> stops = {
-        0.0,
-        (1.0 / 6.0) * 1,
-        (1.0 / 6.0) * 2,
-        (1.0 / 6.0) * 3,
-        (1.0 / 6.0) * 4,
-        (1.0 / 6.0) * 5,
-        1.0,
-    };
 
-    contents->SetStops(std::move(stops));
-    contents->SetColors(std::move(colors));
-    contents->SetTileMode(tile_mode);
-    return contents;
+  std::vector<Color> colors = {
+      Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
+      Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
+      Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
+      Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
+      Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
+      Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
+      Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
+  std::vector<Scalar> stops = {
+      0.0,
+      (1.0 / 6.0) * 1,
+      (1.0 / 6.0) * 2,
+      (1.0 / 6.0) * 3,
+      (1.0 / 6.0) * 4,
+      (1.0 / 6.0) * 5,
+      1.0,
   };
+
+  paint.color_source = ColorSource::MakeSweepGradient(
+      {100, 100}, Degrees(45), Degrees(135), std::move(colors),
+      std::move(stops), tile_mode, {});
+
   canvas.DrawRect({0, 0, 600, 600}, paint);
   ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
 }
@@ -751,20 +709,18 @@
 TEST_P(AiksTest, CanRenderDifferentShapesWithSameColorSource) {
   Canvas canvas;
   Paint paint;
-  paint.color_source = []() {
-    auto contents = std::make_shared<LinearGradientContents>();
-    contents->SetEndPoints({0, 0}, {100, 100});
-    std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
-                                 Color{0.1294, 0.5882, 0.9529, 1.0}};
-    std::vector<Scalar> stops = {
-        0.0,
-        1.0,
-    };
-    contents->SetColors(std::move(colors));
-    contents->SetStops(std::move(stops));
-    contents->SetTileMode(Entity::TileMode::kRepeat);
-    return contents;
+
+  std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
+                               Color{0.1294, 0.5882, 0.9529, 1.0}};
+  std::vector<Scalar> stops = {
+      0.0,
+      1.0,
   };
+
+  paint.color_source = ColorSource::MakeLinearGradient(
+      {0, 0}, {100, 100}, std::move(colors), std::move(stops),
+      Entity::TileMode::kRepeat, {});
+
   canvas.Save();
   canvas.Translate({100, 100, 0});
   canvas.DrawRect({0, 0, 200, 200}, paint);
@@ -1545,24 +1501,13 @@
     paint.color = Color(1.0, 1.0, 1.0, alpha);
     paint.stroke_width = 10;
     auto tile_mode = tile_modes[selected_tile_mode];
-    paint.color_source = [tile_mode]() {
-      std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
-                                   Color{0.1294, 0.5882, 0.9529, 1.0}};
-      std::vector<Scalar> stops = {0.0, 1.0};
-      Matrix matrix = {
-          1, 0, 0, 0,  //
-          0, 1, 0, 0,  //
-          0, 0, 1, 0,  //
-          0, 0, 0, 1   //
-      };
-      auto contents = std::make_shared<LinearGradientContents>();
-      contents->SetEndPoints({0, 0}, {50, 50});
-      contents->SetColors(std::move(colors));
-      contents->SetStops(std::move(stops));
-      contents->SetTileMode(tile_mode);
-      contents->SetEffectTransform(matrix);
-      return contents;
-    };
+
+    std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
+                                 Color{0.1294, 0.5882, 0.9529, 1.0}};
+    std::vector<Scalar> stops = {0.0, 1.0};
+
+    paint.color_source = ColorSource::MakeLinearGradient(
+        {0, 0}, {50, 50}, std::move(colors), std::move(stops), tile_mode, {});
 
     Path path = PathBuilder{}
                     .MoveTo({20, 20})
@@ -1778,18 +1723,14 @@
     ImGui::SliderFloat("FOV", &fov, 1, 180);
     ImGui::End();
 
-    paint.color_source_type = Paint::ColorSourceType::kScene;
-    paint.color_source = [&]() {
-      Scalar angle = GetSecondsElapsed();
-      auto camera_position = Vector3(distance * std::sin(angle), y_pos,
-                                     -distance * std::cos(angle));
-      auto contents = std::make_shared<SceneContents>();
-      contents->SetNode(gltf_scene);
-      contents->SetCameraTransform(
-          Matrix::MakePerspective(Degrees(fov), GetWindowSize(), 0.1, 1000) *
-          Matrix::MakeLookAt(camera_position, {0, 0, 0}, {0, 1, 0}));
-      return contents;
-    };
+    Scalar angle = GetSecondsElapsed();
+    auto camera_position =
+        Vector3(distance * std::sin(angle), y_pos, -distance * std::cos(angle));
+
+    paint.color_source = ColorSource::MakeScene(
+        gltf_scene,
+        Matrix::MakePerspective(Degrees(fov), GetWindowSize(), 0.1, 1000) *
+            Matrix::MakeLookAt(camera_position, {0, 0, 0}, {0, 1, 0}));
 
     Canvas canvas;
     canvas.DrawPaint(Paint{.color = Color::MakeRGBA8(0xf9, 0xf9, 0xf9, 0xff)});
diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc
index 5ae2303..6c69fe0 100644
--- a/impeller/aiks/canvas.cc
+++ b/impeller/aiks/canvas.cc
@@ -22,23 +22,6 @@
 
 namespace impeller {
 
-static bool UseColorSourceContents(
-    const std::shared_ptr<VerticesGeometry>& vertices,
-    const Paint::ColorSourceType& type) {
-  // If there are no vertex color or texture coordinates. Or if there
-  // are vertex coordinates then only if the contents are an image or
-  // a solid color.
-  if (vertices->HasVertexColors()) {
-    return false;
-  }
-  if (vertices->HasTextureCoordinates() &&
-      (type == Paint::ColorSourceType::kImage ||
-       type == Paint::ColorSourceType::kColor)) {
-    return true;
-  }
-  return !vertices->HasTextureCoordinates();
-}
-
 Canvas::Canvas() {
   Initialize();
 }
@@ -191,8 +174,7 @@
 bool Canvas::AttemptDrawBlurredRRect(const Rect& rect,
                                      Scalar corner_radius,
                                      const Paint& paint) {
-  if (paint.color_source == nullptr ||
-      paint.color_source_type != Paint::ColorSourceType::kColor ||
+  if (paint.color_source.GetType() != ColorSource::Type::kColor ||
       paint.style != Paint::Style::kFill) {
     return false;
   }
@@ -436,8 +418,7 @@
   text_contents->SetTextFrame(text_frame);
   text_contents->SetGlyphAtlas(lazy_glyph_atlas_);
 
-  if (paint.color_source.has_value()) {
-    auto& source = paint.color_source.value();
+  if (paint.color_source.GetType() != ColorSource::Type::kColor) {
     auto color_text_contents = std::make_shared<ColorSourceTextContents>();
     entity.SetTransformation(GetCurrentTransformation());
 
@@ -447,7 +428,8 @@
 
     text_contents->SetOffset(-cvg.origin);
     color_text_contents->SetTextContents(std::move(text_contents));
-    color_text_contents->SetColorSourceContents(source());
+    color_text_contents->SetColorSourceContents(
+        paint.color_source.GetContents(paint));
 
     entity.SetContents(
         paint.WithFilters(std::move(color_text_contents), false));
@@ -466,13 +448,30 @@
   GetCurrentPass().AddEntity(entity);
 }
 
+static bool UseColorSourceContents(
+    const std::shared_ptr<VerticesGeometry>& vertices,
+    const Paint& paint) {
+  // If there are no vertex color or texture coordinates. Or if there
+  // are vertex coordinates then only if the contents are an image or
+  // a solid color.
+  if (vertices->HasVertexColors()) {
+    return false;
+  }
+  if (vertices->HasTextureCoordinates() &&
+      (paint.color_source.GetType() == ColorSource::Type::kImage ||
+       paint.color_source.GetType() == ColorSource::Type::kColor)) {
+    return true;
+  }
+  return !vertices->HasTextureCoordinates();
+}
+
 void Canvas::DrawVertices(const std::shared_ptr<VerticesGeometry>& vertices,
                           BlendMode blend_mode,
                           const Paint& paint) {
   // Override the blend mode with kDestination in order to match the behavior
   // of Skia's SK_LEGACY_IGNORE_DRAW_VERTICES_BLEND_WITH_NO_SHADER flag, which
   // is enabled when the Flutter engine builds Skia.
-  if (paint.color_source_type == Paint::ColorSourceType::kColor) {
+  if (paint.color_source.GetType() == ColorSource::Type::kColor) {
     blend_mode = BlendMode::kDestination;
   }
 
@@ -483,7 +482,7 @@
 
   // If there are no vertex color or texture coordinates. Or if there
   // are vertex coordinates then only if the contents are an image.
-  if (UseColorSourceContents(vertices, paint.color_source_type)) {
+  if (UseColorSourceContents(vertices, paint)) {
     auto contents = paint.CreateContentsForGeometry(vertices);
     entity.SetContents(paint.WithFilters(std::move(contents)));
     GetCurrentPass().AddEntity(entity);
@@ -496,7 +495,7 @@
   std::shared_ptr<Contents> src_contents =
       src_paint.CreateContentsForGeometry(vertices);
   if (vertices->HasTextureCoordinates() &&
-      paint.color_source_type != Paint::ColorSourceType::kImage) {
+      paint.color_source.GetType() != ColorSource::Type::kImage) {
     // If the color source has an intrinsic size, then we use that to
     // create the src contents as a simplification. Otherwise we use
     // the extent of the texture coordinates to determine how large
diff --git a/impeller/aiks/color_source.cc b/impeller/aiks/color_source.cc
new file mode 100644
index 0000000..f5644b2
--- /dev/null
+++ b/impeller/aiks/color_source.cc
@@ -0,0 +1,225 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "impeller/aiks/color_source.h"
+
+#include <memory>
+#include <vector>
+
+#include "impeller/aiks/paint.h"
+#include "impeller/core/sampler_descriptor.h"
+#include "impeller/entity/contents/conical_gradient_contents.h"
+#include "impeller/entity/contents/linear_gradient_contents.h"
+#include "impeller/entity/contents/radial_gradient_contents.h"
+#include "impeller/entity/contents/runtime_effect_contents.h"
+#include "impeller/entity/contents/scene_contents.h"
+#include "impeller/entity/contents/solid_color_contents.h"
+#include "impeller/entity/contents/sweep_gradient_contents.h"
+#include "impeller/entity/contents/tiled_texture_contents.h"
+#include "impeller/geometry/matrix.h"
+#include "impeller/geometry/scalar.h"
+#include "impeller/runtime_stage/runtime_stage.h"
+#include "impeller/scene/node.h"
+
+namespace impeller {
+
+ColorSource::ColorSource() noexcept
+    : proc_([](const Paint& paint) -> std::shared_ptr<ColorSourceContents> {
+        auto contents = std::make_shared<SolidColorContents>();
+        contents->SetColor(paint.color);
+        return contents;
+      }){};
+
+ColorSource::~ColorSource() = default;
+
+ColorSource ColorSource::MakeColor() {
+  return {};
+}
+
+ColorSource ColorSource::MakeLinearGradient(Point start_point,
+                                            Point end_point,
+                                            std::vector<Color> colors,
+                                            std::vector<Scalar> stops,
+                                            Entity::TileMode tile_mode,
+                                            Matrix effect_transform) {
+  ColorSource result;
+  result.type_ = Type::kLinearGradient;
+  result.proc_ = [start_point, end_point, colors = std::move(colors),
+                  stops = std::move(stops), tile_mode,
+                  effect_transform](const Paint& paint) {
+    auto contents = std::make_shared<LinearGradientContents>();
+    contents->SetOpacity(paint.color.alpha);
+    contents->SetColors(colors);
+    contents->SetStops(stops);
+    contents->SetEndPoints(start_point, end_point);
+    contents->SetTileMode(tile_mode);
+    contents->SetEffectTransform(effect_transform);
+
+    std::vector<Point> bounds{start_point, end_point};
+    auto intrinsic_size = Rect::MakePointBounds(bounds.begin(), bounds.end());
+    if (intrinsic_size.has_value()) {
+      contents->SetColorSourceSize(intrinsic_size->size);
+    }
+    return contents;
+  };
+  return result;
+}
+
+ColorSource ColorSource::MakeConicalGradient(Point center,
+                                             Scalar radius,
+                                             std::vector<Color> colors,
+                                             std::vector<Scalar> stops,
+                                             Point focus_center,
+                                             Scalar focus_radius,
+                                             Entity::TileMode tile_mode,
+                                             Matrix effect_transform) {
+  ColorSource result;
+  result.type_ = Type::kConicalGradient;
+  result.proc_ = [center, radius, colors = std::move(colors),
+                  stops = std::move(stops), focus_center, focus_radius,
+                  tile_mode, effect_transform](const Paint& paint) {
+    std::shared_ptr<ConicalGradientContents> contents =
+        std::make_shared<ConicalGradientContents>();
+    contents->SetOpacity(paint.color.alpha);
+    contents->SetColors(colors);
+    contents->SetStops(stops);
+    contents->SetCenterAndRadius(center, radius);
+    contents->SetTileMode(tile_mode);
+    contents->SetEffectTransform(effect_transform);
+    contents->SetFocus(focus_center, focus_radius);
+
+    auto radius_pt = Point(radius, radius);
+    std::vector<Point> bounds{center + radius_pt, center - radius_pt};
+    auto intrinsic_size = Rect::MakePointBounds(bounds.begin(), bounds.end());
+    if (intrinsic_size.has_value()) {
+      contents->SetColorSourceSize(intrinsic_size->size);
+    }
+    return contents;
+  };
+  return result;
+}
+
+ColorSource ColorSource::MakeRadialGradient(Point center,
+                                            Scalar radius,
+                                            std::vector<Color> colors,
+                                            std::vector<Scalar> stops,
+                                            Entity::TileMode tile_mode,
+                                            Matrix effect_transform) {
+  ColorSource result;
+  result.type_ = Type::kRadialGradient;
+  result.proc_ = [center, radius, colors = std::move(colors),
+                  stops = std::move(stops), tile_mode,
+                  effect_transform](const Paint& paint) {
+    auto contents = std::make_shared<RadialGradientContents>();
+    contents->SetOpacity(paint.color.alpha);
+    contents->SetColors(colors);
+    contents->SetStops(stops);
+    contents->SetCenterAndRadius(center, radius);
+    contents->SetTileMode(tile_mode);
+    contents->SetEffectTransform(effect_transform);
+
+    auto radius_pt = Point(radius, radius);
+    std::vector<Point> bounds{center + radius_pt, center - radius_pt};
+    auto intrinsic_size = Rect::MakePointBounds(bounds.begin(), bounds.end());
+    if (intrinsic_size.has_value()) {
+      contents->SetColorSourceSize(intrinsic_size->size);
+    }
+    return contents;
+  };
+  return result;
+}
+
+ColorSource ColorSource::MakeSweepGradient(Point center,
+                                           Degrees start_angle,
+                                           Degrees end_angle,
+                                           std::vector<Color> colors,
+                                           std::vector<Scalar> stops,
+                                           Entity::TileMode tile_mode,
+                                           Matrix effect_transform) {
+  ColorSource result;
+  result.type_ = Type::kSweepGradient;
+  result.proc_ = [center, start_angle, end_angle, colors = std::move(colors),
+                  stops = std::move(stops), tile_mode,
+                  effect_transform](const Paint& paint) {
+    auto contents = std::make_shared<SweepGradientContents>();
+    contents->SetOpacity(paint.color.alpha);
+    contents->SetCenterAndAngles(center, start_angle, end_angle);
+    contents->SetColors(colors);
+    contents->SetStops(stops);
+    contents->SetTileMode(tile_mode);
+    contents->SetEffectTransform(effect_transform);
+
+    return contents;
+  };
+  return result;
+}
+
+ColorSource ColorSource::MakeImage(std::shared_ptr<Texture> texture,
+                                   Entity::TileMode x_tile_mode,
+                                   Entity::TileMode y_tile_mode,
+                                   SamplerDescriptor sampler_descriptor,
+                                   Matrix effect_transform) {
+  ColorSource result;
+  result.type_ = Type::kImage;
+  result.proc_ = [texture = std::move(texture), x_tile_mode, y_tile_mode,
+                  sampler_descriptor = std::move(sampler_descriptor),
+                  effect_transform](const Paint& paint) {
+    auto contents = std::make_shared<TiledTextureContents>();
+    contents->SetOpacity(paint.color.alpha);
+    contents->SetTexture(texture);
+    contents->SetTileModes(x_tile_mode, y_tile_mode);
+    contents->SetSamplerDescriptor(sampler_descriptor);
+    contents->SetEffectTransform(effect_transform);
+    contents->SetColorFilter(paint.color_filter);
+    contents->SetColorSourceSize(Size::Ceil(texture->GetSize()));
+    return contents;
+  };
+  return result;
+}
+
+ColorSource ColorSource::MakeRuntimeEffect(
+    std::shared_ptr<RuntimeStage> runtime_stage,
+    std::shared_ptr<std::vector<uint8_t>> uniform_data,
+    std::vector<RuntimeEffectContents::TextureInput> texture_inputs) {
+  ColorSource result;
+  result.type_ = Type::kRuntimeEffect;
+  result.proc_ = [runtime_stage = std::move(runtime_stage),
+                  uniform_data = std::move(uniform_data),
+                  texture_inputs =
+                      std::move(texture_inputs)](const Paint& paint) {
+    auto contents = std::make_shared<RuntimeEffectContents>();
+    contents->SetOpacity(paint.color.alpha);
+    contents->SetRuntimeStage(runtime_stage);
+    contents->SetUniformData(uniform_data);
+    contents->SetTextureInputs(texture_inputs);
+    return contents;
+  };
+  return result;
+}
+
+ColorSource ColorSource::MakeScene(std::shared_ptr<scene::Node> scene_node,
+                                   Matrix camera_transform) {
+  ColorSource result;
+  result.type_ = Type::kScene;
+  result.proc_ = [scene_node = std::move(scene_node),
+                  camera_transform](const Paint& paint) {
+    auto contents = std::make_shared<SceneContents>();
+    contents->SetOpacity(paint.color.alpha);
+    contents->SetNode(scene_node);
+    contents->SetCameraTransform(camera_transform);
+    return contents;
+  };
+  return result;
+}
+
+ColorSource::Type ColorSource::GetType() const {
+  return type_;
+}
+
+std::shared_ptr<ColorSourceContents> ColorSource::GetContents(
+    const Paint& paint) const {
+  return proc_(paint);
+}
+
+}  // namespace impeller
diff --git a/impeller/aiks/color_source.h b/impeller/aiks/color_source.h
new file mode 100644
index 0000000..84c6a6e
--- /dev/null
+++ b/impeller/aiks/color_source.h
@@ -0,0 +1,100 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include "flutter/fml/macros.h"
+#include "impeller/entity/contents/runtime_effect_contents.h"
+#include "impeller/entity/entity.h"
+#include "impeller/geometry/color.h"
+#include "impeller/geometry/matrix.h"
+#include "impeller/geometry/point.h"
+#include "impeller/runtime_stage/runtime_stage.h"
+#include "impeller/scene/node.h"
+
+namespace impeller {
+
+struct Paint;
+
+class ColorSource {
+ public:
+  enum class Type {
+    kColor,
+    kImage,
+    kLinearGradient,
+    kRadialGradient,
+    kConicalGradient,
+    kSweepGradient,
+    kRuntimeEffect,
+    kScene,
+  };
+
+  using ColorSourceProc =
+      std::function<std::shared_ptr<ColorSourceContents>(const Paint& paint)>;
+
+  ColorSource() noexcept;
+
+  ~ColorSource();
+
+  static ColorSource MakeColor();
+
+  static ColorSource MakeLinearGradient(Point start_point,
+                                        Point end_point,
+                                        std::vector<Color> colors,
+                                        std::vector<Scalar> stops,
+                                        Entity::TileMode tile_mode,
+                                        Matrix effect_transform);
+
+  static ColorSource MakeConicalGradient(Point center,
+                                         Scalar radius,
+                                         std::vector<Color> colors,
+                                         std::vector<Scalar> stops,
+                                         Point focus_center,
+                                         Scalar focus_radius,
+                                         Entity::TileMode tile_mode,
+                                         Matrix effect_transform);
+
+  static ColorSource MakeRadialGradient(Point center,
+                                        Scalar radius,
+                                        std::vector<Color> colors,
+                                        std::vector<Scalar> stops,
+                                        Entity::TileMode tile_mode,
+                                        Matrix effect_transform);
+
+  static ColorSource MakeSweepGradient(Point center,
+                                       Degrees start_angle,
+                                       Degrees end_angle,
+                                       std::vector<Color> colors,
+                                       std::vector<Scalar> stops,
+                                       Entity::TileMode tile_mode,
+                                       Matrix effect_transform);
+
+  static ColorSource MakeImage(std::shared_ptr<Texture> texture,
+                               Entity::TileMode x_tile_mode,
+                               Entity::TileMode y_tile_mode,
+                               SamplerDescriptor sampler_descriptor,
+                               Matrix effect_transform);
+
+  static ColorSource MakeRuntimeEffect(
+      std::shared_ptr<RuntimeStage> runtime_stage,
+      std::shared_ptr<std::vector<uint8_t>> uniform_data,
+      std::vector<RuntimeEffectContents::TextureInput> texture_inputs);
+
+  static ColorSource MakeScene(std::shared_ptr<scene::Node> scene_node,
+                               Matrix camera_transform);
+
+  Type GetType() const;
+
+  std::shared_ptr<ColorSourceContents> GetContents(const Paint& paint) const;
+
+ private:
+  Type type_ = Type::kColor;
+  ColorSourceProc proc_;
+};
+
+}  // namespace impeller
diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc
index 330b7b1..47af197 100644
--- a/impeller/aiks/paint.cc
+++ b/impeller/aiks/paint.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "impeller/aiks/paint.h"
+#include "impeller/entity/contents/color_source_contents.h"
 #include "impeller/entity/contents/solid_color_contents.h"
 #include "impeller/entity/geometry.h"
 
@@ -27,38 +28,23 @@
 
 std::shared_ptr<Contents> Paint::CreateContentsForGeometry(
     std::unique_ptr<Geometry> geometry) const {
-  if (color_source.has_value()) {
-    auto& source = color_source.value();
-    auto contents = source();
-    contents->SetGeometry(std::move(geometry));
-    contents->SetOpacity(color.alpha);
-    return contents;
-  }
-  auto solid_color = std::make_shared<SolidColorContents>();
-  solid_color->SetGeometry(std::move(geometry));
-  solid_color->SetColor(color);
-  return solid_color;
+  auto contents = color_source.GetContents(*this);
+  contents->SetGeometry(std::move(geometry));
+  return contents;
 }
 
 std::shared_ptr<Contents> Paint::CreateContentsForGeometry(
     const std::shared_ptr<Geometry>& geometry) const {
-  if (color_source.has_value()) {
-    auto& source = color_source.value();
-    auto contents = source();
-    contents->SetGeometry(geometry);
-    contents->SetOpacity(color.alpha);
-    return contents;
-  }
-  auto solid_color = std::make_shared<SolidColorContents>();
-  solid_color->SetGeometry(geometry);
-  solid_color->SetColor(color);
-  return solid_color;
+  auto contents = color_source.GetContents(*this);
+  contents->SetGeometry(geometry);
+  return contents;
 }
 
 std::shared_ptr<Contents> Paint::WithFilters(
     std::shared_ptr<Contents> input,
     std::optional<bool> is_solid_color) const {
-  bool is_solid_color_val = is_solid_color.value_or(!color_source);
+  bool is_solid_color_val = is_solid_color.value_or(color_source.GetType() ==
+                                                    ColorSource::Type::kColor);
   input = WithColorFilter(input, /*absorb_opacity=*/true);
   input = WithInvertFilter(input);
   input = WithMaskBlur(input, is_solid_color_val, Matrix());
@@ -101,7 +87,7 @@
     bool absorb_opacity) const {
   // Image input types will directly set their color filter,
   // if any. See `TiledTextureContents.SetColorFilter`.
-  if (color_source_type == ColorSourceType::kImage) {
+  if (color_source.GetType() == ColorSource::Type::kImage) {
     return input;
   }
   if (color_filter.has_value()) {
diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h
index 5279a7d..709e66e 100644
--- a/impeller/aiks/paint.h
+++ b/impeller/aiks/paint.h
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "flutter/fml/macros.h"
+#include "impeller/aiks/color_source.h"
 #include "impeller/entity/contents/contents.h"
 #include "impeller/entity/contents/filters/color_filter_contents.h"
 #include "impeller/entity/contents/filters/filter_contents.h"
@@ -37,17 +38,6 @@
     kStroke,
   };
 
-  enum class ColorSourceType {
-    kColor,
-    kImage,
-    kLinearGradient,
-    kRadialGradient,
-    kConicalGradient,
-    kSweepGradient,
-    kRuntimeEffect,
-    kScene,
-  };
-
   struct MaskBlurDescriptor {
     FilterContents::BlurStyle style;
     Sigma sigma;
@@ -59,8 +49,7 @@
   };
 
   Color color = Color::Black();
-  std::optional<ColorSourceProc> color_source;
-  ColorSourceType color_source_type = ColorSourceType::kColor;
+  ColorSource color_source;
 
   Scalar stroke_width = 0.0;
   Cap stroke_cap = Cap::kButt;
diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc
index 9603f25..61fe206 100644
--- a/impeller/display_list/display_list_dispatcher.cc
+++ b/impeller/display_list/display_list_dispatcher.cc
@@ -286,26 +286,26 @@
   }
 }
 
-static std::optional<Paint::ColorSourceType> ToColorSourceType(
+static std::optional<ColorSource::Type> ToColorSourceType(
     flutter::DlColorSourceType type) {
   switch (type) {
     case flutter::DlColorSourceType::kColor:
-      return Paint::ColorSourceType::kColor;
+      return ColorSource::Type::kColor;
     case flutter::DlColorSourceType::kImage:
-      return Paint::ColorSourceType::kImage;
+      return ColorSource::Type::kImage;
     case flutter::DlColorSourceType::kLinearGradient:
-      return Paint::ColorSourceType::kLinearGradient;
+      return ColorSource::Type::kLinearGradient;
     case flutter::DlColorSourceType::kRadialGradient:
-      return Paint::ColorSourceType::kRadialGradient;
+      return ColorSource::Type::kRadialGradient;
     case flutter::DlColorSourceType::kConicalGradient:
-      return Paint::ColorSourceType::kConicalGradient;
+      return ColorSource::Type::kConicalGradient;
     case flutter::DlColorSourceType::kSweepGradient:
-      return Paint::ColorSourceType::kSweepGradient;
+      return ColorSource::Type::kSweepGradient;
     case flutter::DlColorSourceType::kRuntimeEffect:
-      return Paint::ColorSourceType::kRuntimeEffect;
+      return ColorSource::Type::kRuntimeEffect;
 #ifdef IMPELLER_ENABLE_3D
     case flutter::DlColorSourceType::kScene:
-      return Paint::ColorSourceType::kScene;
+      return ColorSource::Type::kScene;
 #endif  // IMPELLER_ENABLE_3D
   }
 }
@@ -314,32 +314,28 @@
 void DisplayListDispatcher::setColorSource(
     const flutter::DlColorSource* source) {
   if (!source) {
-    paint_.color_source = std::nullopt;
-    paint_.color_source_type = Paint::ColorSourceType::kColor;
+    paint_.color_source = ColorSource::MakeColor();
     return;
   }
 
-  std::optional<Paint::ColorSourceType> type =
-      ToColorSourceType(source->type());
+  std::optional<ColorSource::Type> type = ToColorSourceType(source->type());
 
   if (!type.has_value()) {
     FML_LOG(ERROR) << "Requested ColorSourceType::kUnknown";
-    paint_.color_source = std::nullopt;
-    paint_.color_source_type = Paint::ColorSourceType::kColor;
+    paint_.color_source = ColorSource::MakeColor();
     return;
   }
 
-  paint_.color_source_type = type.value();
-
   switch (type.value()) {
-    case Paint::ColorSourceType::kColor: {
+    case ColorSource::Type::kColor: {
       const flutter::DlColorColorSource* color = source->asColor();
-      paint_.color_source = std::nullopt;
+
+      paint_.color_source = ColorSource::MakeColor();
       setColor(color->color());
       FML_DCHECK(color);
       return;
     }
-    case Paint::ColorSourceType::kLinearGradient: {
+    case ColorSource::Type::kLinearGradient: {
       const flutter::DlLinearGradientColorSource* linear =
           source->asLinearGradient();
       FML_DCHECK(linear);
@@ -351,26 +347,13 @@
 
       auto tile_mode = ToTileMode(linear->tile_mode());
       auto matrix = ToMatrix(linear->matrix());
-      paint_.color_source = [start_point, end_point, colors = std::move(colors),
-                             stops = std::move(stops), tile_mode, matrix]() {
-        auto contents = std::make_shared<LinearGradientContents>();
-        contents->SetColors(colors);
-        contents->SetStops(stops);
-        contents->SetEndPoints(start_point, end_point);
-        contents->SetTileMode(tile_mode);
-        contents->SetEffectTransform(matrix);
 
-        std::vector<Point> bounds{start_point, end_point};
-        auto intrinsic_size =
-            Rect::MakePointBounds(bounds.begin(), bounds.end());
-        if (intrinsic_size.has_value()) {
-          contents->SetColorSourceSize(intrinsic_size->size);
-        }
-        return contents;
-      };
+      paint_.color_source = ColorSource::MakeLinearGradient(
+          start_point, end_point, std::move(colors), std::move(stops),
+          tile_mode, matrix);
       return;
     }
-    case Paint::ColorSourceType::kConicalGradient: {
+    case ColorSource::Type::kConicalGradient: {
       const flutter::DlConicalGradientColorSource* conical_gradient =
           source->asConicalGradient();
       FML_DCHECK(conical_gradient);
@@ -385,30 +368,13 @@
 
       auto tile_mode = ToTileMode(conical_gradient->tile_mode());
       auto matrix = ToMatrix(conical_gradient->matrix());
-      paint_.color_source = [center, radius, colors = std::move(colors),
-                             stops = std::move(stops), tile_mode, matrix,
-                             focus_center, focus_radius]() {
-        std::shared_ptr<ConicalGradientContents> contents =
-            std::make_shared<ConicalGradientContents>();
-        contents->SetColors(colors);
-        contents->SetStops(stops);
-        contents->SetCenterAndRadius(center, radius);
-        contents->SetTileMode(tile_mode);
-        contents->SetEffectTransform(matrix);
-        contents->SetFocus(focus_center, focus_radius);
 
-        auto radius_pt = Point(radius, radius);
-        std::vector<Point> bounds{center + radius_pt, center - radius_pt};
-        auto intrinsic_size =
-            Rect::MakePointBounds(bounds.begin(), bounds.end());
-        if (intrinsic_size.has_value()) {
-          contents->SetColorSourceSize(intrinsic_size->size);
-        }
-        return contents;
-      };
+      paint_.color_source = ColorSource::MakeConicalGradient(
+          center, radius, std::move(colors), std::move(stops), focus_center,
+          focus_radius, tile_mode, matrix);
       return;
     }
-    case Paint::ColorSourceType::kRadialGradient: {
+    case ColorSource::Type::kRadialGradient: {
       const flutter::DlRadialGradientColorSource* radialGradient =
           source->asRadialGradient();
       FML_DCHECK(radialGradient);
@@ -420,27 +386,12 @@
 
       auto tile_mode = ToTileMode(radialGradient->tile_mode());
       auto matrix = ToMatrix(radialGradient->matrix());
-      paint_.color_source = [center, radius, colors = std::move(colors),
-                             stops = std::move(stops), tile_mode, matrix]() {
-        auto contents = std::make_shared<RadialGradientContents>();
-        contents->SetColors(colors);
-        contents->SetStops(stops);
-        contents->SetCenterAndRadius(center, radius);
-        contents->SetTileMode(tile_mode);
-        contents->SetEffectTransform(matrix);
-
-        auto radius_pt = Point(radius, radius);
-        std::vector<Point> bounds{center + radius_pt, center - radius_pt};
-        auto intrinsic_size =
-            Rect::MakePointBounds(bounds.begin(), bounds.end());
-        if (intrinsic_size.has_value()) {
-          contents->SetColorSourceSize(intrinsic_size->size);
-        }
-        return contents;
-      };
+      paint_.color_source =
+          ColorSource::MakeRadialGradient(center, radius, std::move(colors),
+                                          std::move(stops), tile_mode, matrix);
       return;
     }
-    case Paint::ColorSourceType::kSweepGradient: {
+    case ColorSource::Type::kSweepGradient: {
       const flutter::DlSweepGradientColorSource* sweepGradient =
           source->asSweepGradient();
       FML_DCHECK(sweepGradient);
@@ -454,21 +405,12 @@
 
       auto tile_mode = ToTileMode(sweepGradient->tile_mode());
       auto matrix = ToMatrix(sweepGradient->matrix());
-      paint_.color_source = [center, start_angle, end_angle,
-                             colors = std::move(colors),
-                             stops = std::move(stops), tile_mode, matrix]() {
-        auto contents = std::make_shared<SweepGradientContents>();
-        contents->SetCenterAndAngles(center, start_angle, end_angle);
-        contents->SetColors(colors);
-        contents->SetStops(stops);
-        contents->SetTileMode(tile_mode);
-        contents->SetEffectTransform(matrix);
-
-        return contents;
-      };
+      paint_.color_source = ColorSource::MakeSweepGradient(
+          center, start_angle, end_angle, std::move(colors), std::move(stops),
+          tile_mode, matrix);
       return;
     }
-    case Paint::ColorSourceType::kImage: {
+    case ColorSource::Type::kImage: {
       const flutter::DlImageColorSource* image_color_source = source->asImage();
       FML_DCHECK(image_color_source &&
                  image_color_source->image()->impeller_texture());
@@ -477,20 +419,11 @@
       auto y_tile_mode = ToTileMode(image_color_source->vertical_tile_mode());
       auto desc = ToSamplerDescriptor(image_color_source->sampling());
       auto matrix = ToMatrix(image_color_source->matrix());
-      paint_.color_source = [texture, x_tile_mode, y_tile_mode, desc, matrix,
-                             &paint = paint_]() {
-        auto contents = std::make_shared<TiledTextureContents>();
-        contents->SetTexture(texture);
-        contents->SetTileModes(x_tile_mode, y_tile_mode);
-        contents->SetSamplerDescriptor(desc);
-        contents->SetEffectTransform(matrix);
-        contents->SetColorFilter(paint.color_filter);
-        contents->SetColorSourceSize(Size::Ceil(texture->GetSize()));
-        return contents;
-      };
+      paint_.color_source = ColorSource::MakeImage(texture, x_tile_mode,
+                                                   y_tile_mode, desc, matrix);
       return;
     }
-    case Paint::ColorSourceType::kRuntimeEffect: {
+    case ColorSource::Type::kRuntimeEffect: {
       const flutter::DlRuntimeEffectColorSource* runtime_effect_color_source =
           source->asRuntimeEffect();
       auto runtime_stage =
@@ -516,28 +449,19 @@
         });
       }
 
-      paint_.color_source = [runtime_stage, uniform_data, texture_inputs]() {
-        auto contents = std::make_shared<RuntimeEffectContents>();
-        contents->SetRuntimeStage(runtime_stage);
-        contents->SetUniformData(uniform_data);
-        contents->SetTextureInputs(texture_inputs);
-        return contents;
-      };
+      paint_.color_source = ColorSource::MakeRuntimeEffect(
+          runtime_stage, uniform_data, texture_inputs);
       return;
     }
-    case Paint::ColorSourceType::kScene: {
+    case ColorSource::Type::kScene: {
 #ifdef IMPELLER_ENABLE_3D
       const flutter::DlSceneColorSource* scene_color_source = source->asScene();
       std::shared_ptr<scene::Node> scene_node =
           scene_color_source->scene_node();
       Matrix camera_transform = scene_color_source->camera_matrix();
 
-      paint_.color_source = [scene_node, camera_transform]() {
-        auto contents = std::make_shared<SceneContents>();
-        contents->SetNode(scene_node);
-        contents->SetCameraTransform(camera_transform);
-        return contents;
-      };
+      paint_.color_source =
+          ColorSource::MakeScene(scene_node, camera_transform);
 #else   // IMPELLER_ENABLE_3D
       FML_LOG(ERROR) << "ColorSourceType::kScene can only be used if Impeller "
                         "Scene is enabled.";
diff --git a/impeller/entity/contents/runtime_effect_contents.h b/impeller/entity/contents/runtime_effect_contents.h
index 0a85d5a..61cb426 100644
--- a/impeller/entity/contents/runtime_effect_contents.h
+++ b/impeller/entity/contents/runtime_effect_contents.h
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#pragma once
+
 #include <functional>
 #include <memory>
 #include <vector>
diff --git a/impeller/entity/contents/solid_color_contents.cc b/impeller/entity/contents/solid_color_contents.cc
index 28c5145..9c14169 100644
--- a/impeller/entity/contents/solid_color_contents.cc
+++ b/impeller/entity/contents/solid_color_contents.cc
@@ -21,21 +21,7 @@
 }
 
 Color SolidColorContents::GetColor() const {
-  return color_.WithAlpha(color_.alpha * inherited_opacity_);
-}
-
-void SolidColorContents::SetGeometry(std::shared_ptr<Geometry> geometry) {
-  geometry_ = std::move(geometry);
-}
-
-// | Contents|
-bool SolidColorContents::CanInheritOpacity(const Entity& entity) const {
-  return true;
-}
-
-// | Contents|
-void SolidColorContents::SetInheritedOpacity(Scalar opacity) {
-  inherited_opacity_ = opacity;
+  return color_.WithAlpha(color_.alpha * GetOpacity());
 }
 
 std::optional<Rect> SolidColorContents::GetCoverage(
@@ -43,10 +29,12 @@
   if (GetColor().IsTransparent()) {
     return std::nullopt;
   }
-  if (geometry_ == nullptr) {
+
+  auto geometry = GetGeometry();
+  if (geometry == nullptr) {
     return std::nullopt;
   }
-  return geometry_->GetCoverage(entity.GetTransformation());
+  return geometry->GetCoverage(entity.GetTransformation());
 };
 
 bool SolidColorContents::ShouldRender(
@@ -68,7 +56,8 @@
   cmd.label = "Solid Fill";
   cmd.stencil_reference = entity.GetStencilDepth();
 
-  auto geometry_result = geometry_->GetPositionBuffer(renderer, entity, pass);
+  auto geometry_result =
+      GetGeometry()->GetPositionBuffer(renderer, entity, pass);
 
   auto options = OptionsFromPassAndEntity(pass, entity);
   if (geometry_result.prevent_overdraw) {
diff --git a/impeller/entity/contents/solid_color_contents.h b/impeller/entity/contents/solid_color_contents.h
index 91d5720..5348b90 100644
--- a/impeller/entity/contents/solid_color_contents.h
+++ b/impeller/entity/contents/solid_color_contents.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "flutter/fml/macros.h"
+#include "impeller/entity/contents/color_source_contents.h"
 #include "impeller/entity/contents/contents.h"
 #include "impeller/entity/geometry.h"
 #include "impeller/geometry/color.h"
@@ -20,7 +21,7 @@
 class HostBuffer;
 struct VertexBuffer;
 
-class SolidColorContents final : public Contents {
+class SolidColorContents final : public ColorSourceContents {
  public:
   SolidColorContents();
 
@@ -29,18 +30,10 @@
   static std::unique_ptr<SolidColorContents> Make(const Path& path,
                                                   Color color);
 
-  void SetGeometry(std::shared_ptr<Geometry> geometry);
-
   void SetColor(Color color);
 
   Color GetColor() const;
 
-  // | Contents|
-  bool CanInheritOpacity(const Entity& entity) const override;
-
-  // | Contents|
-  void SetInheritedOpacity(Scalar opacity) override;
-
   // |Contents|
   std::optional<Rect> GetCoverage(const Entity& entity) const override;
 
@@ -54,10 +47,7 @@
               RenderPass& pass) const override;
 
  private:
-  std::shared_ptr<Geometry> geometry_;
-
   Color color_;
-  Scalar inherited_opacity_ = 1.0;
 
   FML_DISALLOW_COPY_AND_ASSIGN(SolidColorContents);
 };
diff --git a/impeller/golden_tests/golden_tests.cc b/impeller/golden_tests/golden_tests.cc
index c7ea062..24a229e 100644
--- a/impeller/golden_tests/golden_tests.cc
+++ b/impeller/golden_tests/golden_tests.cc
@@ -65,16 +65,11 @@
 TEST_F(GoldenTests, ConicalGradient) {
   Canvas canvas;
   Paint paint;
-  paint.color_source_type = Paint::ColorSourceType::kConicalGradient;
-  paint.color_source = []() {
-    auto result = std::make_shared<ConicalGradientContents>();
-    result->SetCenterAndRadius(Point(125, 125), 125);
-    result->SetColors({Color(1.0, 0.0, 0.0, 1.0), Color(0.0, 0.0, 1.0, 1.0)});
-    result->SetStops({0, 1});
-    result->SetFocus(Point(180, 180), 0);
-    result->SetTileMode(Entity::TileMode::kClamp);
-    return result;
-  };
+
+  paint.color_source = ColorSource::MakeConicalGradient(
+      {125, 125}, 125, {Color(1.0, 0.0, 0.0, 1.0), Color(0.0, 0.0, 1.0, 1.0)},
+      {0, 1}, {180, 180}, 0, Entity::TileMode::kClamp, {});
+
   paint.stroke_width = 0.0;
   paint.style = Paint::Style::kFill;
   canvas.DrawRect(Rect(10, 10, 250, 250), paint);