[CP] Revert "Reland "Sequester all Skia<->DL interactions into the skia sub-module" (#40114)" (#40161) (#40187)

Revert "Reland "Sequester all Skia<->DL interactions into the skia sub-module""

Co-authored-by: Jim Graham <flar@google.com>
diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files
index 92a4609..11d1a1a 100644
--- a/ci/licenses_golden/excluded_files
+++ b/ci/licenses_golden/excluded_files
@@ -29,10 +29,12 @@
 ../../../flutter/build
 ../../../flutter/ci
 ../../../flutter/common/README.md
+../../../flutter/display_list/display_list_canvas_unittests.cc
 ../../../flutter/display_list/display_list_color_filter_unittests.cc
 ../../../flutter/display_list/display_list_color_source_unittests.cc
 ../../../flutter/display_list/display_list_color_unittests.cc
 ../../../flutter/display_list/display_list_complexity_unittests.cc
+../../../flutter/display_list/display_list_enum_unittests.cc
 ../../../flutter/display_list/display_list_image_filter_unittests.cc
 ../../../flutter/display_list/display_list_mask_filter_unittests.cc
 ../../../flutter/display_list/display_list_matrix_clip_tracker_unittests.cc
@@ -40,10 +42,8 @@
 ../../../flutter/display_list/display_list_path_effect_unittests.cc
 ../../../flutter/display_list/display_list_rtree_unittests.cc
 ../../../flutter/display_list/display_list_unittests.cc
+../../../flutter/display_list/display_list_utils_unittests.cc
 ../../../flutter/display_list/display_list_vertices_unittests.cc
-../../../flutter/display_list/dl_rendering_unittests.cc
-../../../flutter/display_list/skia/dl_sk_conversions_unittests.cc
-../../../flutter/display_list/skia/dl_sk_utils_unittests.cc
 ../../../flutter/display_list/testing
 ../../../flutter/docs
 ../../../flutter/examples
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 6f660b7..5ab7c54 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -714,6 +714,8 @@
 ORIGIN: ../../../flutter/display_list/display_list_builder.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/display_list_builder.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/display_list_builder_benchmarks.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/display_list/display_list_canvas_dispatcher.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/display_list/display_list_canvas_dispatcher.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/display_list_color.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/display_list_color_filter.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/display_list_color_filter.h + ../../../flutter/LICENSE
@@ -727,6 +729,8 @@
 ORIGIN: ../../../flutter/display_list/display_list_complexity_helper.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/display_list_complexity_metal.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/display_list_complexity_metal.h + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/display_list/display_list_dispatcher.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/display_list/display_list_dispatcher.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/display_list_flags.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/display_list_flags.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/display_list_image.cc + ../../../flutter/LICENSE
@@ -755,18 +759,9 @@
 ORIGIN: ../../../flutter/display_list/display_list_utils.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/display_list_vertices.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/display_list_vertices.h + ../../../flutter/LICENSE
-ORIGIN: ../../../flutter/display_list/dl_canvas.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/dl_canvas.h + ../../../flutter/LICENSE
-ORIGIN: ../../../flutter/display_list/dl_op_receiver.cc + ../../../flutter/LICENSE
-ORIGIN: ../../../flutter/display_list/dl_op_receiver.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/skia/dl_sk_canvas.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/skia/dl_sk_canvas.h + ../../../flutter/LICENSE
-ORIGIN: ../../../flutter/display_list/skia/dl_sk_conversions.cc + ../../../flutter/LICENSE
-ORIGIN: ../../../flutter/display_list/skia/dl_sk_conversions.h + ../../../flutter/LICENSE
-ORIGIN: ../../../flutter/display_list/skia/dl_sk_dispatcher.cc + ../../../flutter/LICENSE
-ORIGIN: ../../../flutter/display_list/skia/dl_sk_dispatcher.h + ../../../flutter/LICENSE
-ORIGIN: ../../../flutter/display_list/skia/dl_sk_utils.cc + ../../../flutter/LICENSE
-ORIGIN: ../../../flutter/display_list/skia/dl_sk_utils.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/display_list/types.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/flow/compositor_context.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/flow/compositor_context.h + ../../../flutter/LICENSE
@@ -3249,6 +3244,8 @@
 FILE: ../../../flutter/display_list/display_list_builder.cc
 FILE: ../../../flutter/display_list/display_list_builder.h
 FILE: ../../../flutter/display_list/display_list_builder_benchmarks.cc
+FILE: ../../../flutter/display_list/display_list_canvas_dispatcher.cc
+FILE: ../../../flutter/display_list/display_list_canvas_dispatcher.h
 FILE: ../../../flutter/display_list/display_list_color.h
 FILE: ../../../flutter/display_list/display_list_color_filter.cc
 FILE: ../../../flutter/display_list/display_list_color_filter.h
@@ -3262,6 +3259,8 @@
 FILE: ../../../flutter/display_list/display_list_complexity_helper.h
 FILE: ../../../flutter/display_list/display_list_complexity_metal.cc
 FILE: ../../../flutter/display_list/display_list_complexity_metal.h
+FILE: ../../../flutter/display_list/display_list_dispatcher.cc
+FILE: ../../../flutter/display_list/display_list_dispatcher.h
 FILE: ../../../flutter/display_list/display_list_flags.cc
 FILE: ../../../flutter/display_list/display_list_flags.h
 FILE: ../../../flutter/display_list/display_list_image.cc
@@ -3290,18 +3289,9 @@
 FILE: ../../../flutter/display_list/display_list_utils.h
 FILE: ../../../flutter/display_list/display_list_vertices.cc
 FILE: ../../../flutter/display_list/display_list_vertices.h
-FILE: ../../../flutter/display_list/dl_canvas.cc
 FILE: ../../../flutter/display_list/dl_canvas.h
-FILE: ../../../flutter/display_list/dl_op_receiver.cc
-FILE: ../../../flutter/display_list/dl_op_receiver.h
 FILE: ../../../flutter/display_list/skia/dl_sk_canvas.cc
 FILE: ../../../flutter/display_list/skia/dl_sk_canvas.h
-FILE: ../../../flutter/display_list/skia/dl_sk_conversions.cc
-FILE: ../../../flutter/display_list/skia/dl_sk_conversions.h
-FILE: ../../../flutter/display_list/skia/dl_sk_dispatcher.cc
-FILE: ../../../flutter/display_list/skia/dl_sk_dispatcher.h
-FILE: ../../../flutter/display_list/skia/dl_sk_utils.cc
-FILE: ../../../flutter/display_list/skia/dl_sk_utils.h
 FILE: ../../../flutter/display_list/types.h
 FILE: ../../../flutter/flow/compositor_context.cc
 FILE: ../../../flutter/flow/compositor_context.h
diff --git a/display_list/BUILD.gn b/display_list/BUILD.gn
index 83d5b40..35e6c8f 100644
--- a/display_list/BUILD.gn
+++ b/display_list/BUILD.gn
@@ -16,6 +16,8 @@
     "display_list_blend_mode.h",
     "display_list_builder.cc",
     "display_list_builder.h",
+    "display_list_canvas_dispatcher.cc",
+    "display_list_canvas_dispatcher.h",
     "display_list_color.h",
     "display_list_color_filter.cc",
     "display_list_color_filter.h",
@@ -27,6 +29,8 @@
     "display_list_complexity_gl.h",
     "display_list_complexity_metal.cc",
     "display_list_complexity_metal.h",
+    "display_list_dispatcher.cc",
+    "display_list_dispatcher.h",
     "display_list_flags.cc",
     "display_list_flags.h",
     "display_list_image.cc",
@@ -55,18 +59,9 @@
     "display_list_utils.h",
     "display_list_vertices.cc",
     "display_list_vertices.h",
-    "dl_canvas.cc",
     "dl_canvas.h",
-    "dl_op_receiver.cc",
-    "dl_op_receiver.h",
     "skia/dl_sk_canvas.cc",
     "skia/dl_sk_canvas.h",
-    "skia/dl_sk_conversions.cc",
-    "skia/dl_sk_conversions.h",
-    "skia/dl_sk_dispatcher.cc",
-    "skia/dl_sk_dispatcher.h",
-    "skia/dl_sk_utils.cc",
-    "skia/dl_sk_utils.h",
     "types.h",
   ]
 
@@ -98,6 +93,7 @@
       "display_list_color_source_unittests.cc",
       "display_list_color_unittests.cc",
       "display_list_complexity_unittests.cc",
+      "display_list_enum_unittests.cc",
       "display_list_image_filter_unittests.cc",
       "display_list_mask_filter_unittests.cc",
       "display_list_matrix_clip_tracker_unittests.cc",
@@ -105,9 +101,8 @@
       "display_list_path_effect_unittests.cc",
       "display_list_rtree_unittests.cc",
       "display_list_unittests.cc",
+      "display_list_utils_unittests.cc",
       "display_list_vertices_unittests.cc",
-      "skia/dl_sk_conversions_unittests.cc",
-      "skia/dl_sk_utils_unittests.cc",
     ]
 
     deps = [
@@ -135,7 +130,7 @@
   executable("display_list_rendertests") {
     testonly = true
 
-    sources = [ "dl_rendering_unittests.cc" ]
+    sources = [ "display_list_canvas_unittests.cc" ]
 
     deps = [
       ":display_list",
diff --git a/display_list/display_list.cc b/display_list/display_list.cc
index 5ac4ea5..defde8b 100644
--- a/display_list/display_list.cc
+++ b/display_list/display_list.cc
@@ -6,6 +6,7 @@
 
 #include "flutter/display_list/display_list.h"
 #include "flutter/display_list/display_list_builder.h"
+#include "flutter/display_list/display_list_canvas_dispatcher.h"
 #include "flutter/display_list/display_list_ops.h"
 #include "flutter/fml/trace_event.h"
 
@@ -133,40 +134,38 @@
   std::vector<int>::const_iterator end_;
 };
 
-void DisplayList::Dispatch(DlOpReceiver& receiver) const {
+void DisplayList::Dispatch(Dispatcher& ctx) const {
   uint8_t* ptr = storage_.get();
-  Dispatch(receiver, ptr, ptr + byte_count_, NopCuller::instance);
+  Dispatch(ctx, ptr, ptr + byte_count_, NopCuller::instance);
 }
-
-void DisplayList::Dispatch(DlOpReceiver& receiver,
-                           const SkRect& cull_rect) const {
+void DisplayList::Dispatch(Dispatcher& ctx, const SkRect& cull_rect) const {
   if (cull_rect.isEmpty()) {
     return;
   }
   if (cull_rect.contains(bounds())) {
-    Dispatch(receiver);
+    Dispatch(ctx);
     return;
   }
   const DlRTree* rtree = this->rtree().get();
   FML_DCHECK(rtree != nullptr);
   if (rtree == nullptr) {
     FML_LOG(ERROR) << "dispatched with culling rect on DL with no rtree";
-    Dispatch(receiver);
+    Dispatch(ctx);
     return;
   }
   uint8_t* ptr = storage_.get();
   std::vector<int> rect_indices;
   rtree->search(cull_rect, &rect_indices);
   VectorCuller culler(rtree, rect_indices);
-  Dispatch(receiver, ptr, ptr + byte_count_, culler);
+  Dispatch(ctx, ptr, ptr + byte_count_, culler);
 }
 
-void DisplayList::Dispatch(DlOpReceiver& receiver,
+void DisplayList::Dispatch(Dispatcher& dispatcher,
                            uint8_t* ptr,
                            uint8_t* end,
                            Culler& culler) const {
   DispatchContext context = {
-      .receiver = receiver,
+      .dispatcher = dispatcher,
       .cur_index = 0,
       // next_render_index will be initialized by culler.init()
       .next_restore_index = std::numeric_limits<int>::max(),
@@ -295,6 +294,27 @@
   return true;
 }
 
+void DisplayList::RenderTo(DisplayListBuilder* builder) const {
+  if (!builder) {
+    return;
+  }
+  if (has_rtree()) {
+    Dispatch(builder->asDispatcher(), builder->GetLocalClipBounds());
+  } else {
+    Dispatch(builder->asDispatcher());
+  }
+}
+
+void DisplayList::RenderTo(SkCanvas* canvas, SkScalar opacity) const {
+  FML_DCHECK(can_apply_group_opacity() || opacity >= SK_Scalar1);
+  DisplayListCanvasDispatcher dispatcher(canvas, opacity);
+  if (has_rtree()) {
+    Dispatch(dispatcher, canvas->getLocalClipBounds());
+  } else {
+    Dispatch(dispatcher);
+  }
+}
+
 bool DisplayList::Equals(const DisplayList* other) const {
   if (this == other) {
     return true;
diff --git a/display_list/display_list.h b/display_list/display_list.h
index a66040a..dde2676 100644
--- a/display_list/display_list.h
+++ b/display_list/display_list.h
@@ -18,36 +18,41 @@
 //
 // This file contains the definitions for:
 // DisplayList: the base class that holds the information about the
-//              sequence of operations and can dispatch them to a DlOpReceiver
-// DlOpReceiver: a pure virtual interface which can be implemented to field
-//               the requests for purposes such as sending them to an SkCanvas
-//               or detecting various rendering optimization scenarios
-// DisplayListBuilder: a class for constructing a DisplayList from DlCanvas
-//                     method calls and which can act as a DlOpReceiver as well
+//              sequence of operations and can dispatch them to a Dispatcher
+// Dispatcher: a pure virtual interface which can be implemented to field
+//             the requests for purposes such as sending them to an SkCanvas
+//             or detecting various rendering optimization scenarios
+// DisplayListBuilder: a class for constructing a DisplayList from the same
+//                     calls defined in the Dispatcher
 //
 // Other files include various class definitions for dealing with display
 // lists, such as:
-// skia/dl_sk_*.h: classes to interact between SkCanvas and DisplayList
-//                 (SkCanvas->DisplayList adapter and vice versa)
+// display_list_canvas.h: classes to interact between SkCanvas and DisplayList
+//                        (SkCanvas->DisplayList adapter and vice versa)
 //
 // display_list_utils.h: various utility classes to ease implementing
-//                       a DlOpReceiver, including NOP implementations of
+//                       a Dispatcher, including NOP implementations of
 //                       the attribute, clip, and transform methods,
 //                       classes to track attributes, clips, and transforms
 //                       and a class to compute the bounds of a DisplayList
-//                       Any class implementing DlOpReceiver can inherit from
+//                       Any class implementing Dispatcher can inherit from
 //                       these utility classes to simplify its creation
 //
 // The Flutter DisplayList mechanism is used in a similar manner to the Skia
-// SkPicture mechanism.
+// SkPicture mechanism. The primary means of communication into and out
+// of the DisplayList is through the Dispatcher virtual class which
+// provides a nearly 1:1 translation between the records of the DisplayList
+// to method calls.
 //
-// A DisplayList must be created using a DisplayListBuilder using its stateless
-// methods inherited from DlCanvas.
+// A DisplayList must be created using a DisplayListBuilder using either its
+// stateful methods inherited from Dispatcher, or from its stateless methods
+// inherited from DlCanvas.
 //
-// A DisplayList can be read back by implementing the DlOpReceiver virtual
+// A DisplayList can be read back by implementing the Dispatcher virtual
 // methods (with help from some of the classes in the utils file) and
-// passing an instance to the Dispatch() method, or it can be rendered
-// to Skia using a DlSkCanvasDispatcher.
+// passing an instance to the dispatch() method, or it can be rendered
+// to Skia using a DisplayListCanvasDispatcher or simply by passing an
+// SkCanvas pointer to its renderTo() method.
 //
 // The mechanism is inspired by the SkLiteDL class that is not directly
 // supported by Skia, but has been recommended as a basis for custom
@@ -150,7 +155,7 @@
 };
 #undef DL_OP_TO_ENUM_VALUE
 
-class DlOpReceiver;
+class Dispatcher;
 class DisplayListBuilder;
 
 class SaveLayerOptions {
@@ -226,7 +231,7 @@
 class Culler;
 
 // The base class that contains a sequence of rendering operations
-// for dispatch to a DlOpReceiver. These objects must be instantiated
+// for dispatch to a Dispatcher. These objects must be instantiated
 // through an instance of DisplayListBuilder::build().
 class DisplayList : public SkRefCnt {
  public:
@@ -234,8 +239,12 @@
 
   ~DisplayList();
 
-  void Dispatch(DlOpReceiver& ctx) const;
-  void Dispatch(DlOpReceiver& ctx, const SkRect& cull_rect) const;
+  void Dispatch(Dispatcher& ctx) const;
+  void Dispatch(Dispatcher& ctx, const SkRect& cull_rect) const;
+
+  void RenderTo(DisplayListBuilder* builder) const;
+
+  void RenderTo(SkCanvas* canvas, SkScalar opacity = SK_Scalar1) const;
 
   // From historical behavior, SkPicture always included nested bytes,
   // but nested ops are only included if requested. The defaults used
@@ -291,7 +300,7 @@
   const bool can_apply_group_opacity_;
   const sk_sp<const DlRTree> rtree_;
 
-  void Dispatch(DlOpReceiver& ctx,
+  void Dispatch(Dispatcher& ctx,
                 uint8_t* ptr,
                 uint8_t* end,
                 Culler& culler) const;
diff --git a/display_list/display_list_attributes.h b/display_list/display_list_attributes.h
index aa10863..051e4ef 100644
--- a/display_list/display_list_attributes.h
+++ b/display_list/display_list_attributes.h
@@ -36,6 +36,10 @@
 //     wrong type of instance.
 //     (eg. DlColorFilter::asBlend() or DlMaskFilter::asBlur())
 //
+// - Skiafiable:
+//     The classes override an |skia_object| method to easily obtain a Skia
+//     version of the attribute on demand.
+//
 // - Immutable:
 //     Neither the base class or any of the subclasses specify any mutation
 //     methods. Instances are often passed around as const as a reminder,
@@ -52,7 +56,7 @@
 //     compared using a |memcmp| when performing a |DisplayList::Equals|.
 //
 // - Passed by Pointer:
-//     The data shared via the |DlOpReceiver::set<Attribute>| calls are stored
+//     The data shared via the |Dispatcher::set<Attribute>| calls are stored
 //     in the buffer itself and so their lifetime is controlled by the
 //     DisplayList. That memory cannot be shared as by a |shared_ptr|
 //     because the memory may be freed outside the control of the shared
@@ -66,7 +70,7 @@
 // - Shared_Ptr-able:
 //     The classes support a method to return a |std::shared_ptr| version of
 //     themselves, safely instantiating a new copy of the object into a
-//     shared_ptr using |std::make_shared|. For those receiver objects
+//     shared_ptr using |std::make_shared|. For those dispatcher objects
 //     that may want to hold on to the contents of the object (typically
 //     in a |current_attribute_| field), they can obtain a shared_ptr
 //     copy safely and easily using the |shared| method.
@@ -75,9 +79,11 @@
 
 // |D| is the base type for the attribute
 //     (i.e. DlColorFilter, etc.)
+// |S| is the base type for the Skia version of the attribute
+//     (i.e. SkColorFilter, etc.)
 // |T| is the enum that describes the specific subclasses
 //     (i.e DlColorFilterType, etc.)
-template <class D, typename T>
+template <class D, class S, typename T>
 class DlAttribute {
  public:
   // Return the recognized specific type of the attribute.
@@ -92,6 +98,9 @@
   // version is not tied to the storage of this particular instance.
   virtual std::shared_ptr<D> shared() const = 0;
 
+  // Return an equivalent sk_sp<Skia> version of this object.
+  virtual sk_sp<S> skia_object() const = 0;
+
   // Perform a content aware |==| comparison of the Attribute.
   bool operator==(D const& other) const {
     return type() == other.type() && equals_(other);
diff --git a/display_list/display_list_benchmarks.cc b/display_list/display_list_benchmarks.cc
index b9644ec..d7f7638 100644
--- a/display_list/display_list_benchmarks.cc
+++ b/display_list/display_list_benchmarks.cc
@@ -5,7 +5,6 @@
 #include "flutter/display_list/display_list_benchmarks.h"
 #include "flutter/display_list/display_list_builder.h"
 #include "flutter/display_list/display_list_flags.h"
-#include "flutter/display_list/skia/dl_sk_canvas.h"
 
 #include "third_party/skia/include/core/SkPoint.h"
 #include "third_party/skia/include/core/SkSurface.h"
@@ -77,27 +76,27 @@
                  unsigned attributes) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(GetPaintForRun(attributes),
+                                 DisplayListOpFlags::kDrawLineFlags);
   AnnotateAttributes(attributes, state, DisplayListOpFlags::kDrawLineFlags);
 
   size_t length = state.range(0);
 
   surface_provider->InitializeSurface(length, length);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   state.counters["DrawCallCount"] = kLinesToDraw;
   for (size_t i = 0; i < kLinesToDraw; i++) {
-    builder.DrawLine(SkPoint::Make(i % length, 0),
-                     SkPoint::Make(length - i % length, length), paint);
+    builder.drawLine(SkPoint::Make(i % length, 0),
+                     SkPoint::Make(length - i % length, length));
   }
 
   auto display_list = builder.Build();
 
   // We only want to time the actual rasterization.
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -115,15 +114,15 @@
                  unsigned attributes) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(GetPaintForRun(attributes),
+                                 DisplayListOpFlags::kDrawRectFlags);
   AnnotateAttributes(attributes, state, DisplayListOpFlags::kDrawRectFlags);
 
   size_t length = state.range(0);
   size_t canvas_size = length * 2;
   surface_provider->InitializeSurface(canvas_size, canvas_size);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   // As rects have SkScalar dimensions, we want to ensure that we also
   // draw rects with non-integer position and size
@@ -132,7 +131,7 @@
 
   state.counters["DrawCallCount"] = kRectsToDraw;
   for (size_t i = 0; i < kRectsToDraw; i++) {
-    builder.DrawRect(rect, paint);
+    builder.drawRect(rect);
     rect.offset(offset, offset);
     if (rect.right() > canvas_size) {
       rect.offset(-canvas_size, 0);
@@ -146,7 +145,7 @@
 
   // We only want to time the actual rasterization.
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -164,22 +163,22 @@
                  unsigned attributes) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(GetPaintForRun(attributes),
+                                 DisplayListOpFlags::kDrawOvalFlags);
   AnnotateAttributes(attributes, state, DisplayListOpFlags::kDrawOvalFlags);
 
   size_t length = state.range(0);
   size_t canvas_size = length * 2;
   surface_provider->InitializeSurface(canvas_size, canvas_size);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   SkRect rect = SkRect::MakeXYWH(0, 0, length * 1.5f, length);
   const SkScalar offset = 0.5f;
 
   state.counters["DrawCallCount"] = kOvalsToDraw;
   for (size_t i = 0; i < kOvalsToDraw; i++) {
-    builder.DrawOval(rect, paint);
+    builder.drawOval(rect);
     rect.offset(offset, offset);
     if (rect.right() > canvas_size) {
       rect.offset(-canvas_size, 0);
@@ -192,7 +191,7 @@
 
   // We only want to time the actual rasterization.
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -210,15 +209,15 @@
                    unsigned attributes) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(GetPaintForRun(attributes),
+                                 DisplayListOpFlags::kDrawCircleFlags);
   AnnotateAttributes(attributes, state, DisplayListOpFlags::kDrawCircleFlags);
 
   size_t length = state.range(0);
   size_t canvas_size = length * 2;
   surface_provider->InitializeSurface(canvas_size, canvas_size);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   SkScalar radius = length / 2.0f;
   const SkScalar offset = 0.5f;
@@ -227,7 +226,7 @@
 
   state.counters["DrawCallCount"] = kCirclesToDraw;
   for (size_t i = 0; i < kCirclesToDraw; i++) {
-    builder.DrawCircle(center, radius, paint);
+    builder.drawCircle(center, radius);
     center.offset(offset, offset);
     if (center.x() + radius > canvas_size) {
       center.set(radius, center.y());
@@ -240,7 +239,7 @@
 
   // We only want to time the actual rasterization.
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -259,15 +258,15 @@
                   SkRRect::Type type) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(GetPaintForRun(attributes),
+                                 DisplayListOpFlags::kDrawRRectFlags);
   AnnotateAttributes(attributes, state, DisplayListOpFlags::kDrawRRectFlags);
 
   size_t length = state.range(0);
   size_t canvas_size = length * 2;
   surface_provider->InitializeSurface(canvas_size, canvas_size);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   SkVector radii[4] = {};
   switch (type) {
@@ -305,7 +304,7 @@
 
   state.counters["DrawCallCount"] = kRRectsToDraw;
   for (size_t i = 0; i < kRRectsToDraw; i++) {
-    builder.DrawRRect(rrect, paint);
+    builder.drawRRect(rrect);
     rrect.offset(offset, offset);
     if (rrect.rect().right() > canvas_size) {
       rrect.offset(-canvas_size, 0);
@@ -318,7 +317,7 @@
 
   // We only want to time the actual rasterization.
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -340,15 +339,15 @@
                    SkRRect::Type type) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(GetPaintForRun(attributes),
+                                 DisplayListOpFlags::kDrawDRRectFlags);
   AnnotateAttributes(attributes, state, DisplayListOpFlags::kDrawDRRectFlags);
 
   size_t length = state.range(0);
   size_t canvas_size = length * 2;
   surface_provider->InitializeSurface(canvas_size, canvas_size);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   SkVector radii[4] = {};
   switch (type) {
@@ -387,7 +386,7 @@
   state.counters["DrawCallCount"] = kDRRectsToDraw;
   for (size_t i = 0; i < kDRRectsToDraw; i++) {
     rrect.inset(0.1f * length, 0.1f * length, &rrect_2);
-    builder.DrawDRRect(rrect, rrect_2, paint);
+    builder.drawDRRect(rrect, rrect_2);
     rrect.offset(offset, offset);
     if (rrect.rect().right() > canvas_size) {
       rrect.offset(-canvas_size, 0);
@@ -400,7 +399,7 @@
 
   // We only want to time the actual rasterization.
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -414,8 +413,8 @@
                 unsigned attributes) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(GetPaintForRun(attributes),
+                                 DisplayListOpFlags::kDrawArcNoCenterFlags);
   AnnotateAttributes(attributes, state,
                      DisplayListOpFlags::kDrawArcNoCenterFlags);
 
@@ -423,7 +422,7 @@
   size_t canvas_size = length * 2;
   surface_provider->InitializeSurface(canvas_size, canvas_size);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   SkScalar starting_angle = 0.0f;
   SkScalar offset = 0.5f;
@@ -437,7 +436,7 @@
   state.counters["DrawCallCount"] = kArcSweepSetsToDraw * segment_sweeps.size();
   for (size_t i = 0; i < kArcSweepSetsToDraw; i++) {
     for (SkScalar sweep : segment_sweeps) {
-      builder.DrawArc(bounds, starting_angle, sweep, false, paint);
+      builder.drawArc(bounds, starting_angle, sweep, false);
       starting_angle += sweep + 5.0f;
     }
     bounds.offset(offset, offset);
@@ -453,7 +452,7 @@
 
   // We only want to time the actual rasterization.
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -630,14 +629,14 @@
                  SkPath::Verb type) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(GetPaintForRun(attributes),
+                                 DisplayListOpFlags::kDrawPathFlags);
   AnnotateAttributes(attributes, state, DisplayListOpFlags::kDrawPathFlags);
 
   size_t length = kFixedCanvasSize;
   surface_provider->InitializeSurface(length, length);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   SkPath path;
 
@@ -651,12 +650,12 @@
   state.counters["VerbCount"] = path.countVerbs();
   state.counters["DrawCallCount"] = 1;
 
-  builder.DrawPath(path, paint);
+  builder.drawPath(path);
   auto display_list = builder.Build();
 
   // We only want to time the actual rasterization.
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -768,14 +767,14 @@
                      DlVertexMode mode) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(GetPaintForRun(attributes),
+                                 DisplayListOpFlags::kDrawVerticesFlags);
   AnnotateAttributes(attributes, state, DisplayListOpFlags::kDrawVerticesFlags);
 
   size_t length = kFixedCanvasSize;
   surface_provider->InitializeSurface(length, length);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   SkPoint center = SkPoint::Make(length / 2.0f, length / 2.0f);
 
@@ -792,7 +791,7 @@
     std::shared_ptr<DlVertices> vertices =
         GetTestVertices(p, radius, 50, mode, vertex_count);
     total_vertex_count += vertex_count;
-    builder.DrawVertices(vertices.get(), DlBlendMode::kSrc, paint);
+    builder.drawVertices(vertices, DlBlendMode::kSrc);
   }
 
   state.counters["VertexCount"] = total_vertex_count;
@@ -802,7 +801,7 @@
 
   // We only want to time the actual rasterization.
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -864,18 +863,26 @@
                    DlCanvas::PointMode mode) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  SkPaint paint;
   switch (mode) {
     case DlCanvas::PointMode::kPoints:
+      builder.SetAttributesFromPaint(
+          GetPaintForRun(attributes),
+          DisplayListOpFlags::kDrawPointsAsPointsFlags);
       AnnotateAttributes(attributes, state,
                          DisplayListOpFlags::kDrawPointsAsPointsFlags);
       break;
     case DlCanvas::PointMode::kLines:
+      builder.SetAttributesFromPaint(
+          GetPaintForRun(attributes),
+          DisplayListOpFlags::kDrawPointsAsLinesFlags);
       AnnotateAttributes(attributes, state,
                          DisplayListOpFlags::kDrawPointsAsLinesFlags);
       break;
     case DlCanvas::PointMode::kPolygon:
+      builder.SetAttributesFromPaint(
+          GetPaintForRun(attributes),
+          DisplayListOpFlags::kDrawPointsAsPolygonFlags);
       AnnotateAttributes(attributes, state,
                          DisplayListOpFlags::kDrawPointsAsPolygonFlags);
       break;
@@ -884,7 +891,7 @@
   size_t length = kFixedCanvasSize;
   surface_provider->InitializeSurface(length, length);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   size_t point_count = state.range(0);
   state.SetComplexityN(point_count);
@@ -893,12 +900,12 @@
 
   std::vector<SkPoint> points =
       GetTestPoints(point_count, SkISize::Make(length, length));
-  builder.DrawPoints(mode, points.size(), points.data(), paint);
+  builder.drawPoints(mode, points.size(), points.data());
 
   auto display_list = builder.Build();
 
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -926,8 +933,8 @@
                   bool upload_bitmap) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(GetPaintForRun(attributes),
+                                 DisplayListOpFlags::kDrawImageWithPaintFlags);
   AnnotateAttributes(attributes, state,
                      DisplayListOpFlags::kDrawImageWithPaintFlags);
 
@@ -935,7 +942,7 @@
   size_t canvas_size = 2 * bitmap_size;
   surface_provider->InitializeSurface(canvas_size, canvas_size);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   sk_sp<SkImage> image;
   std::shared_ptr<DlSurfaceInstance> offscreen_instance;
@@ -962,7 +969,7 @@
   for (size_t i = 0; i < kImagesToDraw; i++) {
     image = upload_bitmap ? ImageFromBitmapWithNewID(bitmap)
                           : offscreen->makeImageSnapshot();
-    builder.DrawImage(DlImage::Make(image), dst, options, &paint);
+    builder.drawImage(DlImage::Make(image), dst, options, true);
 
     dst.offset(offset, offset);
     if (dst.x() + bitmap_size > canvas_size) {
@@ -976,7 +983,7 @@
   auto display_list = builder.Build();
 
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -986,11 +993,11 @@
   surface_provider->Snapshot(filename);
 }
 
-std::string ConstraintToString(DlCanvas::SrcRectConstraint constraint) {
+std::string ConstraintToString(SkCanvas::SrcRectConstraint constraint) {
   switch (constraint) {
-    case DlCanvas::SrcRectConstraint::kStrict:
+    case SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint:
       return "Strict";
-    case DlCanvas::SrcRectConstraint::kFast:
+    case SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint:
       return "Fast";
     default:
       return "Unknown";
@@ -1005,12 +1012,13 @@
                       BackendType backend_type,
                       unsigned attributes,
                       DlImageSampling options,
-                      DlCanvas::SrcRectConstraint constraint,
+                      SkCanvas::SrcRectConstraint constraint,
                       bool upload_bitmap) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(
+      GetPaintForRun(attributes),
+      DisplayListOpFlags::kDrawImageRectWithPaintFlags);
   AnnotateAttributes(attributes, state,
                      DisplayListOpFlags::kDrawImageRectWithPaintFlags);
 
@@ -1018,7 +1026,7 @@
   size_t canvas_size = 2 * bitmap_size;
   surface_provider->InitializeSurface(canvas_size, canvas_size);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   sk_sp<SkImage> image;
   std::shared_ptr<DlSurfaceInstance> offscreen_instance;
@@ -1048,7 +1056,7 @@
   for (size_t i = 0; i < kImagesToDraw; i++) {
     image = upload_bitmap ? ImageFromBitmapWithNewID(bitmap)
                           : offscreen->makeImageSnapshot();
-    builder.DrawImageRect(DlImage::Make(image), src, dst, options, &paint,
+    builder.drawImageRect(DlImage::Make(image), src, dst, options, true,
                           constraint);
     dst.offset(offset, offset);
     if (dst.right() > canvas_size) {
@@ -1062,7 +1070,7 @@
   auto display_list = builder.Build();
 
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -1096,8 +1104,9 @@
                       bool upload_bitmap) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(
+      GetPaintForRun(attributes),
+      DisplayListOpFlags::kDrawImageNineWithPaintFlags);
   AnnotateAttributes(attributes, state,
                      DisplayListOpFlags::kDrawImageNineWithPaintFlags);
 
@@ -1105,7 +1114,7 @@
   size_t canvas_size = 2 * bitmap_size;
   surface_provider->InitializeSurface(canvas_size, canvas_size);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   SkIRect center = SkIRect::MakeXYWH(bitmap_size / 4, bitmap_size / 4,
                                      bitmap_size / 2, bitmap_size / 2);
@@ -1136,7 +1145,7 @@
   for (size_t i = 0; i < kImagesToDraw; i++) {
     image = upload_bitmap ? ImageFromBitmapWithNewID(bitmap)
                           : offscreen->makeImageSnapshot();
-    builder.DrawImageNine(DlImage::Make(image), center, dst, filter, &paint);
+    builder.drawImageNine(DlImage::Make(image), center, dst, filter, true);
     dst.offset(offset, offset);
     if (dst.right() > canvas_size) {
       dst.offsetTo(0, dst.y());
@@ -1149,7 +1158,7 @@
   auto display_list = builder.Build();
 
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -1172,15 +1181,15 @@
                      unsigned attributes) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(GetPaintForRun(attributes),
+                                 DisplayListOpFlags::kDrawTextBlobFlags);
   AnnotateAttributes(attributes, state, DisplayListOpFlags::kDrawTextBlobFlags);
 
   size_t draw_calls = state.range(0);
   size_t canvas_size = kFixedCanvasSize;
   surface_provider->InitializeSurface(canvas_size, canvas_size);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   state.counters["DrawCallCount_Varies"] = draw_calls;
   state.counters["GlyphCount"] = draw_calls;
@@ -1189,13 +1198,13 @@
   for (size_t i = 0; i < draw_calls; i++) {
     character[0] = 'A' + (i % 26);
     auto blob = SkTextBlob::MakeFromString(character, SkFont());
-    builder.DrawTextBlob(blob, 50.0f, 50.0f, paint);
+    builder.drawTextBlob(blob, 50.0f, 50.0f);
   }
 
   auto display_list = builder.Build();
 
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -1219,14 +1228,14 @@
                    SkPath::Verb type) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(GetPaintForRun(attributes),
+                                 DisplayListOpFlags::kDrawShadowFlags);
   AnnotateAttributes(attributes, state, DisplayListOpFlags::kDrawShadowFlags);
 
   size_t length = kFixedCanvasSize;
   surface_provider->InitializeSurface(length, length);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   SkPath path;
 
@@ -1255,12 +1264,12 @@
 
   // We can hardcode dpr to 1.0f as we're varying elevation, and dpr is only
   // ever used in conjunction with elevation.
-  builder.DrawShadow(path, SK_ColorBLUE, elevation, transparent_occluder, 1.0f);
+  builder.drawShadow(path, SK_ColorBLUE, elevation, transparent_occluder, 1.0f);
   auto display_list = builder.Build();
 
   // We only want to time the actual rasterization.
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
@@ -1283,14 +1292,14 @@
                   size_t save_depth) {
   auto surface_provider = DlSurfaceProvider::Create(backend_type);
   DisplayListBuilder builder;
-  DlPaint paint = GetPaintForRun(attributes);
-
+  builder.SetAttributesFromPaint(GetPaintForRun(attributes),
+                                 DisplayListOpFlags::kSaveLayerFlags);
   AnnotateAttributes(attributes, state, DisplayListOpFlags::kSaveLayerFlags);
 
   size_t length = kFixedCanvasSize;
   surface_provider->InitializeSurface(length, length);
   auto surface = surface_provider->GetPrimarySurface()->sk_surface();
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
+  auto canvas = surface->getCanvas();
 
   size_t save_layer_calls = state.range(0);
 
@@ -1302,19 +1311,19 @@
   state.counters["DrawCallCount_Varies"] = save_layer_calls * save_depth;
   for (size_t i = 0; i < save_layer_calls; i++) {
     for (size_t j = 0; j < save_depth; j++) {
-      builder.SaveLayer(nullptr, nullptr);
-      builder.DrawRect(rect1, paint);
-      builder.DrawRect(rect2, paint);
+      builder.saveLayer(nullptr, false);
+      builder.drawRect(rect1);
+      builder.drawRect(rect2);
     }
     for (size_t j = 0; j < save_depth; j++) {
-      builder.Restore();
+      builder.restore();
     }
   }
   auto display_list = builder.Build();
 
   // We only want to time the actual rasterization.
   for ([[maybe_unused]] auto _ : state) {
-    canvas.DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
     surface->flushAndSubmit(true);
   }
 
diff --git a/display_list/display_list_benchmarks.h b/display_list/display_list_benchmarks.h
index 4d02664..e6df302 100644
--- a/display_list/display_list_benchmarks.h
+++ b/display_list/display_list_benchmarks.h
@@ -78,7 +78,7 @@
                       BackendType backend_type,
                       unsigned attributes,
                       DlImageSampling options,
-                      DlCanvas::SrcRectConstraint constraint,
+                      SkCanvas::SrcRectConstraint constraint,
                       bool upload_bitmap);
 void BM_DrawImageNine(benchmark::State& state,
                       BackendType backend_type,
@@ -343,8 +343,8 @@
       BM_DrawImageRect, Texture/Strict/BACKEND,                         \
       BackendType::k##BACKEND##_Backend,                                \
       ATTRIBUTES,                                                       \
-      DlImageSampling::kNearestNeighbor,                                \
-      DlCanvas::SrcRectConstraint::kStrict, false)                      \
+      DlImageSampling::kNearestNeighbor,                                              \
+      SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint, false)    \
       ->RangeMultiplier(2)                                              \
       ->Range(32, 256)                                                  \
       ->UseRealTime()                                                   \
@@ -354,8 +354,8 @@
       BM_DrawImageRect, Texture/Fast/BACKEND,                           \
       BackendType::k##BACKEND##_Backend,                                \
       ATTRIBUTES,                                                       \
-      DlImageSampling::kNearestNeighbor,                                \
-      DlCanvas::SrcRectConstraint::kFast, false)                        \
+      DlImageSampling::kNearestNeighbor,                                              \
+      SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint, false)      \
       ->RangeMultiplier(2)                                              \
       ->Range(32, 256)                                                  \
       ->UseRealTime()                                                   \
@@ -365,8 +365,8 @@
       BM_DrawImageRect, Upload/Strict/BACKEND,                          \
       BackendType::k##BACKEND##_Backend,                                \
       ATTRIBUTES,                                                       \
-      DlImageSampling::kNearestNeighbor,                                \
-      DlCanvas::SrcRectConstraint::kStrict, true)                       \
+      DlImageSampling::kNearestNeighbor,                                              \
+      SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint, true)     \
       ->RangeMultiplier(2)                                              \
       ->Range(32, 256)                                                  \
       ->UseRealTime()                                                   \
@@ -376,8 +376,8 @@
       BM_DrawImageRect, Upload/Fast/BACKEND,                            \
       BackendType::k##BACKEND##_Backend,                                \
       ATTRIBUTES,                                                       \
-      DlImageSampling::kNearestNeighbor,                                \
-      DlCanvas::SrcRectConstraint::kFast, true)                         \
+      DlImageSampling::kNearestNeighbor,                                              \
+      SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint, true)       \
       ->RangeMultiplier(2)                                              \
       ->Range(32, 256)                                                  \
       ->UseRealTime()                                                   \
diff --git a/display_list/display_list_blend_mode.h b/display_list/display_list_blend_mode.h
index 2b5daa9..b1cdbee 100644
--- a/display_list/display_list_blend_mode.h
+++ b/display_list/display_list_blend_mode.h
@@ -66,6 +66,14 @@
   kDefaultMode = kSrcOver,
 };
 
+inline DlBlendMode ToDl(SkBlendMode mode) {
+  return static_cast<DlBlendMode>(mode);
+}
+
+inline SkBlendMode ToSk(DlBlendMode mode) {
+  return static_cast<SkBlendMode>(mode);
+}
+
 }  // namespace flutter
 
 #endif  // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BLEND_MODE_H_
diff --git a/display_list/display_list_builder.cc b/display_list/display_list_builder.cc
index 36b8b89..160f35e 100644
--- a/display_list/display_list_builder.cc
+++ b/display_list/display_list_builder.cc
@@ -6,6 +6,7 @@
 
 #include "flutter/display_list/display_list.h"
 #include "flutter/display_list/display_list_blend_mode.h"
+#include "flutter/display_list/display_list_canvas_dispatcher.h"
 #include "flutter/display_list/display_list_color_source.h"
 #include "flutter/display_list/display_list_ops.h"
 #include "fml/logging.h"
@@ -249,8 +250,8 @@
         new (pod) DlMatrixImageFilter(matrix_filter);
         break;
       }
-      case DlImageFilterType::kCompose:
-      case DlImageFilterType::kLocalMatrix:
+      case DlImageFilterType::kComposeFilter:
+      case DlImageFilterType::kLocalMatrixFilter:
       case DlImageFilterType::kColorFilter: {
         Push<SetSharedImageFilterOp>(0, 0, filter);
         break;
@@ -842,7 +843,7 @@
   }
 
   void* data_ptr;
-  FML_DCHECK(count < DlOpReceiver::kMaxDrawPointsCount);
+  FML_DCHECK(count < Dispatcher::kMaxDrawPointsCount);
   int bytes = count * sizeof(SkPoint);
   RectBoundsAccumulator ptBounds;
   for (size_t i = 0; i < count; i++) {
@@ -946,7 +947,7 @@
                                        const SkRect& dst,
                                        DlImageSampling sampling,
                                        bool render_with_attributes,
-                                       SrcRectConstraint constraint) {
+                                       SkCanvas::SrcRectConstraint constraint) {
   Push<DrawImageRectOp>(0, 1, image, src, dst, sampling, render_with_attributes,
                         constraint);
   CheckLayerOpacityCompatibility(render_with_attributes);
@@ -960,7 +961,10 @@
                                        const SkRect& dst,
                                        DlImageSampling sampling,
                                        const DlPaint* paint,
-                                       SrcRectConstraint constraint) {
+                                       bool enforce_src_edges) {
+  SkCanvas::SrcRectConstraint constraint =
+      enforce_src_edges ? SkCanvas::kStrict_SrcRectConstraint
+                        : SkCanvas::kFast_SrcRectConstraint;
   if (paint != nullptr) {
     SetAttributesFromPaint(*paint,
                            DisplayListOpFlags::kDrawImageRectWithPaintFlags);
@@ -1073,12 +1077,18 @@
 void DisplayListBuilder::DrawDisplayList(const sk_sp<DisplayList> display_list,
                                          SkScalar opacity) {
   DlPaint current_paint = current_;
-  Push<DrawDisplayListOp>(0, 1, display_list, opacity);
-  // Not really necessary if the developer is interacting with us via
-  // our attribute-state-less DlCanvas methods, but this avoids surprises
-  // for those who may have been using the stateful Dispatcher methods.
-  SetAttributesFromPaint(current_paint,
-                         DisplayListOpFlags::kSaveLayerWithPaintFlags);
+  if (opacity < SK_Scalar1) {
+    SaveLayer(&display_list->bounds(), &DlPaint().setOpacity(opacity));
+  }
+  Push<DrawDisplayListOp>(0, 1, display_list);
+  if (opacity < SK_Scalar1) {
+    Restore();
+    // Not really necessary if the developer is interacting with us via
+    // our attribute-state-less DlCanvas methods, but this avoids surprises
+    // for those who may have been using the stateful Dispatcher methods.
+    SetAttributesFromPaint(current_paint,
+                           DisplayListOpFlags::kSaveLayerWithPaintFlags);
+  }
 
   const SkRect bounds = display_list->bounds();
   switch (accumulator()->type()) {
@@ -1132,8 +1142,8 @@
       ? Push<DrawShadowTransparentOccluderOp>(0, 1, path, color, elevation, dpr)
       : Push<DrawShadowOp>(0, 1, path, color, elevation, dpr);
 
-  SkRect shadow_bounds =
-      DlCanvas::ComputeShadowBounds(path, elevation, dpr, GetTransform());
+  SkRect shadow_bounds = DisplayListCanvasDispatcher::ComputeShadowBounds(
+      path, elevation, dpr, GetTransform());
   AccumulateOpBounds(shadow_bounds, kDrawShadowFlags);
   UpdateLayerOpacityCompatibility(false);
 }
@@ -1180,7 +1190,7 @@
         pad = std::max(pad, SK_ScalarSqrt2);
       }
       SkScalar min_stroke_width = 0.01;
-      pad *= std::max(current_.getStrokeWidth() * 0.5f, min_stroke_width);
+      pad *= std::max(getStrokeWidth() * 0.5f, min_stroke_width);
       bounds.outset(pad, pad);
     }
   }
@@ -1249,7 +1259,7 @@
   // For example, DstIn is used by masking layers.
   // https://code.google.com/p/skia/issues/detail?id=1291
   // https://crbug.com/401593
-  switch (current_.getBlendMode()) {
+  switch (getBlendMode()) {
     // For each of the following transfer modes, if the source
     // alpha is zero (our transparent black), the resulting
     // blended pixel is not necessarily equal to the original
diff --git a/display_list/display_list_builder.h b/display_list/display_list_builder.h
index 98b11fd..4226d6a 100644
--- a/display_list/display_list_builder.h
+++ b/display_list/display_list_builder.h
@@ -8,6 +8,7 @@
 #include "flutter/display_list/display_list.h"
 #include "flutter/display_list/display_list_blend_mode.h"
 #include "flutter/display_list/display_list_comparable.h"
+#include "flutter/display_list/display_list_dispatcher.h"
 #include "flutter/display_list/display_list_flags.h"
 #include "flutter/display_list/display_list_image.h"
 #include "flutter/display_list/display_list_matrix_clip_tracker.h"
@@ -16,18 +17,17 @@
 #include "flutter/display_list/display_list_sampling_options.h"
 #include "flutter/display_list/display_list_utils.h"
 #include "flutter/display_list/dl_canvas.h"
-#include "flutter/display_list/dl_op_receiver.h"
 #include "flutter/display_list/types.h"
 #include "flutter/fml/macros.h"
 
 namespace flutter {
 
 // The primary class used to build a display list. The list of methods
-// here matches the list of methods invoked on a |DlOpReceiver| combined
+// here matches the list of methods invoked on a |Dispatcher| combined
 // with the list of methods invoked on a |DlCanvas|.
 class DisplayListBuilder final : public virtual DlCanvas,
                                  public SkRefCnt,
-                                 virtual DlOpReceiver,
+                                 virtual Dispatcher,
                                  DisplayListOpFlags {
  public:
   static constexpr SkRect kMaxCullRect =
@@ -39,331 +39,168 @@
   explicit DisplayListBuilder(const SkRect& cull_rect = kMaxCullRect,
                               bool prepare_rtree = false);
 
-  ~DisplayListBuilder();
-
-  // |DlCanvas|
   SkISize GetBaseLayerSize() const override;
-  // |DlCanvas|
   SkImageInfo GetImageInfo() const override;
 
-  // |DlCanvas|
-  void Save() override;
+  ~DisplayListBuilder();
 
-  // |DlCanvas|
-  void SaveLayer(const SkRect* bounds,
-                 const DlPaint* paint = nullptr,
-                 const DlImageFilter* backdrop = nullptr) override;
-  // |DlCanvas|
-  void Restore() override;
-  // |DlCanvas|
-  int GetSaveCount() const override { return layer_stack_.size(); }
-  // |DlCanvas|
-  void RestoreToCount(int restore_count) override;
+  Dispatcher& asDispatcher() { return *this; }
 
-  // |DlCanvas|
-  void Translate(SkScalar tx, SkScalar ty) override;
-  // |DlCanvas|
-  void Scale(SkScalar sx, SkScalar sy) override;
-  // |DlCanvas|
-  void Rotate(SkScalar degrees) override;
-  // |DlCanvas|
-  void Skew(SkScalar sx, SkScalar sy) override;
-
-  // clang-format off
-  // 2x3 2D affine subset of a 4x4 transform in row major order
-  // |DlCanvas|
-  void Transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt,
-                         SkScalar myx, SkScalar myy, SkScalar myt) override;
-  // full 4x4 transform in row major order
-  // |DlCanvas|
-  void TransformFullPerspective(
-      SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
-      SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
-      SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt,
-      SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override;
-  // clang-format on
-  // |DlCanvas|
-  void TransformReset() override;
-  // |DlCanvas|
-  void Transform(const SkMatrix* matrix) override;
-  // |DlCanvas|
-  void Transform(const SkM44* matrix44) override;
-  // |DlCanvas|
-  void SetTransform(const SkMatrix* matrix) override {
-    TransformReset();
-    Transform(matrix);
-  }
-  // |DlCanvas|
-  void SetTransform(const SkM44* matrix44) override {
-    TransformReset();
-    Transform(matrix44);
-  }
-  using DlCanvas::Transform;
-
-  /// Returns the 4x4 full perspective transform representing all transform
-  /// operations executed so far in this DisplayList within the enclosing
-  /// save stack.
-  // |DlCanvas|
-  SkM44 GetTransformFullPerspective() const override {
-    return tracker_.matrix_4x4();
-  }
-  /// Returns the 3x3 partial perspective transform representing all transform
-  /// operations executed so far in this DisplayList within the enclosing
-  /// save stack.
-  // |DlCanvas|
-  SkMatrix GetTransform() const override { return tracker_.matrix_3x3(); }
-
-  // |DlCanvas|
-  void ClipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) override;
-  // |DlCanvas|
-  void ClipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override;
-  // |DlCanvas|
-  void ClipPath(const SkPath& path, ClipOp clip_op, bool is_aa) override;
-
-  /// Conservative estimate of the bounds of all outstanding clip operations
-  /// measured in the coordinate space within which this DisplayList will
-  /// be rendered.
-  // |DlCanvas|
-  SkRect GetDestinationClipBounds() const override {
-    return tracker_.device_cull_rect();
-  }
-  /// Conservative estimate of the bounds of all outstanding clip operations
-  /// transformed into the local coordinate space in which currently
-  /// recorded rendering operations are interpreted.
-  // |DlCanvas|
-  SkRect GetLocalClipBounds() const override {
-    return tracker_.local_cull_rect();
-  }
-
-  /// Return true iff the supplied bounds are easily shown to be outside
-  /// of the current clip bounds. This method may conservatively return
-  /// false if it cannot make the determination.
-  // |DlCanvas|
-  bool QuickReject(const SkRect& bounds) const override;
-
-  // |DlCanvas|
-  void DrawPaint(const DlPaint& paint) override;
-  // |DlCanvas|
-  void DrawColor(DlColor color, DlBlendMode mode) override;
-  // |DlCanvas|
-  void DrawLine(const SkPoint& p0,
-                const SkPoint& p1,
-                const DlPaint& paint) override;
-  // |DlCanvas|
-  void DrawRect(const SkRect& rect, const DlPaint& paint) override;
-  // |DlCanvas|
-  void DrawOval(const SkRect& bounds, const DlPaint& paint) override;
-  // |DlCanvas|
-  void DrawCircle(const SkPoint& center,
-                  SkScalar radius,
-                  const DlPaint& paint) override;
-  // |DlCanvas|
-  void DrawRRect(const SkRRect& rrect, const DlPaint& paint) override;
-  // |DlCanvas|
-  void DrawDRRect(const SkRRect& outer,
-                  const SkRRect& inner,
-                  const DlPaint& paint) override;
-  // |DlCanvas|
-  void DrawPath(const SkPath& path, const DlPaint& paint) override;
-  // |DlCanvas|
-  void DrawArc(const SkRect& bounds,
-               SkScalar start,
-               SkScalar sweep,
-               bool useCenter,
-               const DlPaint& paint) override;
-  // |DlCanvas|
-  void DrawPoints(PointMode mode,
-                  uint32_t count,
-                  const SkPoint pts[],
-                  const DlPaint& paint) override;
-  // |DlCanvas|
-  void DrawVertices(const DlVertices* vertices,
-                    DlBlendMode mode,
-                    const DlPaint& paint) override;
-  using DlCanvas::DrawVertices;
-  // |DlCanvas|
-  void DrawImage(const sk_sp<DlImage>& image,
-                 const SkPoint point,
-                 DlImageSampling sampling,
-                 const DlPaint* paint = nullptr) override;
-  // |DlCanvas|
-  void DrawImageRect(
-      const sk_sp<DlImage>& image,
-      const SkRect& src,
-      const SkRect& dst,
-      DlImageSampling sampling,
-      const DlPaint* paint = nullptr,
-      SrcRectConstraint constraint = SrcRectConstraint::kFast) override;
-  // |DlCanvas|
-  void DrawImageNine(const sk_sp<DlImage>& image,
-                     const SkIRect& center,
-                     const SkRect& dst,
-                     DlFilterMode filter,
-                     const DlPaint* paint = nullptr) override;
-  // |DlCanvas|
-  void DrawAtlas(const sk_sp<DlImage>& atlas,
-                 const SkRSXform xform[],
-                 const SkRect tex[],
-                 const DlColor colors[],
-                 int count,
-                 DlBlendMode mode,
-                 DlImageSampling sampling,
-                 const SkRect* cullRect,
-                 const DlPaint* paint = nullptr) override;
-  // |DlCanvas|
-  void DrawDisplayList(const sk_sp<DisplayList> display_list,
-                       SkScalar opacity = SK_Scalar1) override;
-  // |DlCanvas|
-  void DrawTextBlob(const sk_sp<SkTextBlob>& blob,
-                    SkScalar x,
-                    SkScalar y,
-                    const DlPaint& paint) override;
-  // |DlCanvas|
-  void DrawShadow(const SkPath& path,
-                  const DlColor color,
-                  const SkScalar elevation,
-                  bool transparent_occluder,
-                  SkScalar dpr) override;
-
-  // |DlCanvas|
-  void Flush() override {}
-
-  sk_sp<DisplayList> Build();
-
- private:
-  // This method exposes the internal stateful DlOpReceiver implementation
-  // of the DisplayListBuilder, primarily for testing purposes. Its use
-  // is obsolete and forbidden in every other case and is only shared to a
-  // pair of "friend" accessors in the benchmark/unittest files.
-  DlOpReceiver& asReceiver() { return *this; }
-
-  friend DlOpReceiver& DisplayListBuilderBenchmarkAccessor(
-      DisplayListBuilder& builder);
-  friend DlOpReceiver& DisplayListBuilderTestingAccessor(
-      DisplayListBuilder& builder);
-
-  void SetAttributesFromPaint(const DlPaint& paint,
-                              const DisplayListAttributeFlags flags);
-
-  // |DlOpReceiver|
   void setAntiAlias(bool aa) override {
     if (current_.isAntiAlias() != aa) {
       onSetAntiAlias(aa);
     }
   }
-  // |DlOpReceiver|
   void setDither(bool dither) override {
     if (current_.isDither() != dither) {
       onSetDither(dither);
     }
   }
-  // |DlOpReceiver|
   void setInvertColors(bool invert) override {
     if (current_.isInvertColors() != invert) {
       onSetInvertColors(invert);
     }
   }
-  // |DlOpReceiver|
   void setStrokeCap(DlStrokeCap cap) override {
     if (current_.getStrokeCap() != cap) {
       onSetStrokeCap(cap);
     }
   }
-  // |DlOpReceiver|
   void setStrokeJoin(DlStrokeJoin join) override {
     if (current_.getStrokeJoin() != join) {
       onSetStrokeJoin(join);
     }
   }
-  // |DlOpReceiver|
   void setStyle(DlDrawStyle style) override {
     if (current_.getDrawStyle() != style) {
       onSetStyle(style);
     }
   }
-  // |DlOpReceiver|
   void setStrokeWidth(float width) override {
     if (current_.getStrokeWidth() != width) {
       onSetStrokeWidth(width);
     }
   }
-  // |DlOpReceiver|
   void setStrokeMiter(float limit) override {
     if (current_.getStrokeMiter() != limit) {
       onSetStrokeMiter(limit);
     }
   }
-  // |DlOpReceiver|
   void setColor(DlColor color) override {
     if (current_.getColor() != color) {
       onSetColor(color);
     }
   }
-  // |DlOpReceiver|
   void setBlendMode(DlBlendMode mode) override {
     if (current_.getBlendMode() != mode) {
       onSetBlendMode(mode);
     }
   }
-  // |DlOpReceiver|
   void setColorSource(const DlColorSource* source) override {
     if (NotEquals(current_.getColorSource(), source)) {
       onSetColorSource(source);
     }
   }
-  // |DlOpReceiver|
   void setImageFilter(const DlImageFilter* filter) override {
     if (NotEquals(current_.getImageFilter(), filter)) {
       onSetImageFilter(filter);
     }
   }
-  // |DlOpReceiver|
   void setColorFilter(const DlColorFilter* filter) override {
     if (NotEquals(current_.getColorFilter(), filter)) {
       onSetColorFilter(filter);
     }
   }
-  // |DlOpReceiver|
   void setPathEffect(const DlPathEffect* effect) override {
     if (NotEquals(current_.getPathEffect(), effect)) {
       onSetPathEffect(effect);
     }
   }
-  // |DlOpReceiver|
   void setMaskFilter(const DlMaskFilter* filter) override {
     if (NotEquals(current_.getMaskFilter(), filter)) {
       onSetMaskFilter(filter);
     }
   }
 
-  // |DlOpReceiver|
+  bool isAntiAlias() const { return current_.isAntiAlias(); }
+  bool isDither() const { return current_.isDither(); }
+  DlDrawStyle getStyle() const { return current_.getDrawStyle(); }
+  DlColor getColor() const { return current_.getColor(); }
+  float getStrokeWidth() const { return current_.getStrokeWidth(); }
+  float getStrokeMiter() const { return current_.getStrokeMiter(); }
+  DlStrokeCap getStrokeCap() const { return current_.getStrokeCap(); }
+  DlStrokeJoin getStrokeJoin() const { return current_.getStrokeJoin(); }
+  std::shared_ptr<const DlColorSource> getColorSource() const {
+    return current_.getColorSource();
+  }
+  std::shared_ptr<const DlColorFilter> getColorFilter() const {
+    return current_.getColorFilter();
+  }
+  bool isInvertColors() const { return current_.isInvertColors(); }
+  DlBlendMode getBlendMode() const { return current_.getBlendMode(); }
+  std::shared_ptr<const DlPathEffect> getPathEffect() const {
+    return current_.getPathEffect();
+  }
+  std::shared_ptr<const DlMaskFilter> getMaskFilter() const {
+    return current_.getMaskFilter();
+  }
+  std::shared_ptr<const DlImageFilter> getImageFilter() const {
+    return current_.getImageFilter();
+  }
+
+  void Save() override;
   void save() override { Save(); }
+
   // Only the |renders_with_attributes()| option will be accepted here. Any
   // other flags will be ignored and calculated anew as the DisplayList is
   // built. Alternatively, use the |saveLayer(SkRect, bool)| method.
-  // |DlOpReceiver|
   void saveLayer(const SkRect* bounds,
                  const SaveLayerOptions options,
                  const DlImageFilter* backdrop) override;
-  // |DlOpReceiver|
+  // Convenience method with just a boolean to indicate whether the saveLayer
+  // should apply the rendering attributes.
+  void saveLayer(const SkRect* bounds, bool renders_with_attributes) {
+    saveLayer(bounds,
+              renders_with_attributes ? SaveLayerOptions::kWithAttributes
+                                      : SaveLayerOptions::kNoAttributes,
+              nullptr);
+  }
+  void SaveLayer(const SkRect* bounds,
+                 const DlPaint* paint = nullptr,
+                 const DlImageFilter* backdrop = nullptr) override;
+  void Restore() override;
   void restore() override { Restore(); }
+  int GetSaveCount() const override { return layer_stack_.size(); }
+  void RestoreToCount(int restore_count) override;
+  void restoreToCount(int restore_count) { RestoreToCount(restore_count); }
 
-  // |DlOpReceiver|
+  void Translate(SkScalar tx, SkScalar ty) override;
+  void Scale(SkScalar sx, SkScalar sy) override;
+  void Rotate(SkScalar degrees) override;
+  void Skew(SkScalar sx, SkScalar sy) override;
   void translate(SkScalar tx, SkScalar ty) override { Translate(tx, ty); }
-  // |DlOpReceiver|
   void scale(SkScalar sx, SkScalar sy) override { Scale(sx, sy); }
-  // |DlOpReceiver|
   void rotate(SkScalar degrees) override { Rotate(degrees); }
-  // |DlOpReceiver|
   void skew(SkScalar sx, SkScalar sy) override { Skew(sx, sy); }
 
+  void SetAttributesFromPaint(const DlPaint& paint,
+                              const DisplayListAttributeFlags flags);
+
   // clang-format off
-  // |DlOpReceiver|
+
+  // 2x3 2D affine subset of a 4x4 transform in row major order
+  void Transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt,
+                         SkScalar myx, SkScalar myy, SkScalar myt) override;
   void transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt,
                          SkScalar myx, SkScalar myy, SkScalar myt) override {
     Transform2DAffine(mxx, mxy, mxt, myx, myy, myt);
   }
-  // |DlOpReceiver|
+  // full 4x4 transform in row major order
+  void TransformFullPerspective(
+      SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
+      SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
+      SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt,
+      SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override;
   void transformFullPerspective(
       SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
       SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
@@ -374,73 +211,148 @@
                              mzx, mzy, mzz, mzt,
                              mwx, mwy, mwz, mwt);
   }
-  // clang-format off
-  // |DlOpReceiver|
+  // clang-format on
+  void TransformReset() override;
+  void Transform(const SkMatrix* matrix) override;
+  void Transform(const SkM44* matrix44) override;
+  void SetTransform(const SkMatrix* matrix) override {
+    TransformReset();
+    Transform(matrix);
+  }
+  void SetTransform(const SkM44* matrix44) override {
+    TransformReset();
+    Transform(matrix44);
+  }
+  using DlCanvas::Transform;
   void transformReset() override { TransformReset(); }
+  void transform(const SkMatrix* matrix) { Transform(matrix); }
+  void transform(const SkM44* matrix44) { Transform(matrix44); }
+  void transform(const SkMatrix& matrix) { Transform(&matrix); }
+  void transform(const SkM44& matrix44) { Transform(&matrix44); }
 
-  // |DlOpReceiver|
+  /// Returns the 4x4 full perspective transform representing all transform
+  /// operations executed so far in this DisplayList within the enclosing
+  /// save stack.
+  SkM44 GetTransformFullPerspective() const override {
+    return tracker_.matrix_4x4();
+  }
+  /// Returns the 3x3 partial perspective transform representing all transform
+  /// operations executed so far in this DisplayList within the enclosing
+  /// save stack.
+  SkMatrix GetTransform() const override { return tracker_.matrix_3x3(); }
+
+  void ClipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) override;
+  void ClipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override;
+  void ClipPath(const SkPath& path, ClipOp clip_op, bool is_aa) override;
   void clipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) override {
     ClipRect(rect, clip_op, is_aa);
   }
-  // |DlOpReceiver|
   void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override {
     ClipRRect(rrect, clip_op, is_aa);
   }
-  // |DlOpReceiver|
   void clipPath(const SkPath& path, ClipOp clip_op, bool is_aa) override {
     ClipPath(path, clip_op, is_aa);
   }
 
-  // |DlOpReceiver|
+  /// Conservative estimate of the bounds of all outstanding clip operations
+  /// measured in the coordinate space within which this DisplayList will
+  /// be rendered.
+  SkRect GetDestinationClipBounds() const override {
+    return tracker_.device_cull_rect();
+  }
+  /// Conservative estimate of the bounds of all outstanding clip operations
+  /// transformed into the local coordinate space in which currently
+  /// recorded rendering operations are interpreted.
+  SkRect GetLocalClipBounds() const override {
+    return tracker_.local_cull_rect();
+  }
+
+  /// Return true iff the supplied bounds are easily shown to be outside
+  /// of the current clip bounds. This method may conservatively return
+  /// false if it cannot make the determination.
+  bool QuickReject(const SkRect& bounds) const override;
+
   void drawPaint() override;
-  // |DlOpReceiver|
+  void DrawPaint(const DlPaint& paint) override;
+  void DrawColor(DlColor color, DlBlendMode mode) override;
   void drawColor(DlColor color, DlBlendMode mode) override {
     DrawColor(color, mode);
   }
-  // |DlOpReceiver|
   void drawLine(const SkPoint& p0, const SkPoint& p1) override;
-  // |DlOpReceiver|
+  void DrawLine(const SkPoint& p0,
+                const SkPoint& p1,
+                const DlPaint& paint) override;
   void drawRect(const SkRect& rect) override;
-  // |DlOpReceiver|
+  void DrawRect(const SkRect& rect, const DlPaint& paint) override;
   void drawOval(const SkRect& bounds) override;
-  // |DlOpReceiver|
+  void DrawOval(const SkRect& bounds, const DlPaint& paint) override;
   void drawCircle(const SkPoint& center, SkScalar radius) override;
-  // |DlOpReceiver|
+  void DrawCircle(const SkPoint& center,
+                  SkScalar radius,
+                  const DlPaint& paint) override;
   void drawRRect(const SkRRect& rrect) override;
-  // |DlOpReceiver|
+  void DrawRRect(const SkRRect& rrect, const DlPaint& paint) override;
   void drawDRRect(const SkRRect& outer, const SkRRect& inner) override;
-  // |DlOpReceiver|
+  void DrawDRRect(const SkRRect& outer,
+                  const SkRRect& inner,
+                  const DlPaint& paint) override;
   void drawPath(const SkPath& path) override;
-  // |DlOpReceiver|
+  void DrawPath(const SkPath& path, const DlPaint& paint) override;
   void drawArc(const SkRect& bounds,
                SkScalar start,
                SkScalar sweep,
                bool useCenter) override;
-  // |DlOpReceiver|
+  void DrawArc(const SkRect& bounds,
+               SkScalar start,
+               SkScalar sweep,
+               bool useCenter,
+               const DlPaint& paint) override;
   void drawPoints(PointMode mode, uint32_t count, const SkPoint pts[]) override;
-  // |DlOpReceiver|
+  void DrawPoints(PointMode mode,
+                  uint32_t count,
+                  const SkPoint pts[],
+                  const DlPaint& paint) override;
   void drawVertices(const DlVertices* vertices, DlBlendMode mode) override;
-
-  // |DlOpReceiver|
+  void drawVertices(const std::shared_ptr<const DlVertices> vertices,
+                    DlBlendMode mode) {
+    drawVertices(vertices.get(), mode);
+  }
+  void DrawVertices(const DlVertices* vertices,
+                    DlBlendMode mode,
+                    const DlPaint& paint) override;
+  using DlCanvas::DrawVertices;
   void drawImage(const sk_sp<DlImage> image,
                  const SkPoint point,
                  DlImageSampling sampling,
                  bool render_with_attributes) override;
-  // |DlOpReceiver|
+  void DrawImage(const sk_sp<DlImage>& image,
+                 const SkPoint point,
+                 DlImageSampling sampling,
+                 const DlPaint* paint = nullptr) override;
   void drawImageRect(
       const sk_sp<DlImage> image,
       const SkRect& src,
       const SkRect& dst,
       DlImageSampling sampling,
       bool render_with_attributes,
-      SrcRectConstraint constraint = SrcRectConstraint::kFast) override;
-  // |DlOpReceiver|
+      SkCanvas::SrcRectConstraint constraint =
+          SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint) override;
+  void DrawImageRect(const sk_sp<DlImage>& image,
+                     const SkRect& src,
+                     const SkRect& dst,
+                     DlImageSampling sampling,
+                     const DlPaint* paint = nullptr,
+                     bool enforce_src_edges = false) override;
   void drawImageNine(const sk_sp<DlImage> image,
                      const SkIRect& center,
                      const SkRect& dst,
                      DlFilterMode filter,
                      bool render_with_attributes) override;
-  // |DlOpReceiver|
+  void DrawImageNine(const sk_sp<DlImage>& image,
+                     const SkIRect& center,
+                     const SkRect& dst,
+                     DlFilterMode filter,
+                     const DlPaint* paint = nullptr) override;
   void drawAtlas(const sk_sp<DlImage> atlas,
                  const SkRSXform xform[],
                  const SkRect tex[],
@@ -450,17 +362,32 @@
                  DlImageSampling sampling,
                  const SkRect* cullRect,
                  bool render_with_attributes) override;
-
-  // |DlOpReceiver|
-  void drawDisplayList(const sk_sp<DisplayList> display_list,
-                       SkScalar opacity) override {
-    DrawDisplayList(display_list, opacity);
+  void DrawAtlas(const sk_sp<DlImage>& atlas,
+                 const SkRSXform xform[],
+                 const SkRect tex[],
+                 const DlColor colors[],
+                 int count,
+                 DlBlendMode mode,
+                 DlImageSampling sampling,
+                 const SkRect* cullRect,
+                 const DlPaint* paint = nullptr) override;
+  void DrawDisplayList(const sk_sp<DisplayList> display_list,
+                       SkScalar opacity) override;
+  void drawDisplayList(const sk_sp<DisplayList> display_list) override {
+    DrawDisplayList(display_list, SK_Scalar1);
   }
-  // |DlOpReceiver|
   void drawTextBlob(const sk_sp<SkTextBlob> blob,
                     SkScalar x,
                     SkScalar y) override;
-  // |DlOpReceiver|
+  void DrawTextBlob(const sk_sp<SkTextBlob>& blob,
+                    SkScalar x,
+                    SkScalar y,
+                    const DlPaint& paint) override;
+  void DrawShadow(const SkPath& path,
+                  const DlColor color,
+                  const SkScalar elevation,
+                  bool transparent_occluder,
+                  SkScalar dpr) override;
   void drawShadow(const SkPath& path,
                   const DlColor color,
                   const SkScalar elevation,
@@ -469,6 +396,11 @@
     DrawShadow(path, color, elevation, transparent_occluder, dpr);
   }
 
+  void Flush() override {}
+
+  sk_sp<DisplayList> Build();
+
+ private:
   void checkForDeferredSave();
 
   DisplayListStorage storage_;
diff --git a/display_list/display_list_builder_benchmarks.cc b/display_list/display_list_builder_benchmarks.cc
index 87f4bfc..7405901 100644
--- a/display_list/display_list_builder_benchmarks.cc
+++ b/display_list/display_list_builder_benchmarks.cc
@@ -6,11 +6,6 @@
 #include "flutter/display_list/testing/dl_test_snippets.h"
 
 namespace flutter {
-
-DlOpReceiver& DisplayListBuilderBenchmarkAccessor(DisplayListBuilder& builder) {
-  return builder.asReceiver();
-}
-
 namespace {
 
 static std::vector<testing::DisplayListInvocationGroup> allRenderingOps =
@@ -24,11 +19,10 @@
 };
 
 static void InvokeAllRenderingOps(DisplayListBuilder& builder) {
-  DlOpReceiver& receiver = DisplayListBuilderBenchmarkAccessor(builder);
   for (auto& group : allRenderingOps) {
     for (size_t i = 0; i < group.variants.size(); i++) {
       auto& invocation = group.variants[i];
-      invocation.Invoke(receiver);
+      invocation.Invoke(builder);
     }
   }
 }
@@ -75,8 +69,8 @@
   bool prepare_rtree = NeedPrepareRTree(type);
   while (state.KeepRunning()) {
     DisplayListBuilder builder(prepare_rtree);
-    builder.Scale(3.5, 3.5);
-    builder.Translate(10.3, 6.9);
+    builder.scale(3.5, 3.5);
+    builder.translate(10.3, 6.9);
     InvokeAllRenderingOps(builder);
     Complete(builder, type);
   }
@@ -88,7 +82,7 @@
   bool prepare_rtree = NeedPrepareRTree(type);
   while (state.KeepRunning()) {
     DisplayListBuilder builder(prepare_rtree);
-    builder.TransformFullPerspective(0, 1, 0, 12, 1, 0, 0, 33, 3, 2, 5, 29, 0,
+    builder.transformFullPerspective(0, 1, 0, 12, 1, 0, 0, 33, 3, 2, 5, 29, 0,
                                      0, 0, 12);
     InvokeAllRenderingOps(builder);
     Complete(builder, type);
@@ -102,7 +96,7 @@
   bool prepare_rtree = NeedPrepareRTree(type);
   while (state.KeepRunning()) {
     DisplayListBuilder builder(prepare_rtree);
-    builder.ClipRect(clip_bounds, DlCanvas::ClipOp::kIntersect, true);
+    builder.clipRect(clip_bounds, DlCanvas::ClipOp::kIntersect, true);
     InvokeAllRenderingOps(builder);
     Complete(builder, type);
   }
@@ -114,13 +108,12 @@
   bool prepare_rtree = NeedPrepareRTree(type);
   while (state.KeepRunning()) {
     DisplayListBuilder builder(prepare_rtree);
-    DlOpReceiver& receiver = DisplayListBuilderBenchmarkAccessor(builder);
     for (auto& group : allRenderingOps) {
       for (size_t i = 0; i < group.variants.size(); i++) {
         auto& invocation = group.variants[i];
-        builder.SaveLayer(nullptr, nullptr);
-        invocation.Invoke(receiver);
-        builder.Restore();
+        builder.saveLayer(nullptr, false);
+        invocation.Invoke(builder);
+        builder.restore();
       }
     }
     Complete(builder, type);
@@ -136,12 +129,11 @@
   bool prepare_rtree = NeedPrepareRTree(type);
   while (state.KeepRunning()) {
     DisplayListBuilder builder(prepare_rtree);
-    DlOpReceiver& receiver = DisplayListBuilderBenchmarkAccessor(builder);
     for (auto& group : allRenderingOps) {
       for (size_t i = 0; i < group.variants.size(); i++) {
         auto& invocation = group.variants[i];
         builder.SaveLayer(&layer_bounds, &layer_paint);
-        invocation.Invoke(receiver);
+        invocation.Invoke(builder);
         builder.Restore();
       }
     }
diff --git a/display_list/display_list_canvas_dispatcher.cc b/display_list/display_list_canvas_dispatcher.cc
new file mode 100644
index 0000000..34dbe83
--- /dev/null
+++ b/display_list/display_list_canvas_dispatcher.cc
@@ -0,0 +1,305 @@
+// 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 "flutter/display_list/display_list_canvas_dispatcher.h"
+
+#include "flutter/display_list/display_list_blend_mode.h"
+#include "flutter/fml/trace_event.h"
+
+namespace flutter {
+
+const SkScalar kLightHeight = 600;
+const SkScalar kLightRadius = 800;
+
+static SkClipOp ToSk(DlCanvas::ClipOp op) {
+  return static_cast<SkClipOp>(op);
+}
+
+static SkCanvas::PointMode ToSk(DlCanvas::PointMode mode) {
+  return static_cast<SkCanvas::PointMode>(mode);
+}
+
+const SkPaint* DisplayListCanvasDispatcher::safe_paint(bool use_attributes) {
+  if (use_attributes) {
+    // The accumulated SkPaint object will already have incorporated
+    // any attribute overrides.
+    return &paint();
+  } else if (has_opacity()) {
+    temp_paint_.setAlphaf(opacity());
+    return &temp_paint_;
+  } else {
+    return nullptr;
+  }
+}
+
+void DisplayListCanvasDispatcher::save() {
+  canvas_->save();
+  // save has no impact on attributes, but it needs to register a record
+  // on the restore stack so that the eventual call to restore() will
+  // know what to do at that time. We could annotate the restore record
+  // with a flag that the record came from a save call, but it is simpler
+  // to just pass in the current opacity value as the value to be used by
+  // the children and let the utility calls notice that it didn't change.
+  save_opacity(opacity());
+}
+void DisplayListCanvasDispatcher::restore() {
+  canvas_->restore();
+  restore_opacity();
+}
+void DisplayListCanvasDispatcher::saveLayer(const SkRect* bounds,
+                                            const SaveLayerOptions options,
+                                            const DlImageFilter* backdrop) {
+  if (bounds == nullptr && options.can_distribute_opacity() &&
+      backdrop == nullptr) {
+    // We know that:
+    // - no bounds is needed for clipping here
+    // - no backdrop filter is used to initialize the layer
+    // - the current attributes only have an alpha
+    // - the children are compatible with individually rendering with
+    //   an inherited opacity
+    // Therefore we can just use a save instead of a saveLayer and pass the
+    // intended opacity to the children.
+    canvas_->save();
+    // If the saveLayer does not use attributes, the children should continue
+    // to render with the inherited opacity unmodified. If attributes are to
+    // be applied, the children should render with the combination of the
+    // inherited opacity combined with the alpha from the current color.
+    save_opacity(options.renders_with_attributes() ? combined_opacity()
+                                                   : opacity());
+  } else {
+    TRACE_EVENT0("flutter", "Canvas::saveLayer");
+    const SkPaint* paint = safe_paint(options.renders_with_attributes());
+    const sk_sp<SkImageFilter> sk_backdrop =
+        backdrop ? backdrop->skia_object() : nullptr;
+    canvas_->saveLayer(
+        SkCanvas::SaveLayerRec(bounds, paint, sk_backdrop.get(), 0));
+    // saveLayer will apply the current opacity on behalf of the children
+    // so they will inherit an opaque opacity.
+    save_opacity(SK_Scalar1);
+  }
+}
+
+void DisplayListCanvasDispatcher::translate(SkScalar tx, SkScalar ty) {
+  canvas_->translate(tx, ty);
+}
+void DisplayListCanvasDispatcher::scale(SkScalar sx, SkScalar sy) {
+  canvas_->scale(sx, sy);
+}
+void DisplayListCanvasDispatcher::rotate(SkScalar degrees) {
+  canvas_->rotate(degrees);
+}
+void DisplayListCanvasDispatcher::skew(SkScalar sx, SkScalar sy) {
+  canvas_->skew(sx, sy);
+}
+// clang-format off
+// 2x3 2D affine subset of a 4x4 transform in row major order
+void DisplayListCanvasDispatcher::transform2DAffine(
+    SkScalar mxx, SkScalar mxy, SkScalar mxt,
+    SkScalar myx, SkScalar myy, SkScalar myt) {
+  // Internally concat(SkMatrix) gets redirected to concat(SkM44)
+  // so we just jump directly to the SkM44 version
+  canvas_->concat(SkM44(mxx, mxy, 0, mxt,
+                        myx, myy, 0, myt,
+                         0,   0,  1,  0,
+                         0,   0,  0,  1));
+}
+// full 4x4 transform in row major order
+void DisplayListCanvasDispatcher::transformFullPerspective(
+    SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
+    SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
+    SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt,
+    SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) {
+  canvas_->concat(SkM44(mxx, mxy, mxz, mxt,
+                        myx, myy, myz, myt,
+                        mzx, mzy, mzz, mzt,
+                        mwx, mwy, mwz, mwt));
+}
+// clang-format on
+void DisplayListCanvasDispatcher::transformReset() {
+  canvas_->setMatrix(original_transform_);
+}
+
+void DisplayListCanvasDispatcher::clipRect(const SkRect& rect,
+                                           ClipOp clip_op,
+                                           bool is_aa) {
+  canvas_->clipRect(rect, ToSk(clip_op), is_aa);
+}
+void DisplayListCanvasDispatcher::clipRRect(const SkRRect& rrect,
+                                            ClipOp clip_op,
+                                            bool is_aa) {
+  canvas_->clipRRect(rrect, ToSk(clip_op), is_aa);
+}
+void DisplayListCanvasDispatcher::clipPath(const SkPath& path,
+                                           ClipOp clip_op,
+                                           bool is_aa) {
+  canvas_->clipPath(path, ToSk(clip_op), is_aa);
+}
+
+void DisplayListCanvasDispatcher::drawPaint() {
+  const SkPaint& sk_paint = paint();
+  SkImageFilter* filter = sk_paint.getImageFilter();
+  if (filter && !filter->asColorFilter(nullptr)) {
+    // drawPaint does an implicit saveLayer if an SkImageFilter is
+    // present that cannot be replaced by an SkColorFilter.
+    TRACE_EVENT0("flutter", "Canvas::saveLayer");
+  }
+  canvas_->drawPaint(sk_paint);
+}
+void DisplayListCanvasDispatcher::drawColor(DlColor color, DlBlendMode mode) {
+  // SkCanvas::drawColor(SkColor) does the following conversion anyway
+  // We do it here manually to increase precision on applying opacity
+  SkColor4f color4f = SkColor4f::FromColor(color);
+  color4f.fA *= opacity();
+  canvas_->drawColor(color4f, ToSk(mode));
+}
+void DisplayListCanvasDispatcher::drawLine(const SkPoint& p0,
+                                           const SkPoint& p1) {
+  canvas_->drawLine(p0, p1, paint());
+}
+void DisplayListCanvasDispatcher::drawRect(const SkRect& rect) {
+  canvas_->drawRect(rect, paint());
+}
+void DisplayListCanvasDispatcher::drawOval(const SkRect& bounds) {
+  canvas_->drawOval(bounds, paint());
+}
+void DisplayListCanvasDispatcher::drawCircle(const SkPoint& center,
+                                             SkScalar radius) {
+  canvas_->drawCircle(center, radius, paint());
+}
+void DisplayListCanvasDispatcher::drawRRect(const SkRRect& rrect) {
+  canvas_->drawRRect(rrect, paint());
+}
+void DisplayListCanvasDispatcher::drawDRRect(const SkRRect& outer,
+                                             const SkRRect& inner) {
+  canvas_->drawDRRect(outer, inner, paint());
+}
+void DisplayListCanvasDispatcher::drawPath(const SkPath& path) {
+  canvas_->drawPath(path, paint());
+}
+void DisplayListCanvasDispatcher::drawArc(const SkRect& bounds,
+                                          SkScalar start,
+                                          SkScalar sweep,
+                                          bool useCenter) {
+  canvas_->drawArc(bounds, start, sweep, useCenter, paint());
+}
+void DisplayListCanvasDispatcher::drawPoints(PointMode mode,
+                                             uint32_t count,
+                                             const SkPoint pts[]) {
+  canvas_->drawPoints(ToSk(mode), count, pts, paint());
+}
+void DisplayListCanvasDispatcher::drawVertices(const DlVertices* vertices,
+                                               DlBlendMode mode) {
+  canvas_->drawVertices(vertices->skia_object(), ToSk(mode), paint());
+}
+void DisplayListCanvasDispatcher::drawImage(const sk_sp<DlImage> image,
+                                            const SkPoint point,
+                                            DlImageSampling sampling,
+                                            bool render_with_attributes) {
+  canvas_->drawImage(image ? image->skia_image() : nullptr, point.fX, point.fY,
+                     ToSk(sampling), safe_paint(render_with_attributes));
+}
+void DisplayListCanvasDispatcher::drawImageRect(
+    const sk_sp<DlImage> image,
+    const SkRect& src,
+    const SkRect& dst,
+    DlImageSampling sampling,
+    bool render_with_attributes,
+    SkCanvas::SrcRectConstraint constraint) {
+  canvas_->drawImageRect(image ? image->skia_image() : nullptr, src, dst,
+                         ToSk(sampling), safe_paint(render_with_attributes),
+                         constraint);
+}
+void DisplayListCanvasDispatcher::drawImageNine(const sk_sp<DlImage> image,
+                                                const SkIRect& center,
+                                                const SkRect& dst,
+                                                DlFilterMode filter,
+                                                bool render_with_attributes) {
+  if (!image) {
+    return;
+  }
+  auto skia_image = image->skia_image();
+  if (!skia_image) {
+    return;
+  }
+  canvas_->drawImageNine(skia_image.get(), center, dst, ToSk(filter),
+                         safe_paint(render_with_attributes));
+}
+void DisplayListCanvasDispatcher::drawAtlas(const sk_sp<DlImage> atlas,
+                                            const SkRSXform xform[],
+                                            const SkRect tex[],
+                                            const DlColor colors[],
+                                            int count,
+                                            DlBlendMode mode,
+                                            DlImageSampling sampling,
+                                            const SkRect* cullRect,
+                                            bool render_with_attributes) {
+  if (!atlas) {
+    return;
+  }
+  auto skia_atlas = atlas->skia_image();
+  if (!skia_atlas) {
+    return;
+  }
+  const SkColor* sk_colors = reinterpret_cast<const SkColor*>(colors);
+  canvas_->drawAtlas(skia_atlas.get(), xform, tex, sk_colors, count, ToSk(mode),
+                     ToSk(sampling), cullRect,
+                     safe_paint(render_with_attributes));
+}
+void DisplayListCanvasDispatcher::drawDisplayList(
+    const sk_sp<DisplayList> display_list) {
+  int save_count = canvas_->save();
+  display_list->RenderTo(canvas_, opacity());
+  canvas_->restoreToCount(save_count);
+}
+void DisplayListCanvasDispatcher::drawTextBlob(const sk_sp<SkTextBlob> blob,
+                                               SkScalar x,
+                                               SkScalar y) {
+  canvas_->drawTextBlob(blob, x, y, paint());
+}
+
+SkRect DisplayListCanvasDispatcher::ComputeShadowBounds(const SkPath& path,
+                                                        float elevation,
+                                                        SkScalar dpr,
+                                                        const SkMatrix& ctm) {
+  SkRect shadow_bounds(path.getBounds());
+  SkShadowUtils::GetLocalBounds(
+      ctm, path, SkPoint3::Make(0, 0, dpr * elevation),
+      SkPoint3::Make(0, -1, 1), kLightRadius / kLightHeight,
+      SkShadowFlags::kDirectionalLight_ShadowFlag, &shadow_bounds);
+  return shadow_bounds;
+}
+
+void DisplayListCanvasDispatcher::DrawShadow(SkCanvas* canvas,
+                                             const SkPath& path,
+                                             DlColor color,
+                                             float elevation,
+                                             bool transparentOccluder,
+                                             SkScalar dpr) {
+  const SkScalar kAmbientAlpha = 0.039f;
+  const SkScalar kSpotAlpha = 0.25f;
+
+  uint32_t flags = transparentOccluder
+                       ? SkShadowFlags::kTransparentOccluder_ShadowFlag
+                       : SkShadowFlags::kNone_ShadowFlag;
+  flags |= SkShadowFlags::kDirectionalLight_ShadowFlag;
+  SkColor in_ambient = SkColorSetA(color, kAmbientAlpha * SkColorGetA(color));
+  SkColor in_spot = SkColorSetA(color, kSpotAlpha * SkColorGetA(color));
+  SkColor ambient_color, spot_color;
+  SkShadowUtils::ComputeTonalColors(in_ambient, in_spot, &ambient_color,
+                                    &spot_color);
+  SkShadowUtils::DrawShadow(canvas, path, SkPoint3::Make(0, 0, dpr * elevation),
+                            SkPoint3::Make(0, -1, 1),
+                            kLightRadius / kLightHeight, ambient_color,
+                            spot_color, flags);
+}
+
+void DisplayListCanvasDispatcher::drawShadow(const SkPath& path,
+                                             const DlColor color,
+                                             const SkScalar elevation,
+                                             bool transparent_occluder,
+                                             SkScalar dpr) {
+  DrawShadow(canvas_, path, color, elevation, transparent_occluder, dpr);
+}
+
+}  // namespace flutter
diff --git a/display_list/skia/dl_sk_dispatcher.h b/display_list/display_list_canvas_dispatcher.h
similarity index 85%
rename from display_list/skia/dl_sk_dispatcher.h
rename to display_list/display_list_canvas_dispatcher.h
index 1edf2d4..993b5b4 100644
--- a/display_list/skia/dl_sk_dispatcher.h
+++ b/display_list/display_list_canvas_dispatcher.h
@@ -6,8 +6,9 @@
 #define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_CANVAS_DISPATCHER_H_
 
 #include "flutter/display_list/display_list.h"
-#include "flutter/display_list/dl_op_receiver.h"
-#include "flutter/display_list/skia/dl_sk_utils.h"
+#include "flutter/display_list/display_list_blend_mode.h"
+#include "flutter/display_list/display_list_dispatcher.h"
+#include "flutter/display_list/display_list_utils.h"
 #include "flutter/fml/macros.h"
 
 namespace flutter {
@@ -18,10 +19,11 @@
 ///
 /// Receives all methods on Dispatcher and sends them to an SkCanvas
 ///
-class DlSkCanvasDispatcher : public virtual DlOpReceiver,
-                             public SkPaintDispatchHelper {
+class DisplayListCanvasDispatcher : public virtual Dispatcher,
+                                    public SkPaintDispatchHelper {
  public:
-  explicit DlSkCanvasDispatcher(SkCanvas* canvas, SkScalar opacity = SK_Scalar1)
+  explicit DisplayListCanvasDispatcher(SkCanvas* canvas,
+                                       SkScalar opacity = SK_Scalar1)
       : SkPaintDispatchHelper(opacity),
         canvas_(canvas),
         original_transform_(canvas->getLocalToDevice()) {}
@@ -79,7 +81,7 @@
                      const SkRect& dst,
                      DlImageSampling sampling,
                      bool render_with_attributes,
-                     SrcRectConstraint constraint) override;
+                     SkCanvas::SrcRectConstraint constraint) override;
   void drawImageNine(const sk_sp<DlImage> image,
                      const SkIRect& center,
                      const SkRect& dst,
@@ -94,8 +96,7 @@
                  DlImageSampling sampling,
                  const SkRect* cullRect,
                  bool render_with_attributes) override;
-  void drawDisplayList(const sk_sp<DisplayList> display_list,
-                       SkScalar opacity) override;
+  void drawDisplayList(const sk_sp<DisplayList> display_list) override;
   void drawTextBlob(const sk_sp<SkTextBlob> blob,
                     SkScalar x,
                     SkScalar y) override;
@@ -105,6 +106,11 @@
                   bool transparent_occluder,
                   SkScalar dpr) override;
 
+  static SkRect ComputeShadowBounds(const SkPath& path,
+                                    float elevation,
+                                    SkScalar dpr,
+                                    const SkMatrix& ctm);
+
   static void DrawShadow(SkCanvas* canvas,
                          const SkPath& path,
                          DlColor color,
diff --git a/display_list/dl_rendering_unittests.cc b/display_list/display_list_canvas_unittests.cc
similarity index 85%
rename from display_list/dl_rendering_unittests.cc
rename to display_list/display_list_canvas_unittests.cc
index 4f40d1f..a6aed32 100644
--- a/display_list/dl_rendering_unittests.cc
+++ b/display_list/display_list_canvas_unittests.cc
@@ -6,16 +6,15 @@
 
 #include "flutter/display_list/display_list.h"
 #include "flutter/display_list/display_list_builder.h"
+#include "flutter/display_list/display_list_canvas_dispatcher.h"
 #include "flutter/display_list/display_list_comparable.h"
 #include "flutter/display_list/display_list_flags.h"
 #include "flutter/display_list/display_list_sampling_options.h"
 #include "flutter/display_list/skia/dl_sk_canvas.h"
-#include "flutter/display_list/skia/dl_sk_dispatcher.h"
 #include "flutter/display_list/testing/dl_test_surface_provider.h"
 #include "flutter/fml/math.h"
 #include "flutter/testing/display_list_testing.h"
 #include "flutter/testing/testing.h"
-#include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/core/SkPictureRecorder.h"
 #include "third_party/skia/include/core/SkSurface.h"
 
@@ -136,18 +135,6 @@
     (sizeof(kHorizontalMiterDiamondPoints) /
      sizeof(kHorizontalMiterDiamondPoints[0]));
 
-class SkImageSampling {
- public:
-  static constexpr SkSamplingOptions kNearestNeighbor =
-      SkSamplingOptions(SkFilterMode::kNearest);
-  static constexpr SkSamplingOptions kLinear =
-      SkSamplingOptions(SkFilterMode::kLinear);
-  static constexpr SkSamplingOptions kMipmapLinear =
-      SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
-  static constexpr SkSamplingOptions kCubic =
-      SkSamplingOptions(SkCubicResampler{1 / 3.0f, 1 / 3.0f});
-};
-
 // A class to specify how much tolerance to allow in bounds estimates.
 // For some attributes, the machinery must make some conservative
 // assumptions as to the extent of the bounds, but some of our test
@@ -422,7 +409,7 @@
       : display_list_(std::move(display_list)) {}
 
   void Render(SkCanvas* canvas, const RenderJobInfo& info) {
-    DlSkCanvasAdapter(canvas).DrawDisplayList(display_list_, info.opacity);
+    display_list_->RenderTo(canvas, info.opacity);
   }
 
  private:
@@ -1011,12 +998,12 @@
           RenderEnvironment::MakeN32(env.provider());
       SkSetup sk_backdrop_setup = [=](SkCanvas* cv, SkPaint& p) {
         SkPaint setup_p;
-        setup_p.setShader(kTestSkImageColorSource);
+        setup_p.setShader(kTestImageColorSource.skia_object());
         cv->drawPaint(setup_p);
       };
       DlSetup dl_backdrop_setup = [=](DlCanvas* cv, DlPaint& p) {
         DlPaint setup_p;
-        setup_p.setColorSource(&kTestDlImageColorSource);
+        setup_p.setColorSource(&kTestImageColorSource);
         cv->DrawPaint(setup_p);
       };
       SkSetup sk_content_setup = [=](SkCanvas* cv, SkPaint& p) {
@@ -1029,39 +1016,38 @@
                             dl_backdrop_setup, testP.dl_renderer());
       quickCompareToReference(backdrop_env, "backdrop");
 
-      DlBlurImageFilter dl_backdrop(5, 5, DlTileMode::kDecal);
-      auto sk_backdrop =
-          SkImageFilters::Blur(5, 5, SkTileMode::kDecal, nullptr);
+      DlBlurImageFilter backdrop(5, 5, DlTileMode::kDecal);
       RenderWith(testP, backdrop_env, tolerance,
                  CaseParameters(
                      "saveLayer with backdrop",
                      [=](SkCanvas* cv, SkPaint& p) {
                        sk_backdrop_setup(cv, p);
                        cv->saveLayer(SkCanvas::SaveLayerRec(
-                           nullptr, nullptr, sk_backdrop.get(), 0));
+                           nullptr, nullptr, backdrop.skia_object().get(), 0));
                        sk_content_setup(cv, p);
                      },
                      [=](DlCanvas* cv, DlPaint& p) {
                        dl_backdrop_setup(cv, p);
-                       cv->SaveLayer(nullptr, nullptr, &dl_backdrop);
+                       cv->SaveLayer(nullptr, nullptr, &backdrop);
                        dl_content_setup(cv, p);
                      })
                      .with_restore(sk_safe_restore, dl_safe_restore, true));
-      RenderWith(testP, backdrop_env, tolerance,
-                 CaseParameters(
-                     "saveLayer with bounds and backdrop",
-                     [=](SkCanvas* cv, SkPaint& p) {
-                       sk_backdrop_setup(cv, p);
-                       cv->saveLayer(SkCanvas::SaveLayerRec(
-                           &layer_bounds, nullptr, sk_backdrop.get(), 0));
-                       sk_content_setup(cv, p);
-                     },
-                     [=](DlCanvas* cv, DlPaint& p) {
-                       dl_backdrop_setup(cv, p);
-                       cv->SaveLayer(&layer_bounds, nullptr, &dl_backdrop);
-                       dl_content_setup(cv, p);
-                     })
-                     .with_restore(sk_safe_restore, dl_safe_restore, true));
+      RenderWith(
+          testP, backdrop_env, tolerance,
+          CaseParameters(
+              "saveLayer with bounds and backdrop",
+              [=](SkCanvas* cv, SkPaint& p) {
+                sk_backdrop_setup(cv, p);
+                cv->saveLayer(SkCanvas::SaveLayerRec(
+                    &layer_bounds, nullptr, backdrop.skia_object().get(), 0));
+                sk_content_setup(cv, p);
+              },
+              [=](DlCanvas* cv, DlPaint& p) {
+                dl_backdrop_setup(cv, p);
+                cv->SaveLayer(&layer_bounds, nullptr, &backdrop);
+                dl_content_setup(cv, p);
+              })
+              .with_restore(sk_safe_restore, dl_safe_restore, true));
       RenderWith(testP, backdrop_env, tolerance,
                  CaseParameters(
                      "clipped saveLayer with backdrop",
@@ -1069,13 +1055,13 @@
                        sk_backdrop_setup(cv, p);
                        cv->clipRect(layer_bounds);
                        cv->saveLayer(SkCanvas::SaveLayerRec(
-                           nullptr, nullptr, sk_backdrop.get(), 0));
+                           nullptr, nullptr, backdrop.skia_object().get(), 0));
                        sk_content_setup(cv, p);
                      },
                      [=](DlCanvas* cv, DlPaint& p) {
                        dl_backdrop_setup(cv, p);
                        cv->ClipRect(layer_bounds);
-                       cv->SaveLayer(nullptr, nullptr, &dl_backdrop);
+                       cv->SaveLayer(nullptr, nullptr, &backdrop);
                        dl_content_setup(cv, p);
                      })
                      .with_restore(sk_safe_restore, dl_safe_restore, true));
@@ -1090,22 +1076,20 @@
           0, 0, 0, 0.5, 0,
       };
       // clang-format on
-      DlMatrixColorFilter dl_alpha_rotate_filter(rotate_alpha_color_matrix);
-      auto sk_alpha_rotate_filter =
-          SkColorFilters::Matrix(rotate_alpha_color_matrix);
+      DlMatrixColorFilter filter(rotate_alpha_color_matrix);
       {
         RenderWith(testP, env, tolerance,
                    CaseParameters(
                        "saveLayer ColorFilter, no bounds",
                        [=](SkCanvas* cv, SkPaint& p) {
                          SkPaint save_p;
-                         save_p.setColorFilter(sk_alpha_rotate_filter);
+                         save_p.setColorFilter(filter.skia_object());
                          cv->saveLayer(nullptr, &save_p);
                          p.setStrokeWidth(5.0);
                        },
                        [=](DlCanvas* cv, DlPaint& p) {
                          DlPaint save_p;
-                         save_p.setColorFilter(&dl_alpha_rotate_filter);
+                         save_p.setColorFilter(&filter);
                          cv->SaveLayer(nullptr, &save_p);
                          p.setStrokeWidth(5.0);
                        })
@@ -1117,13 +1101,13 @@
                        "saveLayer ColorFilter and bounds",
                        [=](SkCanvas* cv, SkPaint& p) {
                          SkPaint save_p;
-                         save_p.setColorFilter(sk_alpha_rotate_filter);
+                         save_p.setColorFilter(filter.skia_object());
                          cv->saveLayer(kRenderBounds, &save_p);
                          p.setStrokeWidth(5.0);
                        },
                        [=](DlCanvas* cv, DlPaint& p) {
                          DlPaint save_p;
-                         save_p.setColorFilter(&dl_alpha_rotate_filter);
+                         save_p.setColorFilter(&filter);
                          cv->SaveLayer(&kRenderBounds, &save_p);
                          p.setStrokeWidth(5.0);
                        })
@@ -1140,23 +1124,21 @@
           0, 0, 0, 1, 0,
       };
       // clang-format on
-      DlMatrixColorFilter dl_color_filter(color_matrix);
-      DlColorFilterImageFilter dl_cf_image_filter(dl_color_filter);
-      auto sk_cf_image_filter = SkImageFilters::ColorFilter(
-          SkColorFilters::Matrix(color_matrix), nullptr);
+      DlMatrixColorFilter color_filter(color_matrix);
+      DlColorFilterImageFilter filter(color_filter);
       {
         RenderWith(testP, env, tolerance,
                    CaseParameters(
                        "saveLayer ImageFilter, no bounds",
                        [=](SkCanvas* cv, SkPaint& p) {
                          SkPaint save_p;
-                         save_p.setImageFilter(sk_cf_image_filter);
+                         save_p.setImageFilter(filter.skia_object());
                          cv->saveLayer(nullptr, &save_p);
                          p.setStrokeWidth(5.0);
                        },
                        [=](DlCanvas* cv, DlPaint& p) {
                          DlPaint save_p;
-                         save_p.setImageFilter(&dl_cf_image_filter);
+                         save_p.setImageFilter(&filter);
                          cv->SaveLayer(nullptr, &save_p);
                          p.setStrokeWidth(5.0);
                        })
@@ -1168,13 +1150,13 @@
                        "saveLayer ImageFilter and bounds",
                        [=](SkCanvas* cv, SkPaint& p) {
                          SkPaint save_p;
-                         save_p.setImageFilter(sk_cf_image_filter);
+                         save_p.setImageFilter(filter.skia_object());
                          cv->saveLayer(kRenderBounds, &save_p);
                          p.setStrokeWidth(5.0);
                        },
                        [=](DlCanvas* cv, DlPaint& p) {
                          DlPaint save_p;
-                         save_p.setImageFilter(&dl_cf_image_filter);
+                         save_p.setImageFilter(&filter);
                          cv->SaveLayer(&kRenderBounds, &save_p);
                          p.setStrokeWidth(5.0);
                        })
@@ -1243,12 +1225,12 @@
       } else {
         DlColor dither_bg = DlColor::kBlack();
         SkSetup sk_dither_setup = [=](SkCanvas*, SkPaint& p) {
-          p.setShader(kTestSkImageColorSource);
+          p.setShader(kTestImageColorSource.skia_object());
           p.setAlpha(0xf0);
           p.setStrokeWidth(5.0);
         };
         DlSetup dl_dither_setup = [=](DlCanvas*, DlPaint& p) {
-          p.setColorSource(&kTestDlImageColorSource);
+          p.setColorSource(&kTestImageColorSource);
           p.setAlpha(0xf0);
           p.setStrokeWidth(5.0);
         };
@@ -1334,19 +1316,17 @@
       // (for drawPaint) so we create a new environment for these tests.
       RenderEnvironment blur_env = RenderEnvironment::MakeN32(env.provider());
       SkSetup sk_blur_setup = [=](SkCanvas*, SkPaint& p) {
-        p.setShader(kTestSkImageColorSource);
+        p.setShader(kTestImageColorSource.skia_object());
         p.setStrokeWidth(5.0);
       };
       DlSetup dl_blur_setup = [=](DlCanvas*, DlPaint& p) {
-        p.setColorSource(&kTestDlImageColorSource);
+        p.setColorSource(&kTestImageColorSource);
         p.setStrokeWidth(5.0);
       };
       blur_env.init_ref(sk_blur_setup, testP.sk_renderer(),  //
                         dl_blur_setup, testP.dl_renderer());
       quickCompareToReference(blur_env, "blur");
-      DlBlurImageFilter dl_filter_decal_5(5.0, 5.0, DlTileMode::kDecal);
-      auto sk_filter_decal_5 =
-          SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr);
+      DlBlurImageFilter filter_decal_5(5.0, 5.0, DlTileMode::kDecal);
       BoundsTolerance blur_5_tolerance = tolerance.addBoundsPadding(4, 4);
       {
         RenderWith(testP, blur_env, blur_5_tolerance,
@@ -1354,27 +1334,25 @@
                        "ImageFilter == Decal Blur 5",
                        [=](SkCanvas* cv, SkPaint& p) {
                          sk_blur_setup(cv, p);
-                         p.setImageFilter(sk_filter_decal_5);
+                         p.setImageFilter(filter_decal_5.skia_object());
                        },
                        [=](DlCanvas* cv, DlPaint& p) {
                          dl_blur_setup(cv, p);
-                         p.setImageFilter(&dl_filter_decal_5);
+                         p.setImageFilter(&filter_decal_5);
                        }));
       }
-      DlBlurImageFilter dl_filter_clamp_5(5.0, 5.0, DlTileMode::kClamp);
-      auto sk_filter_clamp_5 =
-          SkImageFilters::Blur(5.0, 5.0, SkTileMode::kClamp, nullptr);
+      DlBlurImageFilter filter_clamp_5(5.0, 5.0, DlTileMode::kClamp);
       {
         RenderWith(testP, blur_env, blur_5_tolerance,
                    CaseParameters(
                        "ImageFilter == Clamp Blur 5",
                        [=](SkCanvas* cv, SkPaint& p) {
                          sk_blur_setup(cv, p);
-                         p.setImageFilter(sk_filter_clamp_5);
+                         p.setImageFilter(filter_clamp_5.skia_object());
                        },
                        [=](DlCanvas* cv, DlPaint& p) {
                          dl_blur_setup(cv, p);
-                         p.setImageFilter(&dl_filter_clamp_5);
+                         p.setImageFilter(&filter_clamp_5);
                        }));
       }
     }
@@ -1385,28 +1363,27 @@
       // (for drawPaint) so we create a new environment for these tests.
       RenderEnvironment dilate_env = RenderEnvironment::MakeN32(env.provider());
       SkSetup sk_dilate_setup = [=](SkCanvas*, SkPaint& p) {
-        p.setShader(kTestSkImageColorSource);
+        p.setShader(kTestImageColorSource.skia_object());
         p.setStrokeWidth(5.0);
       };
       DlSetup dl_dilate_setup = [=](DlCanvas*, DlPaint& p) {
-        p.setColorSource(&kTestDlImageColorSource);
+        p.setColorSource(&kTestImageColorSource);
         p.setStrokeWidth(5.0);
       };
       dilate_env.init_ref(sk_dilate_setup, testP.sk_renderer(),  //
                           dl_dilate_setup, testP.dl_renderer());
       quickCompareToReference(dilate_env, "dilate");
-      DlDilateImageFilter dl_dilate_filter_5(5.0, 5.0);
-      auto sk_dilate_filter_5 = SkImageFilters::Dilate(5.0, 5.0, nullptr);
+      DlDilateImageFilter filter_5(5.0, 5.0);
       RenderWith(testP, dilate_env, tolerance,
                  CaseParameters(
                      "ImageFilter == Dilate 5",
                      [=](SkCanvas* cv, SkPaint& p) {
                        sk_dilate_setup(cv, p);
-                       p.setImageFilter(sk_dilate_filter_5);
+                       p.setImageFilter(filter_5.skia_object());
                      },
                      [=](DlCanvas* cv, DlPaint& p) {
                        dl_dilate_setup(cv, p);
-                       p.setImageFilter(&dl_dilate_filter_5);
+                       p.setImageFilter(&filter_5);
                      }));
     }
 
@@ -1416,11 +1393,11 @@
       // (for drawPaint) so we create a new environment for these tests.
       RenderEnvironment erode_env = RenderEnvironment::MakeN32(env.provider());
       SkSetup sk_erode_setup = [=](SkCanvas*, SkPaint& p) {
-        p.setShader(kTestSkImageColorSource);
+        p.setShader(kTestImageColorSource.skia_object());
         p.setStrokeWidth(6.0);
       };
       DlSetup dl_erode_setup = [=](DlCanvas*, DlPaint& p) {
-        p.setColorSource(&kTestDlImageColorSource);
+        p.setColorSource(&kTestImageColorSource);
         p.setStrokeWidth(6.0);
       };
       erode_env.init_ref(sk_erode_setup, testP.sk_renderer(),  //
@@ -1428,18 +1405,17 @@
       quickCompareToReference(erode_env, "erode");
       // do not erode too much, because some tests assert there are enough
       // pixels that are changed.
-      DlErodeImageFilter dl_erode_filter_1(1.0, 1.0);
-      auto sk_erode_filter_1 = SkImageFilters::Erode(1.0, 1.0, nullptr);
+      DlErodeImageFilter filter_1(1.0, 1.0);
       RenderWith(testP, erode_env, tolerance,
                  CaseParameters(
                      "ImageFilter == Erode 1",
                      [=](SkCanvas* cv, SkPaint& p) {
                        sk_erode_setup(cv, p);
-                       p.setImageFilter(sk_erode_filter_1);
+                       p.setImageFilter(filter_1.skia_object());
                      },
                      [=](DlCanvas* cv, DlPaint& p) {
                        dl_erode_setup(cv, p);
-                       p.setImageFilter(&dl_erode_filter_1);
+                       p.setImageFilter(&filter_1);
                      }));
     }
 
@@ -1458,8 +1434,7 @@
          1.0,  1.0,  1.0, 1.0,   0,
       };
       // clang-format on
-      DlMatrixColorFilter dl_color_filter(rotate_color_matrix);
-      auto sk_color_filter = SkColorFilters::Matrix(rotate_color_matrix);
+      DlMatrixColorFilter filter(rotate_color_matrix);
       {
         DlColor bg = DlColor::kWhite();
         RenderWith(testP, env, tolerance,
@@ -1467,35 +1442,34 @@
                        "ColorFilter == RotateRGB",
                        [=](SkCanvas*, SkPaint& p) {
                          p.setColor(DlColor::kYellow());
-                         p.setColorFilter(sk_color_filter);
+                         p.setColorFilter(filter.skia_object());
                        },
                        [=](DlCanvas*, DlPaint& p) {
                          p.setColor(DlColor::kYellow());
-                         p.setColorFilter(&dl_color_filter);
+                         p.setColorFilter(&filter);
                        })
                        .with_bg(bg));
       }
+      filter = DlMatrixColorFilter(invert_color_matrix);
       {
         DlColor bg = DlColor::kWhite();
-        RenderWith(
-            testP, env, tolerance,
-            CaseParameters(
-                "ColorFilter == Invert",
-                [=](SkCanvas*, SkPaint& p) {
-                  p.setColor(DlColor::kYellow());
-                  p.setColorFilter(SkColorFilters::Matrix(invert_color_matrix));
-                },
-                [=](DlCanvas*, DlPaint& p) {
-                  p.setColor(DlColor::kYellow());
-                  p.setInvertColors(true);
-                })
-                .with_bg(bg));
+        RenderWith(testP, env, tolerance,
+                   CaseParameters(
+                       "ColorFilter == Invert",
+                       [=](SkCanvas*, SkPaint& p) {
+                         p.setColor(DlColor::kYellow());
+                         p.setColorFilter(filter.skia_object());
+                       },
+                       [=](DlCanvas*, DlPaint& p) {
+                         p.setColor(DlColor::kYellow());
+                         p.setInvertColors(true);
+                       })
+                       .with_bg(bg));
       }
     }
 
     {
-      const DlBlurMaskFilter dl_mask_filter(kNormal_SkBlurStyle, 5.0);
-      auto sk_mask_filter = SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5.0);
+      const DlBlurMaskFilter filter(kNormal_SkBlurStyle, 5.0);
       BoundsTolerance blur_5_tolerance = tolerance.addBoundsPadding(4, 4);
       {
         // Stroked primitives need some non-trivial stroke size to be blurred
@@ -1504,11 +1478,11 @@
                        "MaskFilter == Blur 5",
                        [=](SkCanvas*, SkPaint& p) {
                          p.setStrokeWidth(5.0);
-                         p.setMaskFilter(sk_mask_filter);
+                         p.setMaskFilter(filter.skia_object());
                        },
                        [=](DlCanvas*, DlPaint& p) {
                          p.setStrokeWidth(5.0);
-                         p.setMaskFilter(&dl_mask_filter);
+                         p.setMaskFilter(&filter);
                        }));
       }
     }
@@ -1518,33 +1492,27 @@
           SkPoint::Make(kRenderBounds.fLeft, kRenderBounds.fTop),
           SkPoint::Make(kRenderBounds.fRight, kRenderBounds.fBottom),
       };
-      DlColor dl_colors[] = {
+      DlColor colors[] = {
           DlColor::kGreen(),
           DlColor::kYellow().withAlpha(0x7f),
           DlColor::kBlue(),
       };
-      SkColor sk_colors[] = {
-          SK_ColorGREEN,
-          SkColorSetA(SK_ColorYELLOW, 0x7f),
-          SK_ColorBLUE,
-      };
       float stops[] = {
           0.0,
           0.5,
           1.0,
       };
-      auto dl_gradient =
-          DlColorSource::MakeLinear(end_points[0], end_points[1], 3, dl_colors,
-                                    stops, DlTileMode::kMirror);
-      auto sk_gradient = SkGradientShader::MakeLinear(
-          end_points, sk_colors, stops, 3, SkTileMode::kMirror, 0, nullptr);
+      std::shared_ptr<DlColorSource> source = DlColorSource::MakeLinear(
+          end_points[0], end_points[1], 3, colors, stops, DlTileMode::kMirror);
       {
         RenderWith(
             testP, env, tolerance,
             CaseParameters(
                 "LinearGradient GYB",
-                [=](SkCanvas*, SkPaint& p) { p.setShader(sk_gradient); },
-                [=](DlCanvas*, DlPaint& p) { p.setColorSource(dl_gradient); }));
+                [=](SkCanvas*, SkPaint& p) {
+                  p.setShader(source->skia_object());
+                },
+                [=](DlCanvas*, DlPaint& p) { p.setColorSource(source); }));
       }
     }
   }
@@ -1729,8 +1697,7 @@
     {
       const SkScalar test_dashes_1[] = {29.0, 2.0};
       const SkScalar test_dashes_2[] = {17.0, 1.5};
-      auto dl_dash_effect = DlDashPathEffect::Make(test_dashes_1, 2, 0.0f);
-      auto sk_dash_effect = SkDashPathEffect::Make(test_dashes_1, 2, 0.0f);
+      auto effect = DlDashPathEffect::Make(test_dashes_1, 2, 0.0f);
       {
         RenderWith(testP, stroke_base_env, tolerance,
                    CaseParameters(
@@ -1738,12 +1705,12 @@
                        [=](SkCanvas*, SkPaint& p) {
                          // Provide some non-trivial stroke size to get dashed
                          p.setStrokeWidth(5.0);
-                         p.setPathEffect(sk_dash_effect);
+                         p.setPathEffect(effect->skia_object());
                        },
                        [=](DlCanvas*, DlPaint& p) {
                          // Provide some non-trivial stroke size to get dashed
                          p.setStrokeWidth(5.0);
-                         p.setPathEffect(dl_dash_effect);
+                         p.setPathEffect(effect);
                        }));
       }
       {
@@ -1755,18 +1722,17 @@
                          p.setStyle(SkPaint::kStroke_Style);
                          // Provide some non-trivial stroke size to get dashed
                          p.setStrokeWidth(5.0);
-                         p.setPathEffect(sk_dash_effect);
+                         p.setPathEffect(effect->skia_object());
                        },
                        [=](DlCanvas*, DlPaint& p) {
                          // Need stroke style to see dashing properly
                          p.setDrawStyle(DlDrawStyle::kStroke);
                          // Provide some non-trivial stroke size to get dashed
                          p.setStrokeWidth(5.0);
-                         p.setPathEffect(dl_dash_effect);
+                         p.setPathEffect(effect);
                        }));
       }
-      dl_dash_effect = DlDashPathEffect::Make(test_dashes_2, 2, 0.0f);
-      sk_dash_effect = SkDashPathEffect::Make(test_dashes_2, 2, 0.0f);
+      effect = DlDashPathEffect::Make(test_dashes_2, 2, 0.0f);
       {
         RenderWith(testP, stroke_base_env, tolerance,
                    CaseParameters(
@@ -1776,14 +1742,14 @@
                          p.setStyle(SkPaint::kStroke_Style);
                          // Provide some non-trivial stroke size to get dashed
                          p.setStrokeWidth(5.0);
-                         p.setPathEffect(sk_dash_effect);
+                         p.setPathEffect(effect->skia_object());
                        },
                        [=](DlCanvas*, DlPaint& p) {
                          // Need stroke style to see dashing properly
                          p.setDrawStyle(DlDrawStyle::kStroke);
                          // Provide some non-trivial stroke size to get dashed
                          p.setStrokeWidth(5.0);
-                         p.setPathEffect(dl_dash_effect);
+                         p.setPathEffect(effect);
                        }));
       }
     }
@@ -2212,23 +2178,6 @@
     ASSERT_GT(pixels_touched, 0) << info;
   }
 
-  static int countModifiedTransparentPixels(const RenderResult* ref_result,
-                                            const RenderResult* test_result) {
-    int count = 0;
-    for (int y = 0; y < kTestHeight; y++) {
-      const uint32_t* ref_row = ref_result->addr32(0, y);
-      const uint32_t* test_row = test_result->addr32(0, y);
-      for (int x = 0; x < kTestWidth; x++) {
-        if (ref_row[x] != test_row[x]) {
-          if (ref_row[x] == 0) {
-            count++;
-          }
-        }
-      }
-    }
-    return count;
-  }
-
   static void quickCompareToReference(const RenderEnvironment& env,
                                       const std::string& info) {
     quickCompareToReference(env.ref_sk_result(), env.ref_dl_result(), true,
@@ -2249,7 +2198,7 @@
       const uint32_t* test_row = test_result->addr32(0, y);
       for (int x = 0; x < w; x++) {
         if (ref_row[x] != test_row[x]) {
-          if (should_match && pixels_different < 5) {
+          if (should_match) {
             FML_LOG(ERROR) << std::hex << ref_row[x] << " != " << test_row[x];
           }
           pixels_different++;
@@ -2309,7 +2258,7 @@
         bool match = fuzzyCompares ? fuzzyCompare(test_row[x], ref_row[x], 1)
                                    : test_row[x] == ref_row[x];
         if (!match) {
-          if (printMismatches && pixels_different < 5) {
+          if (printMismatches) {
             FML_LOG(ERROR) << "pix[" << x << ", " << y
                            << "] mismatch: " << std::hex << test_row[x]
                            << "(test) != (ref)" << ref_row[x] << std::dec;
@@ -2398,8 +2347,7 @@
     return surface->makeImageSnapshot();
   }
 
-  static const DlImageColorSource kTestDlImageColorSource;
-  static const sk_sp<SkShader> kTestSkImageColorSource;
+  static const DlImageColorSource kTestImageColorSource;
 
   static sk_sp<SkTextBlob> MakeTextBlob(const std::string& string,
                                         SkScalar font_height) {
@@ -2417,15 +2365,11 @@
     BoundsTolerance().addAbsolutePadding(1, 1);
 
 const sk_sp<SkImage> CanvasCompareTester::kTestImage = makeTestImage();
-const DlImageColorSource CanvasCompareTester::kTestDlImageColorSource(
+const DlImageColorSource CanvasCompareTester::kTestImageColorSource(
     DlImage::Make(kTestImage),
     DlTileMode::kRepeat,
     DlTileMode::kRepeat,
     DlImageSampling::kLinear);
-const sk_sp<SkShader> CanvasCompareTester::kTestSkImageColorSource =
-    kTestImage->makeShader(SkTileMode::kRepeat,
-                           SkTileMode::kRepeat,
-                           SkImageSampling::kLinear);
 
 // Eventually this bare bones testing::Test fixture will subsume the
 // CanvasCompareTester and the TestParameters could then become just
@@ -2926,27 +2870,21 @@
       SkPoint::Make(kRenderLeft, kRenderCenterY),
       SkPoint::Make(kRenderRight, kRenderBottom),
   };
-  const DlColor dl_colors[6] = {
-      DlColor::kRed(),  DlColor::kBlue(),   DlColor::kGreen(),
-      DlColor::kCyan(), DlColor::kYellow(), DlColor::kMagenta(),
-  };
-  const SkColor sk_colors[6] = {
+  const DlColor colors[6] = {
       SK_ColorRED,  SK_ColorBLUE,   SK_ColorGREEN,
       SK_ColorCYAN, SK_ColorYELLOW, SK_ColorMAGENTA,
   };
-  const std::shared_ptr<DlVertices> dl_vertices =
-      DlVertices::Make(DlVertexMode::kTriangles, 6, pts, nullptr, dl_colors);
-  const auto sk_vertices =
-      SkVertices::MakeCopy(SkVertices::VertexMode::kTriangles_VertexMode, 6,
-                           pts, nullptr, sk_colors);
+  const std::shared_ptr<DlVertices> vertices =
+      DlVertices::Make(DlVertexMode::kTriangles, 6, pts, nullptr, colors);
 
   CanvasCompareTester::RenderAll(  //
       TestParameters(
           [=](SkCanvas* canvas, const SkPaint& paint) {  //
-            canvas->drawVertices(sk_vertices, SkBlendMode::kSrcOver, paint);
+            canvas->drawVertices(vertices->skia_object(), SkBlendMode::kSrcOver,
+                                 paint);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {  //
-            canvas->DrawVertices(dl_vertices, DlBlendMode::kSrcOver, paint);
+            canvas->DrawVertices(vertices, DlBlendMode::kSrcOver, paint);
           },
           kDrawVerticesFlags));
 }
@@ -2979,27 +2917,27 @@
       SkPoint::Make(0, 0),
       SkPoint::Make(kRenderWidth, 0),
   };
-  const std::shared_ptr<DlVertices> dl_vertices =
+  const std::shared_ptr<DlVertices> vertices =
       DlVertices::Make(DlVertexMode::kTriangles, 6, pts, tex, nullptr);
-  const auto sk_vertices = SkVertices::MakeCopy(
-      SkVertices::VertexMode::kTriangles_VertexMode, 6, pts, tex, nullptr);
 
   CanvasCompareTester::RenderAll(  //
       TestParameters(
           [=](SkCanvas* canvas, const SkPaint& paint) {  //
             SkPaint v_paint = paint;
             if (v_paint.getShader() == nullptr) {
-              v_paint.setShader(CanvasCompareTester::kTestSkImageColorSource);
+              v_paint.setShader(
+                  CanvasCompareTester::kTestImageColorSource.skia_object());
             }
-            canvas->drawVertices(sk_vertices, SkBlendMode::kSrcOver, v_paint);
+            canvas->drawVertices(vertices->skia_object(), SkBlendMode::kSrcOver,
+                                 v_paint);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {  //
             DlPaint v_paint = paint;
             if (v_paint.getColorSource() == nullptr) {
               v_paint.setColorSource(
-                  &CanvasCompareTester::kTestDlImageColorSource);
+                  &CanvasCompareTester::kTestImageColorSource);
             }
-            canvas->DrawVertices(dl_vertices, DlBlendMode::kSrcOver, v_paint);
+            canvas->DrawVertices(vertices, DlBlendMode::kSrcOver, v_paint);
           },
           kDrawVerticesFlags));
 }
@@ -3010,7 +2948,7 @@
           [=](SkCanvas* canvas, const SkPaint& paint) {         //
             canvas->drawImage(CanvasCompareTester::kTestImage,  //
                               kRenderLeft, kRenderTop,
-                              SkImageSampling::kNearestNeighbor, &paint);
+                              ToSk(DlImageSampling::kNearestNeighbor), &paint);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {
             canvas->DrawImage(DlImage::Make(CanvasCompareTester::kTestImage),
@@ -3026,7 +2964,7 @@
           [=](SkCanvas* canvas, const SkPaint& paint) {         //
             canvas->drawImage(CanvasCompareTester::kTestImage,  //
                               kRenderLeft, kRenderTop,
-                              SkImageSampling::kNearestNeighbor, nullptr);
+                              ToSk(DlImageSampling::kNearestNeighbor), nullptr);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {
             canvas->DrawImage(DlImage::Make(CanvasCompareTester::kTestImage),
@@ -3041,8 +2979,8 @@
       TestParameters(
           [=](SkCanvas* canvas, const SkPaint& paint) {         //
             canvas->drawImage(CanvasCompareTester::kTestImage,  //
-                              kRenderLeft, kRenderTop, SkImageSampling::kLinear,
-                              &paint);
+                              kRenderLeft, kRenderTop,
+                              ToSk(DlImageSampling::kLinear), &paint);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {
             canvas->DrawImage(DlImage::Make(CanvasCompareTester::kTestImage),
@@ -3059,14 +2997,13 @@
       TestParameters(
           [=](SkCanvas* canvas, const SkPaint& paint) {  //
             canvas->drawImageRect(CanvasCompareTester::kTestImage, src, dst,
-                                  SkImageSampling::kNearestNeighbor, &paint,
-                                  SkCanvas::kFast_SrcRectConstraint);
+                                  ToSk(DlImageSampling::kNearestNeighbor),
+                                  &paint, SkCanvas::kFast_SrcRectConstraint);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {  //
             canvas->DrawImageRect(
                 DlImage::Make(CanvasCompareTester::kTestImage), src, dst,
-                DlImageSampling::kNearestNeighbor, &paint,
-                DlCanvas::SrcRectConstraint::kFast);
+                DlImageSampling::kNearestNeighbor, &paint, false);
           },
           kDrawImageRectWithPaintFlags));
 }
@@ -3078,14 +3015,13 @@
       TestParameters(
           [=](SkCanvas* canvas, const SkPaint& paint) {  //
             canvas->drawImageRect(CanvasCompareTester::kTestImage, src, dst,
-                                  SkImageSampling::kNearestNeighbor, nullptr,
-                                  SkCanvas::kFast_SrcRectConstraint);
+                                  ToSk(DlImageSampling::kNearestNeighbor),
+                                  nullptr, SkCanvas::kFast_SrcRectConstraint);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {  //
             canvas->DrawImageRect(
                 DlImage::Make(CanvasCompareTester::kTestImage), src, dst,
-                DlImageSampling::kNearestNeighbor, nullptr,
-                DlCanvas::SrcRectConstraint::kFast);
+                DlImageSampling::kNearestNeighbor, nullptr, false);
           },
           kDrawImageRectFlags));
 }
@@ -3097,14 +3033,13 @@
       TestParameters(
           [=](SkCanvas* canvas, const SkPaint& paint) {  //
             canvas->drawImageRect(CanvasCompareTester::kTestImage, src, dst,
-                                  SkImageSampling::kLinear, &paint,
+                                  ToSk(DlImageSampling::kLinear), &paint,
                                   SkCanvas::kFast_SrcRectConstraint);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {  //
             canvas->DrawImageRect(
                 DlImage::Make(CanvasCompareTester::kTestImage), src, dst,
-                DlImageSampling::kLinear, &paint,
-                DlCanvas::SrcRectConstraint::kFast);
+                DlImageSampling::kLinear, &paint, false);
           },
           kDrawImageRectWithPaintFlags));
 }
@@ -3190,19 +3125,17 @@
       DlColor::kMagenta(),
   };
   const sk_sp<SkImage> image = CanvasCompareTester::kTestImage;
-  const DlImageSampling dl_sampling = DlImageSampling::kNearestNeighbor;
-  const SkSamplingOptions sk_sampling = SkImageSampling::kNearestNeighbor;
+  const DlImageSampling sampling = DlImageSampling::kNearestNeighbor;
   CanvasCompareTester::RenderAll(  //
       TestParameters(
           [=](SkCanvas* canvas, const SkPaint& paint) {
             canvas->drawAtlas(image.get(), xform, tex, sk_colors, 4,
-                              SkBlendMode::kSrcOver, sk_sampling, nullptr,
+                              SkBlendMode::kSrcOver, ToSk(sampling), nullptr,
                               &paint);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {
             canvas->DrawAtlas(DlImage::Make(image), xform, tex, dl_colors, 4,
-                              DlBlendMode::kSrcOver, dl_sampling, nullptr,
-                              &paint);
+                              DlBlendMode::kSrcOver, sampling, nullptr, &paint);
           },
           kDrawAtlasWithPaintFlags));
 }
@@ -3237,18 +3170,17 @@
       DlColor::kMagenta(),
   };
   const sk_sp<SkImage> image = CanvasCompareTester::kTestImage;
-  const DlImageSampling dl_sampling = DlImageSampling::kNearestNeighbor;
-  const SkSamplingOptions sk_sampling = SkImageSampling::kNearestNeighbor;
+  const DlImageSampling sampling = DlImageSampling::kNearestNeighbor;
   CanvasCompareTester::RenderAll(  //
       TestParameters(
           [=](SkCanvas* canvas, const SkPaint& paint) {
             canvas->drawAtlas(image.get(), xform, tex, sk_colors, 4,
-                              SkBlendMode::kSrcOver, sk_sampling,  //
+                              SkBlendMode::kSrcOver, ToSk(sampling),  //
                               nullptr, nullptr);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {
             canvas->DrawAtlas(DlImage::Make(image), xform, tex, dl_colors, 4,
-                              DlBlendMode::kSrcOver, dl_sampling, nullptr,
+                              DlBlendMode::kSrcOver, sampling, nullptr,
                               nullptr);
           },
           kDrawAtlasFlags));
@@ -3284,39 +3216,34 @@
       DlColor::kMagenta(),
   };
   const sk_sp<SkImage> image = CanvasCompareTester::kTestImage;
-  const DlImageSampling dl_sampling = DlImageSampling::kLinear;
-  const SkSamplingOptions sk_sampling = SkImageSampling::kLinear;
+  const DlImageSampling sampling = DlImageSampling::kLinear;
   CanvasCompareTester::RenderAll(  //
       TestParameters(
           [=](SkCanvas* canvas, const SkPaint& paint) {
             canvas->drawAtlas(image.get(), xform, tex, sk_colors, 2,  //
-                              SkBlendMode::kSrcOver, sk_sampling, nullptr,
+                              SkBlendMode::kSrcOver, ToSk(sampling), nullptr,
                               &paint);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {
             canvas->DrawAtlas(DlImage::Make(image), xform, tex, dl_colors, 2,
-                              DlBlendMode::kSrcOver, dl_sampling, nullptr,
-                              &paint);
+                              DlBlendMode::kSrcOver, sampling, nullptr, &paint);
           },
           kDrawAtlasWithPaintFlags));
 }
 
 sk_sp<DisplayList> makeTestDisplayList() {
   DisplayListBuilder builder;
-  DlPaint paint;
-  paint.setDrawStyle(DlDrawStyle::kFill);
-  paint.setColor(SK_ColorRED);
-  builder.DrawRect({kRenderLeft, kRenderTop, kRenderCenterX, kRenderCenterY},
-                   paint);
-  paint.setColor(SK_ColorBLUE);
-  builder.DrawRect({kRenderCenterX, kRenderTop, kRenderRight, kRenderCenterY},
-                   paint);
-  paint.setColor(SK_ColorGREEN);
-  builder.DrawRect({kRenderLeft, kRenderCenterY, kRenderCenterX, kRenderBottom},
-                   paint);
-  paint.setColor(SK_ColorYELLOW);
-  builder.DrawRect(
-      {kRenderCenterX, kRenderCenterY, kRenderRight, kRenderBottom}, paint);
+  builder.setStyle(DlDrawStyle::kFill);
+  builder.setColor(SK_ColorRED);
+  builder.drawRect({kRenderLeft, kRenderTop, kRenderCenterX, kRenderCenterY});
+  builder.setColor(SK_ColorBLUE);
+  builder.drawRect({kRenderCenterX, kRenderTop, kRenderRight, kRenderCenterY});
+  builder.setColor(SK_ColorGREEN);
+  builder.drawRect(
+      {kRenderLeft, kRenderCenterY, kRenderCenterX, kRenderBottom});
+  builder.setColor(SK_ColorYELLOW);
+  builder.drawRect(
+      {kRenderCenterX, kRenderCenterY, kRenderRight, kRenderBottom});
   return builder.Build();
 }
 
@@ -3325,7 +3252,7 @@
   CanvasCompareTester::RenderAll(  //
       TestParameters(
           [=](SkCanvas* canvas, const SkPaint& paint) {  //
-            DlSkCanvasAdapter(canvas).DrawDisplayList(display_list);
+            display_list->RenderTo(canvas);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {  //
             canvas->DrawDisplayList(display_list);
@@ -3383,8 +3310,8 @@
   CanvasCompareTester::RenderAll(  //
       TestParameters(
           [=](SkCanvas* canvas, const SkPaint& paint) {  //
-            DlSkCanvasDispatcher::DrawShadow(canvas, path, color, elevation,
-                                             false, 1.0);
+            DisplayListCanvasDispatcher::DrawShadow(canvas, path, color,
+                                                    elevation, false, 1.0);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {  //
             canvas->DrawShadow(path, color, elevation, false, 1.0);
@@ -3409,8 +3336,8 @@
   CanvasCompareTester::RenderAll(  //
       TestParameters(
           [=](SkCanvas* canvas, const SkPaint& paint) {  //
-            DlSkCanvasDispatcher::DrawShadow(canvas, path, color, elevation,
-                                             true, 1.0);
+            DisplayListCanvasDispatcher::DrawShadow(canvas, path, color,
+                                                    elevation, true, 1.0);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {  //
             canvas->DrawShadow(path, color, elevation, true, 1.0);
@@ -3435,8 +3362,8 @@
   CanvasCompareTester::RenderAll(  //
       TestParameters(
           [=](SkCanvas* canvas, const SkPaint& paint) {  //
-            DlSkCanvasDispatcher::DrawShadow(canvas, path, color, elevation,
-                                             false, 1.5);
+            DisplayListCanvasDispatcher::DrawShadow(canvas, path, color,
+                                                    elevation, false, 1.5);
           },
           [=](DlCanvas* canvas, const DlPaint& paint) {  //
             canvas->DrawShadow(path, color, elevation, false, 1.5);
@@ -3628,317 +3555,5 @@
   }
 }
 
-TEST_F(DisplayListCanvas, MatrixColorFilterModifyTransparencyCheck) {
-  std::vector<std::unique_ptr<RenderEnvironment>> environments;
-  for (auto& provider : CanvasCompareTester::kTestProviders) {
-    auto env = std::make_unique<RenderEnvironment>(
-        provider.get(), PixelFormat::kN32Premul_PixelFormat);
-    environments.push_back(std::move(env));
-  }
-
-  auto test_matrix = [&environments](int element, SkScalar value) {
-    // clang-format off
-    float matrix[] = {
-        1, 0, 0, 0, 0,
-        0, 1, 0, 0, 0,
-        0, 0, 1, 0, 0,
-        0, 0, 0, 1, 0,
-    };
-    // clang-format on
-    std::string desc =
-        "matrix[" + std::to_string(element) + "] = " + std::to_string(value);
-    float original_value = matrix[element];
-    matrix[element] = value;
-    DlMatrixColorFilter filter(matrix);
-    auto dl_filter = DlMatrixColorFilter::Make(matrix);
-    bool is_identity = (dl_filter == nullptr || original_value == value);
-
-    DlPaint paint(0x7f7f7f7f);
-    DlPaint filter_save_paint = DlPaint().setColorFilter(&filter);
-
-    DisplayListBuilder builder1;
-    builder1.Translate(kTestCenter.fX, kTestCenter.fY);
-    builder1.Rotate(45);
-    builder1.Translate(-kTestCenter.fX, -kTestCenter.fY);
-    builder1.DrawRect(kRenderBounds, paint);
-    auto display_list1 = builder1.Build();
-
-    DisplayListBuilder builder2;
-    builder2.Translate(kTestCenter.fX, kTestCenter.fY);
-    builder2.Rotate(45);
-    builder2.Translate(-kTestCenter.fX, -kTestCenter.fY);
-    builder2.SaveLayer(&kTestBounds, &filter_save_paint);
-    builder2.DrawRect(kRenderBounds, paint);
-    builder2.Restore();
-    auto display_list2 = builder2.Build();
-
-    for (auto& env : environments) {
-      auto results1 = env->getResult(display_list1);
-      auto results2 = env->getResult(display_list2);
-      CanvasCompareTester::quickCompareToReference(
-          results1.get(), results2.get(), is_identity,
-          desc + " filter affects rendering");
-      int modified_transparent_pixels =
-          CanvasCompareTester::countModifiedTransparentPixels(results1.get(),
-                                                              results2.get());
-      EXPECT_EQ(filter.modifies_transparent_black(),
-                modified_transparent_pixels != 0)
-          << desc;
-    }
-  };
-
-  // Tests identity (matrix[0] already == 1 in an identity filter)
-  test_matrix(0, 1);
-  // test_matrix(19, 1);
-  for (int i = 0; i < 20; i++) {
-    test_matrix(i, -0.25);
-    test_matrix(i, 0);
-    test_matrix(i, 0.25);
-    test_matrix(i, 1);
-    test_matrix(i, 1.25);
-    test_matrix(i, SK_ScalarNaN);
-    test_matrix(i, SK_ScalarInfinity);
-    test_matrix(i, -SK_ScalarInfinity);
-  }
-}
-
-TEST_F(DisplayListCanvas, MatrixColorFilterOpacityCommuteCheck) {
-  std::vector<std::unique_ptr<RenderEnvironment>> environments;
-  for (auto& provider : CanvasCompareTester::kTestProviders) {
-    auto env = std::make_unique<RenderEnvironment>(
-        provider.get(), PixelFormat::kN32Premul_PixelFormat);
-    environments.push_back(std::move(env));
-  }
-
-  auto test_matrix = [&environments](int element, SkScalar value) {
-    // clang-format off
-    float matrix[] = {
-        1, 0, 0, 0, 0,
-        0, 1, 0, 0, 0,
-        0, 0, 1, 0, 0,
-        0, 0, 0, 1, 0,
-    };
-    // clang-format on
-    std::string desc =
-        "matrix[" + std::to_string(element) + "] = " + std::to_string(value);
-    matrix[element] = value;
-    auto filter = DlMatrixColorFilter::Make(matrix);
-    EXPECT_EQ(SkScalarIsFinite(value), filter != nullptr);
-
-    DlPaint paint(0x80808080);
-    DlPaint opacity_save_paint = DlPaint().setOpacity(0.5);
-    DlPaint filter_save_paint = DlPaint().setColorFilter(filter);
-
-    DisplayListBuilder builder1;
-    builder1.SaveLayer(&kTestBounds, &opacity_save_paint);
-    builder1.SaveLayer(&kTestBounds, &filter_save_paint);
-    // builder1.DrawRect(kRenderBounds.makeOffset(20, 20), DlPaint());
-    builder1.DrawRect(kRenderBounds, paint);
-    builder1.Restore();
-    builder1.Restore();
-    auto display_list1 = builder1.Build();
-
-    DisplayListBuilder builder2;
-    builder2.SaveLayer(&kTestBounds, &filter_save_paint);
-    builder2.SaveLayer(&kTestBounds, &opacity_save_paint);
-    // builder1.DrawRect(kRenderBounds.makeOffset(20, 20), DlPaint());
-    builder2.DrawRect(kRenderBounds, paint);
-    builder2.Restore();
-    builder2.Restore();
-    auto display_list2 = builder2.Build();
-
-    for (auto& env : environments) {
-      auto results1 = env->getResult(display_list1);
-      auto results2 = env->getResult(display_list2);
-      if (!filter || filter->can_commute_with_opacity()) {
-        CanvasCompareTester::compareToReference(
-            results2.get(), results1.get(), desc, nullptr, nullptr,
-            DlColor::kTransparent(), true, kTestWidth, kTestHeight, true);
-      } else {
-        CanvasCompareTester::quickCompareToReference(
-            results1.get(), results2.get(), false, desc);
-      }
-    }
-  };
-
-  // Tests identity (matrix[0] already == 1 in an identity filter)
-  test_matrix(0, 1);
-  // test_matrix(19, 1);
-  for (int i = 0; i < 20; i++) {
-    test_matrix(i, -0.25);
-    test_matrix(i, 0);
-    test_matrix(i, 0.25);
-    test_matrix(i, 1);
-    test_matrix(i, 1.1);
-    test_matrix(i, SK_ScalarNaN);
-    test_matrix(i, SK_ScalarInfinity);
-    test_matrix(i, -SK_ScalarInfinity);
-  }
-}
-
-#define FOR_EACH_BLEND_MODE_ENUM(FUNC) \
-  FUNC(kSrc)                           \
-  FUNC(kClear)                         \
-  FUNC(kSrc)                           \
-  FUNC(kDst)                           \
-  FUNC(kSrcOver)                       \
-  FUNC(kDstOver)                       \
-  FUNC(kSrcIn)                         \
-  FUNC(kDstIn)                         \
-  FUNC(kSrcOut)                        \
-  FUNC(kDstOut)                        \
-  FUNC(kSrcATop)                       \
-  FUNC(kDstATop)                       \
-  FUNC(kXor)                           \
-  FUNC(kPlus)                          \
-  FUNC(kModulate)                      \
-  FUNC(kScreen)                        \
-  FUNC(kOverlay)                       \
-  FUNC(kDarken)                        \
-  FUNC(kLighten)                       \
-  FUNC(kColorDodge)                    \
-  FUNC(kColorBurn)                     \
-  FUNC(kHardLight)                     \
-  FUNC(kSoftLight)                     \
-  FUNC(kDifference)                    \
-  FUNC(kExclusion)                     \
-  FUNC(kMultiply)                      \
-  FUNC(kHue)                           \
-  FUNC(kSaturation)                    \
-  FUNC(kColor)                         \
-  FUNC(kLuminosity)
-
-TEST_F(DisplayListCanvas, BlendColorFilterModifyTransparencyCheck) {
-  std::vector<std::unique_ptr<RenderEnvironment>> environments;
-  for (auto& provider : CanvasCompareTester::kTestProviders) {
-    auto env = std::make_unique<RenderEnvironment>(
-        provider.get(), PixelFormat::kN32Premul_PixelFormat);
-    environments.push_back(std::move(env));
-  }
-
-  auto test_mode_color = [&environments](DlBlendMode mode, DlColor color) {
-    std::stringstream desc_str;
-    desc_str << "blend[" << mode << ", " << color << "]";
-    std::string desc = desc_str.str();
-    DlBlendColorFilter filter(color, mode);
-    if (filter.modifies_transparent_black()) {
-      ASSERT_NE(DlBlendColorFilter::Make(color, mode), nullptr) << desc;
-    }
-
-    DlPaint paint(0x7f7f7f7f);
-    DlPaint filter_save_paint = DlPaint().setColorFilter(&filter);
-
-    DisplayListBuilder builder1;
-    builder1.Translate(kTestCenter.fX, kTestCenter.fY);
-    builder1.Rotate(45);
-    builder1.Translate(-kTestCenter.fX, -kTestCenter.fY);
-    builder1.DrawRect(kRenderBounds, paint);
-    auto display_list1 = builder1.Build();
-
-    DisplayListBuilder builder2;
-    builder2.Translate(kTestCenter.fX, kTestCenter.fY);
-    builder2.Rotate(45);
-    builder2.Translate(-kTestCenter.fX, -kTestCenter.fY);
-    builder2.SaveLayer(&kTestBounds, &filter_save_paint);
-    builder2.DrawRect(kRenderBounds, paint);
-    builder2.Restore();
-    auto display_list2 = builder2.Build();
-
-    for (auto& env : environments) {
-      auto results1 = env->getResult(display_list1);
-      auto results2 = env->getResult(display_list2);
-      int modified_transparent_pixels =
-          CanvasCompareTester::countModifiedTransparentPixels(results1.get(),
-                                                              results2.get());
-      EXPECT_EQ(filter.modifies_transparent_black(),
-                modified_transparent_pixels != 0)
-          << desc;
-    }
-  };
-
-  auto test_mode = [&test_mode_color](DlBlendMode mode) {
-    test_mode_color(mode, DlColor::kTransparent());
-    test_mode_color(mode, DlColor::kWhite());
-    test_mode_color(mode, DlColor::kWhite().modulateOpacity(0.5));
-    test_mode_color(mode, DlColor::kBlack());
-    test_mode_color(mode, DlColor::kBlack().modulateOpacity(0.5));
-  };
-
-#define TEST_MODE(V) test_mode(DlBlendMode::V);
-  FOR_EACH_BLEND_MODE_ENUM(TEST_MODE)
-#undef TEST_MODE
-}
-
-TEST_F(DisplayListCanvas, BlendColorFilterOpacityCommuteCheck) {
-  std::vector<std::unique_ptr<RenderEnvironment>> environments;
-  for (auto& provider : CanvasCompareTester::kTestProviders) {
-    auto env = std::make_unique<RenderEnvironment>(
-        provider.get(), PixelFormat::kN32Premul_PixelFormat);
-    environments.push_back(std::move(env));
-  }
-
-  auto test_mode_color = [&environments](DlBlendMode mode, DlColor color) {
-    std::stringstream desc_str;
-    desc_str << "blend[" << mode << ", " << color << "]";
-    std::string desc = desc_str.str();
-    DlBlendColorFilter filter(color, mode);
-    if (filter.can_commute_with_opacity()) {
-      // If it can commute with opacity, then it might also be a NOP,
-      // so we won't necessarily get a non-null return from |::Make()|
-    } else {
-      ASSERT_NE(DlBlendColorFilter::Make(color, mode), nullptr) << desc;
-    }
-
-    DlPaint paint(0x80808080);
-    DlPaint opacity_save_paint = DlPaint().setOpacity(0.5);
-    DlPaint filter_save_paint = DlPaint().setColorFilter(&filter);
-
-    DisplayListBuilder builder1;
-    builder1.SaveLayer(&kTestBounds, &opacity_save_paint);
-    builder1.SaveLayer(&kTestBounds, &filter_save_paint);
-    // builder1.DrawRect(kRenderBounds.makeOffset(20, 20), DlPaint());
-    builder1.DrawRect(kRenderBounds, paint);
-    builder1.Restore();
-    builder1.Restore();
-    auto display_list1 = builder1.Build();
-
-    DisplayListBuilder builder2;
-    builder2.SaveLayer(&kTestBounds, &filter_save_paint);
-    builder2.SaveLayer(&kTestBounds, &opacity_save_paint);
-    // builder1.DrawRect(kRenderBounds.makeOffset(20, 20), DlPaint());
-    builder2.DrawRect(kRenderBounds, paint);
-    builder2.Restore();
-    builder2.Restore();
-    auto display_list2 = builder2.Build();
-
-    for (auto& env : environments) {
-      auto results1 = env->getResult(display_list1);
-      auto results2 = env->getResult(display_list2);
-      if (filter.can_commute_with_opacity()) {
-        CanvasCompareTester::compareToReference(
-            results2.get(), results1.get(), desc, nullptr, nullptr,
-            DlColor::kTransparent(), true, kTestWidth, kTestHeight, true);
-      } else {
-        CanvasCompareTester::quickCompareToReference(
-            results1.get(), results2.get(), false, desc);
-      }
-    }
-  };
-
-  auto test_mode = [&test_mode_color](DlBlendMode mode) {
-    test_mode_color(mode, DlColor::kTransparent());
-    test_mode_color(mode, DlColor::kWhite());
-    test_mode_color(mode, DlColor::kWhite().modulateOpacity(0.5));
-    test_mode_color(mode, DlColor::kBlack());
-    test_mode_color(mode, DlColor::kBlack().modulateOpacity(0.5));
-  };
-
-#define TEST_MODE(V) test_mode(DlBlendMode::V);
-  FOR_EACH_BLEND_MODE_ENUM(TEST_MODE)
-#undef TEST_MODE
-}
-
-#undef FOR_EACH_ENUM
-
 }  // namespace testing
 }  // namespace flutter
diff --git a/display_list/display_list_color.h b/display_list/display_list_color.h
index 855df42..d84ccd4 100644
--- a/display_list/display_list_color.h
+++ b/display_list/display_list_color.h
@@ -35,7 +35,6 @@
   uint32_t argb;
 
   bool isOpaque() const { return getAlpha() == 0xFF; }
-  bool isTransparent() const { return getAlpha() == 0; }
 
   int getAlpha() const { return argb >> 24; }
   int getRed() const { return (argb >> 16) & 0xFF; }
diff --git a/display_list/display_list_color_filter.cc b/display_list/display_list_color_filter.cc
index 778214e..6f46b02 100644
--- a/display_list/display_list_color_filter.cc
+++ b/display_list/display_list_color_filter.cc
@@ -8,193 +8,16 @@
 
 namespace flutter {
 
-std::shared_ptr<DlColorFilter> DlBlendColorFilter::Make(DlColor color,
-                                                        DlBlendMode mode) {
-  switch (mode) {
-    case DlBlendMode::kDst: {
-      return nullptr;
-    }
-    case DlBlendMode::kSrcOver: {
-      if (color.isTransparent()) {
-        return nullptr;
-      }
-      if (color.isOpaque()) {
-        mode = DlBlendMode::kSrc;
-      }
-      break;
-    }
-    case DlBlendMode::kDstOver:
-    case DlBlendMode::kDstOut:
-    case DlBlendMode::kSrcATop:
-    case DlBlendMode::kXor:
-    case DlBlendMode::kDarken: {
-      if (color.isTransparent()) {
-        return nullptr;
-      }
-      break;
-    }
-    case DlBlendMode::kDstIn: {
-      if (color.isOpaque()) {
-        return nullptr;
-      }
-      break;
-    }
-    default:
-      break;
-  }
-  return std::make_shared<DlBlendColorFilter>(color, mode);
-}
-
-bool DlBlendColorFilter::modifies_transparent_black() const {
-  switch (mode_) {
-    // These modes all act like kSrc when the dest is all 0s.
-    // So they modify transparent black when the src color is
-    // not transparent.
-    case DlBlendMode::kSrc:
-    case DlBlendMode::kSrcOver:
-    case DlBlendMode::kDstOver:
-    case DlBlendMode::kSrcOut:
-    case DlBlendMode::kDstATop:
-    case DlBlendMode::kXor:
-    case DlBlendMode::kPlus:
-    case DlBlendMode::kScreen:
-    case DlBlendMode::kOverlay:
-    case DlBlendMode::kDarken:
-    case DlBlendMode::kLighten:
-    case DlBlendMode::kColorDodge:
-    case DlBlendMode::kColorBurn:
-    case DlBlendMode::kHardLight:
-    case DlBlendMode::kSoftLight:
-    case DlBlendMode::kDifference:
-    case DlBlendMode::kExclusion:
-    case DlBlendMode::kMultiply:
-    case DlBlendMode::kHue:
-    case DlBlendMode::kSaturation:
-    case DlBlendMode::kColor:
-    case DlBlendMode::kLuminosity:
-      return !color_.isTransparent();
-
-    // These modes are all like kDst when the dest is all 0s.
-    // So they never modify transparent black.
-    case DlBlendMode::kClear:
-    case DlBlendMode::kDst:
-    case DlBlendMode::kSrcIn:
-    case DlBlendMode::kDstIn:
-    case DlBlendMode::kDstOut:
-    case DlBlendMode::kSrcATop:
-    case DlBlendMode::kModulate:
-      return false;
-  }
-}
-
-bool DlBlendColorFilter::can_commute_with_opacity() const {
-  switch (mode_) {
-    case DlBlendMode::kClear:
-    case DlBlendMode::kDst:
-    case DlBlendMode::kSrcIn:
-    case DlBlendMode::kDstIn:
-    case DlBlendMode::kDstOut:
-    case DlBlendMode::kSrcATop:
-    case DlBlendMode::kModulate:
-      return true;
-
-    case DlBlendMode::kSrc:
-    case DlBlendMode::kSrcOver:
-    case DlBlendMode::kDstOver:
-    case DlBlendMode::kSrcOut:
-    case DlBlendMode::kDstATop:
-    case DlBlendMode::kXor:
-    case DlBlendMode::kPlus:
-    case DlBlendMode::kScreen:
-    case DlBlendMode::kOverlay:
-    case DlBlendMode::kDarken:
-    case DlBlendMode::kLighten:
-    case DlBlendMode::kColorDodge:
-    case DlBlendMode::kColorBurn:
-    case DlBlendMode::kHardLight:
-    case DlBlendMode::kSoftLight:
-    case DlBlendMode::kDifference:
-    case DlBlendMode::kExclusion:
-    case DlBlendMode::kMultiply:
-    case DlBlendMode::kHue:
-    case DlBlendMode::kSaturation:
-    case DlBlendMode::kColor:
-    case DlBlendMode::kLuminosity:
-      return color_.isTransparent();
-  }
-}
-
-std::shared_ptr<DlColorFilter> DlMatrixColorFilter::Make(
-    const float matrix[20]) {
-  float product = 0;
-  for (int i = 0; i < 20; i++) {
-    product *= matrix[i];
-  }
-  // If any of the elements of the matrix are infinity or NaN, then
-  // |product| will be NaN, otherwise 0.
-  if (product == 0) {
-    return std::make_shared<DlMatrixColorFilter>(matrix);
-  }
-  return nullptr;
-}
-
-bool DlMatrixColorFilter::modifies_transparent_black() const {
-  // Values are considered in non-premultiplied form when the matrix is
-  // applied, but we only care about this answer for whether it leaves
-  // an incoming color with a transparent alpha as transparent on output.
-  // Thus, we only need to consider the alpha part of the matrix equation,
-  // which is the last row. Since the incoming alpha value is 0, the last
-  // equation ends up becoming A' = matrix_[19]. Negative results will be
-  // clamped to the range [0,1] so we only care about positive values.
-  // Non-finite values are clamped to a zero alpha.
-  return (SkScalarIsFinite(matrix_[19]) && matrix_[19] > 0);
-}
-
-bool DlMatrixColorFilter::can_commute_with_opacity() const {
-  // We need to check if:
-  //   filter(color) * opacity == filter(color * opacity).
-  //
-  // filter(RGBA) = R' = [ R*m[ 0] + G*m[ 1] + B*m[ 2] + A*m[ 3] + m[ 4] ]
-  //                G' = [ R*m[ 5] + G*m[ 6] + B*m[ 7] + A*m[ 8] + m[ 9] ]
-  //                B' = [ R*m[10] + G*m[11] + B*m[12] + A*m[13] + m[14] ]
-  //                A' = [ R*m[15] + G*m[16] + B*m[17] + A*m[18] + m[19] ]
-  //
-  // Applying the opacity only affects the alpha value since the operations
-  // are performed on non-premultiplied colors. (If the data is stored in
-  // premultiplied form, though, there may be rounding errors due to
-  // premul->unpremul->premul conversions.)
-
-  // We test for the successful cases and return false if they fail so that
-  // we fail and return false if any matrix values are NaN.
-
-  // If any of the alpha column are non-zero then the prior alpha affects
-  // the result color, so applying opacity before the filter will change
-  // the incoming alpha and therefore the colors that are produced.
-  if (!(matrix_[3] == 0 &&    // A does not affect R'
-        matrix_[8] == 0 &&    // A does not affect G'
-        matrix_[13] == 0)) {  // A does not affect B'
-    return false;
-  }
-
-  // Similarly, if any of the alpha row are non-zero then the prior colors
-  // affect the result alpha in a way that prevents opacity from commuting
-  // through the filter operation.
-  if (!(matrix_[15] == 0 &&   // R does not affect A'
-        matrix_[16] == 0 &&   // G does not affect A'
-        matrix_[17] == 0 &&   // B does not affect A'
-        matrix_[19] == 0)) {  // A' is not offset by an absolute value
-    return false;
-  }
-
-  return true;
-}
-
 const std::shared_ptr<DlSrgbToLinearGammaColorFilter>
     DlSrgbToLinearGammaColorFilter::instance =
         std::make_shared<DlSrgbToLinearGammaColorFilter>();
+const sk_sp<SkColorFilter> DlSrgbToLinearGammaColorFilter::sk_filter_ =
+    SkColorFilters::SRGBToLinearGamma();
 
 const std::shared_ptr<DlLinearToSrgbGammaColorFilter>
     DlLinearToSrgbGammaColorFilter::instance =
         std::make_shared<DlLinearToSrgbGammaColorFilter>();
+const sk_sp<SkColorFilter> DlLinearToSrgbGammaColorFilter::sk_filter_ =
+    SkColorFilters::LinearToSRGBGamma();
 
 }  // namespace flutter
diff --git a/display_list/display_list_color_filter.h b/display_list/display_list_color_filter.h
index b02bad4..240f361 100644
--- a/display_list/display_list_color_filter.h
+++ b/display_list/display_list_color_filter.h
@@ -28,7 +28,8 @@
   kLinearToSrgbGamma,
 };
 
-class DlColorFilter : public DlAttribute<DlColorFilter, DlColorFilterType> {
+class DlColorFilter
+    : public DlAttribute<DlColorFilter, SkColorFilter, DlColorFilterType> {
  public:
   // Return a boolean indicating whether the color filtering operation will
   // modify transparent black. This is typically used to determine if applying
@@ -67,18 +68,23 @@
   DlBlendColorFilter(const DlBlendColorFilter* filter)
       : DlBlendColorFilter(filter->color_, filter->mode_) {}
 
-  static std::shared_ptr<DlColorFilter> Make(DlColor color, DlBlendMode mode);
-
   DlColorFilterType type() const override { return DlColorFilterType::kBlend; }
   size_t size() const override { return sizeof(*this); }
-
-  bool modifies_transparent_black() const override;
-  bool can_commute_with_opacity() const override;
+  bool modifies_transparent_black() const override {
+    // Look at blend and color to make a faster determination?
+    sk_sp<SkColorFilter> sk_filter = skia_object();
+    return sk_filter &&
+           sk_filter->filterColor(SK_ColorTRANSPARENT) != SK_ColorTRANSPARENT;
+  }
 
   std::shared_ptr<DlColorFilter> shared() const override {
     return std::make_shared<DlBlendColorFilter>(this);
   }
 
+  sk_sp<SkColorFilter> skia_object() const override {
+    return SkColorFilters::Blend(color_, ToSk(mode_));
+  }
+
   const DlBlendColorFilter* asBlend() const override { return this; }
 
   DlColor color() const { return color_; }
@@ -108,10 +114,6 @@
 //
 // The resulting color [oR,oG,oB,oA] is then clamped to the range of
 // valid pixel components before storing in the output.
-//
-// The incoming and outgoing [iR,iG,iB,iA] and [oR,oG,oB,oA] are
-// considered to be non-premultiplied. When working on premultiplied
-// pixel data, the necessary pre<->non-pre conversions must be performed.
 class DlMatrixColorFilter final : public DlColorFilter {
  public:
   DlMatrixColorFilter(const float matrix[20]) {
@@ -122,18 +124,30 @@
   DlMatrixColorFilter(const DlMatrixColorFilter* filter)
       : DlMatrixColorFilter(filter->matrix_) {}
 
-  static std::shared_ptr<DlColorFilter> Make(const float matrix[20]);
-
   DlColorFilterType type() const override { return DlColorFilterType::kMatrix; }
   size_t size() const override { return sizeof(*this); }
+  bool modifies_transparent_black() const override {
+    // Look at the matrix to make a faster determination?
+    // Basically, are the translation components all 0?
+    sk_sp<SkColorFilter> sk_filter = skia_object();
+    return sk_filter &&
+           sk_filter->filterColor(SK_ColorTRANSPARENT) != SK_ColorTRANSPARENT;
+  }
 
-  bool modifies_transparent_black() const override;
-  bool can_commute_with_opacity() const override;
+  bool can_commute_with_opacity() const override {
+    return matrix_[3] == 0 && matrix_[8] == 0 && matrix_[13] == 0 &&
+           matrix_[15] == 0 && matrix_[16] == 0 && matrix_[17] == 0 &&
+           (matrix_[18] >= 0.0 && matrix_[18] <= 1.0) && matrix_[19] == 0;
+  }
 
   std::shared_ptr<DlColorFilter> shared() const override {
     return std::make_shared<DlMatrixColorFilter>(this);
   }
 
+  sk_sp<SkColorFilter> skia_object() const override {
+    return SkColorFilters::Matrix(matrix_);
+  }
+
   const DlMatrixColorFilter* asMatrix() const override { return this; }
 
   const float& operator[](int index) const { return matrix_[index]; }
@@ -172,6 +186,7 @@
   bool can_commute_with_opacity() const override { return true; }
 
   std::shared_ptr<DlColorFilter> shared() const override { return instance; }
+  sk_sp<SkColorFilter> skia_object() const override { return sk_filter_; }
 
  protected:
   bool equals_(const DlColorFilter& other) const override {
@@ -180,6 +195,7 @@
   }
 
  private:
+  static const sk_sp<SkColorFilter> sk_filter_;
   friend class DlColorFilter;
 };
 
@@ -203,6 +219,7 @@
   bool can_commute_with_opacity() const override { return true; }
 
   std::shared_ptr<DlColorFilter> shared() const override { return instance; }
+  sk_sp<SkColorFilter> skia_object() const override { return sk_filter_; }
 
  protected:
   bool equals_(const DlColorFilter& other) const override {
@@ -211,6 +228,7 @@
   }
 
  private:
+  static const sk_sp<SkColorFilter> sk_filter_;
   friend class DlColorFilter;
 };
 
diff --git a/display_list/display_list_color_filter_unittests.cc b/display_list/display_list_color_filter_unittests.cc
index efdd1b5..2403a6d 100644
--- a/display_list/display_list_color_filter_unittests.cc
+++ b/display_list/display_list_color_filter_unittests.cc
@@ -17,6 +17,18 @@
     16, 17, 18, 19, 20,  //
 };
 
+TEST(DisplayListColorFilter, BuilderSetGet) {
+  DlBlendColorFilter filter(DlColor::kRed(), DlBlendMode::kDstATop);
+  DisplayListBuilder builder;
+  ASSERT_EQ(builder.getColorFilter(), nullptr);
+  builder.setColorFilter(&filter);
+  ASSERT_NE(builder.getColorFilter(), nullptr);
+  ASSERT_TRUE(
+      Equals(builder.getColorFilter(), static_cast<DlColorFilter*>(&filter)));
+  builder.setColorFilter(nullptr);
+  ASSERT_EQ(builder.getColorFilter(), nullptr);
+}
+
 TEST(DisplayListColorFilter, BlendConstructor) {
   DlBlendColorFilter filter(DlColor::kRed(), DlBlendMode::kDstATop);
 }
diff --git a/display_list/display_list_color_source.cc b/display_list/display_list_color_source.cc
index 01da26d..94c9fbb 100644
--- a/display_list/display_list_color_source.cc
+++ b/display_list/display_list_color_source.cc
@@ -19,7 +19,7 @@
   ::operator delete(p);
 }
 
-std::shared_ptr<DlLinearGradientColorSource> DlColorSource::MakeLinear(
+std::shared_ptr<DlColorSource> DlColorSource::MakeLinear(
     const SkPoint start_point,
     const SkPoint end_point,
     uint32_t stop_count,
@@ -37,10 +37,10 @@
                 DlLinearGradientColorSource(start_point, end_point, stop_count,
                                             colors, stops, tile_mode, matrix),
             DlGradientDeleter);
-  return ret;
+  return std::move(ret);
 }
 
-std::shared_ptr<DlRadialGradientColorSource> DlColorSource::MakeRadial(
+std::shared_ptr<DlColorSource> DlColorSource::MakeRadial(
     SkPoint center,
     SkScalar radius,
     uint32_t stop_count,
@@ -57,10 +57,10 @@
   ret.reset(new (storage) DlRadialGradientColorSource(
                 center, radius, stop_count, colors, stops, tile_mode, matrix),
             DlGradientDeleter);
-  return ret;
+  return std::move(ret);
 }
 
-std::shared_ptr<DlConicalGradientColorSource> DlColorSource::MakeConical(
+std::shared_ptr<DlColorSource> DlColorSource::MakeConical(
     SkPoint start_center,
     SkScalar start_radius,
     SkPoint end_center,
@@ -80,10 +80,10 @@
                 start_center, start_radius, end_center, end_radius, stop_count,
                 colors, stops, tile_mode, matrix),
             DlGradientDeleter);
-  return ret;
+  return std::move(ret);
 }
 
-std::shared_ptr<DlSweepGradientColorSource> DlColorSource::MakeSweep(
+std::shared_ptr<DlColorSource> DlColorSource::MakeSweep(
     SkPoint center,
     SkScalar start,
     SkScalar end,
@@ -102,7 +102,7 @@
                 DlSweepGradientColorSource(center, start, end, stop_count,
                                            colors, stops, tile_mode, matrix),
             DlGradientDeleter);
-  return ret;
+  return std::move(ret);
 }
 
 std::shared_ptr<DlRuntimeEffectColorSource> DlColorSource::MakeRuntimeEffect(
diff --git a/display_list/display_list_color_source.h b/display_list/display_list_color_source.h
index 5293722..7705893 100644
--- a/display_list/display_list_color_source.h
+++ b/display_list/display_list_color_source.h
@@ -63,9 +63,10 @@
 #endif  // IMPELLER_ENABLE_3D
 };
 
-class DlColorSource : public DlAttribute<DlColorSource, DlColorSourceType> {
+class DlColorSource
+    : public DlAttribute<DlColorSource, SkShader, DlColorSourceType> {
  public:
-  static std::shared_ptr<DlLinearGradientColorSource> MakeLinear(
+  static std::shared_ptr<DlColorSource> MakeLinear(
       const SkPoint start_point,
       const SkPoint end_point,
       uint32_t stop_count,
@@ -74,7 +75,7 @@
       DlTileMode tile_mode,
       const SkMatrix* matrix = nullptr);
 
-  static std::shared_ptr<DlRadialGradientColorSource> MakeRadial(
+  static std::shared_ptr<DlColorSource> MakeRadial(
       SkPoint center,
       SkScalar radius,
       uint32_t stop_count,
@@ -83,7 +84,7 @@
       DlTileMode tile_mode,
       const SkMatrix* matrix = nullptr);
 
-  static std::shared_ptr<DlConicalGradientColorSource> MakeConical(
+  static std::shared_ptr<DlColorSource> MakeConical(
       SkPoint start_center,
       SkScalar start_radius,
       SkPoint end_center,
@@ -94,7 +95,7 @@
       DlTileMode tile_mode,
       const SkMatrix* matrix = nullptr);
 
-  static std::shared_ptr<DlSweepGradientColorSource> MakeSweep(
+  static std::shared_ptr<DlColorSource> MakeSweep(
       SkPoint center,
       SkScalar start,
       SkScalar end,
@@ -189,6 +190,10 @@
 
   DlColor color() const { return color_; }
 
+  sk_sp<SkShader> skia_object() const override {
+    return SkShaders::Color(color_);
+  }
+
  protected:
   bool equals_(DlColorSource const& other) const override {
     FML_DCHECK(other.type() == DlColorSourceType::kColor);
@@ -258,6 +263,15 @@
   DlTileMode vertical_tile_mode() const { return vertical_tile_mode_; }
   DlImageSampling sampling() const { return sampling_; }
 
+  virtual sk_sp<SkShader> skia_object() const override {
+    if (!image_->skia_image()) {
+      return nullptr;
+    }
+    return image_->skia_image()->makeShader(ToSk(horizontal_tile_mode_),
+                                            ToSk(vertical_tile_mode_),
+                                            ToSk(sampling_), matrix_ptr());
+  }
+
  protected:
   bool equals_(DlColorSource const& other) const override {
     FML_DCHECK(other.type() == DlColorSourceType::kImage);
@@ -372,6 +386,13 @@
   const SkPoint& start_point() const { return start_point_; }
   const SkPoint& end_point() const { return end_point_; }
 
+  sk_sp<SkShader> skia_object() const override {
+    SkPoint pts[] = {start_point_, end_point_};
+    const SkColor* sk_colors = reinterpret_cast<const SkColor*>(colors());
+    return SkGradientShader::MakeLinear(pts, sk_colors, stops(), stop_count(),
+                                        ToSk(tile_mode()), 0, matrix_ptr());
+  }
+
  protected:
   virtual const void* pod() const override { return this + 1; }
 
@@ -433,6 +454,13 @@
   SkPoint center() const { return center_; }
   SkScalar radius() const { return radius_; }
 
+  sk_sp<SkShader> skia_object() const override {
+    const SkColor* sk_colors = reinterpret_cast<const SkColor*>(colors());
+    return SkGradientShader::MakeRadial(center_, radius_, sk_colors, stops(),
+                                        stop_count(), ToSk(tile_mode()), 0,
+                                        matrix_ptr());
+  }
+
  protected:
   virtual const void* pod() const override { return this + 1; }
 
@@ -497,6 +525,13 @@
   SkPoint end_center() const { return end_center_; }
   SkScalar end_radius() const { return end_radius_; }
 
+  sk_sp<SkShader> skia_object() const override {
+    const SkColor* sk_colors = reinterpret_cast<const SkColor*>(colors());
+    return SkGradientShader::MakeTwoPointConical(
+        start_center_, start_radius_, end_center_, end_radius_, sk_colors,
+        stops(), stop_count(), ToSk(tile_mode()), 0, matrix_ptr());
+  }
+
  protected:
   virtual const void* pod() const override { return this + 1; }
 
@@ -569,6 +604,13 @@
   SkScalar start() const { return start_; }
   SkScalar end() const { return end_; }
 
+  sk_sp<SkShader> skia_object() const override {
+    const SkColor* sk_colors = reinterpret_cast<const SkColor*>(colors());
+    return SkGradientShader::MakeSweep(center_.x(), center_.y(), sk_colors,
+                                       stops(), stop_count(), ToSk(tile_mode()),
+                                       start_, end_, 0, matrix_ptr());
+  }
+
  protected:
   virtual const void* pod() const override { return this + 1; }
 
@@ -651,6 +693,35 @@
     return uniform_data_;
   }
 
+  sk_sp<SkShader> skia_object() const override {
+    if (!runtime_effect_) {
+      return nullptr;
+    }
+    if (!runtime_effect_->skia_runtime_effect()) {
+      return nullptr;
+    }
+    std::vector<sk_sp<SkShader>> sk_samplers(samplers_.size());
+    for (size_t i = 0; i < samplers_.size(); i++) {
+      auto sampler = samplers_[i];
+      if (sampler == nullptr) {
+        return nullptr;
+      }
+      sk_samplers[i] = sampler->skia_object();
+    }
+
+    auto ref = new std::shared_ptr<std::vector<uint8_t>>(uniform_data_);
+    auto uniform_data = SkData::MakeWithProc(
+        uniform_data_->data(), uniform_data_->size(),
+        [](const void* ptr, void* context) {
+          delete reinterpret_cast<std::shared_ptr<std::vector<uint8_t>>*>(
+              context);
+        },
+        ref);
+
+    return runtime_effect_->skia_runtime_effect()->makeShader(
+        uniform_data, sk_samplers.data(), sk_samplers.size());
+  }
+
  protected:
   bool equals_(DlColorSource const& other) const override {
     FML_DCHECK(other.type() == DlColorSourceType::kRuntimeEffect);
diff --git a/display_list/display_list_color_source_unittests.cc b/display_list/display_list_color_source_unittests.cc
index 582d723..a4c982c 100644
--- a/display_list/display_list_color_source_unittests.cc
+++ b/display_list/display_list_color_source_unittests.cc
@@ -86,6 +86,19 @@
     SkPoint::Make(107, 118),
 };
 
+TEST(DisplayListColorSource, BuilderSetGet) {
+  DlImageColorSource source(kTestImage1, DlTileMode::kClamp, DlTileMode::kClamp,
+                            DlImageSampling::kLinear, &kTestMatrix1);
+  DisplayListBuilder builder;
+  ASSERT_EQ(builder.getColorSource(), nullptr);
+  builder.setColorSource(&source);
+  ASSERT_NE(builder.getColorSource(), nullptr);
+  ASSERT_TRUE(
+      Equals(builder.getColorSource(), static_cast<DlColorSource*>(&source)));
+  builder.setColorSource(nullptr);
+  ASSERT_EQ(builder.getColorSource(), nullptr);
+}
+
 TEST(DisplayListColorSource, ColorConstructor) {
   DlColorColorSource source(SK_ColorRED);
 }
@@ -750,11 +763,23 @@
   ASSERT_EQ(source1->asConicalGradient(), nullptr);
   ASSERT_EQ(source1->asSweepGradient(), nullptr);
 
+  ASSERT_NE(source1->skia_object(), nullptr);
+  ASSERT_EQ(source3->skia_object(), nullptr);
+
   TestEquals(source1, source1);
   TestEquals(source3, source3);
   TestNotEquals(source1, source2, "SkRuntimeEffect differs");
   TestNotEquals(source2, source3, "SkRuntimeEffect differs");
 }
 
+TEST(DisplayListColorSource, RuntimeEffectWithNullSampler) {
+  std::shared_ptr<DlRuntimeEffectColorSource> source1 =
+      DlColorSource::MakeRuntimeEffect(
+          kTestRuntimeEffect1, {nullptr},
+          std::make_shared<std::vector<uint8_t>>());
+
+  ASSERT_EQ(source1->skia_object(), nullptr);
+}
+
 }  // namespace testing
 }  // namespace flutter
diff --git a/display_list/display_list_complexity_gl.cc b/display_list/display_list_complexity_gl.cc
index 165744d..40c3bde 100644
--- a/display_list/display_list_complexity_gl.cc
+++ b/display_list/display_list_complexity_gl.cc
@@ -114,7 +114,7 @@
   //
   // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
   // currently use it anywhere in Flutter.
-  if (DrawStyle() == DlDrawStyle::kFill) {
+  if (Style() == SkPaint::Style::kFill_Style) {
     // No real difference for AA with filled styles
     unsigned int area = rect.width() * rect.height();
 
@@ -165,7 +165,7 @@
 
   // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
   // currently use it anywhere in Flutter.
-  if (DrawStyle() == DlDrawStyle::kFill) {
+  if (Style() == SkPaint::Style::kFill_Style) {
     // With filled styles, there is no significant AA penalty.
     // m = 1/6000
     // c = 0
@@ -199,7 +199,7 @@
 
   // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
   // currently use it anywhere in Flutter.
-  if (DrawStyle() == DlDrawStyle::kFill) {
+  if (Style() == SkPaint::Style::kFill_Style) {
     // We can ignore pi here
     unsigned int area = radius * radius;
     // m = 1/525
@@ -245,7 +245,7 @@
   // These values were worked out by creating a straight line graph (y=mx+c)
   // approximately matching the measured data, normalising the data so that
   // 0.0005ms resulted in a score of 100 then simplifying down the formula.
-  if (DrawStyle() == DlDrawStyle::kFill ||
+  if (Style() == SkPaint::Style::kFill_Style ||
       ((rrect.getType() == SkRRect::Type::kSimple_Type) && IsAntiAliased())) {
     unsigned int area = rrect.width() * rrect.height();
     // m = 1/3200
@@ -295,7 +295,7 @@
   //
   // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
   // currently use it anywhere in Flutter.
-  if (DrawStyle() == DlDrawStyle::kFill) {
+  if (Style() == SkPaint::Style::kFill_Style) {
     unsigned int area = outer.width() * outer.height();
     if (outer.getType() == SkRRect::Type::kComplex_Type) {
       // m = 1/500
@@ -382,7 +382,7 @@
   //
   // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
   // currently use it anywhere in Flutter.
-  if (DrawStyle() == DlDrawStyle::kStroke) {
+  if (Style() == SkPaint::Style::kStroke_Style) {
     if (IsAntiAliased()) {
       // m = 1/3800
       // c = 12
@@ -550,7 +550,7 @@
     const SkISize& size,
     bool texture_backed,
     bool render_with_attributes,
-    bool enforce_src_edges) {
+    SkCanvas::SrcRectConstraint constraint) {
   if (IsComplex()) {
     return;
   }
@@ -563,8 +563,10 @@
   // approximately matching the measured data, normalising the data so that
   // 0.0005ms resulted in a score of 100 then simplifying down the formula.
   unsigned int complexity;
-  if (!texture_backed || (texture_backed && render_with_attributes &&
-                          enforce_src_edges && IsAntiAliased())) {
+  if (!texture_backed ||
+      (texture_backed && render_with_attributes &&
+       constraint == SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint &&
+       IsAntiAliased())) {
     unsigned int area = size.width() * size.height();
     // m = 1/4000
     // c = 5
@@ -608,15 +610,11 @@
 }
 
 void DisplayListGLComplexityCalculator::GLHelper::drawDisplayList(
-    const sk_sp<DisplayList> display_list,
-    SkScalar opacity) {
+    const sk_sp<DisplayList> display_list) {
   if (IsComplex()) {
     return;
   }
   GLHelper helper(Ceiling() - CurrentComplexityScore());
-  if (opacity < SK_Scalar1 && !display_list->can_apply_group_opacity()) {
-    helper.saveLayer(nullptr, SaveLayerOptions::kWithAttributes, nullptr);
-  }
   display_list->Dispatch(helper);
   AccumulateComplexity(helper.ComplexityScore());
 }
diff --git a/display_list/display_list_complexity_gl.h b/display_list/display_list_complexity_gl.h
index 821c696..9756943 100644
--- a/display_list/display_list_complexity_gl.h
+++ b/display_list/display_list_complexity_gl.h
@@ -65,8 +65,7 @@
                        const SkRect& dst,
                        DlFilterMode filter,
                        bool render_with_attributes) override;
-    void drawDisplayList(const sk_sp<DisplayList> display_list,
-                         SkScalar opacity) override;
+    void drawDisplayList(const sk_sp<DisplayList> display_list) override;
     void drawTextBlob(const sk_sp<SkTextBlob> blob,
                       SkScalar x,
                       SkScalar y) override;
@@ -80,7 +79,7 @@
     void ImageRect(const SkISize& size,
                    bool texture_backed,
                    bool render_with_attributes,
-                   bool enforce_src_edges) override;
+                   SkCanvas::SrcRectConstraint constraint) override;
 
     unsigned int BatchedComplexity() override;
 
diff --git a/display_list/display_list_complexity_helper.h b/display_list/display_list_complexity_helper.h
index 488dc32..eb738b9 100644
--- a/display_list/display_list_complexity_helper.h
+++ b/display_list/display_list_complexity_helper.h
@@ -7,8 +7,8 @@
 
 #include "flutter/display_list/display_list_blend_mode.h"
 #include "flutter/display_list/display_list_complexity.h"
+#include "flutter/display_list/display_list_dispatcher.h"
 #include "flutter/display_list/display_list_utils.h"
-#include "flutter/display_list/dl_op_receiver.h"
 
 namespace flutter {
 
@@ -92,7 +92,7 @@
 //   y = 4x
 
 class ComplexityCalculatorHelper
-    : public virtual DlOpReceiver,
+    : public virtual Dispatcher,
       public virtual IgnoreClipDispatchHelper,
       public virtual IgnoreTransformDispatchHelper {
  public:
@@ -121,7 +121,7 @@
   void setAntiAlias(bool aa) override { current_paint_.setAntiAlias(aa); }
 
   void setStyle(DlDrawStyle style) override {
-    current_paint_.setDrawStyle(style);
+    current_paint_.setStyle(ToSk(style));
   }
 
   void setStrokeWidth(SkScalar width) override {
@@ -145,18 +145,17 @@
     AccumulateComplexity(50);
   }
 
-  void drawImageRect(
-      const sk_sp<DlImage> image,
-      const SkRect& src,
-      const SkRect& dst,
-      DlImageSampling sampling,
-      bool render_with_attributes,
-      SrcRectConstraint constraint = SrcRectConstraint::kFast) override {
+  void drawImageRect(const sk_sp<DlImage> image,
+                     const SkRect& src,
+                     const SkRect& dst,
+                     DlImageSampling sampling,
+                     bool render_with_attributes,
+                     SkCanvas::SrcRectConstraint constraint) override {
     if (IsComplex()) {
       return;
     }
     ImageRect(image->dimensions(), image->isTextureBacked(),
-              render_with_attributes, constraint == SrcRectConstraint::kStrict);
+              render_with_attributes, constraint);
   }
 
   void drawAtlas(const sk_sp<DlImage> atlas,
@@ -175,7 +174,8 @@
     // This is equivalent to calling drawImageRect lots of times
     for (int i = 0; i < count; i++) {
       ImageRect(SkISize::Make(tex[i].width(), tex[i].height()), true,
-                render_with_attributes, true);
+                render_with_attributes,
+                SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint);
     }
   }
 
@@ -211,7 +211,7 @@
 
   inline bool IsAntiAliased() { return current_paint_.isAntiAlias(); }
   inline bool IsHairline() { return current_paint_.getStrokeWidth() == 0.0f; }
-  inline DlDrawStyle DrawStyle() { return current_paint_.getDrawStyle(); }
+  inline SkPaint::Style Style() { return current_paint_.getStyle(); }
   inline bool IsComplex() { return is_complex_; }
   inline unsigned int Ceiling() { return ceiling_; }
   inline unsigned int CurrentComplexityScore() { return complexity_score_; }
@@ -248,7 +248,7 @@
   virtual void ImageRect(const SkISize& size,
                          bool texture_backed,
                          bool render_with_attributes,
-                         bool enforce_src_edges) = 0;
+                         SkCanvas::SrcRectConstraint constraint) = 0;
 
   // This calculates and returns the cost of draw calls which are batched and
   // thus have a time cost proportional to the number of draw calls made, such
@@ -256,7 +256,7 @@
   virtual unsigned int BatchedComplexity() = 0;
 
  private:
-  DlPaint current_paint_;
+  SkPaint current_paint_;
 
   // If we exceed the ceiling (defaults to the largest number representable
   // by unsigned int), then set the is_complex_ bool and we no longer
diff --git a/display_list/display_list_complexity_metal.cc b/display_list/display_list_complexity_metal.cc
index 8edb87a..2cd5c7c 100644
--- a/display_list/display_list_complexity_metal.cc
+++ b/display_list/display_list_complexity_metal.cc
@@ -128,7 +128,7 @@
   //
   // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
   // currently use it anywhere in Flutter.
-  if (DrawStyle() == DlDrawStyle::kFill) {
+  if (Style() == SkPaint::Style::kFill_Style) {
     // No real difference for AA with filled styles.
     unsigned int area = rect.width() * rect.height();
 
@@ -170,7 +170,7 @@
 
   // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
   // currently use it anywhere in Flutter.
-  if (DrawStyle() == DlDrawStyle::kFill) {
+  if (Style() == SkPaint::Style::kFill_Style) {
     // With filled styles, there is no significant AA penalty.
     // m = 1/16000
     // c = 0
@@ -204,7 +204,7 @@
 
   // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
   // currently use it anywhere in Flutter.
-  if (DrawStyle() == DlDrawStyle::kFill) {
+  if (Style() == SkPaint::Style::kFill_Style) {
     // We can ignore pi here.
     unsigned int area = radius * radius;
     // m = 1/1300
@@ -244,7 +244,7 @@
   //
   // Expensive: All filled style, symmetric w/AA.
   bool expensive =
-      (DrawStyle() == DlDrawStyle::kFill) ||
+      (Style() == SkPaint::Style::kFill_Style) ||
       ((rrect.getType() == SkRRect::Type::kSimple_Type) && IsAntiAliased());
 
   unsigned int complexity;
@@ -292,7 +292,7 @@
   //
   // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
   // currently use it anywhere in Flutter.
-  if (DrawStyle() == DlDrawStyle::kFill) {
+  if (Style() == SkPaint::Style::kFill_Style) {
     unsigned int area = outer.width() * outer.height();
     if (outer.getType() == SkRRect::Type::kComplex_Type) {
       // m = 1/1000
@@ -374,7 +374,7 @@
   //
   // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
   // currently use it anywhere in Flutter.
-  if (DrawStyle() == DlDrawStyle::kStroke) {
+  if (Style() == SkPaint::Style::kStroke_Style) {
     if (IsAntiAliased()) {
       // m = 1/8500
       // c = 16
@@ -493,7 +493,7 @@
     const SkISize& size,
     bool texture_backed,
     bool render_with_attributes,
-    bool enforce_src_edges) {
+    SkCanvas::SrcRectConstraint constraint) {
   if (IsComplex()) {
     return;
   }
@@ -512,12 +512,16 @@
     // m = 1/23000
     // c = 2.3
     complexity = (area + 52900) * 2 / 115;
-    if (render_with_attributes && enforce_src_edges && IsAntiAliased()) {
+    if (render_with_attributes &&
+        constraint == SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint &&
+        IsAntiAliased()) {
       // There's about a 30% performance penalty from the baseline.
       complexity *= 1.3f;
     }
   } else {
-    if (render_with_attributes && enforce_src_edges && IsAntiAliased()) {
+    if (render_with_attributes &&
+        constraint == SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint &&
+        IsAntiAliased()) {
       // m = 1/12200
       // c = 2.75
       complexity = (area + 33550) * 2 / 61;
@@ -552,15 +556,11 @@
 }
 
 void DisplayListMetalComplexityCalculator::MetalHelper::drawDisplayList(
-    const sk_sp<DisplayList> display_list,
-    SkScalar opacity) {
+    const sk_sp<DisplayList> display_list) {
   if (IsComplex()) {
     return;
   }
   MetalHelper helper(Ceiling() - CurrentComplexityScore());
-  if (opacity < SK_Scalar1 && !display_list->can_apply_group_opacity()) {
-    helper.saveLayer(nullptr, SaveLayerOptions::kWithAttributes, nullptr);
-  }
   display_list->Dispatch(helper);
   AccumulateComplexity(helper.ComplexityScore());
 }
diff --git a/display_list/display_list_complexity_metal.h b/display_list/display_list_complexity_metal.h
index 94d3ca5..530335e 100644
--- a/display_list/display_list_complexity_metal.h
+++ b/display_list/display_list_complexity_metal.h
@@ -65,8 +65,7 @@
                        const SkRect& dst,
                        DlFilterMode filter,
                        bool render_with_attributes) override;
-    void drawDisplayList(const sk_sp<DisplayList> display_list,
-                         SkScalar opacity) override;
+    void drawDisplayList(const sk_sp<DisplayList> display_list) override;
     void drawTextBlob(const sk_sp<SkTextBlob> blob,
                       SkScalar x,
                       SkScalar y) override;
@@ -80,7 +79,7 @@
     void ImageRect(const SkISize& size,
                    bool texture_backed,
                    bool render_with_attributes,
-                   bool enforce_src_edges) override;
+                   SkCanvas::SrcRectConstraint constraint) override;
 
     unsigned int BatchedComplexity() override;
 
diff --git a/display_list/display_list_complexity_unittests.cc b/display_list/display_list_complexity_unittests.cc
index 3d4f9b0..b459a78 100644
--- a/display_list/display_list_complexity_unittests.cc
+++ b/display_list/display_list_complexity_unittests.cc
@@ -76,13 +76,12 @@
 
 TEST(DisplayListComplexity, AntiAliasing) {
   DisplayListBuilder builder_no_aa;
-  builder_no_aa.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100),
-                         DlPaint());
+  builder_no_aa.drawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100));
   auto display_list_no_aa = builder_no_aa.Build();
 
   DisplayListBuilder builder_aa;
-  builder_aa.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100),
-                      DlPaint().setAntiAlias(true));
+  builder_aa.setAntiAlias(true);
+  builder_aa.drawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100));
   auto display_list_aa = builder_aa.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -94,13 +93,13 @@
 
 TEST(DisplayListComplexity, StrokeWidth) {
   DisplayListBuilder builder_stroke_0;
-  builder_stroke_0.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100),
-                            DlPaint().setStrokeWidth(0.0f));
+  builder_stroke_0.setStrokeWidth(0.0f);
+  builder_stroke_0.drawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100));
   auto display_list_stroke_0 = builder_stroke_0.Build();
 
   DisplayListBuilder builder_stroke_1;
-  builder_stroke_0.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100),
-                            DlPaint().setStrokeWidth(1.0f));
+  builder_stroke_1.setStrokeWidth(1.0f);
+  builder_stroke_1.drawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100));
   auto display_list_stroke_1 = builder_stroke_1.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -112,13 +111,13 @@
 
 TEST(DisplayListComplexity, Style) {
   DisplayListBuilder builder_filled;
-  builder_filled.DrawRect(SkRect::MakeXYWH(10, 10, 80, 80),
-                          DlPaint().setDrawStyle(DlDrawStyle::kFill));
+  builder_filled.setStyle(DlDrawStyle::kFill);
+  builder_filled.drawRect(SkRect::MakeXYWH(10, 10, 80, 80));
   auto display_list_filled = builder_filled.Build();
 
   DisplayListBuilder builder_stroked;
-  builder_stroked.DrawRect(SkRect::MakeXYWH(10, 10, 80, 80),
-                           DlPaint().setDrawStyle(DlDrawStyle::kStroke));
+  builder_stroked.setStyle(DlDrawStyle::kStroke);
+  builder_stroked.drawRect(SkRect::MakeXYWH(10, 10, 80, 80));
   auto display_list_stroked = builder_stroked.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -130,7 +129,7 @@
 
 TEST(DisplayListComplexity, SaveLayers) {
   DisplayListBuilder builder;
-  builder.SaveLayer(nullptr, nullptr);
+  builder.saveLayer(nullptr, true);
   auto display_list = builder.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -145,7 +144,7 @@
   line_path.moveTo(SkPoint::Make(0, 0));
   line_path.lineTo(SkPoint::Make(10, 10));
   line_path.close();
-  builder_line.DrawPath(line_path, DlPaint());
+  builder_line.drawPath(line_path);
   auto display_list_line = builder_line.Build();
 
   DisplayListBuilder builder_quad;
@@ -153,7 +152,7 @@
   quad_path.moveTo(SkPoint::Make(0, 0));
   quad_path.quadTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20));
   quad_path.close();
-  builder_quad.DrawPath(quad_path, DlPaint());
+  builder_quad.drawPath(quad_path);
   auto display_list_quad = builder_quad.Build();
 
   DisplayListBuilder builder_conic;
@@ -161,7 +160,7 @@
   conic_path.moveTo(SkPoint::Make(0, 0));
   conic_path.conicTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20), 1.5f);
   conic_path.close();
-  builder_conic.DrawPath(conic_path, DlPaint());
+  builder_conic.drawPath(conic_path);
   auto display_list_conic = builder_conic.Build();
 
   DisplayListBuilder builder_cubic;
@@ -169,7 +168,7 @@
   cubic_path.moveTo(SkPoint::Make(0, 0));
   cubic_path.cubicTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20),
                      SkPoint::Make(20, 20));
-  builder_cubic.DrawPath(cubic_path, DlPaint());
+  builder_cubic.drawPath(cubic_path);
   auto display_list_cubic = builder_cubic.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -187,7 +186,7 @@
   line_path.moveTo(SkPoint::Make(0, 0));
   line_path.lineTo(SkPoint::Make(10, 10));
   line_path.close();
-  builder_line.DrawShadow(line_path, SK_ColorRED, 10.0f, false, 1.0f);
+  builder_line.drawShadow(line_path, SK_ColorRED, 10.0f, false, 1.0f);
   auto display_list_line = builder_line.Build();
 
   DisplayListBuilder builder_quad;
@@ -195,7 +194,7 @@
   quad_path.moveTo(SkPoint::Make(0, 0));
   quad_path.quadTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20));
   quad_path.close();
-  builder_quad.DrawShadow(quad_path, SK_ColorRED, 10.0f, false, 1.0f);
+  builder_quad.drawShadow(quad_path, SK_ColorRED, 10.0f, false, 1.0f);
   auto display_list_quad = builder_quad.Build();
 
   DisplayListBuilder builder_conic;
@@ -203,7 +202,7 @@
   conic_path.moveTo(SkPoint::Make(0, 0));
   conic_path.conicTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20), 1.5f);
   conic_path.close();
-  builder_conic.DrawShadow(conic_path, SK_ColorRED, 10.0f, false, 1.0f);
+  builder_conic.drawShadow(conic_path, SK_ColorRED, 10.0f, false, 1.0f);
   auto display_list_conic = builder_conic.Build();
 
   DisplayListBuilder builder_cubic;
@@ -211,7 +210,7 @@
   cubic_path.moveTo(SkPoint::Make(0, 0));
   cubic_path.cubicTo(SkPoint::Make(10, 10), SkPoint::Make(10, 20),
                      SkPoint::Make(20, 20));
-  builder_cubic.DrawShadow(cubic_path, SK_ColorRED, 10.0f, false, 1.0f);
+  builder_cubic.drawShadow(cubic_path, SK_ColorRED, 10.0f, false, 1.0f);
   auto display_list_cubic = builder_cubic.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -225,7 +224,7 @@
 
 TEST(DisplayListComplexity, DrawOval) {
   DisplayListBuilder builder;
-  builder.DrawOval(SkRect::MakeXYWH(10, 10, 100, 80), DlPaint());
+  builder.drawOval(SkRect::MakeXYWH(10, 10, 100, 80));
   auto display_list = builder.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -236,7 +235,7 @@
 
 TEST(DisplayListComplexity, DrawCircle) {
   DisplayListBuilder builder;
-  builder.DrawCircle(SkPoint::Make(50, 50), 10.0f, DlPaint());
+  builder.drawCircle(SkPoint::Make(50, 50), 10.0f);
   auto display_list = builder.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -247,9 +246,8 @@
 
 TEST(DisplayListComplexity, DrawRRect) {
   DisplayListBuilder builder;
-  builder.DrawRRect(
-      SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 10, 80, 80), 2.0f, 3.0f),
-      DlPaint());
+  builder.drawRRect(
+      SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 10, 80, 80), 2.0f, 3.0f));
   auto display_list = builder.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -264,7 +262,7 @@
       SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 10, 80, 80), 2.0f, 3.0f);
   SkRRect inner =
       SkRRect::MakeRectXY(SkRect::MakeXYWH(15, 15, 70, 70), 1.5f, 1.5f);
-  builder.DrawDRRect(outer, inner, DlPaint());
+  builder.drawDRRect(outer, inner);
   auto display_list = builder.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -275,8 +273,7 @@
 
 TEST(DisplayListComplexity, DrawArc) {
   DisplayListBuilder builder;
-  builder.DrawArc(SkRect::MakeXYWH(10, 10, 100, 80), 0.0f, 10.0f, true,
-                  DlPaint());
+  builder.drawArc(SkRect::MakeXYWH(10, 10, 100, 80), 0.0f, 10.0f, true);
   auto display_list = builder.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -290,7 +287,7 @@
   auto vertices = DlVertices::Make(DlVertexMode::kTriangles, points.size(),
                                    points.data(), nullptr, nullptr);
   DisplayListBuilder builder;
-  builder.DrawVertices(vertices.get(), DlBlendMode::kSrc, DlPaint());
+  builder.drawVertices(vertices, DlBlendMode::kSrc);
   auto display_list = builder.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -304,12 +301,12 @@
       "The quick brown fox jumps over the lazy dog.", SkFont());
 
   DisplayListBuilder builder;
-  builder.DrawTextBlob(text_blob, 0.0f, 0.0f, DlPaint());
+  builder.drawTextBlob(text_blob, 0.0f, 0.0f);
   auto display_list = builder.Build();
 
   DisplayListBuilder builder_multiple;
-  builder_multiple.DrawTextBlob(text_blob, 0.0f, 0.0f, DlPaint());
-  builder_multiple.DrawTextBlob(text_blob, 0.0f, 0.0f, DlPaint());
+  builder_multiple.drawTextBlob(text_blob, 0.0f, 0.0f);
+  builder_multiple.drawTextBlob(text_blob, 0.0f, 0.0f);
   auto display_list_multiple = builder_multiple.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -323,18 +320,18 @@
 TEST(DisplayListComplexity, DrawPoints) {
   auto points = GetTestPoints();
   DisplayListBuilder builder_lines;
-  builder_lines.DrawPoints(DlCanvas::PointMode::kLines, points.size(),
-                           points.data(), DlPaint());
+  builder_lines.drawPoints(DlCanvas::PointMode::kLines, points.size(),
+                           points.data());
   auto display_list_lines = builder_lines.Build();
 
   DisplayListBuilder builder_points;
-  builder_points.DrawPoints(DlCanvas::PointMode::kPoints, points.size(),
-                            points.data(), DlPaint());
+  builder_points.drawPoints(DlCanvas::PointMode::kPoints, points.size(),
+                            points.data());
   auto display_list_points = builder_points.Build();
 
   DisplayListBuilder builder_polygon;
-  builder_polygon.DrawPoints(DlCanvas::PointMode::kPolygon, points.size(),
-                             points.data(), DlPaint());
+  builder_polygon.drawPoints(DlCanvas::PointMode::kPolygon, points.size(),
+                             points.data());
   auto display_list_polygon = builder_polygon.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -354,8 +351,8 @@
   auto image = SkImage::MakeFromBitmap(bitmap);
 
   DisplayListBuilder builder;
-  builder.DrawImage(DlImage::Make(image), SkPoint::Make(0, 0),
-                    DlImageSampling::kNearestNeighbor, nullptr);
+  builder.drawImage(DlImage::Make(image), SkPoint::Make(0, 0),
+                    DlImageSampling::kNearestNeighbor, false);
   auto display_list = builder.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -376,8 +373,8 @@
   SkRect dest = SkRect::MakeXYWH(0, 0, 50, 50);
 
   DisplayListBuilder builder;
-  builder.DrawImageNine(DlImage::Make(image), center, dest,
-                        DlFilterMode::kNearest, nullptr);
+  builder.drawImageNine(DlImage::Make(image), center, dest,
+                        DlFilterMode::kNearest, true);
   auto display_list = builder.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -398,8 +395,8 @@
   SkRect dest = SkRect::MakeXYWH(0, 0, 50, 50);
 
   DisplayListBuilder builder;
-  builder.DrawImageRect(DlImage::Make(image), src, dest,
-                        DlImageSampling::kNearestNeighbor, nullptr);
+  builder.drawImageRect(DlImage::Make(image), src, dest,
+                        DlImageSampling::kNearestNeighbor, true);
   auto display_list = builder.Build();
 
   auto calculators = AccumulatorCalculators();
@@ -424,9 +421,9 @@
   }
 
   DisplayListBuilder builder;
-  builder.DrawAtlas(DlImage::Make(image), xforms.data(), rects.data(), nullptr,
+  builder.drawAtlas(DlImage::Make(image), xforms.data(), rects.data(), nullptr,
                     10, DlBlendMode::kSrc, DlImageSampling::kNearestNeighbor,
-                    nullptr, nullptr);
+                    nullptr, true);
   auto display_list = builder.Build();
 
   auto calculators = AccumulatorCalculators();
diff --git a/display_list/dl_op_receiver.cc b/display_list/display_list_dispatcher.cc
similarity index 64%
rename from display_list/dl_op_receiver.cc
rename to display_list/display_list_dispatcher.cc
index b1b6c01..e321f02 100644
--- a/display_list/dl_op_receiver.cc
+++ b/display_list/display_list_dispatcher.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "flutter/display_list/dl_op_receiver.h"
+#include "flutter/display_list/display_list_dispatcher.h"
 
 namespace flutter {
 
-// Just exists to ensure that the header can be cleanly imported.
+//
 
 }  // namespace flutter
diff --git a/display_list/dl_op_receiver.h b/display_list/display_list_dispatcher.h
similarity index 96%
rename from display_list/dl_op_receiver.h
rename to display_list/display_list_dispatcher.h
index 0f406f9..00dc966 100644
--- a/display_list/dl_op_receiver.h
+++ b/display_list/display_list_dispatcher.h
@@ -28,11 +28,10 @@
 ///             through the DisplayListBuilder and also the methods that will be
 ///             invoked through the DisplayList::dispatch() method.
 ///
-class DlOpReceiver {
+class Dispatcher {
  protected:
   using ClipOp = DlCanvas::ClipOp;
   using PointMode = DlCanvas::PointMode;
-  using SrcRectConstraint = DlCanvas::SrcRectConstraint;
 
  public:
   // MaxDrawPointsCount * sizeof(SkPoint) must be less than 1 << 32
@@ -220,13 +219,12 @@
                          const SkPoint point,
                          DlImageSampling sampling,
                          bool render_with_attributes) = 0;
-  virtual void drawImageRect(
-      const sk_sp<DlImage> image,
-      const SkRect& src,
-      const SkRect& dst,
-      DlImageSampling sampling,
-      bool render_with_attributes,
-      SrcRectConstraint constraint = SrcRectConstraint::kFast) = 0;
+  virtual void drawImageRect(const sk_sp<DlImage> image,
+                             const SkRect& src,
+                             const SkRect& dst,
+                             DlImageSampling sampling,
+                             bool render_with_attributes,
+                             SkCanvas::SrcRectConstraint constraint) = 0;
   virtual void drawImageNine(const sk_sp<DlImage> image,
                              const SkIRect& center,
                              const SkRect& dst,
@@ -241,8 +239,7 @@
                          DlImageSampling sampling,
                          const SkRect* cull_rect,
                          bool render_with_attributes) = 0;
-  virtual void drawDisplayList(const sk_sp<DisplayList> display_list,
-                               SkScalar opacity = SK_Scalar1) = 0;
+  virtual void drawDisplayList(const sk_sp<DisplayList> display_list) = 0;
   virtual void drawTextBlob(const sk_sp<SkTextBlob> blob,
                             SkScalar x,
                             SkScalar y) = 0;
diff --git a/display_list/display_list_enum_unittests.cc b/display_list/display_list_enum_unittests.cc
new file mode 100644
index 0000000..0da01c0
--- /dev/null
+++ b/display_list/display_list_enum_unittests.cc
@@ -0,0 +1,172 @@
+// 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 "flutter/display_list/display_list_blend_mode.h"
+#include "flutter/display_list/display_list_paint.h"
+#include "flutter/display_list/display_list_sampling_options.h"
+#include "flutter/display_list/display_list_tile_mode.h"
+#include "flutter/display_list/display_list_vertices.h"
+#include "flutter/display_list/types.h"
+#include "gtest/gtest.h"
+#include "include/core/SkSamplingOptions.h"
+
+namespace flutter {
+namespace testing {
+
+TEST(DisplayListEnum, ToDlTileMode) {
+  ASSERT_EQ(ToDl(SkTileMode::kClamp), DlTileMode::kClamp);
+  ASSERT_EQ(ToDl(SkTileMode::kRepeat), DlTileMode::kRepeat);
+  ASSERT_EQ(ToDl(SkTileMode::kMirror), DlTileMode::kMirror);
+  ASSERT_EQ(ToDl(SkTileMode::kDecal), DlTileMode::kDecal);
+}
+
+TEST(DisplayListEnum, ToSkTileMode) {
+  ASSERT_EQ(ToSk(DlTileMode::kClamp), SkTileMode::kClamp);
+  ASSERT_EQ(ToSk(DlTileMode::kRepeat), SkTileMode::kRepeat);
+  ASSERT_EQ(ToSk(DlTileMode::kMirror), SkTileMode::kMirror);
+  ASSERT_EQ(ToSk(DlTileMode::kDecal), SkTileMode::kDecal);
+}
+
+TEST(DisplayListEnum, ToDlDrawStyle) {
+  ASSERT_EQ(ToDl(SkPaint::Style::kFill_Style), DlDrawStyle::kFill);
+  ASSERT_EQ(ToDl(SkPaint::Style::kStroke_Style), DlDrawStyle::kStroke);
+  ASSERT_EQ(ToDl(SkPaint::Style::kStrokeAndFill_Style),
+            DlDrawStyle::kStrokeAndFill);
+}
+
+TEST(DisplayListEnum, ToSkDrawStyle) {
+  ASSERT_EQ(ToSk(DlDrawStyle::kFill), SkPaint::Style::kFill_Style);
+  ASSERT_EQ(ToSk(DlDrawStyle::kStroke), SkPaint::Style::kStroke_Style);
+  ASSERT_EQ(ToSk(DlDrawStyle::kStrokeAndFill),
+            SkPaint::Style::kStrokeAndFill_Style);
+}
+
+TEST(DisplayListEnum, ToDlStrokeCap) {
+  ASSERT_EQ(ToDl(SkPaint::Cap::kButt_Cap), DlStrokeCap::kButt);
+  ASSERT_EQ(ToDl(SkPaint::Cap::kRound_Cap), DlStrokeCap::kRound);
+  ASSERT_EQ(ToDl(SkPaint::Cap::kSquare_Cap), DlStrokeCap::kSquare);
+}
+
+TEST(DisplayListEnum, ToSkStrokeCap) {
+  ASSERT_EQ(ToSk(DlStrokeCap::kButt), SkPaint::Cap::kButt_Cap);
+  ASSERT_EQ(ToSk(DlStrokeCap::kRound), SkPaint::Cap::kRound_Cap);
+  ASSERT_EQ(ToSk(DlStrokeCap::kSquare), SkPaint::Cap::kSquare_Cap);
+}
+
+TEST(DisplayListEnum, ToDlStrokeJoin) {
+  ASSERT_EQ(ToDl(SkPaint::Join::kMiter_Join), DlStrokeJoin::kMiter);
+  ASSERT_EQ(ToDl(SkPaint::Join::kRound_Join), DlStrokeJoin::kRound);
+  ASSERT_EQ(ToDl(SkPaint::Join::kBevel_Join), DlStrokeJoin::kBevel);
+}
+
+TEST(DisplayListEnum, ToSkStrokeJoin) {
+  ASSERT_EQ(ToSk(DlStrokeJoin::kMiter), SkPaint::Join::kMiter_Join);
+  ASSERT_EQ(ToSk(DlStrokeJoin::kRound), SkPaint::Join::kRound_Join);
+  ASSERT_EQ(ToSk(DlStrokeJoin::kBevel), SkPaint::Join::kBevel_Join);
+}
+
+TEST(DisplayListEnum, ToDlVertexMode) {
+  ASSERT_EQ(ToDl(SkVertices::VertexMode::kTriangles_VertexMode),
+            DlVertexMode::kTriangles);
+  ASSERT_EQ(ToDl(SkVertices::VertexMode::kTriangleStrip_VertexMode),
+            DlVertexMode::kTriangleStrip);
+  ASSERT_EQ(ToDl(SkVertices::VertexMode::kTriangleFan_VertexMode),
+            DlVertexMode::kTriangleFan);
+}
+
+TEST(DisplayListEnum, ToSkVertexMode) {
+  ASSERT_EQ(ToSk(DlVertexMode::kTriangles),
+            SkVertices::VertexMode::kTriangles_VertexMode);
+  ASSERT_EQ(ToSk(DlVertexMode::kTriangleStrip),
+            SkVertices::VertexMode::kTriangleStrip_VertexMode);
+  ASSERT_EQ(ToSk(DlVertexMode::kTriangleFan),
+            SkVertices::VertexMode::kTriangleFan_VertexMode);
+}
+
+TEST(DisplayListEnum, ToDlFilterMode) {
+  ASSERT_EQ(ToDl(SkFilterMode::kLinear), DlFilterMode::kLinear);
+  ASSERT_EQ(ToDl(SkFilterMode::kNearest), DlFilterMode::kNearest);
+  ASSERT_EQ(ToDl(SkFilterMode::kLast), DlFilterMode::kLast);
+}
+
+TEST(DisplayListEnum, ToSkFilterMode) {
+  ASSERT_EQ(ToSk(DlFilterMode::kLinear), SkFilterMode::kLinear);
+  ASSERT_EQ(ToSk(DlFilterMode::kNearest), SkFilterMode::kNearest);
+  ASSERT_EQ(ToSk(DlFilterMode::kLast), SkFilterMode::kLast);
+}
+
+TEST(DisplayListEnum, ToDlImageSampling) {
+  ASSERT_EQ(ToDl(SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone)),
+            DlImageSampling::kLinear);
+  ASSERT_EQ(
+      ToDl(SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear)),
+      DlImageSampling::kMipmapLinear);
+  ASSERT_EQ(
+      ToDl(SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone)),
+      DlImageSampling::kNearestNeighbor);
+  ASSERT_EQ(ToDl(SkSamplingOptions(SkCubicResampler{1 / 3.0f, 1 / 3.0f})),
+            DlImageSampling::kCubic);
+}
+
+TEST(DisplayListEnum, ToSkSamplingOptions) {
+  ASSERT_EQ(ToSk(DlImageSampling::kLinear),
+            SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone));
+  ASSERT_EQ(ToSk(DlImageSampling::kMipmapLinear),
+            SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear));
+  ASSERT_EQ(ToSk(DlImageSampling::kNearestNeighbor),
+            SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone));
+  ASSERT_EQ(ToSk(DlImageSampling::kCubic),
+            SkSamplingOptions(SkCubicResampler{1 / 3.0f, 1 / 3.0f}));
+}
+
+#define CHECK_TO_DLENUM(V) ASSERT_EQ(ToDl(SkBlendMode::V), DlBlendMode::V);
+#define CHECK_TO_SKENUM(V) ASSERT_EQ(ToSk(DlBlendMode::V), SkBlendMode::V);
+
+#define FOR_EACH_ENUM(FUNC) \
+  FUNC(kSrc)                \
+  FUNC(kClear)              \
+  FUNC(kSrc)                \
+  FUNC(kDst)                \
+  FUNC(kSrcOver)            \
+  FUNC(kDstOver)            \
+  FUNC(kSrcIn)              \
+  FUNC(kDstIn)              \
+  FUNC(kSrcOut)             \
+  FUNC(kDstOut)             \
+  FUNC(kSrcATop)            \
+  FUNC(kDstATop)            \
+  FUNC(kXor)                \
+  FUNC(kPlus)               \
+  FUNC(kModulate)           \
+  FUNC(kScreen)             \
+  FUNC(kOverlay)            \
+  FUNC(kDarken)             \
+  FUNC(kLighten)            \
+  FUNC(kColorDodge)         \
+  FUNC(kColorBurn)          \
+  FUNC(kHardLight)          \
+  FUNC(kSoftLight)          \
+  FUNC(kDifference)         \
+  FUNC(kExclusion)          \
+  FUNC(kMultiply)           \
+  FUNC(kHue)                \
+  FUNC(kSaturation)         \
+  FUNC(kColor)              \
+  FUNC(kLuminosity)         \
+  FUNC(kLastCoeffMode)      \
+  FUNC(kLastSeparableMode)  \
+  FUNC(kLastMode)
+
+TEST(DisplayListEnum, ToDlBlendMode){FOR_EACH_ENUM(CHECK_TO_DLENUM)}
+
+TEST(DisplayListEnum, ToSkBlendMode) {
+  FOR_EACH_ENUM(CHECK_TO_SKENUM)
+}
+
+#undef CHECK_TO_DLENUM
+#undef CHECK_TO_SKENUM
+#undef FOR_EACH_ENUM
+
+}  // namespace testing
+}  // namespace flutter
diff --git a/display_list/display_list_image_filter.h b/display_list/display_list_image_filter.h
index c497d4f..5e29bdb 100644
--- a/display_list/display_list_image_filter.h
+++ b/display_list/display_list_image_filter.h
@@ -29,9 +29,9 @@
   kDilate,
   kErode,
   kMatrix,
-  kCompose,
+  kComposeFilter,
   kColorFilter,
-  kLocalMatrix,
+  kLocalMatrixFilter,
 };
 
 class DlBlurImageFilter;
@@ -42,7 +42,8 @@
 class DlComposeImageFilter;
 class DlColorFilterImageFilter;
 
-class DlImageFilter : public DlAttribute<DlImageFilter, DlImageFilterType> {
+class DlImageFilter
+    : public DlAttribute<DlImageFilter, SkImageFilter, DlImageFilterType> {
  public:
   enum class MatrixCapability {
     kTranslate,
@@ -226,16 +227,6 @@
   explicit DlBlurImageFilter(const DlBlurImageFilter& filter)
       : DlBlurImageFilter(&filter) {}
 
-  static std::shared_ptr<DlImageFilter> Make(SkScalar sigma_x,
-                                             SkScalar sigma_y,
-                                             DlTileMode tile_mode) {
-    if (SkScalarIsFinite(sigma_x) && sigma_x > SK_ScalarNearlyZero &&
-        SkScalarIsFinite(sigma_y) && sigma_y > SK_ScalarNearlyZero) {
-      return std::make_shared<DlBlurImageFilter>(sigma_x, sigma_y, tile_mode);
-    }
-    return nullptr;
-  }
-
   std::shared_ptr<DlImageFilter> shared() const override {
     return std::make_shared<DlBlurImageFilter>(this);
   }
@@ -271,6 +262,10 @@
   SkScalar sigma_y() const { return sigma_y_; }
   DlTileMode tile_mode() const { return tile_mode_; }
 
+  sk_sp<SkImageFilter> skia_object() const override {
+    return SkImageFilters::Blur(sigma_x_, sigma_y_, ToSk(tile_mode_), nullptr);
+  }
+
  protected:
   bool equals_(const DlImageFilter& other) const override {
     FML_DCHECK(other.type() == DlImageFilterType::kBlur);
@@ -294,15 +289,6 @@
   explicit DlDilateImageFilter(const DlDilateImageFilter& filter)
       : DlDilateImageFilter(&filter) {}
 
-  static std::shared_ptr<DlImageFilter> Make(SkScalar radius_x,
-                                             SkScalar radius_y) {
-    if (SkScalarIsFinite(radius_x) && radius_x > SK_ScalarNearlyZero &&
-        SkScalarIsFinite(radius_y) && radius_y > SK_ScalarNearlyZero) {
-      return std::make_shared<DlDilateImageFilter>(radius_x, radius_y);
-    }
-    return nullptr;
-  }
-
   std::shared_ptr<DlImageFilter> shared() const override {
     return std::make_shared<DlDilateImageFilter>(this);
   }
@@ -337,6 +323,10 @@
   SkScalar radius_x() const { return radius_x_; }
   SkScalar radius_y() const { return radius_y_; }
 
+  sk_sp<SkImageFilter> skia_object() const override {
+    return SkImageFilters::Dilate(radius_x_, radius_y_, nullptr);
+  }
+
  protected:
   bool equals_(const DlImageFilter& other) const override {
     FML_DCHECK(other.type() == DlImageFilterType::kDilate);
@@ -358,15 +348,6 @@
   explicit DlErodeImageFilter(const DlErodeImageFilter& filter)
       : DlErodeImageFilter(&filter) {}
 
-  static std::shared_ptr<DlImageFilter> Make(SkScalar radius_x,
-                                             SkScalar radius_y) {
-    if (SkScalarIsFinite(radius_x) && radius_x > SK_ScalarNearlyZero &&
-        SkScalarIsFinite(radius_y) && radius_y > SK_ScalarNearlyZero) {
-      return std::make_shared<DlErodeImageFilter>(radius_x, radius_y);
-    }
-    return nullptr;
-  }
-
   std::shared_ptr<DlImageFilter> shared() const override {
     return std::make_shared<DlErodeImageFilter>(this);
   }
@@ -401,6 +382,10 @@
   SkScalar radius_x() const { return radius_x_; }
   SkScalar radius_y() const { return radius_y_; }
 
+  sk_sp<SkImageFilter> skia_object() const override {
+    return SkImageFilters::Erode(radius_x_, radius_y_, nullptr);
+  }
+
  protected:
   bool equals_(const DlImageFilter& other) const override {
     FML_DCHECK(other.type() == DlImageFilterType::kErode);
@@ -422,14 +407,6 @@
   explicit DlMatrixImageFilter(const DlMatrixImageFilter& filter)
       : DlMatrixImageFilter(&filter) {}
 
-  static std::shared_ptr<DlImageFilter> Make(const SkMatrix& matrix,
-                                             DlImageSampling sampling) {
-    if (matrix.isFinite() && !matrix.isIdentity()) {
-      return std::make_shared<DlMatrixImageFilter>(matrix, sampling);
-    }
-    return nullptr;
-  }
-
   std::shared_ptr<DlImageFilter> shared() const override {
     return std::make_shared<DlMatrixImageFilter>(this);
   }
@@ -483,6 +460,10 @@
     return &input_bounds;
   }
 
+  sk_sp<SkImageFilter> skia_object() const override {
+    return SkImageFilters::MatrixTransform(matrix_, ToSk(sampling_), nullptr);
+  }
+
  protected:
   bool equals_(const DlImageFilter& other) const override {
     FML_DCHECK(other.type() == DlImageFilterType::kMatrix);
@@ -497,8 +478,8 @@
 
 class DlComposeImageFilter final : public DlImageFilter {
  public:
-  DlComposeImageFilter(std::shared_ptr<const DlImageFilter> outer,
-                       std::shared_ptr<const DlImageFilter> inner)
+  DlComposeImageFilter(std::shared_ptr<DlImageFilter> outer,
+                       std::shared_ptr<DlImageFilter> inner)
       : outer_(std::move(outer)), inner_(std::move(inner)) {}
   DlComposeImageFilter(const DlImageFilter* outer, const DlImageFilter* inner)
       : outer_(outer->shared()), inner_(inner->shared()) {}
@@ -509,29 +490,17 @@
   explicit DlComposeImageFilter(const DlComposeImageFilter& filter)
       : DlComposeImageFilter(&filter) {}
 
-  static std::shared_ptr<const DlImageFilter> Make(
-      std::shared_ptr<const DlImageFilter> outer,
-      std::shared_ptr<const DlImageFilter> inner) {
-    if (!outer) {
-      return inner;
-    }
-    if (!inner) {
-      return outer;
-    }
-    return std::make_shared<DlComposeImageFilter>(outer, inner);
-  }
-
   std::shared_ptr<DlImageFilter> shared() const override {
     return std::make_shared<DlComposeImageFilter>(this);
   }
 
   DlImageFilterType type() const override {
-    return DlImageFilterType::kCompose;
+    return DlImageFilterType::kComposeFilter;
   }
   size_t size() const override { return sizeof(*this); }
 
-  std::shared_ptr<const DlImageFilter> outer() const { return outer_; }
-  std::shared_ptr<const DlImageFilter> inner() const { return inner_; }
+  std::shared_ptr<DlImageFilter> outer() const { return outer_; }
+  std::shared_ptr<DlImageFilter> inner() const { return inner_; }
 
   const DlComposeImageFilter* asCompose() const override { return this; }
 
@@ -556,25 +525,30 @@
                                    const SkMatrix& ctm,
                                    SkIRect& input_bounds) const override;
 
+  sk_sp<SkImageFilter> skia_object() const override {
+    return SkImageFilters::Compose(outer_->skia_object(),
+                                   inner_->skia_object());
+  }
+
   MatrixCapability matrix_capability() const override {
     return std::min(outer_->matrix_capability(), inner_->matrix_capability());
   }
 
  protected:
   bool equals_(const DlImageFilter& other) const override {
-    FML_DCHECK(other.type() == DlImageFilterType::kCompose);
+    FML_DCHECK(other.type() == DlImageFilterType::kComposeFilter);
     auto that = static_cast<const DlComposeImageFilter*>(&other);
     return (Equals(outer_, that->outer_) && Equals(inner_, that->inner_));
   }
 
  private:
-  std::shared_ptr<const DlImageFilter> outer_;
-  std::shared_ptr<const DlImageFilter> inner_;
+  std::shared_ptr<DlImageFilter> outer_;
+  std::shared_ptr<DlImageFilter> inner_;
 };
 
 class DlColorFilterImageFilter final : public DlImageFilter {
  public:
-  explicit DlColorFilterImageFilter(std::shared_ptr<const DlColorFilter> filter)
+  explicit DlColorFilterImageFilter(std::shared_ptr<DlColorFilter> filter)
       : color_filter_(std::move(filter)) {}
   explicit DlColorFilterImageFilter(const DlColorFilter* filter)
       : color_filter_(filter->shared()) {}
@@ -585,14 +559,6 @@
   explicit DlColorFilterImageFilter(const DlColorFilterImageFilter& filter)
       : DlColorFilterImageFilter(&filter) {}
 
-  static std::shared_ptr<DlImageFilter> Make(
-      std::shared_ptr<const DlColorFilter> filter) {
-    if (filter) {
-      return std::make_shared<DlColorFilterImageFilter>(filter);
-    }
-    return nullptr;
-  }
-
   std::shared_ptr<DlImageFilter> shared() const override {
     return std::make_shared<DlColorFilterImageFilter>(color_filter_);
   }
@@ -602,7 +568,7 @@
   }
   size_t size() const override { return sizeof(*this); }
 
-  const std::shared_ptr<const DlColorFilter> color_filter() const {
+  const std::shared_ptr<DlColorFilter> color_filter() const {
     return color_filter_;
   }
 
@@ -636,6 +602,10 @@
     return map_device_bounds(output_bounds, ctm, input_bounds);
   }
 
+  sk_sp<SkImageFilter> skia_object() const override {
+    return SkImageFilters::ColorFilter(color_filter_->skia_object(), nullptr);
+  }
+
   MatrixCapability matrix_capability() const override {
     return MatrixCapability::kComplex;
   }
@@ -653,7 +623,7 @@
   }
 
  private:
-  std::shared_ptr<const DlColorFilter> color_filter_;
+  std::shared_ptr<DlColorFilter> color_filter_;
 };
 
 class DlLocalMatrixImageFilter final : public DlImageFilter {
@@ -670,7 +640,7 @@
   }
 
   DlImageFilterType type() const override {
-    return DlImageFilterType::kLocalMatrix;
+    return DlImageFilterType::kLocalMatrixFilter;
   }
   size_t size() const override { return sizeof(*this); }
 
@@ -719,9 +689,20 @@
         output_bounds, SkMatrix::Concat(ctm, matrix_), input_bounds);
   }
 
+  sk_sp<SkImageFilter> skia_object() const override {
+    if (!image_filter_) {
+      return nullptr;
+    }
+    sk_sp<SkImageFilter> skia_object = image_filter_->skia_object();
+    if (!skia_object) {
+      return nullptr;
+    }
+    return skia_object->makeWithLocalMatrix(matrix_);
+  }
+
  protected:
   bool equals_(const DlImageFilter& other) const override {
-    FML_DCHECK(other.type() == DlImageFilterType::kLocalMatrix);
+    FML_DCHECK(other.type() == DlImageFilterType::kLocalMatrixFilter);
     auto that = static_cast<const DlLocalMatrixImageFilter*>(&other);
     return (matrix_ == that->matrix_ &&
             Equals(image_filter_, that->image_filter_));
diff --git a/display_list/display_list_image_filter_unittests.cc b/display_list/display_list_image_filter_unittests.cc
index 1cc0231..7e125db 100644
--- a/display_list/display_list_image_filter_unittests.cc
+++ b/display_list/display_list_image_filter_unittests.cc
@@ -17,6 +17,21 @@
 namespace flutter {
 namespace testing {
 
+TEST(DisplayListImageFilter, BuilderSetGet) {
+  DlBlurImageFilter filter(5.0, 5.0, DlTileMode::kDecal);
+  DisplayListBuilder builder;
+
+  ASSERT_EQ(builder.getImageFilter(), nullptr);
+
+  builder.setImageFilter(&filter);
+  ASSERT_NE(builder.getImageFilter(), nullptr);
+  ASSERT_TRUE(
+      Equals(builder.getImageFilter(), static_cast<DlImageFilter*>(&filter)));
+
+  builder.setImageFilter(nullptr);
+  ASSERT_EQ(builder.getImageFilter(), nullptr);
+}
+
 // SkRect::contains treats the rect as a half-open interval which is
 // appropriate for so many operations. Unfortunately, we are using
 // it here to test containment of the corners of a transformed quad
@@ -693,8 +708,8 @@
       SkImageFilters::ColorFilter(
           SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kSrcOver), nullptr),
       SkImageFilters::Dilate(5.0, 10.0, nullptr),
-      SkImageFilters::MatrixTransform(
-          filter_matrix, SkSamplingOptions(SkFilterMode::kLinear), nullptr),
+      SkImageFilters::MatrixTransform(filter_matrix,
+                                      ToSk(DlImageSampling::kLinear), nullptr),
       SkImageFilters::Compose(
           SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr),
           SkImageFilters::ColorFilter(
@@ -770,5 +785,15 @@
   }
 }
 
+TEST(DisplayListImageFilter, LocalImageSkiaNull) {
+  auto blur_filter =
+      std::make_shared<DlBlurImageFilter>(0, 0, DlTileMode::kClamp);
+  DlLocalMatrixImageFilter dl_local_matrix_filter(SkMatrix::RotateDeg(45),
+                                                  blur_filter);
+  // With sigmas set to zero on the blur filter, Skia will return a null filter.
+  // The local matrix filter should return nullptr instead of crashing.
+  ASSERT_EQ(dl_local_matrix_filter.skia_object(), nullptr);
+}
+
 }  // namespace testing
 }  // namespace flutter
diff --git a/display_list/display_list_mask_filter.h b/display_list/display_list_mask_filter.h
index c48dae5..7e8c678 100644
--- a/display_list/display_list_mask_filter.h
+++ b/display_list/display_list_mask_filter.h
@@ -20,7 +20,8 @@
 // An enumerated type for the supported MaskFilter operations.
 enum class DlMaskFilterType { kBlur };
 
-class DlMaskFilter : public DlAttribute<DlMaskFilter, DlMaskFilterType> {
+class DlMaskFilter
+    : public DlAttribute<DlMaskFilter, SkMaskFilter, DlMaskFilterType> {
  public:
   // Return a DlBlurMaskFilter pointer to this object iff it is a Blur
   // type of MaskFilter, otherwise return nullptr.
@@ -42,15 +43,6 @@
       : DlBlurMaskFilter(filter->style_, filter->sigma_, filter->respect_ctm_) {
   }
 
-  static std::shared_ptr<DlMaskFilter> Make(SkBlurStyle style,
-                                            SkScalar sigma,
-                                            bool respect_ctm = true) {
-    if (SkScalarIsFinite(sigma) && sigma > 0) {
-      return std::make_shared<DlBlurMaskFilter>(style, sigma, respect_ctm);
-    }
-    return nullptr;
-  }
-
   DlMaskFilterType type() const override { return DlMaskFilterType::kBlur; }
   size_t size() const override { return sizeof(*this); }
 
@@ -58,6 +50,10 @@
     return std::make_shared<DlBlurMaskFilter>(this);
   }
 
+  sk_sp<SkMaskFilter> skia_object() const override {
+    return SkMaskFilter::MakeBlur(style_, sigma_, respect_ctm_);
+  }
+
   const DlBlurMaskFilter* asBlur() const override { return this; }
 
   SkBlurStyle style() const { return style_; }
diff --git a/display_list/display_list_mask_filter_unittests.cc b/display_list/display_list_mask_filter_unittests.cc
index b794360..5ae2261 100644
--- a/display_list/display_list_mask_filter_unittests.cc
+++ b/display_list/display_list_mask_filter_unittests.cc
@@ -12,6 +12,18 @@
 namespace flutter {
 namespace testing {
 
+TEST(DisplayListMaskFilter, BuilderSetGet) {
+  DlBlurMaskFilter filter(SkBlurStyle::kNormal_SkBlurStyle, 5.0);
+  DisplayListBuilder builder;
+  ASSERT_EQ(builder.getMaskFilter(), nullptr);
+  builder.setMaskFilter(&filter);
+  ASSERT_NE(builder.getMaskFilter(), nullptr);
+  ASSERT_TRUE(
+      Equals(builder.getMaskFilter(), static_cast<DlMaskFilter*>(&filter)));
+  builder.setMaskFilter(nullptr);
+  ASSERT_EQ(builder.getMaskFilter(), nullptr);
+}
+
 TEST(DisplayListMaskFilter, BlurConstructor) {
   DlBlurMaskFilter filter(SkBlurStyle::kNormal_SkBlurStyle, 5.0);
 }
diff --git a/display_list/display_list_ops.h b/display_list/display_list_ops.h
index e9c6642..fb949b5 100644
--- a/display_list/display_list_ops.h
+++ b/display_list/display_list_ops.h
@@ -8,8 +8,8 @@
 #include "display_list_color_source.h"
 #include "flutter/display_list/display_list.h"
 #include "flutter/display_list/display_list_blend_mode.h"
+#include "flutter/display_list/display_list_dispatcher.h"
 #include "flutter/display_list/display_list_sampling_options.h"
-#include "flutter/display_list/dl_op_receiver.h"
 #include "flutter/display_list/types.h"
 #include "flutter/fml/macros.h"
 
@@ -37,7 +37,7 @@
 // cheap to deal with a complicated "lifetime" tracking to
 // determine if they will be used.
 struct DispatchContext {
-  DlOpReceiver& receiver;
+  Dispatcher& dispatcher;
 
   int cur_index;
   int next_render_index;
@@ -111,7 +111,7 @@
     const bool value;                                        \
                                                              \
     void dispatch(DispatchContext& ctx) const {              \
-      ctx.receiver.set##name(value);                         \
+      ctx.dispatcher.set##name(value);                       \
     }                                                        \
   };
 DEFINE_SET_BOOL_OP(AntiAlias)
@@ -129,7 +129,7 @@
     const DlStroke##name value;                                          \
                                                                          \
     void dispatch(DispatchContext& ctx) const {                          \
-      ctx.receiver.setStroke##name(value);                               \
+      ctx.dispatcher.setStroke##name(value);                             \
     }                                                                    \
   };
 DEFINE_SET_ENUM_OP(Cap)
@@ -144,7 +144,7 @@
 
   const DlDrawStyle style;
 
-  void dispatch(DispatchContext& ctx) const { ctx.receiver.setStyle(style); }
+  void dispatch(DispatchContext& ctx) const { ctx.dispatcher.setStyle(style); }
 };
 // 4 byte header + 4 byte payload packs into minimum 8 bytes
 struct SetStrokeWidthOp final : DLOp {
@@ -155,7 +155,7 @@
   const float width;
 
   void dispatch(DispatchContext& ctx) const {
-    ctx.receiver.setStrokeWidth(width);
+    ctx.dispatcher.setStrokeWidth(width);
   }
 };
 // 4 byte header + 4 byte payload packs into minimum 8 bytes
@@ -167,7 +167,7 @@
   const float limit;
 
   void dispatch(DispatchContext& ctx) const {
-    ctx.receiver.setStrokeMiter(limit);
+    ctx.dispatcher.setStrokeMiter(limit);
   }
 };
 
@@ -179,7 +179,7 @@
 
   const DlColor color;
 
-  void dispatch(DispatchContext& ctx) const { ctx.receiver.setColor(color); }
+  void dispatch(DispatchContext& ctx) const { ctx.dispatcher.setColor(color); }
 };
 // 4 byte header + 4 byte payload packs into minimum 8 bytes
 struct SetBlendModeOp final : DLOp {
@@ -189,8 +189,8 @@
 
   const DlBlendMode mode;
 
-  void dispatch(DispatchContext& ctx) const {  //
-    ctx.receiver.setBlendMode(mode);
+  void dispatch(DispatchContext& ctx) const {
+    ctx.dispatcher.setBlendMode(mode);
   }
 };
 
@@ -207,7 +207,7 @@
     Clear##name##Op() {}                                                    \
                                                                             \
     void dispatch(DispatchContext& ctx) const {                             \
-      ctx.receiver.set##name(nullptr);                                      \
+      ctx.dispatcher.set##name(nullptr);                                    \
     }                                                                       \
   };                                                                        \
   struct SetPod##name##Op final : DLOp {                                    \
@@ -217,7 +217,7 @@
                                                                             \
     void dispatch(DispatchContext& ctx) const {                             \
       const Dl##name* filter = reinterpret_cast<const Dl##name*>(this + 1); \
-      ctx.receiver.set##name(filter);                                       \
+      ctx.dispatcher.set##name(filter);                                     \
     }                                                                       \
   };
 DEFINE_SET_CLEAR_DLATTR_OP(ColorFilter, ColorFilter, filter)
@@ -242,7 +242,7 @@
   const DlImageColorSource source;
 
   void dispatch(DispatchContext& ctx) const {
-    ctx.receiver.setColorSource(&source);
+    ctx.dispatcher.setColorSource(&source);
   }
 };
 
@@ -259,7 +259,7 @@
   const DlRuntimeEffectColorSource source;
 
   void dispatch(DispatchContext& ctx) const {
-    ctx.receiver.setColorSource(&source);
+    ctx.dispatcher.setColorSource(&source);
   }
 
   DisplayListCompare equals(const SetRuntimeEffectColorSourceOp* other) const {
@@ -278,7 +278,7 @@
   const DlSceneColorSource source;
 
   void dispatch(DispatchContext& ctx) const {
-    ctx.receiver.setColorSource(&source);
+    ctx.dispatcher.setColorSource(&source);
   }
 
   DisplayListCompare equals(const SetSceneColorSourceOp* other) const {
@@ -298,7 +298,7 @@
   const std::shared_ptr<DlImageFilter> filter;
 
   void dispatch(DispatchContext& ctx) const {
-    ctx.receiver.setImageFilter(filter.get());
+    ctx.dispatcher.setImageFilter(filter.get());
   }
 
   DisplayListCompare equals(const SetSharedImageFilterOp* other) const {
@@ -336,7 +336,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (save_needed(ctx)) {
-      ctx.receiver.save();
+      ctx.dispatcher.save();
     }
   }
 };
@@ -348,7 +348,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (save_needed(ctx)) {
-      ctx.receiver.saveLayer(nullptr, options);
+      ctx.dispatcher.saveLayer(nullptr, options);
     }
   }
 };
@@ -363,7 +363,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (save_needed(ctx)) {
-      ctx.receiver.saveLayer(&rect, options);
+      ctx.dispatcher.saveLayer(&rect, options);
     }
   }
 };
@@ -379,7 +379,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (save_needed(ctx)) {
-      ctx.receiver.saveLayer(nullptr, options, backdrop.get());
+      ctx.dispatcher.saveLayer(nullptr, options, backdrop.get());
     }
   }
 
@@ -403,7 +403,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (save_needed(ctx)) {
-      ctx.receiver.saveLayer(&rect, options, backdrop.get());
+      ctx.dispatcher.saveLayer(&rect, options, backdrop.get());
     }
   }
 
@@ -423,7 +423,7 @@
   void dispatch(DispatchContext& ctx) const {
     DispatchContext::SaveInfo& info = ctx.save_infos.back();
     if (info.save_was_needed) {
-      ctx.receiver.restore();
+      ctx.dispatcher.restore();
     }
     ctx.next_restore_index = info.previous_restore_index;
     ctx.save_infos.pop_back();
@@ -447,7 +447,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.translate(tx, ty);
+      ctx.dispatcher.translate(tx, ty);
     }
   }
 };
@@ -463,7 +463,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.scale(sx, sy);
+      ctx.dispatcher.scale(sx, sy);
     }
   }
 };
@@ -477,7 +477,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.rotate(degrees);
+      ctx.dispatcher.rotate(degrees);
     }
   }
 };
@@ -493,7 +493,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.skew(sx, sy);
+      ctx.dispatcher.skew(sx, sy);
     }
   }
 };
@@ -513,8 +513,8 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.transform2DAffine(mxx, mxy, mxt,  //
-                                     myx, myy, myt);
+      ctx.dispatcher.transform2DAffine(mxx, mxy, mxt,  //
+                                       myx, myy, myt);
     }
   }
 };
@@ -542,10 +542,10 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.transformFullPerspective(mxx, mxy, mxz, mxt,  //
-                                            myx, myy, myz, myt,  //
-                                            mzx, mzy, mzz, mzt,  //
-                                            mwx, mwy, mwz, mwt);
+      ctx.dispatcher.transformFullPerspective(mxx, mxy, mxz, mxt,  //
+                                              myx, myy, myz, myt,  //
+                                              mzx, mzy, mzz, mzt,  //
+                                              mwx, mwy, mwz, mwt);
     }
   }
 };
@@ -558,7 +558,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.transformReset();
+      ctx.dispatcher.transformReset();
     }
   }
 };
@@ -585,8 +585,8 @@
                                                                            \
     void dispatch(DispatchContext& ctx) const {                            \
       if (op_needed(ctx)) {                                                \
-        ctx.receiver.clip##shapetype(shape, DlCanvas::ClipOp::k##clipop,   \
-                                     is_aa);                               \
+        ctx.dispatcher.clip##shapetype(shape, DlCanvas::ClipOp::k##clipop, \
+                                       is_aa);                             \
       }                                                                    \
     }                                                                      \
   };
@@ -596,27 +596,27 @@
 DEFINE_CLIP_SHAPE_OP(RRect, Difference)
 #undef DEFINE_CLIP_SHAPE_OP
 
-#define DEFINE_CLIP_PATH_OP(clipop)                                      \
-  struct Clip##clipop##PathOp final : TransformClipOpBase {              \
-    static const auto kType = DisplayListOpType::kClip##clipop##Path;    \
-                                                                         \
-    Clip##clipop##PathOp(SkPath path, bool is_aa)                        \
-        : is_aa(is_aa), path(path) {}                                    \
-                                                                         \
-    const bool is_aa;                                                    \
-    const SkPath path;                                                   \
-                                                                         \
-    void dispatch(DispatchContext& ctx) const {                          \
-      if (op_needed(ctx)) {                                              \
-        ctx.receiver.clipPath(path, DlCanvas::ClipOp::k##clipop, is_aa); \
-      }                                                                  \
-    }                                                                    \
-                                                                         \
-    DisplayListCompare equals(const Clip##clipop##PathOp* other) const { \
-      return is_aa == other->is_aa && path == other->path                \
-                 ? DisplayListCompare::kEqual                            \
-                 : DisplayListCompare::kNotEqual;                        \
-    }                                                                    \
+#define DEFINE_CLIP_PATH_OP(clipop)                                        \
+  struct Clip##clipop##PathOp final : TransformClipOpBase {                \
+    static const auto kType = DisplayListOpType::kClip##clipop##Path;      \
+                                                                           \
+    Clip##clipop##PathOp(SkPath path, bool is_aa)                          \
+        : is_aa(is_aa), path(path) {}                                      \
+                                                                           \
+    const bool is_aa;                                                      \
+    const SkPath path;                                                     \
+                                                                           \
+    void dispatch(DispatchContext& ctx) const {                            \
+      if (op_needed(ctx)) {                                                \
+        ctx.dispatcher.clipPath(path, DlCanvas::ClipOp::k##clipop, is_aa); \
+      }                                                                    \
+    }                                                                      \
+                                                                           \
+    DisplayListCompare equals(const Clip##clipop##PathOp* other) const {   \
+      return is_aa == other->is_aa && path == other->path                  \
+                 ? DisplayListCompare::kEqual                              \
+                 : DisplayListCompare::kNotEqual;                          \
+    }                                                                      \
   };
 DEFINE_CLIP_PATH_OP(Intersect)
 DEFINE_CLIP_PATH_OP(Difference)
@@ -636,7 +636,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.drawPaint();
+      ctx.dispatcher.drawPaint();
     }
   }
 };
@@ -652,7 +652,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.drawColor(color, mode);
+      ctx.dispatcher.drawColor(color, mode);
     }
   }
 };
@@ -672,7 +672,7 @@
                                                                           \
     void dispatch(DispatchContext& ctx) const {                           \
       if (op_needed(ctx)) {                                               \
-        ctx.receiver.draw##op_name(arg_name);                             \
+        ctx.dispatcher.draw##op_name(arg_name);                           \
       }                                                                   \
     }                                                                     \
   };
@@ -692,7 +692,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.drawPath(path);
+      ctx.dispatcher.drawPath(path);
     }
   }
 
@@ -720,7 +720,7 @@
                                                                  \
     void dispatch(DispatchContext& ctx) const {                  \
       if (op_needed(ctx)) {                                      \
-        ctx.receiver.draw##op_name(name1, name2);                \
+        ctx.dispatcher.draw##op_name(name1, name2);              \
       }                                                          \
     }                                                            \
   };
@@ -743,7 +743,7 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.drawArc(bounds, start, sweep, center);
+      ctx.dispatcher.drawArc(bounds, start, sweep, center);
     }
   }
 };
@@ -754,20 +754,20 @@
 // so this op will always pack efficiently
 // The point type is packed into 3 different OpTypes to avoid expanding
 // the fixed payload beyond the 8 bytes
-#define DEFINE_DRAW_POINTS_OP(name, mode)                                \
-  struct Draw##name##Op final : DrawOpBase {                             \
-    static const auto kType = DisplayListOpType::kDraw##name;            \
-                                                                         \
-    explicit Draw##name##Op(uint32_t count) : count(count) {}            \
-                                                                         \
-    const uint32_t count;                                                \
-                                                                         \
-    void dispatch(DispatchContext& ctx) const {                          \
-      if (op_needed(ctx)) {                                              \
-        const SkPoint* pts = reinterpret_cast<const SkPoint*>(this + 1); \
-        ctx.receiver.drawPoints(DlCanvas::PointMode::mode, count, pts);  \
-      }                                                                  \
-    }                                                                    \
+#define DEFINE_DRAW_POINTS_OP(name, mode)                                 \
+  struct Draw##name##Op final : DrawOpBase {                              \
+    static const auto kType = DisplayListOpType::kDraw##name;             \
+                                                                          \
+    explicit Draw##name##Op(uint32_t count) : count(count) {}             \
+                                                                          \
+    const uint32_t count;                                                 \
+                                                                          \
+    void dispatch(DispatchContext& ctx) const {                           \
+      if (op_needed(ctx)) {                                               \
+        const SkPoint* pts = reinterpret_cast<const SkPoint*>(this + 1);  \
+        ctx.dispatcher.drawPoints(DlCanvas::PointMode::mode, count, pts); \
+      }                                                                   \
+    }                                                                     \
   };
 DEFINE_DRAW_POINTS_OP(Points, kPoints);
 DEFINE_DRAW_POINTS_OP(Lines, kLines);
@@ -792,38 +792,38 @@
     if (op_needed(ctx)) {
       const DlVertices* vertices =
           reinterpret_cast<const DlVertices*>(this + 1);
-      ctx.receiver.drawVertices(vertices, mode);
+      ctx.dispatcher.drawVertices(vertices, mode);
     }
   }
 };
 
 // 4 byte header + 40 byte payload uses 44 bytes but is rounded up to 48 bytes
 // (4 bytes unused)
-#define DEFINE_DRAW_IMAGE_OP(name, with_attributes)                      \
-  struct name##Op final : DrawOpBase {                                   \
-    static const auto kType = DisplayListOpType::k##name;                \
-                                                                         \
-    name##Op(const sk_sp<DlImage> image,                                 \
-             const SkPoint& point,                                       \
-             DlImageSampling sampling)                                   \
-        : point(point), sampling(sampling), image(std::move(image)) {}   \
-                                                                         \
-    const SkPoint point;                                                 \
-    const DlImageSampling sampling;                                      \
-    const sk_sp<DlImage> image;                                          \
-                                                                         \
-    void dispatch(DispatchContext& ctx) const {                          \
-      if (op_needed(ctx)) {                                              \
-        ctx.receiver.drawImage(image, point, sampling, with_attributes); \
-      }                                                                  \
-    }                                                                    \
-                                                                         \
-    DisplayListCompare equals(const name##Op* other) const {             \
-      return (point == other->point && sampling == other->sampling &&    \
-              image->Equals(other->image))                               \
-                 ? DisplayListCompare::kEqual                            \
-                 : DisplayListCompare::kNotEqual;                        \
-    }                                                                    \
+#define DEFINE_DRAW_IMAGE_OP(name, with_attributes)                        \
+  struct name##Op final : DrawOpBase {                                     \
+    static const auto kType = DisplayListOpType::k##name;                  \
+                                                                           \
+    name##Op(const sk_sp<DlImage> image,                                   \
+             const SkPoint& point,                                         \
+             DlImageSampling sampling)                                     \
+        : point(point), sampling(sampling), image(std::move(image)) {}     \
+                                                                           \
+    const SkPoint point;                                                   \
+    const DlImageSampling sampling;                                        \
+    const sk_sp<DlImage> image;                                            \
+                                                                           \
+    void dispatch(DispatchContext& ctx) const {                            \
+      if (op_needed(ctx)) {                                                \
+        ctx.dispatcher.drawImage(image, point, sampling, with_attributes); \
+      }                                                                    \
+    }                                                                      \
+                                                                           \
+    DisplayListCompare equals(const name##Op* other) const {               \
+      return (point == other->point && sampling == other->sampling &&      \
+              image->Equals(other->image))                                 \
+                 ? DisplayListCompare::kEqual                              \
+                 : DisplayListCompare::kNotEqual;                          \
+    }                                                                      \
   };
 DEFINE_DRAW_IMAGE_OP(DrawImage, false)
 DEFINE_DRAW_IMAGE_OP(DrawImageWithAttr, true)
@@ -839,7 +839,7 @@
                   const SkRect& dst,
                   DlImageSampling sampling,
                   bool render_with_attributes,
-                  DlCanvas::SrcRectConstraint constraint)
+                  SkCanvas::SrcRectConstraint constraint)
       : src(src),
         dst(dst),
         sampling(sampling),
@@ -851,13 +851,13 @@
   const SkRect dst;
   const DlImageSampling sampling;
   const bool render_with_attributes;
-  const DlCanvas::SrcRectConstraint constraint;
+  const SkCanvas::SrcRectConstraint constraint;
   const sk_sp<DlImage> image;
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.drawImageRect(image, src, dst, sampling,
-                                 render_with_attributes, constraint);
+      ctx.dispatcher.drawImageRect(image, src, dst, sampling,
+                                   render_with_attributes, constraint);
     }
   }
 
@@ -889,8 +889,8 @@
                                                                            \
     void dispatch(DispatchContext& ctx) const {                            \
       if (op_needed(ctx)) {                                                \
-        ctx.receiver.drawImageNine(image, center, dst, mode,               \
-                                   render_with_attributes);                \
+        ctx.dispatcher.drawImageNine(image, center, dst, mode,             \
+                                     render_with_attributes);              \
       }                                                                    \
     }                                                                      \
                                                                            \
@@ -976,8 +976,8 @@
       const DlColor* colors =
           has_colors ? reinterpret_cast<const DlColor*>(tex + count) : nullptr;
       const DlBlendMode mode = static_cast<DlBlendMode>(mode_index);
-      ctx.receiver.drawAtlas(atlas, xform, tex, colors, count, mode, sampling,
-                             nullptr, render_with_attributes);
+      ctx.dispatcher.drawAtlas(atlas, xform, tex, colors, count, mode, sampling,
+                               nullptr, render_with_attributes);
     }
   }
 
@@ -1021,8 +1021,8 @@
       const DlColor* colors =
           has_colors ? reinterpret_cast<const DlColor*>(tex + count) : nullptr;
       const DlBlendMode mode = static_cast<DlBlendMode>(mode_index);
-      ctx.receiver.drawAtlas(atlas, xform, tex, colors, count, mode, sampling,
-                             &cull_rect, render_with_attributes);
+      ctx.dispatcher.drawAtlas(atlas, xform, tex, colors, count, mode, sampling,
+                               &cull_rect, render_with_attributes);
     }
   }
 
@@ -1041,22 +1041,19 @@
 struct DrawDisplayListOp final : DrawOpBase {
   static const auto kType = DisplayListOpType::kDrawDisplayList;
 
-  explicit DrawDisplayListOp(const sk_sp<DisplayList> display_list,
-                             SkScalar opacity)
-      : opacity(opacity), display_list(std::move(display_list)) {}
+  explicit DrawDisplayListOp(const sk_sp<DisplayList> display_list)
+      : display_list(std::move(display_list)) {}
 
-  SkScalar opacity;
   const sk_sp<DisplayList> display_list;
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.drawDisplayList(display_list, opacity);
+      ctx.dispatcher.drawDisplayList(display_list);
     }
   }
 
   DisplayListCompare equals(const DrawDisplayListOp* other) const {
-    return (opacity == other->opacity &&
-            display_list->Equals(other->display_list))
+    return display_list->Equals(other->display_list)
                ? DisplayListCompare::kEqual
                : DisplayListCompare::kNotEqual;
   }
@@ -1076,33 +1073,33 @@
 
   void dispatch(DispatchContext& ctx) const {
     if (op_needed(ctx)) {
-      ctx.receiver.drawTextBlob(blob, x, y);
+      ctx.dispatcher.drawTextBlob(blob, x, y);
     }
   }
 };
 
 // 4 byte header + 28 byte payload packs evenly into 32 bytes
-#define DEFINE_DRAW_SHADOW_OP(name, transparent_occluder)                     \
-  struct Draw##name##Op final : DrawOpBase {                                  \
-    static const auto kType = DisplayListOpType::kDraw##name;                 \
-                                                                              \
-    Draw##name##Op(const SkPath& path,                                        \
-                   DlColor color,                                             \
-                   SkScalar elevation,                                        \
-                   SkScalar dpr)                                              \
-        : color(color), elevation(elevation), dpr(dpr), path(path) {}         \
-                                                                              \
-    const DlColor color;                                                      \
-    const SkScalar elevation;                                                 \
-    const SkScalar dpr;                                                       \
-    const SkPath path;                                                        \
-                                                                              \
-    void dispatch(DispatchContext& ctx) const {                               \
-      if (op_needed(ctx)) {                                                   \
-        ctx.receiver.drawShadow(path, color, elevation, transparent_occluder, \
-                                dpr);                                         \
-      }                                                                       \
-    }                                                                         \
+#define DEFINE_DRAW_SHADOW_OP(name, transparent_occluder)             \
+  struct Draw##name##Op final : DrawOpBase {                          \
+    static const auto kType = DisplayListOpType::kDraw##name;         \
+                                                                      \
+    Draw##name##Op(const SkPath& path,                                \
+                   DlColor color,                                     \
+                   SkScalar elevation,                                \
+                   SkScalar dpr)                                      \
+        : color(color), elevation(elevation), dpr(dpr), path(path) {} \
+                                                                      \
+    const DlColor color;                                              \
+    const SkScalar elevation;                                         \
+    const SkScalar dpr;                                               \
+    const SkPath path;                                                \
+                                                                      \
+    void dispatch(DispatchContext& ctx) const {                       \
+      if (op_needed(ctx)) {                                           \
+        ctx.dispatcher.drawShadow(path, color, elevation,             \
+                                  transparent_occluder, dpr);         \
+      }                                                               \
+    }                                                                 \
   };
 DEFINE_DRAW_SHADOW_OP(Shadow, false)
 DEFINE_DRAW_SHADOW_OP(ShadowTransparentOccluder, true)
diff --git a/display_list/display_list_paint.h b/display_list/display_list_paint.h
index 7bfdbfa..871be22 100644
--- a/display_list/display_list_paint.h
+++ b/display_list/display_list_paint.h
@@ -25,6 +25,14 @@
   kDefaultStyle = kFill,
 };
 
+inline DlDrawStyle ToDl(SkPaint::Style style) {
+  return static_cast<DlDrawStyle>(style);
+}
+
+inline SkPaint::Style ToSk(DlDrawStyle style) {
+  return static_cast<SkPaint::Style>(style);
+}
+
 enum class DlStrokeCap {
   kButt,    //!< no stroke extension
   kRound,   //!< adds circle
@@ -34,6 +42,14 @@
   kDefaultCap = kButt,
 };
 
+inline DlStrokeCap ToDl(SkPaint::Cap cap) {
+  return static_cast<DlStrokeCap>(cap);
+}
+
+inline SkPaint::Cap ToSk(DlStrokeCap cap) {
+  return static_cast<SkPaint::Cap>(cap);
+}
+
 enum class DlStrokeJoin {
   kMiter,  //!< extends to miter limit
   kRound,  //!< adds circle
@@ -43,6 +59,14 @@
   kDefaultJoin = kMiter,
 };
 
+inline DlStrokeJoin ToDl(SkPaint::Join join) {
+  return static_cast<DlStrokeJoin>(join);
+}
+
+inline SkPaint::Join ToSk(DlStrokeJoin join) {
+  return static_cast<SkPaint::Join>(join);
+}
+
 class DlPaint {
  public:
   static constexpr DlColor kDefaultColor = DlColor::kBlack();
@@ -150,7 +174,7 @@
   }
   const DlColorFilter* getColorFilterPtr() const { return colorFilter_.get(); }
   DlPaint& setColorFilter(const std::shared_ptr<const DlColorFilter> filter) {
-    colorFilter_ = filter;
+    colorFilter_ = filter ? filter->shared() : nullptr;
     return *this;
   }
   DlPaint& setColorFilter(const DlColorFilter* filter) {
@@ -192,10 +216,6 @@
     pathEffect_ = pathEffect;
     return *this;
   }
-  DlPaint& setPathEffect(const DlPathEffect* effect) {
-    pathEffect_ = effect ? effect->shared() : nullptr;
-    return *this;
-  }
 
   bool isDefault() const { return *this == kDefault; }
 
diff --git a/display_list/display_list_path_effect.h b/display_list/display_list_path_effect.h
index f8bd030..d03f194 100644
--- a/display_list/display_list_path_effect.h
+++ b/display_list/display_list_path_effect.h
@@ -25,7 +25,8 @@
   kDash,
 };
 
-class DlPathEffect : public DlAttribute<DlPathEffect, DlPathEffectType> {
+class DlPathEffect
+    : public DlAttribute<DlPathEffect, SkPathEffect, DlPathEffectType> {
  public:
   virtual const DlDashPathEffect* asDash() const { return nullptr; }
 
@@ -76,11 +77,13 @@
 
   const DlDashPathEffect* asDash() const override { return this; }
 
+  sk_sp<SkPathEffect> skia_object() const override {
+    return SkDashPathEffect::Make(intervals(), count_, phase_);
+  }
+
   const SkScalar* intervals() const {
     return reinterpret_cast<const SkScalar*>(this + 1);
   }
-  int count() const { return count_; }
-  SkScalar phase() const { return phase_; }
 
   std::optional<SkRect> effect_bounds(SkRect& rect) const override;
 
diff --git a/display_list/display_list_path_effect_unittests.cc b/display_list/display_list_path_effect_unittests.cc
index e111108..0067c37 100644
--- a/display_list/display_list_path_effect_unittests.cc
+++ b/display_list/display_list_path_effect_unittests.cc
@@ -13,6 +13,19 @@
 namespace flutter {
 namespace testing {
 
+TEST(DisplayListPathEffect, BuilderSetGet) {
+  const SkScalar test_dashes[] = {4.0, 2.0};
+  auto dash_path_effect = DlDashPathEffect::Make(test_dashes, 2, 0.0);
+  DisplayListBuilder builder;
+  ASSERT_EQ(builder.getPathEffect(), nullptr);
+  builder.setPathEffect(dash_path_effect.get());
+  ASSERT_NE(builder.getPathEffect(), nullptr);
+  ASSERT_TRUE(Equals(builder.getPathEffect(),
+                     static_cast<DlPathEffect*>(dash_path_effect.get())));
+  builder.setPathEffect(nullptr);
+  ASSERT_EQ(builder.getPathEffect(), nullptr);
+}
+
 TEST(DisplayListPathEffect, EffectShared) {
   const SkScalar TestDashes2[] = {1.0, 1.5};
   auto effect = DlDashPathEffect::Make(TestDashes2, 2, 0.0);
diff --git a/display_list/display_list_sampling_options.h b/display_list/display_list_sampling_options.h
index c3896bf..b3f1afa 100644
--- a/display_list/display_list_sampling_options.h
+++ b/display_list/display_list_sampling_options.h
@@ -16,6 +16,14 @@
   kLast = kLinear,
 };
 
+inline DlFilterMode ToDl(const SkFilterMode filter_mode) {
+  return static_cast<DlFilterMode>(filter_mode);
+}
+
+inline SkFilterMode ToSk(const DlFilterMode filter_mode) {
+  return static_cast<SkFilterMode>(filter_mode);
+}
+
 enum class DlImageSampling {
   kNearestNeighbor,
   kLinear,
@@ -23,6 +31,34 @@
   kCubic,
 };
 
+inline DlImageSampling ToDl(const SkSamplingOptions& so) {
+  if (so.useCubic) {
+    return DlImageSampling::kCubic;
+  }
+  if (so.filter == SkFilterMode::kLinear) {
+    if (so.mipmap == SkMipmapMode::kNone) {
+      return DlImageSampling::kLinear;
+    }
+    if (so.mipmap == SkMipmapMode::kLinear) {
+      return DlImageSampling::kMipmapLinear;
+    }
+  }
+  return DlImageSampling::kNearestNeighbor;
+}
+
+inline SkSamplingOptions ToSk(DlImageSampling sampling) {
+  switch (sampling) {
+    case DlImageSampling::kCubic:
+      return SkSamplingOptions(SkCubicResampler{1 / 3.0f, 1 / 3.0f});
+    case DlImageSampling::kLinear:
+      return SkSamplingOptions(SkFilterMode::kLinear);
+    case DlImageSampling::kMipmapLinear:
+      return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
+    case DlImageSampling::kNearestNeighbor:
+      return SkSamplingOptions(SkFilterMode::kNearest);
+  }
+}
+
 }  // namespace flutter
 
 #endif  // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_SAMPLING_OPTIONS_H_
diff --git a/display_list/display_list_tile_mode.h b/display_list/display_list_tile_mode.h
index 15c04cf..0be9b54 100644
--- a/display_list/display_list_tile_mode.h
+++ b/display_list/display_list_tile_mode.h
@@ -30,6 +30,14 @@
   kDecal,
 };
 
+inline DlTileMode ToDl(SkTileMode sk_mode) {
+  return static_cast<DlTileMode>(sk_mode);
+}
+
+inline SkTileMode ToSk(DlTileMode dl_mode) {
+  return static_cast<SkTileMode>(dl_mode);
+}
+
 }  // namespace flutter
 
 #endif  // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_TILE_MODE_H_
diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc
index 2d89155..c7b2311 100644
--- a/display_list/display_list_unittests.cc
+++ b/display_list/display_list_unittests.cc
@@ -14,7 +14,6 @@
 #include "flutter/display_list/display_list_paint.h"
 #include "flutter/display_list/display_list_rtree.h"
 #include "flutter/display_list/display_list_utils.h"
-#include "flutter/display_list/skia/dl_sk_dispatcher.h"
 #include "flutter/display_list/testing/dl_test_snippets.h"
 #include "flutter/fml/logging.h"
 #include "flutter/fml/math.h"
@@ -25,11 +24,6 @@
 #include "third_party/skia/include/core/SkSurface.h"
 
 namespace flutter {
-
-DlOpReceiver& DisplayListBuilderTestingAccessor(DisplayListBuilder& builder) {
-  return builder.asReceiver();
-}
-
 namespace testing {
 
 static std::vector<testing::DisplayListInvocationGroup> allGroups =
@@ -38,61 +32,7 @@
 using ClipOp = DlCanvas::ClipOp;
 using PointMode = DlCanvas::PointMode;
 
-template <typename BaseT>
-class DisplayListTestBase : public BaseT {
- public:
-  DisplayListTestBase() = default;
-
-  static DlOpReceiver& ToReceiver(DisplayListBuilder& builder) {
-    return DisplayListBuilderTestingAccessor(builder);
-  }
-
-  static sk_sp<DisplayList> Build(DisplayListInvocation& invocation) {
-    DisplayListBuilder builder;
-    invocation.Invoke(ToReceiver(builder));
-    return builder.Build();
-  }
-
-  static sk_sp<DisplayList> Build(size_t g_index, size_t v_index) {
-    DisplayListBuilder builder;
-    DlOpReceiver& receiver =
-        DisplayListTestBase<::testing::Test>::ToReceiver(builder);
-    unsigned int op_count = 0;
-    size_t byte_count = 0;
-    for (size_t i = 0; i < allGroups.size(); i++) {
-      DisplayListInvocationGroup& group = allGroups[i];
-      size_t j = (i == g_index ? v_index : 0);
-      if (j >= group.variants.size()) {
-        continue;
-      }
-      DisplayListInvocation& invocation = group.variants[j];
-      op_count += invocation.op_count();
-      byte_count += invocation.raw_byte_count();
-      invocation.invoker(receiver);
-    }
-    sk_sp<DisplayList> dl = builder.Build();
-    std::string name;
-    if (g_index >= allGroups.size()) {
-      name = "Default";
-    } else {
-      name = allGroups[g_index].op_name;
-      if (v_index >= allGroups[g_index].variants.size()) {
-        name += " skipped";
-      } else {
-        name += " variant " + std::to_string(v_index + 1);
-      }
-    }
-    EXPECT_EQ(dl->op_count(false), op_count) << name;
-    EXPECT_EQ(dl->bytes(false), byte_count + sizeof(DisplayList)) << name;
-    return dl;
-  }
-
- private:
-  FML_DISALLOW_COPY_AND_ASSIGN(DisplayListTestBase);
-};
-using DisplayListTest = DisplayListTestBase<::testing::Test>;
-
-TEST_F(DisplayListTest, BuilderCanBeReused) {
+TEST(DisplayList, BuilderCanBeReused) {
   DisplayListBuilder builder(kTestBounds);
   builder.DrawRect(kTestBounds, DlPaint());
   auto dl = builder.Build();
@@ -101,7 +41,7 @@
   ASSERT_TRUE(dl->Equals(dl2));
 }
 
-TEST_F(DisplayListTest, BuilderBoundsTransformComparedToSkia) {
+TEST(DisplayList, BuilderBoundsTransformComparedToSkia) {
   const SkRect frame_rect = SkRect::MakeLTRB(10, 10, 100, 100);
   DisplayListBuilder builder(frame_rect);
   SkPictureRecorder recorder;
@@ -113,60 +53,60 @@
   ASSERT_EQ(builder.GetTransform(), canvas->getTotalMatrix());
 }
 
-TEST_F(DisplayListTest, BuilderInitialClipBounds) {
+TEST(DisplayList, BuilderInitialClipBounds) {
   SkRect cull_rect = SkRect::MakeWH(100, 100);
   SkRect clip_bounds = SkRect::MakeWH(100, 100);
   DisplayListBuilder builder(cull_rect);
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
 }
 
-TEST_F(DisplayListTest, BuilderInitialClipBoundsNaN) {
+TEST(DisplayList, BuilderInitialClipBoundsNaN) {
   SkRect cull_rect = SkRect::MakeWH(SK_ScalarNaN, SK_ScalarNaN);
   SkRect clip_bounds = SkRect::MakeEmpty();
   DisplayListBuilder builder(cull_rect);
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
 }
 
-TEST_F(DisplayListTest, BuilderClipBoundsAfterClipRect) {
+TEST(DisplayList, BuilderClipBoundsAfterClipRect) {
   SkRect cull_rect = SkRect::MakeWH(100, 100);
   SkRect clip_rect = SkRect::MakeLTRB(10, 10, 20, 20);
   SkRect clip_bounds = SkRect::MakeLTRB(10, 10, 20, 20);
   DisplayListBuilder builder(cull_rect);
-  builder.ClipRect(clip_rect, ClipOp::kIntersect, false);
+  builder.clipRect(clip_rect, ClipOp::kIntersect, false);
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
 }
 
-TEST_F(DisplayListTest, BuilderClipBoundsAfterClipRRect) {
+TEST(DisplayList, BuilderClipBoundsAfterClipRRect) {
   SkRect cull_rect = SkRect::MakeWH(100, 100);
   SkRect clip_rect = SkRect::MakeLTRB(10, 10, 20, 20);
   SkRRect clip_rrect = SkRRect::MakeRectXY(clip_rect, 2, 2);
   SkRect clip_bounds = SkRect::MakeLTRB(10, 10, 20, 20);
   DisplayListBuilder builder(cull_rect);
-  builder.ClipRRect(clip_rrect, ClipOp::kIntersect, false);
+  builder.clipRRect(clip_rrect, ClipOp::kIntersect, false);
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
 }
 
-TEST_F(DisplayListTest, BuilderClipBoundsAfterClipPath) {
+TEST(DisplayList, BuilderClipBoundsAfterClipPath) {
   SkRect cull_rect = SkRect::MakeWH(100, 100);
   SkPath clip_path = SkPath().addRect(10, 10, 15, 15).addRect(15, 15, 20, 20);
   SkRect clip_bounds = SkRect::MakeLTRB(10, 10, 20, 20);
   DisplayListBuilder builder(cull_rect);
-  builder.ClipPath(clip_path, ClipOp::kIntersect, false);
+  builder.clipPath(clip_path, ClipOp::kIntersect, false);
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
 }
 
-TEST_F(DisplayListTest, BuilderInitialClipBoundsNonZero) {
+TEST(DisplayList, BuilderInitialClipBoundsNonZero) {
   SkRect cull_rect = SkRect::MakeLTRB(10, 10, 100, 100);
   SkRect clip_bounds = SkRect::MakeLTRB(10, 10, 100, 100);
   DisplayListBuilder builder(cull_rect);
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
 }
 
-TEST_F(DisplayListTest, SingleOpSizes) {
+TEST(DisplayList, SingleOpSizes) {
   for (auto& group : allGroups) {
     for (size_t i = 0; i < group.variants.size(); i++) {
       auto& invocation = group.variants[i];
-      sk_sp<DisplayList> dl = Build(invocation);
+      sk_sp<DisplayList> dl = invocation.Build();
       auto desc = group.op_name + "(variant " + std::to_string(i + 1) + ")";
       ASSERT_EQ(dl->op_count(false), invocation.op_count()) << desc;
       ASSERT_EQ(dl->bytes(false), invocation.byte_count()) << desc;
@@ -174,11 +114,11 @@
   }
 }
 
-TEST_F(DisplayListTest, SingleOpDisplayListsNotEqualEmpty) {
+TEST(DisplayList, SingleOpDisplayListsNotEqualEmpty) {
   sk_sp<DisplayList> empty = DisplayListBuilder().Build();
   for (auto& group : allGroups) {
     for (size_t i = 0; i < group.variants.size(); i++) {
-      sk_sp<DisplayList> dl = Build(group.variants[i]);
+      sk_sp<DisplayList> dl = group.variants[i].Build();
       auto desc =
           group.op_name + "(variant " + std::to_string(i + 1) + " != empty)";
       if (group.variants[i].is_empty()) {
@@ -192,16 +132,15 @@
   }
 }
 
-TEST_F(DisplayListTest, SingleOpDisplayListsRecapturedAreEqual) {
+TEST(DisplayList, SingleOpDisplayListsRecapturedAreEqual) {
   for (auto& group : allGroups) {
     for (size_t i = 0; i < group.variants.size(); i++) {
-      sk_sp<DisplayList> dl = Build(group.variants[i]);
+      sk_sp<DisplayList> dl = group.variants[i].Build();
       // Verify recapturing the replay of the display list is Equals()
       // when dispatching directly from the DL to another builder
-      DisplayListBuilder copy_builder;
-      DlOpReceiver& r = ToReceiver(copy_builder);
-      dl->Dispatch(r);
-      sk_sp<DisplayList> copy = copy_builder.Build();
+      DisplayListBuilder builder;
+      dl->Dispatch(builder.asDispatcher());
+      sk_sp<DisplayList> copy = builder.Build();
       auto desc =
           group.op_name + "(variant " + std::to_string(i + 1) + " == copy)";
       ASSERT_EQ(copy->op_count(false), dl->op_count(false)) << desc;
@@ -215,13 +154,13 @@
   }
 }
 
-TEST_F(DisplayListTest, SingleOpDisplayListsCompareToEachOther) {
+TEST(DisplayList, SingleOpDisplayListsCompareToEachOther) {
   for (auto& group : allGroups) {
     std::vector<sk_sp<DisplayList>> lists_a;
     std::vector<sk_sp<DisplayList>> lists_b;
     for (size_t i = 0; i < group.variants.size(); i++) {
-      lists_a.push_back(Build(group.variants[i]));
-      lists_b.push_back(Build(group.variants[i]));
+      lists_a.push_back(group.variants[i].Build());
+      lists_b.push_back(group.variants[i].Build());
     }
 
     for (size_t i = 0; i < lists_a.size(); i++) {
@@ -251,13 +190,13 @@
   }
 }
 
-TEST_F(DisplayListTest, SingleOpDisplayListsAreEqualWithOrWithoutRtree) {
+TEST(DisplayList, SingleOpDisplayListsAreEqualWhetherOrNotToPrepareRtree) {
   for (auto& group : allGroups) {
     for (size_t i = 0; i < group.variants.size(); i++) {
       DisplayListBuilder builder1(/*prepare_rtree=*/false);
       DisplayListBuilder builder2(/*prepare_rtree=*/true);
-      group.variants[i].invoker(ToReceiver(builder1));
-      group.variants[i].invoker(ToReceiver(builder2));
+      group.variants[i].invoker(builder1);
+      group.variants[i].invoker(builder2);
       sk_sp<DisplayList> dl1 = builder1.Build();
       sk_sp<DisplayList> dl2 = builder2.Build();
 
@@ -275,14 +214,13 @@
   }
 }
 
-TEST_F(DisplayListTest, FullRotationsAreNop) {
+TEST(DisplayList, FullRotationsAreNop) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.rotate(0);
-  receiver.rotate(360);
-  receiver.rotate(720);
-  receiver.rotate(1080);
-  receiver.rotate(1440);
+  builder.rotate(0);
+  builder.rotate(360);
+  builder.rotate(720);
+  builder.rotate(1080);
+  builder.rotate(1440);
   sk_sp<DisplayList> dl = builder.Build();
   ASSERT_EQ(dl->bytes(false), sizeof(DisplayList));
   ASSERT_EQ(dl->bytes(true), sizeof(DisplayList));
@@ -290,10 +228,9 @@
   ASSERT_EQ(dl->op_count(true), 0u);
 }
 
-TEST_F(DisplayListTest, AllBlendModeNops) {
+TEST(DisplayList, AllBlendModeNops) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.setBlendMode(DlBlendMode::kSrcOver);
+  builder.setBlendMode(DlBlendMode::kSrcOver);
   sk_sp<DisplayList> dl = builder.Build();
   ASSERT_EQ(dl->bytes(false), sizeof(DisplayList));
   ASSERT_EQ(dl->bytes(true), sizeof(DisplayList));
@@ -301,7 +238,39 @@
   ASSERT_EQ(dl->op_count(true), 0u);
 }
 
-TEST_F(DisplayListTest, DisplayListsWithVaryingOpComparisons) {
+static sk_sp<DisplayList> Build(size_t g_index, size_t v_index) {
+  DisplayListBuilder builder;
+  unsigned int op_count = 0;
+  size_t byte_count = 0;
+  for (size_t i = 0; i < allGroups.size(); i++) {
+    DisplayListInvocationGroup& group = allGroups[i];
+    size_t j = (i == g_index ? v_index : 0);
+    if (j >= group.variants.size()) {
+      continue;
+    }
+    DisplayListInvocation& invocation = group.variants[j];
+    op_count += invocation.op_count();
+    byte_count += invocation.raw_byte_count();
+    invocation.invoker(builder);
+  }
+  sk_sp<DisplayList> dl = builder.Build();
+  std::string name;
+  if (g_index >= allGroups.size()) {
+    name = "Default";
+  } else {
+    name = allGroups[g_index].op_name;
+    if (v_index >= allGroups[g_index].variants.size()) {
+      name += " skipped";
+    } else {
+      name += " variant " + std::to_string(v_index + 1);
+    }
+  }
+  EXPECT_EQ(dl->op_count(false), op_count) << name;
+  EXPECT_EQ(dl->bytes(false), byte_count + sizeof(DisplayList)) << name;
+  return dl;
+}
+
+TEST(DisplayList, DisplayListsWithVaryingOpComparisons) {
   sk_sp<DisplayList> default_dl = Build(allGroups.size(), 0);
   ASSERT_TRUE(default_dl->Equals(*default_dl)) << "Default == itself";
   for (size_t gi = 0; gi < allGroups.size(); gi++) {
@@ -334,7 +303,7 @@
   }
 }
 
-TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) {
+TEST(DisplayList, DisplayListSaveLayerBoundsWithAlphaFilter) {
   SkRect build_bounds = SkRect::MakeLTRB(-100, -100, 200, 200);
   SkRect save_bounds = SkRect::MakeWH(100, 100);
   SkRect rect = SkRect::MakeLTRB(30, 30, 70, 70);
@@ -356,16 +325,13 @@
   };
   // clang-format on
   DlMatrixColorFilter alpha_color_filter(alpha_matrix);
-  sk_sp<SkColorFilter> sk_alpha_color_filter =
-      SkColorFilters::Matrix(alpha_matrix);
 
   {
     // No tricky stuff, just verifying drawing a rect produces rect bounds
     DisplayListBuilder builder(build_bounds);
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes);
-    receiver.drawRect(rect);
-    receiver.restore();
+    builder.saveLayer(&save_bounds, true);
+    builder.drawRect(rect);
+    builder.restore();
     sk_sp<DisplayList> display_list = builder.Build();
     ASSERT_EQ(display_list->bounds(), rect);
   }
@@ -373,12 +339,11 @@
   {
     // Now checking that a normal color filter still produces rect bounds
     DisplayListBuilder builder(build_bounds);
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.setColorFilter(&base_color_filter);
-    receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes);
-    receiver.setColorFilter(nullptr);
-    receiver.drawRect(rect);
-    receiver.restore();
+    builder.setColorFilter(&base_color_filter);
+    builder.saveLayer(&save_bounds, true);
+    builder.setColorFilter(nullptr);
+    builder.drawRect(rect);
+    builder.restore();
     sk_sp<DisplayList> display_list = builder.Build();
     ASSERT_EQ(display_list->bounds(), rect);
   }
@@ -391,7 +356,7 @@
     SkRTreeFactory rtree_factory;
     SkCanvas* canvas = recorder.beginRecording(build_bounds, &rtree_factory);
     SkPaint p1;
-    p1.setColorFilter(sk_alpha_color_filter);
+    p1.setColorFilter(alpha_color_filter.skia_object());
     canvas->saveLayer(save_bounds, &p1);
     SkPaint p2;
     canvas->drawRect(rect, p2);
@@ -406,12 +371,11 @@
     // cull rect of the DisplayListBuilder when it encounters a
     // save layer that modifies an unbounded region
     DisplayListBuilder builder(build_bounds);
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.setColorFilter(&alpha_color_filter);
-    receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes);
-    receiver.setColorFilter(nullptr);
-    receiver.drawRect(rect);
-    receiver.restore();
+    builder.setColorFilter(&alpha_color_filter);
+    builder.saveLayer(&save_bounds, true);
+    builder.setColorFilter(nullptr);
+    builder.drawRect(rect);
+    builder.restore();
     sk_sp<DisplayList> display_list = builder.Build();
     ASSERT_EQ(display_list->bounds(), build_bounds);
   }
@@ -420,12 +384,11 @@
     // Verifying that the save layer bounds are not relevant
     // to the behavior in the previous example
     DisplayListBuilder builder(build_bounds);
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.setColorFilter(&alpha_color_filter);
-    receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-    receiver.setColorFilter(nullptr);
-    receiver.drawRect(rect);
-    receiver.restore();
+    builder.setColorFilter(&alpha_color_filter);
+    builder.saveLayer(nullptr, true);
+    builder.setColorFilter(nullptr);
+    builder.drawRect(rect);
+    builder.restore();
     sk_sp<DisplayList> display_list = builder.Build();
     ASSERT_EQ(display_list->bounds(), build_bounds);
   }
@@ -434,13 +397,12 @@
     // Making sure hiding a ColorFilter as an ImageFilter will
     // generate the same behavior as setting it as a ColorFilter
     DisplayListBuilder builder(build_bounds);
-    DlOpReceiver& receiver = ToReceiver(builder);
     DlColorFilterImageFilter color_filter_image_filter(base_color_filter);
-    receiver.setImageFilter(&color_filter_image_filter);
-    receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes);
-    receiver.setImageFilter(nullptr);
-    receiver.drawRect(rect);
-    receiver.restore();
+    builder.setImageFilter(&color_filter_image_filter);
+    builder.saveLayer(&save_bounds, true);
+    builder.setImageFilter(nullptr);
+    builder.drawRect(rect);
+    builder.restore();
     sk_sp<DisplayList> display_list = builder.Build();
     ASSERT_EQ(display_list->bounds(), rect);
   }
@@ -449,13 +411,12 @@
     // Making sure hiding a problematic ColorFilter as an ImageFilter
     // will generate the same behavior as setting it as a ColorFilter
     DisplayListBuilder builder(build_bounds);
-    DlOpReceiver& receiver = ToReceiver(builder);
     DlColorFilterImageFilter color_filter_image_filter(alpha_color_filter);
-    receiver.setImageFilter(&color_filter_image_filter);
-    receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes);
-    receiver.setImageFilter(nullptr);
-    receiver.drawRect(rect);
-    receiver.restore();
+    builder.setImageFilter(&color_filter_image_filter);
+    builder.saveLayer(&save_bounds, true);
+    builder.setImageFilter(nullptr);
+    builder.drawRect(rect);
+    builder.restore();
     sk_sp<DisplayList> display_list = builder.Build();
     ASSERT_EQ(display_list->bounds(), build_bounds);
   }
@@ -463,13 +424,12 @@
   {
     // Same as above (ImageFilter hiding ColorFilter) with no save bounds
     DisplayListBuilder builder(build_bounds);
-    DlOpReceiver& receiver = ToReceiver(builder);
     DlColorFilterImageFilter color_filter_image_filter(alpha_color_filter);
-    receiver.setImageFilter(&color_filter_image_filter);
-    receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-    receiver.setImageFilter(nullptr);
-    receiver.drawRect(rect);
-    receiver.restore();
+    builder.setImageFilter(&color_filter_image_filter);
+    builder.saveLayer(nullptr, true);
+    builder.setImageFilter(nullptr);
+    builder.drawRect(rect);
+    builder.restore();
     sk_sp<DisplayList> display_list = builder.Build();
     ASSERT_EQ(display_list->bounds(), build_bounds);
   }
@@ -477,12 +437,11 @@
   {
     // Testing behavior with an unboundable blend mode
     DisplayListBuilder builder(build_bounds);
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.setBlendMode(DlBlendMode::kClear);
-    receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes);
-    receiver.setBlendMode(DlBlendMode::kSrcOver);
-    receiver.drawRect(rect);
-    receiver.restore();
+    builder.setBlendMode(DlBlendMode::kClear);
+    builder.saveLayer(&save_bounds, true);
+    builder.setBlendMode(DlBlendMode::kSrcOver);
+    builder.drawRect(rect);
+    builder.restore();
     sk_sp<DisplayList> display_list = builder.Build();
     ASSERT_EQ(display_list->bounds(), build_bounds);
   }
@@ -490,18 +449,17 @@
   {
     // Same as previous with no save bounds
     DisplayListBuilder builder(build_bounds);
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.setBlendMode(DlBlendMode::kClear);
-    receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-    receiver.setBlendMode(DlBlendMode::kSrcOver);
-    receiver.drawRect(rect);
-    receiver.restore();
+    builder.setBlendMode(DlBlendMode::kClear);
+    builder.saveLayer(nullptr, true);
+    builder.setBlendMode(DlBlendMode::kSrcOver);
+    builder.drawRect(rect);
+    builder.restore();
     sk_sp<DisplayList> display_list = builder.Build();
     ASSERT_EQ(display_list->bounds(), build_bounds);
   }
 }
 
-TEST_F(DisplayListTest, NestedOpCountMetricsSameAsSkPicture) {
+TEST(DisplayList, NestedOpCountMetricsSameAsSkPicture) {
   SkPictureRecorder recorder;
   recorder.beginRecording(SkRect::MakeWH(150, 100));
   SkCanvas* canvas = recorder.getRecordingCanvas();
@@ -522,16 +480,14 @@
   ASSERT_EQ(picture->approximateOpCount(true), 36);
 
   DisplayListBuilder builder(SkRect::MakeWH(150, 100));
-  DlOpReceiver& receiver = ToReceiver(builder);
   for (int y = 10; y <= 60; y += 10) {
     for (int x = 10; x <= 60; x += 10) {
-      receiver.setColor(((x + y) % 20) == 10 ? SK_ColorRED : SK_ColorBLUE);
-      receiver.drawRect(SkRect::MakeXYWH(x, y, 80, 80));
+      builder.setColor(((x + y) % 20) == 10 ? SK_ColorRED : SK_ColorBLUE);
+      builder.drawRect(SkRect::MakeXYWH(x, y, 80, 80));
     }
   }
   DisplayListBuilder outer_builder(SkRect::MakeWH(150, 100));
-  DlOpReceiver& outer_receiver = ToReceiver(outer_builder);
-  outer_receiver.drawDisplayList(builder.Build());
+  outer_builder.drawDisplayList(builder.Build());
 
   auto display_list = outer_builder.Build();
   ASSERT_EQ(display_list->op_count(), 1u);
@@ -543,7 +499,7 @@
             static_cast<int>(display_list->op_count(true)));
 }
 
-TEST_F(DisplayListTest, DisplayListFullPerspectiveTransformHandling) {
+TEST(DisplayList, DisplayListFullPerspectiveTransformHandling) {
   // SkM44 constructor takes row-major order
   SkM44 sk_matrix = SkM44(
       // clang-format off
@@ -556,9 +512,8 @@
 
   {  // First test ==
     DisplayListBuilder builder;
-    DlOpReceiver& receiver = ToReceiver(builder);
-    // receiver.transformFullPerspective takes row-major order
-    receiver.transformFullPerspective(
+    // builder.transformFullPerspective takes row-major order
+    builder.transformFullPerspective(
         // clang-format off
          1,  2,  3,  4,
          5,  6,  7,  8,
@@ -569,18 +524,14 @@
     sk_sp<DisplayList> display_list = builder.Build();
     sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(10, 10);
     SkCanvas* canvas = surface->getCanvas();
-    // We can't use DlSkCanvas.DrawDisplayList as that method protects
-    // the canvas against mutations from the display list being drawn.
-    auto dispatcher = DlSkCanvasDispatcher(surface->getCanvas());
-    display_list->Dispatch(dispatcher);
+    display_list->RenderTo(canvas);
     SkM44 dl_matrix = canvas->getLocalToDevice();
     ASSERT_EQ(sk_matrix, dl_matrix);
   }
   {  // Next test !=
     DisplayListBuilder builder;
-    DlOpReceiver& receiver = ToReceiver(builder);
-    // receiver.transformFullPerspective takes row-major order
-    receiver.transformFullPerspective(
+    // builder.transformFullPerspective takes row-major order
+    builder.transformFullPerspective(
         // clang-format off
          1,  5,  9, 13,
          2,  6,  7, 11,
@@ -591,41 +542,33 @@
     sk_sp<DisplayList> display_list = builder.Build();
     sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(10, 10);
     SkCanvas* canvas = surface->getCanvas();
-    // We can't use DlSkCanvas.DrawDisplayList as that method protects
-    // the canvas against mutations from the display list being drawn.
-    auto dispatcher = DlSkCanvasDispatcher(surface->getCanvas());
-    display_list->Dispatch(dispatcher);
+    display_list->RenderTo(canvas);
     SkM44 dl_matrix = canvas->getLocalToDevice();
     ASSERT_NE(sk_matrix, dl_matrix);
   }
 }
 
-TEST_F(DisplayListTest, DisplayListTransformResetHandling) {
+TEST(DisplayList, DisplayListTransformResetHandling) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.scale(20.0, 20.0);
-  receiver.transformReset();
-  auto display_list = builder.Build();
-  ASSERT_NE(display_list, nullptr);
+  builder.scale(20.0, 20.0);
+  builder.transformReset();
+  auto list = builder.Build();
+  ASSERT_NE(list, nullptr);
   sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(10, 10);
   SkCanvas* canvas = surface->getCanvas();
-  // We can't use DlSkCanvas.DrawDisplayList as that method protects
-  // the canvas against mutations from the display list being drawn.
-  auto dispatcher = DlSkCanvasDispatcher(surface->getCanvas());
-  display_list->Dispatch(dispatcher);
+  list->RenderTo(canvas);
   ASSERT_TRUE(canvas->getTotalMatrix().isIdentity());
 }
 
-TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) {
+TEST(DisplayList, SingleOpsMightSupportGroupOpacityWithOrWithoutBlendMode) {
   auto run_tests = [](const std::string& name,
-                      void build(DlOpReceiver & receiver), bool expect_for_op,
-                      bool expect_with_kSrc) {
+                      void build(DisplayListBuilder & builder),
+                      bool expect_for_op, bool expect_with_kSrc) {
     {
       // First test is the draw op, by itself
       // (usually supports group opacity)
       DisplayListBuilder builder;
-      DlOpReceiver& receiver = ToReceiver(builder);
-      build(receiver);
+      build(builder);
       auto display_list = builder.Build();
       EXPECT_EQ(display_list->can_apply_group_opacity(), expect_for_op)
           << "{" << std::endl
@@ -636,13 +579,12 @@
       // Second test i the draw op with kSrc,
       // (usually fails group opacity)
       DisplayListBuilder builder;
-      DlOpReceiver& receiver = ToReceiver(builder);
-      receiver.setBlendMode(DlBlendMode::kSrc);
-      build(receiver);
+      builder.setBlendMode(DlBlendMode::kSrc);
+      build(builder);
       auto display_list = builder.Build();
       EXPECT_EQ(display_list->can_apply_group_opacity(), expect_with_kSrc)
           << "{" << std::endl
-          << "  receiver.setBlendMode(kSrc);" << std::endl
+          << "  builder.setBlendMode(kSrc);" << std::endl
           << "  " << name << std::endl
           << "}";
     }
@@ -650,163 +592,150 @@
 
 #define RUN_TESTS(body) \
   run_tests(            \
-      #body, [](DlOpReceiver& receiver) { body }, true, false)
+      #body, [](DisplayListBuilder& builder) { body }, true, false)
 #define RUN_TESTS2(body, expect) \
   run_tests(                     \
-      #body, [](DlOpReceiver& receiver) { body }, expect, expect)
+      #body, [](DisplayListBuilder& builder) { body }, expect, expect)
 
-  RUN_TESTS(receiver.drawPaint(););
-  RUN_TESTS2(receiver.drawColor(SK_ColorRED, DlBlendMode::kSrcOver);, true);
-  RUN_TESTS2(receiver.drawColor(SK_ColorRED, DlBlendMode::kSrc);, false);
-  RUN_TESTS(receiver.drawLine({0, 0}, {10, 10}););
-  RUN_TESTS(receiver.drawRect({0, 0, 10, 10}););
-  RUN_TESTS(receiver.drawOval({0, 0, 10, 10}););
-  RUN_TESTS(receiver.drawCircle({10, 10}, 5););
-  RUN_TESTS(receiver.drawRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2)););
-  RUN_TESTS(receiver.drawDRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2),
-                                SkRRect::MakeRectXY({2, 2, 8, 8}, 2, 2)););
-  RUN_TESTS(receiver.drawPath(
+  RUN_TESTS(builder.drawPaint(););
+  RUN_TESTS2(builder.drawColor(SK_ColorRED, DlBlendMode::kSrcOver);, true);
+  RUN_TESTS2(builder.drawColor(SK_ColorRED, DlBlendMode::kSrc);, false);
+  RUN_TESTS(builder.drawLine({0, 0}, {10, 10}););
+  RUN_TESTS(builder.drawRect({0, 0, 10, 10}););
+  RUN_TESTS(builder.drawOval({0, 0, 10, 10}););
+  RUN_TESTS(builder.drawCircle({10, 10}, 5););
+  RUN_TESTS(builder.drawRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2)););
+  RUN_TESTS(builder.drawDRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2),
+                               SkRRect::MakeRectXY({2, 2, 8, 8}, 2, 2)););
+  RUN_TESTS(builder.drawPath(
       SkPath().addOval({0, 0, 10, 10}).addOval({5, 5, 15, 15})););
-  RUN_TESTS(receiver.drawArc({0, 0, 10, 10}, 0, math::kPi, true););
-  RUN_TESTS2(
-      receiver.drawPoints(PointMode::kPoints, TestPointCount, TestPoints);
-      , false);
-  RUN_TESTS2(receiver.drawVertices(TestVertices1.get(), DlBlendMode::kSrc);
+  RUN_TESTS(builder.drawArc({0, 0, 10, 10}, 0, math::kPi, true););
+  RUN_TESTS2(builder.drawPoints(PointMode::kPoints, TestPointCount, TestPoints);
              , false);
-  RUN_TESTS(receiver.drawImage(TestImage1, {0, 0}, kLinearSampling, true););
-  RUN_TESTS2(receiver.drawImage(TestImage1, {0, 0}, kLinearSampling, false);
+  RUN_TESTS2(builder.drawVertices(TestVertices1, DlBlendMode::kSrc);, false);
+  RUN_TESTS(builder.drawImage(TestImage1, {0, 0}, kLinearSampling, true););
+  RUN_TESTS2(builder.drawImage(TestImage1, {0, 0}, kLinearSampling, false);
              , true);
-  RUN_TESTS(receiver.drawImageRect(TestImage1, {10, 10, 20, 20}, {0, 0, 10, 10},
-                                   kNearestSampling, true,
-                                   DlCanvas::SrcRectConstraint::kFast););
-  RUN_TESTS2(receiver.drawImageRect(TestImage1, {10, 10, 20, 20},
-                                    {0, 0, 10, 10}, kNearestSampling, false,
-                                    DlCanvas::SrcRectConstraint::kFast);
+  RUN_TESTS(builder.drawImageRect(TestImage1, {10, 10, 20, 20}, {0, 0, 10, 10},
+                                  kNearestSampling, true););
+  RUN_TESTS2(builder.drawImageRect(TestImage1, {10, 10, 20, 20}, {0, 0, 10, 10},
+                                   kNearestSampling, false);
              , true);
-  RUN_TESTS(receiver.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20},
-                                   DlFilterMode::kLinear, true););
-  RUN_TESTS2(
-      receiver.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20},
-                             DlFilterMode::kLinear, false);
-      , true);
+  RUN_TESTS(builder.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20},
+                                  DlFilterMode::kLinear, true););
+  RUN_TESTS2(builder.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20},
+                                   DlFilterMode::kLinear, false);
+             , true);
   static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
   static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}};
   RUN_TESTS2(
-      receiver.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
-                         DlBlendMode::kSrcIn, kNearestSampling, nullptr, true);
+      builder.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
+                        DlBlendMode::kSrcIn, kNearestSampling, nullptr, true);
       , false);
   RUN_TESTS2(
-      receiver.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
-                         DlBlendMode::kSrcIn, kNearestSampling, nullptr, false);
+      builder.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
+                        DlBlendMode::kSrcIn, kNearestSampling, nullptr, false);
       , false);
   EXPECT_TRUE(TestDisplayList1->can_apply_group_opacity());
-  RUN_TESTS2(receiver.drawDisplayList(TestDisplayList1);, true);
+  RUN_TESTS2(builder.drawDisplayList(TestDisplayList1);, true);
   {
     static DisplayListBuilder builder;
-    builder.DrawRect({0, 0, 10, 10}, DlPaint());
-    builder.DrawRect({5, 5, 15, 15}, DlPaint());
+    builder.drawRect({0, 0, 10, 10});
+    builder.drawRect({5, 5, 15, 15});
     static auto display_list = builder.Build();
-    RUN_TESTS2(receiver.drawDisplayList(display_list);, false);
+    RUN_TESTS2(builder.drawDisplayList(display_list);, false);
   }
-  RUN_TESTS(receiver.drawTextBlob(TestBlob1, 0, 0););
-  RUN_TESTS2(receiver.drawShadow(kTestPath1, SK_ColorBLACK, 1.0, false, 1.0);
+  RUN_TESTS(builder.drawTextBlob(TestBlob1, 0, 0););
+  RUN_TESTS2(builder.drawShadow(kTestPath1, SK_ColorBLACK, 1.0, false, 1.0);
              , false);
 
 #undef RUN_TESTS2
 #undef RUN_TESTS
 }
 
-TEST_F(DisplayListTest, OverlappingOpsDoNotSupportGroupOpacity) {
+TEST(DisplayList, OverlappingOpsDoNotSupportGroupOpacity) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
   for (int i = 0; i < 10; i++) {
-    receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30));
+    builder.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30));
   }
   auto display_list = builder.Build();
   EXPECT_FALSE(display_list->can_apply_group_opacity());
 }
 
-TEST_F(DisplayListTest, SaveLayerFalseSupportsGroupOpacityOverlappingChidren) {
+TEST(DisplayList, SaveLayerFalseSupportsGroupOpacityWithOverlappingChidren) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
+  builder.saveLayer(nullptr, false);
   for (int i = 0; i < 10; i++) {
-    receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30));
+    builder.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30));
   }
-  receiver.restore();
+  builder.restore();
   auto display_list = builder.Build();
   EXPECT_TRUE(display_list->can_apply_group_opacity());
 }
 
-TEST_F(DisplayListTest, SaveLayerTrueSupportsGroupOpacityOverlappingChidren) {
+TEST(DisplayList, SaveLayerTrueSupportsGroupOpacityWithOverlappingChidren) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
+  builder.saveLayer(nullptr, true);
   for (int i = 0; i < 10; i++) {
-    receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30));
+    builder.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30));
   }
-  receiver.restore();
+  builder.restore();
   auto display_list = builder.Build();
   EXPECT_TRUE(display_list->can_apply_group_opacity());
 }
 
-TEST_F(DisplayListTest, SaveLayerFalseWithSrcBlendSupportsGroupOpacity) {
+TEST(DisplayList, SaveLayerFalseWithSrcBlendSupportsGroupOpacity) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.setBlendMode(DlBlendMode::kSrc);
-  receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
-  receiver.drawRect({0, 0, 10, 10});
-  receiver.restore();
+  builder.setBlendMode(DlBlendMode::kSrc);
+  builder.saveLayer(nullptr, false);
+  builder.drawRect({0, 0, 10, 10});
+  builder.restore();
   auto display_list = builder.Build();
   EXPECT_TRUE(display_list->can_apply_group_opacity());
 }
 
-TEST_F(DisplayListTest, SaveLayerTrueWithSrcBlendDoesNotSupportGroupOpacity) {
+TEST(DisplayList, SaveLayerTrueWithSrcBlendDoesNotSupportGroupOpacity) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.setBlendMode(DlBlendMode::kSrc);
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.drawRect({0, 0, 10, 10});
-  receiver.restore();
+  builder.setBlendMode(DlBlendMode::kSrc);
+  builder.saveLayer(nullptr, true);
+  builder.drawRect({0, 0, 10, 10});
+  builder.restore();
   auto display_list = builder.Build();
   EXPECT_FALSE(display_list->can_apply_group_opacity());
 }
 
-TEST_F(DisplayListTest, SaveLayerFalseSupportsGroupOpacityWithChildSrcBlend) {
+TEST(DisplayList, SaveLayerFalseSupportsGroupOpacityWithChildSrcBlend) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
-  receiver.setBlendMode(DlBlendMode::kSrc);
-  receiver.drawRect({0, 0, 10, 10});
-  receiver.restore();
+  builder.saveLayer(nullptr, false);
+  builder.setBlendMode(DlBlendMode::kSrc);
+  builder.drawRect({0, 0, 10, 10});
+  builder.restore();
   auto display_list = builder.Build();
   EXPECT_TRUE(display_list->can_apply_group_opacity());
 }
 
-TEST_F(DisplayListTest, SaveLayerTrueSupportsGroupOpacityWithChildSrcBlend) {
+TEST(DisplayList, SaveLayerTrueSupportsGroupOpacityWithChildSrcBlend) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.setBlendMode(DlBlendMode::kSrc);
-  receiver.drawRect({0, 0, 10, 10});
-  receiver.restore();
+  builder.saveLayer(nullptr, true);
+  builder.setBlendMode(DlBlendMode::kSrc);
+  builder.drawRect({0, 0, 10, 10});
+  builder.restore();
   auto display_list = builder.Build();
   EXPECT_TRUE(display_list->can_apply_group_opacity());
 }
 
-TEST_F(DisplayListTest, SaveLayerBoundsSnapshotsImageFilter) {
+TEST(DisplayList, SaveLayerBoundsSnapshotsImageFilter) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.drawRect({50, 50, 100, 100});
+  builder.saveLayer(nullptr, true);
+  builder.drawRect({50, 50, 100, 100});
   // This image filter should be ignored since it was not set before saveLayer
-  receiver.setImageFilter(&kTestBlurImageFilter1);
-  receiver.restore();
+  builder.setImageFilter(&kTestBlurImageFilter1);
+  builder.restore();
   SkRect bounds = builder.Build()->bounds();
   EXPECT_EQ(bounds, SkRect::MakeLTRB(50, 50, 100, 100));
 }
 
-class SaveLayerOptionsExpector : public virtual DlOpReceiver,
+class SaveLayerOptionsExpector : public virtual Dispatcher,
                                  public IgnoreAttributeDispatchHelper,
                                  public IgnoreClipDispatchHelper,
                                  public IgnoreTransformDispatchHelper,
@@ -833,54 +762,51 @@
   int save_layer_count_ = 0;
 };
 
-TEST_F(DisplayListTest, SaveLayerOneSimpleOpInheritsOpacity) {
+TEST(DisplayList, SaveLayerOneSimpleOpSupportsOpacityOptimization) {
   SaveLayerOptions expected =
       SaveLayerOptions::kWithAttributes.with_can_distribute_opacity();
   SaveLayerOptionsExpector expector(expected);
 
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.setColor(SkColorSetARGB(127, 255, 255, 255));
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.drawRect({10, 10, 20, 20});
-  receiver.restore();
+  builder.setColor(SkColorSetARGB(127, 255, 255, 255));
+  builder.saveLayer(nullptr, true);
+  builder.drawRect({10, 10, 20, 20});
+  builder.restore();
 
   builder.Build()->Dispatch(expector);
   EXPECT_EQ(expector.save_layer_count(), 1);
 }
 
-TEST_F(DisplayListTest, SaveLayerNoAttributesInheritsOpacity) {
+TEST(DisplayList, SaveLayerNoAttributesSupportsOpacityOptimization) {
   SaveLayerOptions expected =
       SaveLayerOptions::kNoAttributes.with_can_distribute_opacity();
   SaveLayerOptionsExpector expector(expected);
 
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
-  receiver.drawRect({10, 10, 20, 20});
-  receiver.restore();
+  builder.saveLayer(nullptr, false);
+  builder.drawRect({10, 10, 20, 20});
+  builder.restore();
 
   builder.Build()->Dispatch(expector);
   EXPECT_EQ(expector.save_layer_count(), 1);
 }
 
-TEST_F(DisplayListTest, SaveLayerTwoOverlappingOpsDoesNotInheritOpacity) {
+TEST(DisplayList, SaveLayerTwoOverlappingOpsPreventsOpacityOptimization) {
   SaveLayerOptions expected = SaveLayerOptions::kWithAttributes;
   SaveLayerOptionsExpector expector(expected);
 
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.setColor(SkColorSetARGB(127, 255, 255, 255));
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.drawRect({10, 10, 20, 20});
-  receiver.drawRect({15, 15, 25, 25});
-  receiver.restore();
+  builder.setColor(SkColorSetARGB(127, 255, 255, 255));
+  builder.saveLayer(nullptr, true);
+  builder.drawRect({10, 10, 20, 20});
+  builder.drawRect({15, 15, 25, 25});
+  builder.restore();
 
   builder.Build()->Dispatch(expector);
   EXPECT_EQ(expector.save_layer_count(), 1);
 }
 
-TEST_F(DisplayListTest, NestedSaveLayersMightInheritOpacity) {
+TEST(DisplayList, NestedSaveLayersMightSupportOpacityOptimization) {
   SaveLayerOptions expected1 =
       SaveLayerOptions::kWithAttributes.with_can_distribute_opacity();
   SaveLayerOptions expected2 = SaveLayerOptions::kWithAttributes;
@@ -889,22 +815,21 @@
   SaveLayerOptionsExpector expector({expected1, expected2, expected3});
 
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.setColor(SkColorSetARGB(127, 255, 255, 255));
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.drawRect({10, 10, 20, 20});
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.drawRect({15, 15, 25, 25});
-  receiver.restore();
-  receiver.restore();
-  receiver.restore();
+  builder.setColor(SkColorSetARGB(127, 255, 255, 255));
+  builder.saveLayer(nullptr, true);
+  builder.saveLayer(nullptr, true);
+  builder.drawRect({10, 10, 20, 20});
+  builder.saveLayer(nullptr, true);
+  builder.drawRect({15, 15, 25, 25});
+  builder.restore();
+  builder.restore();
+  builder.restore();
 
   builder.Build()->Dispatch(expector);
   EXPECT_EQ(expector.save_layer_count(), 3);
 }
 
-TEST_F(DisplayListTest, NestedSaveLayersCanBothSupportOpacityOptimization) {
+TEST(DisplayList, NestedSaveLayersCanBothSupportOpacityOptimization) {
   SaveLayerOptions expected1 =
       SaveLayerOptions::kWithAttributes.with_can_distribute_opacity();
   SaveLayerOptions expected2 =
@@ -912,119 +837,112 @@
   SaveLayerOptionsExpector expector({expected1, expected2});
 
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.setColor(SkColorSetARGB(127, 255, 255, 255));
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
-  receiver.drawRect({10, 10, 20, 20});
-  receiver.restore();
-  receiver.restore();
+  builder.setColor(SkColorSetARGB(127, 255, 255, 255));
+  builder.saveLayer(nullptr, true);
+  builder.saveLayer(nullptr, false);
+  builder.drawRect({10, 10, 20, 20});
+  builder.restore();
+  builder.restore();
 
   builder.Build()->Dispatch(expector);
   EXPECT_EQ(expector.save_layer_count(), 2);
 }
 
-TEST_F(DisplayListTest, SaveLayerImageFilterDoesNotInheritOpacity) {
+TEST(DisplayList, SaveLayerImageFilterPreventsOpacityOptimization) {
   SaveLayerOptions expected = SaveLayerOptions::kWithAttributes;
   SaveLayerOptionsExpector expector(expected);
 
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.setColor(SkColorSetARGB(127, 255, 255, 255));
-  receiver.setImageFilter(&kTestBlurImageFilter1);
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.setImageFilter(nullptr);
-  receiver.drawRect({10, 10, 20, 20});
-  receiver.restore();
+  builder.setColor(SkColorSetARGB(127, 255, 255, 255));
+  builder.setImageFilter(&kTestBlurImageFilter1);
+  builder.saveLayer(nullptr, true);
+  builder.setImageFilter(nullptr);
+  builder.drawRect({10, 10, 20, 20});
+  builder.restore();
 
   builder.Build()->Dispatch(expector);
   EXPECT_EQ(expector.save_layer_count(), 1);
 }
 
-TEST_F(DisplayListTest, SaveLayerColorFilterDoesNotInheritOpacity) {
+TEST(DisplayList, SaveLayerColorFilterPreventsOpacityOptimization) {
   SaveLayerOptions expected = SaveLayerOptions::kWithAttributes;
   SaveLayerOptionsExpector expector(expected);
 
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.setColor(SkColorSetARGB(127, 255, 255, 255));
-  receiver.setColorFilter(&kTestMatrixColorFilter1);
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.setColorFilter(nullptr);
-  receiver.drawRect({10, 10, 20, 20});
-  receiver.restore();
+  builder.setColor(SkColorSetARGB(127, 255, 255, 255));
+  builder.setColorFilter(&kTestMatrixColorFilter1);
+  builder.saveLayer(nullptr, true);
+  builder.setColorFilter(nullptr);
+  builder.drawRect({10, 10, 20, 20});
+  builder.restore();
 
   builder.Build()->Dispatch(expector);
   EXPECT_EQ(expector.save_layer_count(), 1);
 }
 
-TEST_F(DisplayListTest, SaveLayerSrcBlendDoesNotInheritOpacity) {
+TEST(DisplayList, SaveLayerSrcBlendPreventsOpacityOptimization) {
   SaveLayerOptions expected = SaveLayerOptions::kWithAttributes;
   SaveLayerOptionsExpector expector(expected);
 
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.setColor(SkColorSetARGB(127, 255, 255, 255));
-  receiver.setBlendMode(DlBlendMode::kSrc);
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.setBlendMode(DlBlendMode::kSrcOver);
-  receiver.drawRect({10, 10, 20, 20});
-  receiver.restore();
+  builder.setColor(SkColorSetARGB(127, 255, 255, 255));
+  builder.setBlendMode(DlBlendMode::kSrc);
+  builder.saveLayer(nullptr, true);
+  builder.setBlendMode(DlBlendMode::kSrcOver);
+  builder.drawRect({10, 10, 20, 20});
+  builder.restore();
 
   builder.Build()->Dispatch(expector);
   EXPECT_EQ(expector.save_layer_count(), 1);
 }
 
-TEST_F(DisplayListTest, SaveLayerImageFilterOnChildInheritsOpacity) {
+TEST(DisplayList, SaveLayerImageFilterOnChildSupportsOpacityOptimization) {
   SaveLayerOptions expected =
       SaveLayerOptions::kWithAttributes.with_can_distribute_opacity();
   SaveLayerOptionsExpector expector(expected);
 
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.setColor(SkColorSetARGB(127, 255, 255, 255));
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.setImageFilter(&kTestBlurImageFilter1);
-  receiver.drawRect({10, 10, 20, 20});
-  receiver.restore();
+  builder.setColor(SkColorSetARGB(127, 255, 255, 255));
+  builder.saveLayer(nullptr, true);
+  builder.setImageFilter(&kTestBlurImageFilter1);
+  builder.drawRect({10, 10, 20, 20});
+  builder.restore();
 
   builder.Build()->Dispatch(expector);
   EXPECT_EQ(expector.save_layer_count(), 1);
 }
 
-TEST_F(DisplayListTest, SaveLayerColorFilterOnChildDoesNotInheritOpacity) {
+TEST(DisplayList, SaveLayerColorFilterOnChildPreventsOpacityOptimization) {
   SaveLayerOptions expected = SaveLayerOptions::kWithAttributes;
   SaveLayerOptionsExpector expector(expected);
 
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.setColor(SkColorSetARGB(127, 255, 255, 255));
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.setColorFilter(&kTestMatrixColorFilter1);
-  receiver.drawRect({10, 10, 20, 20});
-  receiver.restore();
+  builder.setColor(SkColorSetARGB(127, 255, 255, 255));
+  builder.saveLayer(nullptr, true);
+  builder.setColorFilter(&kTestMatrixColorFilter1);
+  builder.drawRect({10, 10, 20, 20});
+  builder.restore();
 
   builder.Build()->Dispatch(expector);
   EXPECT_EQ(expector.save_layer_count(), 1);
 }
 
-TEST_F(DisplayListTest, SaveLayerSrcBlendOnChildDoesNotInheritOpacity) {
+TEST(DisplayList, SaveLayerSrcBlendOnChildPreventsOpacityOptimization) {
   SaveLayerOptions expected = SaveLayerOptions::kWithAttributes;
   SaveLayerOptionsExpector expector(expected);
 
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.setColor(SkColorSetARGB(127, 255, 255, 255));
-  receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-  receiver.setBlendMode(DlBlendMode::kSrc);
-  receiver.drawRect({10, 10, 20, 20});
-  receiver.restore();
+  builder.setColor(SkColorSetARGB(127, 255, 255, 255));
+  builder.saveLayer(nullptr, true);
+  builder.setBlendMode(DlBlendMode::kSrc);
+  builder.drawRect({10, 10, 20, 20});
+  builder.restore();
 
   builder.Build()->Dispatch(expector);
   EXPECT_EQ(expector.save_layer_count(), 1);
 }
 
-TEST_F(DisplayListTest, FlutterSvgIssue661BoundsWereEmpty) {
+TEST(DisplayList, FlutterSvgIssue661BoundsWereEmpty) {
   // See https://github.com/dnfield/flutter_svg/issues/661
 
   SkPath path1;
@@ -1095,40 +1013,42 @@
   path2.close();
 
   DisplayListBuilder builder;
-  DlPaint paint = DlPaint(DlColor::kWhite()).setAntiAlias(true);
   {
-    builder.Save();
-    builder.ClipRect({0, 0, 100, 100}, ClipOp::kIntersect, true);
+    builder.save();
+    builder.clipRect({0, 0, 100, 100}, ClipOp::kIntersect, true);
     {
-      builder.Save();
-      builder.Transform2DAffine(2.17391, 0, -2547.83,  //
+      builder.save();
+      builder.transform2DAffine(2.17391, 0, -2547.83,  //
                                 0, 2.04082, -500);
       {
-        builder.Save();
-        builder.ClipRect({1172, 245, 1218, 294}, ClipOp::kIntersect, true);
+        builder.save();
+        builder.clipRect({1172, 245, 1218, 294}, ClipOp::kIntersect, true);
         {
-          builder.SaveLayer(nullptr, nullptr, nullptr);
+          builder.saveLayer(nullptr, SaveLayerOptions::kWithAttributes,
+                            nullptr);
           {
-            builder.Save();
-            builder.Transform2DAffine(1.4375, 0, 1164.09,  //
+            builder.save();
+            builder.transform2DAffine(1.4375, 0, 1164.09,  //
                                       0, 1.53125, 236.548);
-            builder.DrawPath(path1, paint);
-            builder.Restore();
+            builder.setAntiAlias(1);
+            builder.setColor(0xffffffff);
+            builder.drawPath(path1);
+            builder.restore();
           }
           {
-            builder.Save();
-            builder.Transform2DAffine(1.4375, 0, 1164.09,  //
+            builder.save();
+            builder.transform2DAffine(1.4375, 0, 1164.09,  //
                                       0, 1.53125, 236.548);
-            builder.DrawPath(path2, paint);
-            builder.Restore();
+            builder.drawPath(path2);
+            builder.restore();
           }
-          builder.Restore();
+          builder.restore();
         }
-        builder.Restore();
+        builder.restore();
       }
-      builder.Restore();
+      builder.restore();
     }
-    builder.Restore();
+    builder.restore();
   }
   sk_sp<DisplayList> display_list = builder.Build();
   // Prior to the fix, the bounds were empty.
@@ -1150,17 +1070,16 @@
   EXPECT_EQ(display_list->bytes(), sizeof(DisplayList) + 352u);
 }
 
-TEST_F(DisplayListTest, TranslateAffectsCurrentTransform) {
+TEST(DisplayList, TranslateAffectsCurrentTransform) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.translate(12.3, 14.5);
+  builder.translate(12.3, 14.5);
   SkMatrix matrix = SkMatrix::Translate(12.3, 14.5);
   SkM44 m44 = SkM44(matrix);
   SkM44 cur_m44 = builder.GetTransformFullPerspective();
   SkMatrix cur_matrix = builder.GetTransform();
   ASSERT_EQ(cur_m44, m44);
   ASSERT_EQ(cur_matrix, matrix);
-  receiver.translate(10, 10);
+  builder.translate(10, 10);
   // CurrentTransform has changed
   ASSERT_NE(builder.GetTransformFullPerspective(), m44);
   ASSERT_NE(builder.GetTransform(), cur_matrix);
@@ -1169,17 +1088,16 @@
   ASSERT_EQ(cur_matrix, matrix);
 }
 
-TEST_F(DisplayListTest, ScaleAffectsCurrentTransform) {
+TEST(DisplayList, ScaleAffectsCurrentTransform) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.scale(12.3, 14.5);
+  builder.scale(12.3, 14.5);
   SkMatrix matrix = SkMatrix::Scale(12.3, 14.5);
   SkM44 m44 = SkM44(matrix);
   SkM44 cur_m44 = builder.GetTransformFullPerspective();
   SkMatrix cur_matrix = builder.GetTransform();
   ASSERT_EQ(cur_m44, m44);
   ASSERT_EQ(cur_matrix, matrix);
-  receiver.translate(10, 10);
+  builder.translate(10, 10);
   // CurrentTransform has changed
   ASSERT_NE(builder.GetTransformFullPerspective(), m44);
   ASSERT_NE(builder.GetTransform(), cur_matrix);
@@ -1188,17 +1106,16 @@
   ASSERT_EQ(cur_matrix, matrix);
 }
 
-TEST_F(DisplayListTest, RotateAffectsCurrentTransform) {
+TEST(DisplayList, RotateAffectsCurrentTransform) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.rotate(12.3);
+  builder.rotate(12.3);
   SkMatrix matrix = SkMatrix::RotateDeg(12.3);
   SkM44 m44 = SkM44(matrix);
   SkM44 cur_m44 = builder.GetTransformFullPerspective();
   SkMatrix cur_matrix = builder.GetTransform();
   ASSERT_EQ(cur_m44, m44);
   ASSERT_EQ(cur_matrix, matrix);
-  receiver.translate(10, 10);
+  builder.translate(10, 10);
   // CurrentTransform has changed
   ASSERT_NE(builder.GetTransformFullPerspective(), m44);
   ASSERT_NE(builder.GetTransform(), cur_matrix);
@@ -1207,17 +1124,16 @@
   ASSERT_EQ(cur_matrix, matrix);
 }
 
-TEST_F(DisplayListTest, SkewAffectsCurrentTransform) {
+TEST(DisplayList, SkewAffectsCurrentTransform) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.skew(12.3, 14.5);
+  builder.skew(12.3, 14.5);
   SkMatrix matrix = SkMatrix::Skew(12.3, 14.5);
   SkM44 m44 = SkM44(matrix);
   SkM44 cur_m44 = builder.GetTransformFullPerspective();
   SkMatrix cur_matrix = builder.GetTransform();
   ASSERT_EQ(cur_m44, m44);
   ASSERT_EQ(cur_matrix, matrix);
-  receiver.translate(10, 10);
+  builder.translate(10, 10);
   // CurrentTransform has changed
   ASSERT_NE(builder.GetTransformFullPerspective(), m44);
   ASSERT_NE(builder.GetTransform(), cur_matrix);
@@ -1226,11 +1142,10 @@
   ASSERT_EQ(cur_matrix, matrix);
 }
 
-TEST_F(DisplayListTest, TransformAffectsCurrentTransform) {
+TEST(DisplayList, TransformAffectsCurrentTransform) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.transform2DAffine(3, 0, 12.3,  //
-                             1, 5, 14.5);
+  builder.transform2DAffine(3, 0, 12.3,  //
+                            1, 5, 14.5);
   SkMatrix matrix = SkMatrix::MakeAll(3, 0, 12.3,  //
                                       1, 5, 14.5,  //
                                       0, 0, 1);
@@ -1239,7 +1154,7 @@
   SkMatrix cur_matrix = builder.GetTransform();
   ASSERT_EQ(cur_m44, m44);
   ASSERT_EQ(cur_matrix, matrix);
-  receiver.translate(10, 10);
+  builder.translate(10, 10);
   // CurrentTransform has changed
   ASSERT_NE(builder.GetTransformFullPerspective(), m44);
   ASSERT_NE(builder.GetTransform(), cur_matrix);
@@ -1248,13 +1163,12 @@
   ASSERT_EQ(cur_matrix, matrix);
 }
 
-TEST_F(DisplayListTest, FullTransformAffectsCurrentTransform) {
+TEST(DisplayList, FullTransformAffectsCurrentTransform) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.transformFullPerspective(3, 0, 4, 12.3,  //
-                                    1, 5, 3, 14.5,  //
-                                    0, 0, 7, 16.2,  //
-                                    0, 0, 0, 1);
+  builder.transformFullPerspective(3, 0, 4, 12.3,  //
+                                   1, 5, 3, 14.5,  //
+                                   0, 0, 7, 16.2,  //
+                                   0, 0, 0, 1);
   SkMatrix matrix = SkMatrix::MakeAll(3, 0, 12.3,  //
                                       1, 5, 14.5,  //
                                       0, 0, 1);
@@ -1266,7 +1180,7 @@
   SkMatrix cur_matrix = builder.GetTransform();
   ASSERT_EQ(cur_m44, m44);
   ASSERT_EQ(cur_matrix, matrix);
-  receiver.translate(10, 10);
+  builder.translate(10, 10);
   // CurrentTransform has changed
   ASSERT_NE(builder.GetTransformFullPerspective(), m44);
   ASSERT_NE(builder.GetTransform(), cur_matrix);
@@ -1275,11 +1189,10 @@
   ASSERT_EQ(cur_matrix, matrix);
 }
 
-TEST_F(DisplayListTest, ClipRectAffectsClipBounds) {
+TEST(DisplayList, ClipRectAffectsClipBounds) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
-  receiver.clipRect(clip_bounds, ClipOp::kIntersect, false);
+  builder.clipRect(clip_bounds, ClipOp::kIntersect, false);
 
   // Save initial return values for testing restored values
   SkRect initial_local_bounds = builder.GetLocalClipBounds();
@@ -1287,39 +1200,38 @@
   ASSERT_EQ(initial_local_bounds, clip_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_bounds);
 
-  receiver.save();
-  receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false);
+  builder.save();
+  builder.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false);
   // Both clip bounds have changed
   ASSERT_NE(builder.GetLocalClipBounds(), clip_bounds);
   ASSERT_NE(builder.GetDestinationClipBounds(), clip_bounds);
   // Previous return values have not changed
   ASSERT_EQ(initial_local_bounds, clip_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_bounds);
-  receiver.restore();
+  builder.restore();
 
   // save/restore returned the values to their original values
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 
-  receiver.save();
-  receiver.scale(2, 2);
+  builder.save();
+  builder.scale(2, 2);
   SkRect scaled_clip_bounds = SkRect::MakeLTRB(5.1, 5.65, 10.2, 12.85);
   ASSERT_EQ(builder.GetLocalClipBounds(), scaled_clip_bounds);
   // Destination bounds are unaffected by transform
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
-  receiver.restore();
+  builder.restore();
 
   // save/restore returned the values to their original values
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 }
 
-TEST_F(DisplayListTest, ClipRectDoAAAffectsClipBounds) {
+TEST(DisplayList, ClipRectDoAAAffectsClipBounds) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
   SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26);
-  receiver.clipRect(clip_bounds, ClipOp::kIntersect, true);
+  builder.clipRect(clip_bounds, ClipOp::kIntersect, true);
 
   // Save initial return values for testing restored values
   SkRect initial_local_bounds = builder.GetLocalClipBounds();
@@ -1327,59 +1239,57 @@
   ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
 
-  receiver.save();
-  receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true);
+  builder.save();
+  builder.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true);
   // Both clip bounds have changed
   ASSERT_NE(builder.GetLocalClipBounds(), clip_expanded_bounds);
   ASSERT_NE(builder.GetDestinationClipBounds(), clip_expanded_bounds);
   // Previous return values have not changed
   ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
-  receiver.restore();
+  builder.restore();
 
   // save/restore returned the values to their original values
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 
-  receiver.save();
-  receiver.scale(2, 2);
+  builder.save();
+  builder.scale(2, 2);
   SkRect scaled_expanded_bounds = SkRect::MakeLTRB(5, 5.5, 10.5, 13);
   ASSERT_EQ(builder.GetLocalClipBounds(), scaled_expanded_bounds);
   // Destination bounds are unaffected by transform
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_expanded_bounds);
-  receiver.restore();
+  builder.restore();
 
   // save/restore returned the values to their original values
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 }
 
-TEST_F(DisplayListTest, ClipRectAffectsClipBoundsWithMatrix) {
+TEST(DisplayList, ClipRectAffectsClipBoundsWithMatrix) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkRect clip_bounds_1 = SkRect::MakeLTRB(0, 0, 10, 10);
   SkRect clip_bounds_2 = SkRect::MakeLTRB(10, 10, 20, 20);
-  receiver.save();
-  receiver.clipRect(clip_bounds_1, ClipOp::kIntersect, false);
-  receiver.translate(10, 0);
-  receiver.clipRect(clip_bounds_1, ClipOp::kIntersect, false);
+  builder.save();
+  builder.clipRect(clip_bounds_1, ClipOp::kIntersect, false);
+  builder.translate(10, 0);
+  builder.clipRect(clip_bounds_1, ClipOp::kIntersect, false);
   ASSERT_TRUE(builder.GetDestinationClipBounds().isEmpty());
-  receiver.restore();
+  builder.restore();
 
-  receiver.save();
-  receiver.clipRect(clip_bounds_1, ClipOp::kIntersect, false);
-  receiver.translate(-10, -10);
-  receiver.clipRect(clip_bounds_2, ClipOp::kIntersect, false);
+  builder.save();
+  builder.clipRect(clip_bounds_1, ClipOp::kIntersect, false);
+  builder.translate(-10, -10);
+  builder.clipRect(clip_bounds_2, ClipOp::kIntersect, false);
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds_1);
-  receiver.restore();
+  builder.restore();
 }
 
-TEST_F(DisplayListTest, ClipRRectAffectsClipBounds) {
+TEST(DisplayList, ClipRRectAffectsClipBounds) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
   SkRRect clip = SkRRect::MakeRectXY(clip_bounds, 3, 2);
-  receiver.clipRRect(clip, ClipOp::kIntersect, false);
+  builder.clipRRect(clip, ClipOp::kIntersect, false);
 
   // Save initial return values for testing restored values
   SkRect initial_local_bounds = builder.GetLocalClipBounds();
@@ -1387,40 +1297,39 @@
   ASSERT_EQ(initial_local_bounds, clip_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_bounds);
 
-  receiver.save();
-  receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false);
+  builder.save();
+  builder.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false);
   // Both clip bounds have changed
   ASSERT_NE(builder.GetLocalClipBounds(), clip_bounds);
   ASSERT_NE(builder.GetDestinationClipBounds(), clip_bounds);
   // Previous return values have not changed
   ASSERT_EQ(initial_local_bounds, clip_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_bounds);
-  receiver.restore();
+  builder.restore();
 
   // save/restore returned the values to their original values
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 
-  receiver.save();
-  receiver.scale(2, 2);
+  builder.save();
+  builder.scale(2, 2);
   SkRect scaled_clip_bounds = SkRect::MakeLTRB(5.1, 5.65, 10.2, 12.85);
   ASSERT_EQ(builder.GetLocalClipBounds(), scaled_clip_bounds);
   // Destination bounds are unaffected by transform
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
-  receiver.restore();
+  builder.restore();
 
   // save/restore returned the values to their original values
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 }
 
-TEST_F(DisplayListTest, ClipRRectDoAAAffectsClipBounds) {
+TEST(DisplayList, ClipRRectDoAAAffectsClipBounds) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
   SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26);
   SkRRect clip = SkRRect::MakeRectXY(clip_bounds, 3, 2);
-  receiver.clipRRect(clip, ClipOp::kIntersect, true);
+  builder.clipRRect(clip, ClipOp::kIntersect, true);
 
   // Save initial return values for testing restored values
   SkRect initial_local_bounds = builder.GetLocalClipBounds();
@@ -1428,62 +1337,60 @@
   ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
 
-  receiver.save();
-  receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true);
+  builder.save();
+  builder.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true);
   // Both clip bounds have changed
   ASSERT_NE(builder.GetLocalClipBounds(), clip_expanded_bounds);
   ASSERT_NE(builder.GetDestinationClipBounds(), clip_expanded_bounds);
   // Previous return values have not changed
   ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
-  receiver.restore();
+  builder.restore();
 
   // save/restore returned the values to their original values
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 
-  receiver.save();
-  receiver.scale(2, 2);
+  builder.save();
+  builder.scale(2, 2);
   SkRect scaled_expanded_bounds = SkRect::MakeLTRB(5, 5.5, 10.5, 13);
   ASSERT_EQ(builder.GetLocalClipBounds(), scaled_expanded_bounds);
   // Destination bounds are unaffected by transform
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_expanded_bounds);
-  receiver.restore();
+  builder.restore();
 
   // save/restore returned the values to their original values
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 }
 
-TEST_F(DisplayListTest, ClipRRectAffectsClipBoundsWithMatrix) {
+TEST(DisplayList, ClipRRectAffectsClipBoundsWithMatrix) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkRect clip_bounds_1 = SkRect::MakeLTRB(0, 0, 10, 10);
   SkRect clip_bounds_2 = SkRect::MakeLTRB(10, 10, 20, 20);
   SkRRect clip1 = SkRRect::MakeRectXY(clip_bounds_1, 3, 2);
   SkRRect clip2 = SkRRect::MakeRectXY(clip_bounds_2, 3, 2);
 
-  receiver.save();
-  receiver.clipRRect(clip1, ClipOp::kIntersect, false);
-  receiver.translate(10, 0);
-  receiver.clipRRect(clip1, ClipOp::kIntersect, false);
+  builder.save();
+  builder.clipRRect(clip1, ClipOp::kIntersect, false);
+  builder.translate(10, 0);
+  builder.clipRRect(clip1, ClipOp::kIntersect, false);
   ASSERT_TRUE(builder.GetDestinationClipBounds().isEmpty());
-  receiver.restore();
+  builder.restore();
 
-  receiver.save();
-  receiver.clipRRect(clip1, ClipOp::kIntersect, false);
-  receiver.translate(-10, -10);
-  receiver.clipRRect(clip2, ClipOp::kIntersect, false);
+  builder.save();
+  builder.clipRRect(clip1, ClipOp::kIntersect, false);
+  builder.translate(-10, -10);
+  builder.clipRRect(clip2, ClipOp::kIntersect, false);
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds_1);
-  receiver.restore();
+  builder.restore();
 }
 
-TEST_F(DisplayListTest, ClipPathAffectsClipBounds) {
+TEST(DisplayList, ClipPathAffectsClipBounds) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
   SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7);
-  receiver.clipPath(clip, ClipOp::kIntersect, false);
+  builder.clipPath(clip, ClipOp::kIntersect, false);
 
   // Save initial return values for testing restored values
   SkRect initial_local_bounds = builder.GetLocalClipBounds();
@@ -1491,39 +1398,38 @@
   ASSERT_EQ(initial_local_bounds, clip_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_bounds);
 
-  receiver.save();
-  receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false);
+  builder.save();
+  builder.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false);
   // Both clip bounds have changed
   ASSERT_NE(builder.GetLocalClipBounds(), clip_bounds);
   ASSERT_NE(builder.GetDestinationClipBounds(), clip_bounds);
   // Previous return values have not changed
   ASSERT_EQ(initial_local_bounds, clip_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_bounds);
-  receiver.restore();
+  builder.restore();
 
   // save/restore returned the values to their original values
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 
-  receiver.save();
-  receiver.scale(2, 2);
+  builder.save();
+  builder.scale(2, 2);
   SkRect scaled_clip_bounds = SkRect::MakeLTRB(4.1, 4.65, 11.2, 13.85);
   ASSERT_EQ(builder.GetLocalClipBounds(), scaled_clip_bounds);
   // Destination bounds are unaffected by transform
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
-  receiver.restore();
+  builder.restore();
 
   // save/restore returned the values to their original values
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 }
 
-TEST_F(DisplayListTest, ClipPathDoAAAffectsClipBounds) {
+TEST(DisplayList, ClipPathDoAAAffectsClipBounds) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
   SkRect clip_expanded_bounds = SkRect::MakeLTRB(8, 9, 23, 28);
-  receiver.clipPath(clip, ClipOp::kIntersect, true);
+  builder.clipPath(clip, ClipOp::kIntersect, true);
 
   // Save initial return values for testing restored values
   SkRect initial_local_bounds = builder.GetLocalClipBounds();
@@ -1531,61 +1437,59 @@
   ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
 
-  receiver.save();
-  receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true);
+  builder.save();
+  builder.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true);
   // Both clip bounds have changed
   ASSERT_NE(builder.GetLocalClipBounds(), clip_expanded_bounds);
   ASSERT_NE(builder.GetDestinationClipBounds(), clip_expanded_bounds);
   // Previous return values have not changed
   ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
-  receiver.restore();
+  builder.restore();
 
   // save/restore returned the values to their original values
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 
-  receiver.save();
-  receiver.scale(2, 2);
+  builder.save();
+  builder.scale(2, 2);
   SkRect scaled_expanded_bounds = SkRect::MakeLTRB(4, 4.5, 11.5, 14);
   ASSERT_EQ(builder.GetLocalClipBounds(), scaled_expanded_bounds);
   // Destination bounds are unaffected by transform
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_expanded_bounds);
-  receiver.restore();
+  builder.restore();
 
   // save/restore returned the values to their original values
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 }
 
-TEST_F(DisplayListTest, ClipPathAffectsClipBoundsWithMatrix) {
+TEST(DisplayList, ClipPathAffectsClipBoundsWithMatrix) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkRect clip_bounds = SkRect::MakeLTRB(0, 0, 10, 10);
   SkPath clip1 = SkPath().addCircle(2.5, 2.5, 2.5).addCircle(7.5, 7.5, 2.5);
   SkPath clip2 = SkPath().addCircle(12.5, 12.5, 2.5).addCircle(17.5, 17.5, 2.5);
 
-  receiver.save();
-  receiver.clipPath(clip1, ClipOp::kIntersect, false);
-  receiver.translate(10, 0);
-  receiver.clipPath(clip1, ClipOp::kIntersect, false);
+  builder.save();
+  builder.clipPath(clip1, ClipOp::kIntersect, false);
+  builder.translate(10, 0);
+  builder.clipPath(clip1, ClipOp::kIntersect, false);
   ASSERT_TRUE(builder.GetDestinationClipBounds().isEmpty());
-  receiver.restore();
+  builder.restore();
 
-  receiver.save();
-  receiver.clipPath(clip1, ClipOp::kIntersect, false);
-  receiver.translate(-10, -10);
-  receiver.clipPath(clip2, ClipOp::kIntersect, false);
+  builder.save();
+  builder.clipPath(clip1, ClipOp::kIntersect, false);
+  builder.translate(-10, -10);
+  builder.clipPath(clip2, ClipOp::kIntersect, false);
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
-  receiver.restore();
+  builder.restore();
 }
 
-TEST_F(DisplayListTest, DiffClipRectDoesNotAffectClipBounds) {
+TEST(DisplayList, DiffClipRectDoesNotAffectClipBounds) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkRect diff_clip = SkRect::MakeLTRB(0, 0, 15, 15);
   SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
-  receiver.clipRect(clip_bounds, ClipOp::kIntersect, false);
+  builder.clipRect(clip_bounds, ClipOp::kIntersect, false);
 
   // Save initial return values for testing after kDifference clip
   SkRect initial_local_bounds = builder.GetLocalClipBounds();
@@ -1593,18 +1497,17 @@
   ASSERT_EQ(initial_local_bounds, clip_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_bounds);
 
-  receiver.clipRect(diff_clip, ClipOp::kDifference, false);
+  builder.clipRect(diff_clip, ClipOp::kDifference, false);
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 }
 
-TEST_F(DisplayListTest, DiffClipRRectDoesNotAffectClipBounds) {
+TEST(DisplayList, DiffClipRRectDoesNotAffectClipBounds) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkRRect diff_clip = SkRRect::MakeRectXY({0, 0, 15, 15}, 1, 1);
   SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
   SkRRect clip = SkRRect::MakeRectXY({10.2, 11.3, 20.4, 25.7}, 3, 2);
-  receiver.clipRRect(clip, ClipOp::kIntersect, false);
+  builder.clipRRect(clip, ClipOp::kIntersect, false);
 
   // Save initial return values for testing after kDifference clip
   SkRect initial_local_bounds = builder.GetLocalClipBounds();
@@ -1612,18 +1515,17 @@
   ASSERT_EQ(initial_local_bounds, clip_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_bounds);
 
-  receiver.clipRRect(diff_clip, ClipOp::kDifference, false);
+  builder.clipRRect(diff_clip, ClipOp::kDifference, false);
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 }
 
-TEST_F(DisplayListTest, DiffClipPathDoesNotAffectClipBounds) {
+TEST(DisplayList, DiffClipPathDoesNotAffectClipBounds) {
   DisplayListBuilder builder;
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkPath diff_clip = SkPath().addRect({0, 0, 15, 15});
   SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
   SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7);
-  receiver.clipPath(clip, ClipOp::kIntersect, false);
+  builder.clipPath(clip, ClipOp::kIntersect, false);
 
   // Save initial return values for testing after kDifference clip
   SkRect initial_local_bounds = builder.GetLocalClipBounds();
@@ -1631,43 +1533,40 @@
   ASSERT_EQ(initial_local_bounds, clip_bounds);
   ASSERT_EQ(initial_destination_bounds, clip_bounds);
 
-  receiver.clipPath(diff_clip, ClipOp::kDifference, false);
+  builder.clipPath(diff_clip, ClipOp::kDifference, false);
   ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds);
 }
 
-TEST_F(DisplayListTest, ClipPathWithInvertFillTypeDoesNotAffectClipBounds) {
+TEST(DisplayList, ClipPathWithInvertFillTypeDoesNotAffectClipBounds) {
   SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0);
   DisplayListBuilder builder(cull_rect);
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
   clip.setFillType(SkPathFillType::kInverseWinding);
-  receiver.clipPath(clip, ClipOp::kIntersect, false);
+  builder.clipPath(clip, ClipOp::kIntersect, false);
 
   ASSERT_EQ(builder.GetLocalClipBounds(), cull_rect);
   ASSERT_EQ(builder.GetDestinationClipBounds(), cull_rect);
 }
 
-TEST_F(DisplayListTest, DiffClipPathWithInvertFillTypeAffectsClipBounds) {
+TEST(DisplayList, DiffClipPathWithInvertFillTypeAffectsClipBounds) {
   SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0);
   DisplayListBuilder builder(cull_rect);
-  DlOpReceiver& receiver = ToReceiver(builder);
   SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
   clip.setFillType(SkPathFillType::kInverseWinding);
   SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7);
-  receiver.clipPath(clip, ClipOp::kDifference, false);
+  builder.clipPath(clip, ClipOp::kDifference, false);
 
   ASSERT_EQ(builder.GetLocalClipBounds(), clip_bounds);
   ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds);
 }
 
-TEST_F(DisplayListTest, FlatDrawPointsProducesBounds) {
+TEST(DisplayList, FlatDrawPointsProducesBounds) {
   SkPoint horizontal_points[2] = {{10, 10}, {20, 10}};
   SkPoint vertical_points[2] = {{10, 10}, {10, 20}};
   {
     DisplayListBuilder builder;
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.drawPoints(PointMode::kPolygon, 2, horizontal_points);
+    builder.drawPoints(PointMode::kPolygon, 2, horizontal_points);
     SkRect bounds = builder.Build()->bounds();
     EXPECT_TRUE(bounds.contains(10, 10));
     EXPECT_TRUE(bounds.contains(20, 10));
@@ -1675,8 +1574,7 @@
   }
   {
     DisplayListBuilder builder;
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.drawPoints(PointMode::kPolygon, 2, vertical_points);
+    builder.drawPoints(PointMode::kPolygon, 2, vertical_points);
     SkRect bounds = builder.Build()->bounds();
     EXPECT_TRUE(bounds.contains(10, 10));
     EXPECT_TRUE(bounds.contains(10, 20));
@@ -1684,16 +1582,14 @@
   }
   {
     DisplayListBuilder builder;
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.drawPoints(PointMode::kPoints, 1, horizontal_points);
+    builder.drawPoints(PointMode::kPoints, 1, horizontal_points);
     SkRect bounds = builder.Build()->bounds();
     EXPECT_TRUE(bounds.contains(10, 10));
   }
   {
     DisplayListBuilder builder;
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.setStrokeWidth(2);
-    receiver.drawPoints(PointMode::kPolygon, 2, horizontal_points);
+    builder.setStrokeWidth(2);
+    builder.drawPoints(PointMode::kPolygon, 2, horizontal_points);
     SkRect bounds = builder.Build()->bounds();
     EXPECT_TRUE(bounds.contains(10, 10));
     EXPECT_TRUE(bounds.contains(20, 10));
@@ -1701,9 +1597,8 @@
   }
   {
     DisplayListBuilder builder;
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.setStrokeWidth(2);
-    receiver.drawPoints(PointMode::kPolygon, 2, vertical_points);
+    builder.setStrokeWidth(2);
+    builder.drawPoints(PointMode::kPolygon, 2, vertical_points);
     SkRect bounds = builder.Build()->bounds();
     EXPECT_TRUE(bounds.contains(10, 10));
     EXPECT_TRUE(bounds.contains(10, 20));
@@ -1711,9 +1606,8 @@
   }
   {
     DisplayListBuilder builder;
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.setStrokeWidth(2);
-    receiver.drawPoints(PointMode::kPoints, 1, horizontal_points);
+    builder.setStrokeWidth(2);
+    builder.drawPoints(PointMode::kPoints, 1, horizontal_points);
     SkRect bounds = builder.Build()->bounds();
     EXPECT_TRUE(bounds.contains(10, 10));
     EXPECT_EQ(bounds, SkRect::MakeLTRB(9, 9, 11, 11));
@@ -1737,11 +1631,10 @@
   }
 }
 
-TEST_F(DisplayListTest, RTreeOfSimpleScene) {
+TEST(DisplayList, RTreeOfSimpleScene) {
   DisplayListBuilder builder(/*prepare_rtree=*/true);
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.drawRect({10, 10, 20, 20});
-  receiver.drawRect({50, 50, 60, 60});
+  builder.drawRect({10, 10, 20, 20});
+  builder.drawRect({50, 50, 60, 60});
   auto display_list = builder.Build();
   auto rtree = display_list->rtree();
   std::vector<SkRect> rects = {
@@ -1765,13 +1658,12 @@
   test_rtree(rtree, {19, 19, 51, 51}, rects, {0, 1});
 }
 
-TEST_F(DisplayListTest, RTreeOfSaveRestoreScene) {
+TEST(DisplayList, RTreeOfSaveRestoreScene) {
   DisplayListBuilder builder(/*prepare_rtree=*/true);
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.drawRect({10, 10, 20, 20});
-  receiver.save();
-  receiver.drawRect({50, 50, 60, 60});
-  receiver.restore();
+  builder.drawRect({10, 10, 20, 20});
+  builder.save();
+  builder.drawRect({50, 50, 60, 60});
+  builder.restore();
   auto display_list = builder.Build();
   auto rtree = display_list->rtree();
   std::vector<SkRect> rects = {
@@ -1795,7 +1687,7 @@
   test_rtree(rtree, {19, 19, 51, 51}, rects, {0, 1});
 }
 
-TEST_F(DisplayListTest, RTreeOfSaveLayerFilterScene) {
+TEST(DisplayList, RTreeOfSaveLayerFilterScene) {
   DisplayListBuilder builder(/*prepare_rtree=*/true);
   // blur filter with sigma=1 expands by 3 on all sides
   auto filter = DlBlurImageFilter(1.0, 1.0, DlTileMode::kClamp);
@@ -1830,16 +1722,14 @@
   test_rtree(rtree, {19, 19, 51, 51}, rects, {0, 1});
 }
 
-TEST_F(DisplayListTest, NestedDisplayListRTreesAreSparse) {
+TEST(DisplayList, NestedDisplayListRTreesAreSparse) {
   DisplayListBuilder nested_dl_builder(/**prepare_rtree=*/true);
-  DlOpReceiver& nested_dl_receiver = ToReceiver(nested_dl_builder);
-  nested_dl_receiver.drawRect({10, 10, 20, 20});
-  nested_dl_receiver.drawRect({50, 50, 60, 60});
+  nested_dl_builder.drawRect({10, 10, 20, 20});
+  nested_dl_builder.drawRect({50, 50, 60, 60});
   auto nested_display_list = nested_dl_builder.Build();
 
   DisplayListBuilder builder(/**prepare_rtree=*/true);
-  DlOpReceiver& receiver = ToReceiver(builder);
-  receiver.drawDisplayList(nested_display_list);
+  builder.drawDisplayList(nested_display_list);
   auto display_list = builder.Build();
 
   auto rtree = display_list->rtree();
@@ -1852,106 +1742,98 @@
   test_rtree(rtree, {19, 19, 51, 51}, rects, {0, 1});
 }
 
-TEST_F(DisplayListTest, RemoveUnnecessarySaveRestorePairs) {
+TEST(DisplayList, RemoveUnnecessarySaveRestorePairs) {
   {
     DisplayListBuilder builder;
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.drawRect({10, 10, 20, 20});
-    receiver.save();  // This save op is unnecessary
-    receiver.drawRect({50, 50, 60, 60});
-    receiver.restore();
+    builder.drawRect({10, 10, 20, 20});
+    builder.save();  // This save op is unnecessary
+    builder.drawRect({50, 50, 60, 60});
+    builder.restore();
 
     DisplayListBuilder builder2;
-    DlOpReceiver& receiver2 = ToReceiver(builder2);
-    receiver2.drawRect({10, 10, 20, 20});
-    receiver2.drawRect({50, 50, 60, 60});
+    builder2.drawRect({10, 10, 20, 20});
+    builder2.drawRect({50, 50, 60, 60});
     ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), builder2.Build()));
   }
 
   {
     DisplayListBuilder builder;
-    DlOpReceiver& receiver = ToReceiver(builder);
-    receiver.drawRect({10, 10, 20, 20});
-    receiver.save();
-    receiver.translate(1.0, 1.0);
+    builder.drawRect({10, 10, 20, 20});
+    builder.save();
+    builder.translate(1.0, 1.0);
     {
-      receiver.save();  // unnecessary
-      receiver.drawRect({50, 50, 60, 60});
-      receiver.restore();
+      builder.save();  // unnecessary
+      builder.drawRect({50, 50, 60, 60});
+      builder.restore();
     }
 
-    receiver.restore();
+    builder.restore();
 
     DisplayListBuilder builder2;
-    DlOpReceiver& receiver2 = ToReceiver(builder2);
-    receiver2.drawRect({10, 10, 20, 20});
-    receiver2.save();
-    receiver2.translate(1.0, 1.0);
-    { receiver2.drawRect({50, 50, 60, 60}); }
-    receiver2.restore();
+    builder2.drawRect({10, 10, 20, 20});
+    builder2.save();
+    builder2.translate(1.0, 1.0);
+    { builder2.drawRect({50, 50, 60, 60}); }
+    builder2.restore();
     ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), builder2.Build()));
   }
 }
 
-TEST_F(DisplayListTest, CollapseMultipleNestedSaveRestore) {
+TEST(DisplayList, CollapseMultipleNestedSaveRestore) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.save();
-  receiver1.translate(10, 10);
-  receiver1.scale(2, 2);
-  receiver1.clipRect({10, 10, 20, 20}, ClipOp::kIntersect, false);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.restore();
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.save();
+  builder1.translate(10, 10);
+  builder1.scale(2, 2);
+  builder1.clipRect({10, 10, 20, 20}, ClipOp::kIntersect, false);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.restore();
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.save();
-  receiver2.translate(10, 10);
-  receiver2.scale(2, 2);
-  receiver2.clipRect({10, 10, 20, 20}, ClipOp::kIntersect, false);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.restore();
+  builder2.save();
+  builder2.translate(10, 10);
+  builder2.scale(2, 2);
+  builder2.clipRect({10, 10, 20, 20}, ClipOp::kIntersect, false);
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.restore();
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, CollapseNestedSaveAndSaveLayerRestore) {
+TEST(DisplayList, CollapseNestedSaveAndSaveLayerRestore) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.scale(2, 2);
-  receiver1.restore();
-  receiver1.restore();
+  builder1.save();
+  builder1.saveLayer(nullptr, false);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.scale(2, 2);
+  builder1.restore();
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.scale(2, 2);
-  receiver2.restore();
+  builder2.saveLayer(nullptr, false);
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.scale(2, 2);
+  builder2.restore();
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, RemoveUnnecessarySaveRestorePairsInSetPaint) {
+TEST(DisplayList, RemoveUnnecessarySaveRestorePairsInSetPaint) {
   SkRect build_bounds = SkRect::MakeLTRB(-100, -100, 200, 200);
   SkRect rect = SkRect::MakeLTRB(30, 30, 70, 70);
   // clang-format off
   const float alpha_matrix[] = {
-      0, 0, 0, 0, 0,
-      0, 1, 0, 0, 0,
-      0, 0, 1, 0, 0,
-      0, 0, 0, 0, 1,
+    0, 0, 0, 0, 0,
+    0, 1, 0, 0, 0,
+    0, 0, 1, 0, 0,
+    0, 0, 0, 0, 1,
   };
   // clang-format on
   DlMatrixColorFilter alpha_color_filter(alpha_matrix);
@@ -1998,437 +1880,359 @@
   }
 }
 
-TEST_F(DisplayListTest, TransformTriggersDeferredSave) {
+TEST(DisplayList, TransformTriggersDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.transformFullPerspective(1, 0, 0, 10,   //
-                                     0, 1, 0, 100,  //
-                                     0, 0, 1, 0,    //
-                                     0, 0, 0, 1);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.transformFullPerspective(1, 0, 0, 10,   //
-                                     0, 1, 0, 100,  //
-                                     0, 0, 1, 0,    //
-                                     0, 0, 0, 1);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.transform(SkM44::Translate(10, 100));
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.transform(SkM44::Translate(10, 100));
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.save();
-  receiver2.transformFullPerspective(1, 0, 0, 10,   //
-                                     0, 1, 0, 100,  //
-                                     0, 0, 1, 0,    //
-                                     0, 0, 0, 1);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.restore();
-  receiver2.save();
-  receiver2.transformFullPerspective(1, 0, 0, 10,   //
-                                     0, 1, 0, 100,  //
-                                     0, 0, 1, 0,    //
-                                     0, 0, 0, 1);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.restore();
+  builder2.save();
+  builder2.transform(SkM44::Translate(10, 100));
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.restore();
+  builder2.save();
+  builder2.transform(SkM44::Translate(10, 100));
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.restore();
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, Transform2DTriggersDeferredSave) {
+TEST(DisplayList, Transform2DTriggersDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.transform2DAffine(0, 1, 12, 1, 0, 33);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.transform2DAffine(0, 1, 12, 1, 0, 33);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.save();
-  receiver2.transform2DAffine(0, 1, 12, 1, 0, 33);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.restore();
+  builder2.save();
+  builder2.transform2DAffine(0, 1, 12, 1, 0, 33);
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.restore();
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, TransformPerspectiveTriggersDeferredSave) {
+TEST(DisplayList, TransformPerspectiveTriggersDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.transformFullPerspective(0, 1, 0, 12,  //
-                                     1, 0, 0, 33,  //
-                                     3, 2, 5, 29,  //
-                                     0, 0, 0, 12);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.transformFullPerspective(0, 1, 0, 12, 1, 0, 0, 33, 3, 2, 5, 29, 0, 0,
+                                    0, 12);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.save();
-  receiver2.transformFullPerspective(0, 1, 0, 12,  //
-                                     1, 0, 0, 33,  //
-                                     3, 2, 5, 29,  //
-                                     0, 0, 0, 12);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.restore();
+  builder2.save();
+  builder2.transformFullPerspective(0, 1, 0, 12, 1, 0, 0, 33, 3, 2, 5, 29, 0, 0,
+                                    0, 12);
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.restore();
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, ResetTransformTriggersDeferredSave) {
+TEST(DisplayList, ResetTransformTriggersDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.transformReset();
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.transformReset();
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.save();
-  receiver2.transformReset();
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.restore();
+  builder2.save();
+  builder2.transformReset();
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.restore();
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, SkewTriggersDeferredSave) {
+TEST(DisplayList, SkewTriggersDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.skew(10, 10);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.skew(10, 10);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.save();
-  receiver2.skew(10, 10);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.restore();
+  builder2.save();
+  builder2.skew(10, 10);
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.restore();
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, TranslateTriggersDeferredSave) {
+TEST(DisplayList, TranslateTriggersDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.translate(10, 10);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.translate(10, 10);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.save();
-  receiver2.translate(10, 10);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.restore();
+  builder2.save();
+  builder2.translate(10, 10);
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.restore();
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, ScaleTriggersDeferredSave) {
+TEST(DisplayList, ScaleTriggersDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.scale(0.5, 0.5);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.scale(0.5, 0.5);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.save();
-  receiver2.scale(0.5, 0.5);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.restore();
+  builder2.save();
+  builder2.scale(0.5, 0.5);
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.restore();
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, ClipRectTriggersDeferredSave) {
+TEST(DisplayList, ClipRectTriggersDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.clipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect,
-                     true);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.transformFullPerspective(1, 0, 0, 0,  //
-                                     0, 1, 0, 0,  //
-                                     0, 0, 1, 0,  //
-                                     0, 0, 0, 1);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.clipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect, true);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.transform(SkM44());
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.save();
-  receiver2.clipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect,
-                     true);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.restore();
-  receiver2.transformFullPerspective(1, 0, 0, 0,  //
-                                     0, 1, 0, 0,  //
-                                     0, 0, 1, 0,  //
-                                     0, 0, 0, 1);
-  receiver2.drawRect({0, 0, 100, 100});
+  builder2.save();
+  builder2.clipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect, true);
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.restore();
+  builder2.transform(SkM44());
+  builder2.drawRect({0, 0, 100, 100});
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, ClipRRectTriggersDeferredSave) {
+TEST(DisplayList, ClipRRectTriggersDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.clipRRect(kTestRRect, ClipOp::kIntersect, true);
+  builder1.save();
+  builder1.save();
+  builder1.clipRRect(kTestRRect, ClipOp::kIntersect, true);
 
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.transformFullPerspective(1, 0, 0, 0,  //
-                                     0, 1, 0, 0,  //
-                                     0, 0, 1, 0,  //
-                                     0, 0, 0, 1);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.transform(SkM44());
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.save();
-  receiver2.clipRRect(kTestRRect, ClipOp::kIntersect, true);
+  builder2.save();
+  builder2.clipRRect(kTestRRect, ClipOp::kIntersect, true);
 
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.restore();
-  receiver2.transformFullPerspective(1, 0, 0, 0,  //
-                                     0, 1, 0, 0,  //
-                                     0, 0, 1, 0,  //
-                                     0, 0, 0, 1);
-  receiver2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.restore();
+  builder2.transform(SkM44());
+  builder2.drawRect({0, 0, 100, 100});
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, ClipPathTriggersDeferredSave) {
+TEST(DisplayList, ClipPathTriggersDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.clipPath(kTestPath1, ClipOp::kIntersect, true);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.transformFullPerspective(1, 0, 0, 0,  //
-                                     0, 1, 0, 0,  //
-                                     0, 0, 1, 0,  //
-                                     0, 0, 0, 1);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.clipPath(kTestPath1, ClipOp::kIntersect, true);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.transform(SkM44());
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.save();
-  receiver2.clipPath(kTestPath1, ClipOp::kIntersect, true);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.restore();
-  receiver2.transformFullPerspective(1, 0, 0, 0,  //
-                                     0, 1, 0, 0,  //
-                                     0, 0, 1, 0,  //
-                                     0, 0, 0, 1);
-  receiver2.drawRect({0, 0, 100, 100});
+  builder2.save();
+  builder2.clipPath(kTestPath1, ClipOp::kIntersect, true);
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.restore();
+  builder2.transform(SkM44());
+  builder2.drawRect({0, 0, 100, 100});
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, NOPTranslateDoesNotTriggerDeferredSave) {
+TEST(DisplayList, NOPTranslateDoesNotTriggerDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.translate(0, 0);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.translate(0, 0);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, NOPScaleDoesNotTriggerDeferredSave) {
+TEST(DisplayList, NOPScaleDoesNotTriggerDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.scale(1.0, 1.0);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.scale(1.0, 1.0);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, NOPRotationDoesNotTriggerDeferredSave) {
+TEST(DisplayList, NOPRotationDoesNotTriggerDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.rotate(360);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.rotate(360);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, NOPSkewDoesNotTriggerDeferredSave) {
+TEST(DisplayList, NOPSkewDoesNotTriggerDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.skew(0, 0);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.skew(0, 0);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, NOPTransformDoesNotTriggerDeferredSave) {
+TEST(DisplayList, NOPTransformDoesNotTriggerDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.transformFullPerspective(1, 0, 0, 0,  //
-                                     0, 1, 0, 0,  //
-                                     0, 0, 1, 0,  //
-                                     0, 0, 0, 1);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.transformFullPerspective(1, 0, 0, 0,  //
-                                     0, 1, 0, 0,  //
-                                     0, 0, 1, 0,  //
-                                     0, 0, 0, 1);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.transform(SkM44());
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.transform(SkM44());
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, NOPTransform2DDoesNotTriggerDeferredSave) {
+TEST(DisplayList, NOPTransform2DDoesNotTriggerDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.transform2DAffine(1, 0, 0, 0, 1, 0);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.transform2DAffine(1, 0, 0, 0, 1, 0);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, NOPTransformFullPerspectiveDoesNotTriggerDeferredSave) {
+TEST(DisplayList, NOPTransformFullPerspectiveDoesNotTriggerDeferredSave) {
   {
     DisplayListBuilder builder1;
-    DlOpReceiver& receiver1 = ToReceiver(builder1);
-    receiver1.save();
-    receiver1.save();
-    receiver1.transformFullPerspective(1, 0, 0, 0,  //
-                                       0, 1, 0, 0,  //
-                                       0, 0, 1, 0,  //
-                                       0, 0, 0, 1);
-    receiver1.drawRect({0, 0, 100, 100});
-    receiver1.restore();
-    receiver1.drawRect({0, 0, 100, 100});
-    receiver1.restore();
+    builder1.save();
+    builder1.save();
+    builder1.transformFullPerspective(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,
+                                      0, 1);
+    builder1.drawRect({0, 0, 100, 100});
+    builder1.restore();
+    builder1.drawRect({0, 0, 100, 100});
+    builder1.restore();
     auto display_list1 = builder1.Build();
 
     DisplayListBuilder builder2;
-    DlOpReceiver& receiver2 = ToReceiver(builder2);
-    receiver2.drawRect({0, 0, 100, 100});
-    receiver2.drawRect({0, 0, 100, 100});
+    builder2.drawRect({0, 0, 100, 100});
+    builder2.drawRect({0, 0, 100, 100});
     auto display_list2 = builder2.Build();
 
     ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
@@ -2436,27 +2240,23 @@
 
   {
     DisplayListBuilder builder1;
-    DlOpReceiver& receiver1 = ToReceiver(builder1);
-    receiver1.save();
-    receiver1.save();
-    receiver1.transformFullPerspective(1, 0, 0, 0,  //
-                                       0, 1, 0, 0,  //
-                                       0, 0, 1, 0,  //
-                                       0, 0, 0, 1);
-    receiver1.transformReset();
-    receiver1.drawRect({0, 0, 100, 100});
-    receiver1.restore();
-    receiver1.drawRect({0, 0, 100, 100});
-    receiver1.restore();
+    builder1.save();
+    builder1.save();
+    builder1.transformFullPerspective(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,
+                                      0, 1);
+    builder1.transformReset();
+    builder1.drawRect({0, 0, 100, 100});
+    builder1.restore();
+    builder1.drawRect({0, 0, 100, 100});
+    builder1.restore();
     auto display_list1 = builder1.Build();
 
     DisplayListBuilder builder2;
-    DlOpReceiver& receiver2 = ToReceiver(builder2);
-    receiver2.save();
-    receiver2.transformReset();
-    receiver2.drawRect({0, 0, 100, 100});
-    receiver2.restore();
-    receiver2.drawRect({0, 0, 100, 100});
+    builder2.save();
+    builder2.transformReset();
+    builder2.drawRect({0, 0, 100, 100});
+    builder2.restore();
+    builder2.drawRect({0, 0, 100, 100});
 
     auto display_list2 = builder2.Build();
 
@@ -2464,42 +2264,40 @@
   }
 }
 
-TEST_F(DisplayListTest, NOPClipDoesNotTriggerDeferredSave) {
+TEST(DisplayList, NOPClipDoesNotTriggerDeferredSave) {
   DisplayListBuilder builder1;
-  DlOpReceiver& receiver1 = ToReceiver(builder1);
-  receiver1.save();
-  receiver1.save();
-  receiver1.clipRect(SkRect::MakeLTRB(0, SK_ScalarNaN, SK_ScalarNaN, 0),
-                     ClipOp::kIntersect, true);
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
-  receiver1.drawRect({0, 0, 100, 100});
-  receiver1.restore();
+  builder1.save();
+  builder1.save();
+  builder1.clipRect(SkRect::MakeLTRB(0, SK_ScalarNaN, SK_ScalarNaN, 0),
+                    ClipOp::kIntersect, true);
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
+  builder1.drawRect({0, 0, 100, 100});
+  builder1.restore();
   auto display_list1 = builder1.Build();
 
   DisplayListBuilder builder2;
-  DlOpReceiver& receiver2 = ToReceiver(builder2);
-  receiver2.drawRect({0, 0, 100, 100});
-  receiver2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
+  builder2.drawRect({0, 0, 100, 100});
   auto display_list2 = builder2.Build();
 
   ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2));
 }
 
-TEST_F(DisplayListTest, RTreeOfClippedSaveLayerFilterScene) {
+TEST(DisplayList, RTreeOfClippedSaveLayerFilterScene) {
   DisplayListBuilder builder(/*prepare_rtree=*/true);
   // blur filter with sigma=1 expands by 30 on all sides
   auto filter = DlBlurImageFilter(10.0, 10.0, DlTileMode::kClamp);
   DlPaint default_paint = DlPaint();
   DlPaint filter_paint = DlPaint().setImageFilter(&filter);
   builder.DrawRect({10, 10, 20, 20}, default_paint);
-  builder.ClipRect({50, 50, 60, 60}, ClipOp::kIntersect, false);
+  builder.clipRect({50, 50, 60, 60}, ClipOp::kIntersect, false);
   builder.SaveLayer(nullptr, &filter_paint);
   // the following rectangle will be expanded to 23,23,87,87
   // by the saveLayer filter during the restore operation
   // but it will then be clipped to 50,50,60,60
   builder.DrawRect({53, 53, 57, 57}, default_paint);
-  builder.Restore();
+  builder.restore();
   auto display_list = builder.Build();
   auto rtree = display_list->rtree();
   std::vector<SkRect> rects = {
@@ -2523,13 +2321,12 @@
   test_rtree(rtree, {19, 19, 51, 51}, rects, {0, 1});
 }
 
-TEST_F(DisplayListTest, RTreeRenderCulling) {
+TEST(DisplayList, RTreeRenderCulling) {
   DisplayListBuilder main_builder(true);
-  DlOpReceiver& main_receiver = ToReceiver(main_builder);
-  main_receiver.drawRect({0, 0, 10, 10});
-  main_receiver.drawRect({20, 0, 30, 10});
-  main_receiver.drawRect({0, 20, 10, 30});
-  main_receiver.drawRect({20, 20, 30, 30});
+  main_builder.drawRect({0, 0, 10, 10});
+  main_builder.drawRect({20, 0, 30, 10});
+  main_builder.drawRect({0, 20, 10, 30});
+  main_builder.drawRect({20, 20, 30, 30});
   auto main = main_builder.Build();
 
   {  // No rects
@@ -2539,7 +2336,7 @@
     auto expected = expected_builder.Build();
 
     DisplayListBuilder culling_builder(cull_rect);
-    main->Dispatch(ToReceiver(culling_builder), cull_rect);
+    main->RenderTo(&culling_builder);
 
     EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
   }
@@ -2548,12 +2345,11 @@
     SkRect cull_rect = {9, 9, 19, 19};
 
     DisplayListBuilder expected_builder;
-    DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
-    expected_receiver.drawRect({0, 0, 10, 10});
+    expected_builder.drawRect({0, 0, 10, 10});
     auto expected = expected_builder.Build();
 
     DisplayListBuilder culling_builder(cull_rect);
-    main->Dispatch(ToReceiver(culling_builder), cull_rect);
+    main->RenderTo(&culling_builder);
 
     EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
   }
@@ -2562,12 +2358,11 @@
     SkRect cull_rect = {11, 9, 21, 19};
 
     DisplayListBuilder expected_builder;
-    DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
-    expected_receiver.drawRect({20, 0, 30, 10});
+    expected_builder.drawRect({20, 0, 30, 10});
     auto expected = expected_builder.Build();
 
     DisplayListBuilder culling_builder(cull_rect);
-    main->Dispatch(ToReceiver(culling_builder), cull_rect);
+    main->RenderTo(&culling_builder);
 
     EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
   }
@@ -2576,12 +2371,11 @@
     SkRect cull_rect = {9, 11, 19, 21};
 
     DisplayListBuilder expected_builder;
-    DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
-    expected_receiver.drawRect({0, 20, 10, 30});
+    expected_builder.drawRect({0, 20, 10, 30});
     auto expected = expected_builder.Build();
 
     DisplayListBuilder culling_builder(cull_rect);
-    main->Dispatch(ToReceiver(culling_builder), cull_rect);
+    main->RenderTo(&culling_builder);
 
     EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
   }
@@ -2590,12 +2384,11 @@
     SkRect cull_rect = {11, 11, 21, 21};
 
     DisplayListBuilder expected_builder;
-    DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
-    expected_receiver.drawRect({20, 20, 30, 30});
+    expected_builder.drawRect({20, 20, 30, 30});
     auto expected = expected_builder.Build();
 
     DisplayListBuilder culling_builder(cull_rect);
-    main->Dispatch(ToReceiver(culling_builder), cull_rect);
+    main->RenderTo(&culling_builder);
 
     EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
   }
@@ -2604,7 +2397,7 @@
     SkRect cull_rect = {9, 9, 21, 21};
 
     DisplayListBuilder culling_builder(cull_rect);
-    main->Dispatch(ToReceiver(culling_builder), cull_rect);
+    main->RenderTo(&culling_builder);
 
     EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), main));
   }
diff --git a/display_list/display_list_utils.cc b/display_list/display_list_utils.cc
index e6c21d5..2ffed31 100644
--- a/display_list/display_list_utils.cc
+++ b/display_list/display_list_utils.cc
@@ -4,8 +4,106 @@
 
 #include "flutter/display_list/display_list_utils.h"
 
+#include <math.h>
+#include <optional>
+#include <type_traits>
+
+#include "flutter/display_list/display_list_blend_mode.h"
+#include "flutter/display_list/display_list_canvas_dispatcher.h"
+#include "flutter/fml/logging.h"
+#include "third_party/skia/include/core/SkMaskFilter.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkRSXform.h"
+#include "third_party/skia/include/core/SkTextBlob.h"
+#include "third_party/skia/include/utils/SkShadowUtils.h"
+
 namespace flutter {
 
+// clang-format off
+constexpr float kInvertColorMatrix[20] = {
+  -1.0,    0,    0, 1.0, 0,
+     0, -1.0,    0, 1.0, 0,
+     0,    0, -1.0, 1.0, 0,
+   1.0,  1.0,  1.0, 1.0, 0
+};
+// clang-format on
+
+void SkPaintDispatchHelper::save_opacity(SkScalar child_opacity) {
+  save_stack_.emplace_back(opacity_);
+  set_opacity(child_opacity);
+}
+void SkPaintDispatchHelper::restore_opacity() {
+  if (save_stack_.empty()) {
+    return;
+  }
+  set_opacity(save_stack_.back().opacity);
+  save_stack_.pop_back();
+}
+
+void SkPaintDispatchHelper::setAntiAlias(bool aa) {
+  paint_.setAntiAlias(aa);
+}
+void SkPaintDispatchHelper::setDither(bool dither) {
+  paint_.setDither(dither);
+}
+void SkPaintDispatchHelper::setInvertColors(bool invert) {
+  invert_colors_ = invert;
+  paint_.setColorFilter(makeColorFilter());
+}
+void SkPaintDispatchHelper::setStrokeCap(DlStrokeCap cap) {
+  paint_.setStrokeCap(ToSk(cap));
+}
+void SkPaintDispatchHelper::setStrokeJoin(DlStrokeJoin join) {
+  paint_.setStrokeJoin(ToSk(join));
+}
+void SkPaintDispatchHelper::setStyle(DlDrawStyle style) {
+  paint_.setStyle(ToSk(style));
+}
+void SkPaintDispatchHelper::setStrokeWidth(SkScalar width) {
+  paint_.setStrokeWidth(width);
+}
+void SkPaintDispatchHelper::setStrokeMiter(SkScalar limit) {
+  paint_.setStrokeMiter(limit);
+}
+void SkPaintDispatchHelper::setColor(DlColor color) {
+  current_color_ = color;
+  paint_.setColor(color);
+  if (has_opacity()) {
+    paint_.setAlphaf(paint_.getAlphaf() * opacity());
+  }
+}
+void SkPaintDispatchHelper::setBlendMode(DlBlendMode mode) {
+  paint_.setBlendMode(ToSk(mode));
+}
+void SkPaintDispatchHelper::setColorSource(const DlColorSource* source) {
+  paint_.setShader(source ? source->skia_object() : nullptr);
+}
+void SkPaintDispatchHelper::setImageFilter(const DlImageFilter* filter) {
+  paint_.setImageFilter(filter ? filter->skia_object() : nullptr);
+}
+void SkPaintDispatchHelper::setColorFilter(const DlColorFilter* filter) {
+  color_filter_ = filter ? filter->shared() : nullptr;
+  paint_.setColorFilter(makeColorFilter());
+}
+void SkPaintDispatchHelper::setPathEffect(const DlPathEffect* effect) {
+  paint_.setPathEffect(effect ? effect->skia_object() : nullptr);
+}
+void SkPaintDispatchHelper::setMaskFilter(const DlMaskFilter* filter) {
+  paint_.setMaskFilter(filter ? filter->skia_object() : nullptr);
+}
+
+sk_sp<SkColorFilter> SkPaintDispatchHelper::makeColorFilter() const {
+  if (!invert_colors_) {
+    return color_filter_ ? color_filter_->skia_object() : nullptr;
+  }
+  sk_sp<SkColorFilter> invert_filter =
+      SkColorFilters::Matrix(kInvertColorMatrix);
+  if (color_filter_) {
+    invert_filter = invert_filter->makeComposed(color_filter_->skia_object());
+  }
+  return invert_filter;
+}
+
 void RectBoundsAccumulator::accumulate(const SkRect& r, int index) {
   if (r.fLeft < r.fRight && r.fTop < r.fBottom) {
     rect_.accumulate(r.fLeft, r.fTop);
diff --git a/display_list/display_list_utils.h b/display_list/display_list_utils.h
index 152ae10..c2bed14 100644
--- a/display_list/display_list_utils.h
+++ b/display_list/display_list_utils.h
@@ -7,24 +7,33 @@
 
 #include <optional>
 
+#include "flutter/display_list/display_list.h"
+#include "flutter/display_list/display_list_blend_mode.h"
+#include "flutter/display_list/display_list_dispatcher.h"
+#include "flutter/display_list/display_list_flags.h"
 #include "flutter/display_list/display_list_rtree.h"
-#include "flutter/display_list/dl_op_receiver.h"
 #include "flutter/fml/logging.h"
+#include "flutter/fml/macros.h"
+#include "third_party/skia/include/core/SkMaskFilter.h"
 
 // This file contains various utility classes to ease implementing
-// a Flutter DisplayList DlOpReceiver, including:
+// a Flutter DisplayList Dispatcher, including:
 //
 // IgnoreAttributeDispatchHelper:
 // IgnoreClipDispatchHelper:
 // IgnoreTransformDispatchHelper
-//     Empty overrides of all of the associated methods of DlOpReceiver
-//     for receivers that only track some of the rendering operations
+//     Empty overrides of all of the associated methods of Dispatcher
+//     for dispatchers that only track some of the rendering operations
+//
+// SkPaintAttributeDispatchHelper:
+//     Tracks the attribute methods and maintains their state in an
+//     SkPaint object.
 
 namespace flutter {
 
-// A utility class that will ignore all DlOpReceiver methods relating
+// A utility class that will ignore all Dispatcher methods relating
 // to the setting of attributes.
-class IgnoreAttributeDispatchHelper : public virtual DlOpReceiver {
+class IgnoreAttributeDispatchHelper : public virtual Dispatcher {
  public:
   void setAntiAlias(bool aa) override {}
   void setDither(bool dither) override {}
@@ -43,9 +52,9 @@
   void setMaskFilter(const DlMaskFilter* filter) override {}
 };
 
-// A utility class that will ignore all DlOpReceiver methods relating
+// A utility class that will ignore all Dispatcher methods relating
 // to setting a clip.
-class IgnoreClipDispatchHelper : public virtual DlOpReceiver {
+class IgnoreClipDispatchHelper : public virtual Dispatcher {
   void clipRect(const SkRect& rect,
                 DlCanvas::ClipOp clip_op,
                 bool is_aa) override {}
@@ -57,9 +66,9 @@
                 bool is_aa) override {}
 };
 
-// A utility class that will ignore all DlOpReceiver methods relating
+// A utility class that will ignore all Dispatcher methods relating
 // to modifying the transform.
-class IgnoreTransformDispatchHelper : public virtual DlOpReceiver {
+class IgnoreTransformDispatchHelper : public virtual Dispatcher {
  public:
   void translate(SkScalar tx, SkScalar ty) override {}
   void scale(SkScalar sx, SkScalar sy) override {}
@@ -79,7 +88,7 @@
   void transformReset() override {}
 };
 
-class IgnoreDrawDispatchHelper : public virtual DlOpReceiver {
+class IgnoreDrawDispatchHelper : public virtual Dispatcher {
  public:
   void save() override {}
   void saveLayer(const SkRect* bounds,
@@ -112,7 +121,7 @@
                      const SkRect& dst,
                      DlImageSampling sampling,
                      bool render_with_attributes,
-                     SrcRectConstraint constraint) override {}
+                     SkCanvas::SrcRectConstraint constraint) override {}
   void drawImageNine(const sk_sp<DlImage> image,
                      const SkIRect& center,
                      const SkRect& dst,
@@ -127,8 +136,7 @@
                  DlImageSampling sampling,
                  const SkRect* cull_rect,
                  bool render_with_attributes) override {}
-  void drawDisplayList(const sk_sp<DisplayList> display_list,
-                       SkScalar opacity) override {}
+  void drawDisplayList(const sk_sp<DisplayList> display_list) override {}
   void drawTextBlob(const sk_sp<SkTextBlob> blob,
                     SkScalar x,
                     SkScalar y) override {}
@@ -139,6 +147,77 @@
                   SkScalar dpr) override {}
 };
 
+// A utility class that will monitor the Dispatcher methods relating
+// to the rendering attributes and accumulate them into an SkPaint
+// which can be accessed at any time via paint().
+class SkPaintDispatchHelper : public virtual Dispatcher {
+ public:
+  SkPaintDispatchHelper(SkScalar opacity = SK_Scalar1)
+      : current_color_(SK_ColorBLACK), opacity_(opacity) {
+    if (opacity < SK_Scalar1) {
+      paint_.setAlphaf(opacity);
+    }
+  }
+
+  void setAntiAlias(bool aa) override;
+  void setDither(bool dither) override;
+  void setStyle(DlDrawStyle style) override;
+  void setColor(DlColor color) override;
+  void setStrokeWidth(SkScalar width) override;
+  void setStrokeMiter(SkScalar limit) override;
+  void setStrokeCap(DlStrokeCap cap) override;
+  void setStrokeJoin(DlStrokeJoin join) override;
+  void setColorSource(const DlColorSource* source) override;
+  void setColorFilter(const DlColorFilter* filter) override;
+  void setInvertColors(bool invert) override;
+  void setBlendMode(DlBlendMode mode) override;
+  void setPathEffect(const DlPathEffect* effect) override;
+  void setMaskFilter(const DlMaskFilter* filter) override;
+  void setImageFilter(const DlImageFilter* filter) override;
+
+  const SkPaint& paint() { return paint_; }
+
+  /// Returns the current opacity attribute which is used to reduce
+  /// the alpha of all setColor calls encountered in the streeam
+  SkScalar opacity() { return opacity_; }
+  /// Returns the combined opacity that includes both the current
+  /// opacity attribute and the alpha of the most recent color.
+  /// The most recently set color will have combined the two and
+  /// stored the combined value in the alpha of the paint.
+  SkScalar combined_opacity() { return paint_.getAlphaf(); }
+  /// Returns true iff the current opacity attribute is not opaque,
+  /// irrespective of the alpha of the current color
+  bool has_opacity() { return opacity_ < SK_Scalar1; }
+
+ protected:
+  void save_opacity(SkScalar opacity_for_children);
+  void restore_opacity();
+
+ private:
+  SkPaint paint_;
+  bool invert_colors_ = false;
+  std::shared_ptr<const DlColorFilter> color_filter_;
+
+  sk_sp<SkColorFilter> makeColorFilter() const;
+
+  struct SaveInfo {
+    SaveInfo(SkScalar opacity) : opacity(opacity) {}
+
+    SkScalar opacity;
+  };
+  std::vector<SaveInfo> save_stack_;
+
+  void set_opacity(SkScalar opacity) {
+    if (opacity_ != opacity) {
+      opacity_ = opacity;
+      setColor(current_color_);
+    }
+  }
+
+  SkColor current_color_;
+  SkScalar opacity_;
+};
+
 enum class BoundsAccumulatorType {
   kRect,
   kRTree,
diff --git a/display_list/skia/dl_sk_utils_unittests.cc b/display_list/display_list_utils_unittests.cc
similarity index 89%
rename from display_list/skia/dl_sk_utils_unittests.cc
rename to display_list/display_list_utils_unittests.cc
index ccab201..9f77c94 100644
--- a/display_list/skia/dl_sk_utils_unittests.cc
+++ b/display_list/display_list_utils_unittests.cc
@@ -3,13 +3,12 @@
 // found in the LICENSE file.
 
 #include "flutter/display_list/display_list_utils.h"
-#include "flutter/display_list/skia/dl_sk_utils.h"
 #include "gtest/gtest.h"
 
 namespace flutter {
 namespace testing {
 
-class MockDispatchHelper final : public virtual DlOpReceiver,
+class MockDispatchHelper final : public virtual Dispatcher,
                                  public SkPaintDispatchHelper,
                                  public IgnoreClipDispatchHelper,
                                  public IgnoreTransformDispatchHelper,
diff --git a/display_list/display_list_vertices.cc b/display_list/display_list_vertices.cc
index e6a9b72..6c20c3a 100644
--- a/display_list/display_list_vertices.cc
+++ b/display_list/display_list_vertices.cc
@@ -176,6 +176,13 @@
   FML_DCHECK((index_count_ != 0) == (indices() != nullptr));
 }
 
+sk_sp<SkVertices> DlVertices::skia_object() const {
+  const SkColor* sk_colors = reinterpret_cast<const SkColor*>(colors());
+  return SkVertices::MakeCopy(ToSk(mode_), vertex_count_, vertices(),
+                              texture_coordinates(), sk_colors, index_count_,
+                              indices());
+}
+
 bool DlVertices::operator==(DlVertices const& other) const {
   auto lists_equal = [](auto* a, auto* b, int count) {
     if (a == nullptr || b == nullptr) {
diff --git a/display_list/display_list_vertices.h b/display_list/display_list_vertices.h
index 04044ba..885ef6c 100644
--- a/display_list/display_list_vertices.h
+++ b/display_list/display_list_vertices.h
@@ -30,6 +30,14 @@
   kTriangleFan,
 };
 
+inline SkVertices::VertexMode ToSk(DlVertexMode dl_mode) {
+  return static_cast<SkVertices::VertexMode>(dl_mode);
+}
+
+inline DlVertexMode ToDl(SkVertices::VertexMode sk_mode) {
+  return static_cast<DlVertexMode>(sk_mode);
+}
+
 //------------------------------------------------------------------------------
 /// @brief      Holds all of the data (both required and optional) for a
 ///             DisplayList drawVertices call.
@@ -225,6 +233,9 @@
     return static_cast<const uint16_t*>(pod(indices_offset_));
   }
 
+  // Returns an equivalent sk_sp<SkVertices> analog to this object.
+  sk_sp<SkVertices> skia_object() const;
+
   bool operator==(DlVertices const& other) const;
 
   bool operator!=(DlVertices const& other) const { return !(*this == other); }
diff --git a/display_list/display_list_vertices_unittests.cc b/display_list/display_list_vertices_unittests.cc
index 5a11dc2e..0323b62 100644
--- a/display_list/display_list_vertices_unittests.cc
+++ b/display_list/display_list_vertices_unittests.cc
@@ -22,6 +22,7 @@
   EXPECT_EQ(vertices1->colors(), nullptr);
   EXPECT_EQ(vertices1->index_count(), 0);
   EXPECT_EQ(vertices1->indices(), nullptr);
+  EXPECT_NE(vertices1->skia_object(), nullptr);
 
   std::shared_ptr<const DlVertices> vertices2 = DlVertices::Make(
       DlVertexMode::kTriangles, -1, nullptr, nullptr, nullptr, -1, nullptr);
@@ -32,6 +33,7 @@
   EXPECT_EQ(vertices2->colors(), nullptr);
   EXPECT_EQ(vertices2->index_count(), 0);
   EXPECT_EQ(vertices2->indices(), nullptr);
+  EXPECT_NE(vertices2->skia_object(), nullptr);
 
   TestEquals(*vertices1, *vertices2);
 }
@@ -423,6 +425,7 @@
   EXPECT_EQ(vertices1->colors(), nullptr);
   EXPECT_EQ(vertices1->index_count(), 0);
   EXPECT_EQ(vertices1->indices(), nullptr);
+  EXPECT_NE(vertices1->skia_object(), nullptr);
 
   Builder builder2(DlVertexMode::kTriangles, -1, Builder::kNone, -1);
   EXPECT_TRUE(builder2.is_valid());
@@ -434,6 +437,7 @@
   EXPECT_EQ(vertices2->colors(), nullptr);
   EXPECT_EQ(vertices2->index_count(), 0);
   EXPECT_EQ(vertices2->indices(), nullptr);
+  EXPECT_NE(vertices2->skia_object(), nullptr);
 
   TestEquals(*vertices1, *vertices2);
 }
diff --git a/display_list/dl_canvas.cc b/display_list/dl_canvas.cc
deleted file mode 100644
index 7f9e3fc..0000000
--- a/display_list/dl_canvas.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// 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 "flutter/display_list/dl_canvas.h"
-
-#include "third_party/skia/include/utils/SkShadowUtils.h"
-
-namespace flutter {
-
-SkRect DlCanvas::ComputeShadowBounds(const SkPath& path,
-                                     float elevation,
-                                     SkScalar dpr,
-                                     const SkMatrix& ctm) {
-  SkRect shadow_bounds(path.getBounds());
-  SkShadowUtils::GetLocalBounds(
-      ctm, path, SkPoint3::Make(0, 0, dpr * elevation),
-      SkPoint3::Make(0, -1, 1), kShadowLightRadius / kShadowLightHeight,
-      SkShadowFlags::kDirectionalLight_ShadowFlag, &shadow_bounds);
-  return shadow_bounds;
-}
-
-}  // namespace flutter
diff --git a/display_list/dl_canvas.h b/display_list/dl_canvas.h
index 7d94352c..5d8d97e 100644
--- a/display_list/dl_canvas.h
+++ b/display_list/dl_canvas.h
@@ -35,11 +35,6 @@
     kPolygon,  //!< draw each pair of overlapping points as a line segment
   };
 
-  enum class SrcRectConstraint {
-    kStrict,
-    kFast,
-  };
-
   virtual ~DlCanvas() = default;
 
   virtual SkISize GetBaseLayerSize() const = 0;
@@ -151,29 +146,28 @@
                          const SkPoint point,
                          DlImageSampling sampling,
                          const DlPaint* paint = nullptr) = 0;
-  virtual void DrawImageRect(
-      const sk_sp<DlImage>& image,
-      const SkRect& src,
-      const SkRect& dst,
-      DlImageSampling sampling,
-      const DlPaint* paint = nullptr,
-      SrcRectConstraint constraint = SrcRectConstraint::kFast) = 0;
-  virtual void DrawImageRect(
-      const sk_sp<DlImage>& image,
-      const SkIRect& src,
-      const SkRect& dst,
-      DlImageSampling sampling,
-      const DlPaint* paint = nullptr,
-      SrcRectConstraint constraint = SrcRectConstraint::kFast) {
-    DrawImageRect(image, SkRect::Make(src), dst, sampling, paint, constraint);
+  virtual void DrawImageRect(const sk_sp<DlImage>& image,
+                             const SkRect& src,
+                             const SkRect& dst,
+                             DlImageSampling sampling,
+                             const DlPaint* paint = nullptr,
+                             bool enforce_src_edges = false) = 0;
+  virtual void DrawImageRect(const sk_sp<DlImage>& image,
+                             const SkIRect& src,
+                             const SkRect& dst,
+                             DlImageSampling sampling,
+                             const DlPaint* paint = nullptr,
+                             bool enforce_src_edges = false) {
+    DrawImageRect(image, SkRect::Make(src), dst, sampling, paint,
+                  enforce_src_edges);
   }
-  virtual void DrawImageRect(
-      const sk_sp<DlImage>& image,
-      const SkRect& dst,
-      DlImageSampling sampling,
-      const DlPaint* paint = nullptr,
-      SrcRectConstraint constraint = SrcRectConstraint::kFast) {
-    DrawImageRect(image, image->bounds(), dst, sampling, paint, constraint);
+  virtual void DrawImageRect(const sk_sp<DlImage>& image,
+                             const SkRect& dst,
+                             DlImageSampling sampling,
+                             const DlPaint* paint = nullptr,
+                             bool enforce_src_edges = false) {
+    DrawImageRect(image, image->bounds(), dst, sampling, paint,
+                  enforce_src_edges);
   }
   virtual void DrawImageNine(const sk_sp<DlImage>& image,
                              const SkIRect& center,
@@ -202,14 +196,6 @@
                           SkScalar dpr) = 0;
 
   virtual void Flush() = 0;
-
-  static constexpr SkScalar kShadowLightHeight = 600;
-  static constexpr SkScalar kShadowLightRadius = 800;
-
-  static SkRect ComputeShadowBounds(const SkPath& path,
-                                    float elevation,
-                                    SkScalar dpr,
-                                    const SkMatrix& ctm);
 };
 
 class DlAutoCanvasRestore {
diff --git a/display_list/skia/dl_sk_canvas.cc b/display_list/skia/dl_sk_canvas.cc
index 29375a2..f7a0de8 100644
--- a/display_list/skia/dl_sk_canvas.cc
+++ b/display_list/skia/dl_sk_canvas.cc
@@ -4,11 +4,43 @@
 
 #include "flutter/display_list/skia/dl_sk_canvas.h"
 
-#include "flutter/display_list/skia/dl_sk_conversions.h"
-#include "flutter/display_list/skia/dl_sk_dispatcher.h"
+#include "flutter/display_list/display_list_canvas_dispatcher.h"
 
 namespace flutter {
 
+static sk_sp<SkShader> ToSk(const DlColorSource* source) {
+  return source ? source->skia_object() : nullptr;
+}
+
+static sk_sp<SkImageFilter> ToSk(const DlImageFilter* filter) {
+  return filter ? filter->skia_object() : nullptr;
+}
+
+static sk_sp<SkColorFilter> ToSk(const DlColorFilter* filter) {
+  return filter ? filter->skia_object() : nullptr;
+}
+
+static sk_sp<SkMaskFilter> ToSk(const DlMaskFilter* filter) {
+  return filter ? filter->skia_object() : nullptr;
+}
+
+static sk_sp<SkPathEffect> ToSk(const DlPathEffect* effect) {
+  return effect ? effect->skia_object() : nullptr;
+}
+
+static SkCanvas::SrcRectConstraint ToSkConstraint(bool enforce_edges) {
+  return enforce_edges ? SkCanvas::kStrict_SrcRectConstraint
+                       : SkCanvas::kFast_SrcRectConstraint;
+}
+
+static SkClipOp ToSk(DlCanvas::ClipOp op) {
+  return static_cast<SkClipOp>(op);
+}
+
+static SkCanvas::PointMode ToSk(DlCanvas::PointMode mode) {
+  return static_cast<SkCanvas::PointMode>(mode);
+}
+
 // clang-format off
 constexpr float kInvertColorMatrix[20] = {
   -1.0,    0,    0, 1.0, 0,
@@ -87,10 +119,10 @@
 void DlSkCanvasAdapter::SaveLayer(const SkRect* bounds,
                                   const DlPaint* paint,
                                   const DlImageFilter* backdrop) {
-  sk_sp<SkImageFilter> sk_backdrop = ToSk(backdrop);
+  sk_sp<SkImageFilter> sk_filter = backdrop ? backdrop->skia_object() : nullptr;
   SkOptionalPaint sk_paint(paint);
   delegate_->saveLayer(
-      SkCanvas::SaveLayerRec{bounds, sk_paint(), sk_backdrop.get(), 0});
+      SkCanvas::SaveLayerRec{bounds, sk_paint(), sk_filter.get(), 0});
 }
 
 void DlSkCanvasAdapter::Restore() {
@@ -277,7 +309,7 @@
 void DlSkCanvasAdapter::DrawVertices(const DlVertices* vertices,
                                      DlBlendMode mode,
                                      const DlPaint& paint) {
-  delegate_->drawVertices(ToSk(vertices), ToSk(mode), ToSk(paint));
+  delegate_->drawVertices(vertices->skia_object(), ToSk(mode), ToSk(paint));
 }
 
 void DlSkCanvasAdapter::DrawImage(const sk_sp<DlImage>& image,
@@ -295,11 +327,11 @@
                                       const SkRect& dst,
                                       DlImageSampling sampling,
                                       const DlPaint* paint,
-                                      SrcRectConstraint constraint) {
+                                      bool enforce_src_edges) {
   SkOptionalPaint sk_paint(paint);
   sk_sp<SkImage> sk_image = image->skia_image();
   delegate_->drawImageRect(sk_image.get(), src, dst, ToSk(sampling), sk_paint(),
-                           ToSk(constraint));
+                           ToSkConstraint(enforce_src_edges));
 }
 
 void DlSkCanvasAdapter::DrawImageNine(const sk_sp<DlImage>& image,
@@ -331,26 +363,7 @@
 
 void DlSkCanvasAdapter::DrawDisplayList(const sk_sp<DisplayList> display_list,
                                         SkScalar opacity) {
-  int restore_count = delegate_->getSaveCount();
-
-  // Figure out whether we can apply the opacity during dispatch or
-  // if we need a saveLayer.
-  if (opacity < SK_Scalar1 && !display_list->can_apply_group_opacity()) {
-    DlPaint save_paint = DlPaint().setOpacity(opacity);
-    SaveLayer(&display_list->bounds(), &save_paint);
-    opacity = SK_Scalar1;
-  } else {
-    Save();
-  }
-
-  DlSkCanvasDispatcher dispatcher(delegate_, opacity);
-  if (display_list->has_rtree()) {
-    display_list->Dispatch(dispatcher, delegate_->getLocalClipBounds());
-  } else {
-    display_list->Dispatch(dispatcher);
-  }
-
-  delegate_->restoreToCount(restore_count);
+  display_list->RenderTo(delegate_, opacity);
 }
 
 void DlSkCanvasAdapter::DrawTextBlob(const sk_sp<SkTextBlob>& blob,
@@ -365,8 +378,8 @@
                                    const SkScalar elevation,
                                    bool transparent_occluder,
                                    SkScalar dpr) {
-  DlSkCanvasDispatcher::DrawShadow(delegate_, path, color, elevation,
-                                   transparent_occluder, dpr);
+  DisplayListCanvasDispatcher::DrawShadow(delegate_, path, color, elevation,
+                                          transparent_occluder, dpr);
 }
 
 void DlSkCanvasAdapter::Flush() {
diff --git a/display_list/skia/dl_sk_canvas.h b/display_list/skia/dl_sk_canvas.h
index fc66bcc..249e518 100644
--- a/display_list/skia/dl_sk_canvas.h
+++ b/display_list/skia/dl_sk_canvas.h
@@ -114,13 +114,12 @@
                  const SkPoint point,
                  DlImageSampling sampling,
                  const DlPaint* paint = nullptr) override;
-  void DrawImageRect(
-      const sk_sp<DlImage>& image,
-      const SkRect& src,
-      const SkRect& dst,
-      DlImageSampling sampling,
-      const DlPaint* paint = nullptr,
-      SrcRectConstraint constraint = SrcRectConstraint::kFast) override;
+  void DrawImageRect(const sk_sp<DlImage>& image,
+                     const SkRect& src,
+                     const SkRect& dst,
+                     DlImageSampling sampling,
+                     const DlPaint* paint = nullptr,
+                     bool enforce_src_edges = false) override;
   void DrawImageNine(const sk_sp<DlImage>& image,
                      const SkIRect& center,
                      const SkRect& dst,
@@ -136,7 +135,7 @@
                  const SkRect* cullRect,
                  const DlPaint* paint = nullptr) override;
   void DrawDisplayList(const sk_sp<DisplayList> display_list,
-                       SkScalar opacity = SK_Scalar1) override;
+                       SkScalar opacity) override;
   void DrawTextBlob(const sk_sp<SkTextBlob>& blob,
                     SkScalar x,
                     SkScalar y,
diff --git a/display_list/skia/dl_sk_conversions.cc b/display_list/skia/dl_sk_conversions.cc
deleted file mode 100644
index 7ec52f9..0000000
--- a/display_list/skia/dl_sk_conversions.cc
+++ /dev/null
@@ -1,235 +0,0 @@
-// 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 "flutter/display_list/skia/dl_sk_conversions.h"
-
-namespace flutter {
-
-sk_sp<SkShader> ToSk(const DlColorSource* source) {
-  if (!source) {
-    return nullptr;
-  }
-  static auto ToSkColors = [](const DlGradientColorSourceBase* gradient) {
-    return reinterpret_cast<const SkColor*>(gradient->colors());
-  };
-  switch (source->type()) {
-    case DlColorSourceType::kColor: {
-      const DlColorColorSource* color_source = source->asColor();
-      FML_DCHECK(color_source != nullptr);
-      return SkShaders::Color(color_source->color());
-    }
-    case DlColorSourceType::kImage: {
-      const DlImageColorSource* image_source = source->asImage();
-      FML_DCHECK(image_source != nullptr);
-      auto image = image_source->image();
-      if (!image || !image->skia_image()) {
-        return nullptr;
-      }
-      return image->skia_image()->makeShader(
-          ToSk(image_source->horizontal_tile_mode()),
-          ToSk(image_source->vertical_tile_mode()),
-          ToSk(image_source->sampling()), image_source->matrix_ptr());
-    }
-    case DlColorSourceType::kLinearGradient: {
-      const DlLinearGradientColorSource* linear_source =
-          source->asLinearGradient();
-      FML_DCHECK(linear_source != nullptr);
-      SkPoint pts[] = {linear_source->start_point(),
-                       linear_source->end_point()};
-      return SkGradientShader::MakeLinear(
-          pts, ToSkColors(linear_source), linear_source->stops(),
-          linear_source->stop_count(), ToSk(linear_source->tile_mode()), 0,
-          linear_source->matrix_ptr());
-    }
-    case DlColorSourceType::kRadialGradient: {
-      const DlRadialGradientColorSource* radial_source =
-          source->asRadialGradient();
-      FML_DCHECK(radial_source != nullptr);
-      return SkGradientShader::MakeRadial(
-          radial_source->center(), radial_source->radius(),
-          ToSkColors(radial_source), radial_source->stops(),
-          radial_source->stop_count(), ToSk(radial_source->tile_mode()), 0,
-          radial_source->matrix_ptr());
-    }
-    case DlColorSourceType::kConicalGradient: {
-      const DlConicalGradientColorSource* conical_source =
-          source->asConicalGradient();
-      FML_DCHECK(conical_source != nullptr);
-      return SkGradientShader::MakeTwoPointConical(
-          conical_source->start_center(), conical_source->start_radius(),
-          conical_source->end_center(), conical_source->end_radius(),
-          ToSkColors(conical_source), conical_source->stops(),
-          conical_source->stop_count(), ToSk(conical_source->tile_mode()), 0,
-          conical_source->matrix_ptr());
-    }
-    case DlColorSourceType::kSweepGradient: {
-      const DlSweepGradientColorSource* sweep_source =
-          source->asSweepGradient();
-      FML_DCHECK(sweep_source != nullptr);
-      return SkGradientShader::MakeSweep(
-          sweep_source->center().x(), sweep_source->center().y(),
-          ToSkColors(sweep_source), sweep_source->stops(),
-          sweep_source->stop_count(), ToSk(sweep_source->tile_mode()),
-          sweep_source->start(), sweep_source->end(), 0,
-          sweep_source->matrix_ptr());
-    }
-    case DlColorSourceType::kRuntimeEffect: {
-      const DlRuntimeEffectColorSource* runtime_source =
-          source->asRuntimeEffect();
-      FML_DCHECK(runtime_source != nullptr);
-      auto runtime_effect = runtime_source->runtime_effect();
-      if (!runtime_effect || !runtime_effect->skia_runtime_effect()) {
-        return nullptr;
-      }
-
-      auto samplers = runtime_source->samplers();
-      std::vector<sk_sp<SkShader>> sk_samplers(samplers.size());
-      for (size_t i = 0; i < samplers.size(); i++) {
-        auto sampler = samplers[i];
-        if (sampler == nullptr) {
-          return nullptr;
-        }
-        sk_samplers[i] = ToSk(sampler);
-      }
-
-      auto uniform_data = runtime_source->uniform_data();
-      auto ref = new std::shared_ptr<std::vector<uint8_t>>(uniform_data);
-      auto sk_uniform_data = SkData::MakeWithProc(
-          uniform_data->data(), uniform_data->size(),
-          [](const void* ptr, void* context) {
-            delete reinterpret_cast<std::shared_ptr<std::vector<uint8_t>>*>(
-                context);
-          },
-          ref);
-
-      return runtime_effect->skia_runtime_effect()->makeShader(
-          sk_uniform_data, sk_samplers.data(), sk_samplers.size());
-    }
-  }
-}
-
-sk_sp<SkImageFilter> ToSk(const DlImageFilter* filter) {
-  if (!filter) {
-    return nullptr;
-  }
-  switch (filter->type()) {
-    case DlImageFilterType::kBlur: {
-      const DlBlurImageFilter* blur_filter = filter->asBlur();
-      FML_DCHECK(blur_filter != nullptr);
-      return SkImageFilters::Blur(blur_filter->sigma_x(),
-                                  blur_filter->sigma_y(),
-                                  ToSk(blur_filter->tile_mode()), nullptr);
-    }
-    case DlImageFilterType::kDilate: {
-      const DlDilateImageFilter* dilate_filter = filter->asDilate();
-      FML_DCHECK(dilate_filter != nullptr);
-      return SkImageFilters::Dilate(dilate_filter->radius_x(),
-                                    dilate_filter->radius_y(), nullptr);
-    }
-    case DlImageFilterType::kErode: {
-      const DlErodeImageFilter* erode_filter = filter->asErode();
-      FML_DCHECK(erode_filter != nullptr);
-      return SkImageFilters::Erode(erode_filter->radius_x(),
-                                   erode_filter->radius_y(), nullptr);
-    }
-    case DlImageFilterType::kMatrix: {
-      const DlMatrixImageFilter* matrix_filter = filter->asMatrix();
-      FML_DCHECK(matrix_filter != nullptr);
-      return SkImageFilters::MatrixTransform(
-          matrix_filter->matrix(), ToSk(matrix_filter->sampling()), nullptr);
-    }
-    case DlImageFilterType::kCompose: {
-      const DlComposeImageFilter* compose_filter = filter->asCompose();
-      FML_DCHECK(compose_filter != nullptr);
-      return SkImageFilters::Compose(ToSk(compose_filter->outer()),
-                                     ToSk(compose_filter->inner()));
-    }
-    case DlImageFilterType::kColorFilter: {
-      const DlColorFilterImageFilter* cf_filter = filter->asColorFilter();
-      FML_DCHECK(cf_filter != nullptr);
-      return SkImageFilters::ColorFilter(ToSk(cf_filter->color_filter()),
-                                         nullptr);
-    }
-    case DlImageFilterType::kLocalMatrix: {
-      const DlLocalMatrixImageFilter* lm_filter = filter->asLocalMatrix();
-      FML_DCHECK(lm_filter != nullptr);
-      sk_sp<SkImageFilter> skia_filter = ToSk(lm_filter->image_filter());
-      // The image_filter property itself might have been null, or the
-      // construction of the SkImageFilter might be optimized to null
-      // for any number of reasons. In any case, if the filter is null
-      // or optimizaed away, let's then optimize away this local matrix
-      // case by returning null.
-      if (!skia_filter) {
-        return nullptr;
-      }
-      return skia_filter->makeWithLocalMatrix(lm_filter->matrix());
-    }
-  }
-}
-
-sk_sp<SkColorFilter> ToSk(const DlColorFilter* filter) {
-  if (!filter) {
-    return nullptr;
-  }
-  switch (filter->type()) {
-    case DlColorFilterType::kBlend: {
-      const DlBlendColorFilter* blend_filter = filter->asBlend();
-      FML_DCHECK(blend_filter != nullptr);
-      return SkColorFilters::Blend(blend_filter->color(),
-                                   ToSk(blend_filter->mode()));
-    }
-    case DlColorFilterType::kMatrix: {
-      const DlMatrixColorFilter* matrix_filter = filter->asMatrix();
-      FML_DCHECK(matrix_filter != nullptr);
-      float matrix[20];
-      matrix_filter->get_matrix(matrix);
-      return SkColorFilters::Matrix(matrix);
-    }
-    case DlColorFilterType::kSrgbToLinearGamma: {
-      return SkColorFilters::SRGBToLinearGamma();
-    }
-    case DlColorFilterType::kLinearToSrgbGamma: {
-      return SkColorFilters::LinearToSRGBGamma();
-    }
-  }
-}
-
-sk_sp<SkMaskFilter> ToSk(const DlMaskFilter* filter) {
-  if (!filter) {
-    return nullptr;
-  }
-  switch (filter->type()) {
-    case DlMaskFilterType::kBlur: {
-      const DlBlurMaskFilter* blur_filter = filter->asBlur();
-      FML_DCHECK(blur_filter != nullptr);
-      return SkMaskFilter::MakeBlur(blur_filter->style(), blur_filter->sigma(),
-                                    blur_filter->respectCTM());
-    }
-  }
-}
-
-sk_sp<SkPathEffect> ToSk(const DlPathEffect* effect) {
-  if (!effect) {
-    return nullptr;
-  }
-  switch (effect->type()) {
-    case DlPathEffectType::kDash: {
-      const DlDashPathEffect* dash_effect = effect->asDash();
-      FML_DCHECK(dash_effect != nullptr);
-      return SkDashPathEffect::Make(dash_effect->intervals(),
-                                    dash_effect->count(), dash_effect->phase());
-    }
-  }
-}
-
-sk_sp<SkVertices> ToSk(const DlVertices* vertices) {
-  const SkColor* sk_colors =
-      reinterpret_cast<const SkColor*>(vertices->colors());
-  return SkVertices::MakeCopy(ToSk(vertices->mode()), vertices->vertex_count(),
-                              vertices->vertices(),
-                              vertices->texture_coordinates(), sk_colors,
-                              vertices->index_count(), vertices->indices());
-}
-
-}  // namespace flutter
diff --git a/display_list/skia/dl_sk_conversions.h b/display_list/skia/dl_sk_conversions.h
deleted file mode 100644
index 54056c4..0000000
--- a/display_list/skia/dl_sk_conversions.h
+++ /dev/null
@@ -1,116 +0,0 @@
-// 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.
-
-#ifndef FLUTTER_DISPLAY_LIST_SKIA_DL_SK_CONVERSIONS_H_
-#define FLUTTER_DISPLAY_LIST_SKIA_DL_SK_CONVERSIONS_H_
-
-#include "flutter/display_list/dl_op_receiver.h"
-
-namespace flutter {
-
-inline SkBlendMode ToSk(DlBlendMode mode) {
-  return static_cast<SkBlendMode>(mode);
-}
-
-inline SkPaint::Style ToSk(DlDrawStyle style) {
-  return static_cast<SkPaint::Style>(style);
-}
-
-inline SkPaint::Cap ToSk(DlStrokeCap cap) {
-  return static_cast<SkPaint::Cap>(cap);
-}
-
-inline SkPaint::Join ToSk(DlStrokeJoin join) {
-  return static_cast<SkPaint::Join>(join);
-}
-
-inline SkTileMode ToSk(DlTileMode dl_mode) {
-  return static_cast<SkTileMode>(dl_mode);
-}
-
-inline SkFilterMode ToSk(const DlFilterMode filter_mode) {
-  return static_cast<SkFilterMode>(filter_mode);
-}
-
-inline SkVertices::VertexMode ToSk(DlVertexMode dl_mode) {
-  return static_cast<SkVertices::VertexMode>(dl_mode);
-}
-
-inline SkSamplingOptions ToSk(DlImageSampling sampling) {
-  switch (sampling) {
-    case DlImageSampling::kCubic:
-      return SkSamplingOptions(SkCubicResampler{1 / 3.0f, 1 / 3.0f});
-    case DlImageSampling::kLinear:
-      return SkSamplingOptions(SkFilterMode::kLinear);
-    case DlImageSampling::kMipmapLinear:
-      return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
-    case DlImageSampling::kNearestNeighbor:
-      return SkSamplingOptions(SkFilterMode::kNearest);
-  }
-}
-
-inline SkCanvas::SrcRectConstraint ToSk(
-    DlCanvas::SrcRectConstraint constraint) {
-  return static_cast<SkCanvas::SrcRectConstraint>(constraint);
-}
-
-inline SkClipOp ToSk(DlCanvas::ClipOp op) {
-  return static_cast<SkClipOp>(op);
-}
-
-inline SkCanvas::PointMode ToSk(DlCanvas::PointMode mode) {
-  return static_cast<SkCanvas::PointMode>(mode);
-}
-
-extern sk_sp<SkShader> ToSk(const DlColorSource* source);
-inline sk_sp<SkShader> ToSk(std::shared_ptr<const DlColorSource> source) {
-  return ToSk(source.get());
-}
-inline sk_sp<SkShader> ToSk(const DlColorSource& source) {
-  return ToSk(&source);
-}
-
-extern sk_sp<SkImageFilter> ToSk(const DlImageFilter* filter);
-inline sk_sp<SkImageFilter> ToSk(std::shared_ptr<const DlImageFilter> filter) {
-  return ToSk(filter.get());
-}
-inline sk_sp<SkImageFilter> ToSk(const DlImageFilter& filter) {
-  return ToSk(&filter);
-}
-
-extern sk_sp<SkColorFilter> ToSk(const DlColorFilter* filter);
-inline sk_sp<SkColorFilter> ToSk(std::shared_ptr<const DlColorFilter> filter) {
-  return ToSk(filter.get());
-}
-inline sk_sp<SkColorFilter> ToSk(const DlColorFilter& filter) {
-  return ToSk(&filter);
-}
-
-extern sk_sp<SkMaskFilter> ToSk(const DlMaskFilter* filter);
-inline sk_sp<SkMaskFilter> ToSk(std::shared_ptr<const DlMaskFilter> filter) {
-  return ToSk(filter.get());
-}
-inline sk_sp<SkMaskFilter> ToSk(const DlMaskFilter& filter) {
-  return ToSk(&filter);
-}
-
-extern sk_sp<SkPathEffect> ToSk(const DlPathEffect* effect);
-inline sk_sp<SkPathEffect> ToSk(std::shared_ptr<const DlPathEffect> effect) {
-  return ToSk(effect.get());
-}
-inline sk_sp<SkPathEffect> ToSk(const DlPathEffect& effect) {
-  return ToSk(&effect);
-}
-
-extern sk_sp<SkVertices> ToSk(const DlVertices* vertices);
-inline sk_sp<SkVertices> ToSk(std::shared_ptr<const DlVertices> vertices) {
-  return ToSk(vertices.get());
-}
-inline sk_sp<SkVertices> ToSk(const DlVertices& vertices) {
-  return ToSk(&vertices);
-}
-
-}  // namespace flutter
-
-#endif  // FLUTTER_DISPLAY_LIST_SKIA_DL_SK_CONVERSIONS_H_
diff --git a/display_list/skia/dl_sk_conversions_unittests.cc b/display_list/skia/dl_sk_conversions_unittests.cc
deleted file mode 100644
index c35a3c2..0000000
--- a/display_list/skia/dl_sk_conversions_unittests.cc
+++ /dev/null
@@ -1,267 +0,0 @@
-// 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 "flutter/display_list/skia/dl_sk_conversions.h"
-
-#include "flutter/display_list/display_list_blend_mode.h"
-#include "flutter/display_list/display_list_paint.h"
-#include "flutter/display_list/display_list_sampling_options.h"
-#include "flutter/display_list/display_list_tile_mode.h"
-#include "flutter/display_list/display_list_vertices.h"
-#include "flutter/display_list/types.h"
-#include "gtest/gtest.h"
-#include "include/core/SkSamplingOptions.h"
-
-namespace flutter {
-namespace testing {
-
-TEST(DisplayListImageFilter, LocalImageSkiaNull) {
-  auto blur_filter =
-      std::make_shared<DlBlurImageFilter>(0, 0, DlTileMode::kClamp);
-  DlLocalMatrixImageFilter dl_local_matrix_filter(SkMatrix::RotateDeg(45),
-                                                  blur_filter);
-  // With sigmas set to zero on the blur filter, Skia will return a null filter.
-  // The local matrix filter should return nullptr instead of crashing.
-  ASSERT_EQ(ToSk(dl_local_matrix_filter), nullptr);
-}
-
-TEST(DisplayListSkConversions, ToSkTileMode) {
-  ASSERT_EQ(ToSk(DlTileMode::kClamp), SkTileMode::kClamp);
-  ASSERT_EQ(ToSk(DlTileMode::kRepeat), SkTileMode::kRepeat);
-  ASSERT_EQ(ToSk(DlTileMode::kMirror), SkTileMode::kMirror);
-  ASSERT_EQ(ToSk(DlTileMode::kDecal), SkTileMode::kDecal);
-}
-
-TEST(DisplayListSkConversions, ToSkDrawStyle) {
-  ASSERT_EQ(ToSk(DlDrawStyle::kFill), SkPaint::Style::kFill_Style);
-  ASSERT_EQ(ToSk(DlDrawStyle::kStroke), SkPaint::Style::kStroke_Style);
-  ASSERT_EQ(ToSk(DlDrawStyle::kStrokeAndFill),
-            SkPaint::Style::kStrokeAndFill_Style);
-}
-
-TEST(DisplayListSkConversions, ToSkStrokeCap) {
-  ASSERT_EQ(ToSk(DlStrokeCap::kButt), SkPaint::Cap::kButt_Cap);
-  ASSERT_EQ(ToSk(DlStrokeCap::kRound), SkPaint::Cap::kRound_Cap);
-  ASSERT_EQ(ToSk(DlStrokeCap::kSquare), SkPaint::Cap::kSquare_Cap);
-}
-
-TEST(DisplayListSkConversions, ToSkStrokeJoin) {
-  ASSERT_EQ(ToSk(DlStrokeJoin::kMiter), SkPaint::Join::kMiter_Join);
-  ASSERT_EQ(ToSk(DlStrokeJoin::kRound), SkPaint::Join::kRound_Join);
-  ASSERT_EQ(ToSk(DlStrokeJoin::kBevel), SkPaint::Join::kBevel_Join);
-}
-
-TEST(DisplayListSkConversions, ToSkVertexMode) {
-  ASSERT_EQ(ToSk(DlVertexMode::kTriangles),
-            SkVertices::VertexMode::kTriangles_VertexMode);
-  ASSERT_EQ(ToSk(DlVertexMode::kTriangleStrip),
-            SkVertices::VertexMode::kTriangleStrip_VertexMode);
-  ASSERT_EQ(ToSk(DlVertexMode::kTriangleFan),
-            SkVertices::VertexMode::kTriangleFan_VertexMode);
-}
-
-TEST(DisplayListSkConversions, ToSkFilterMode) {
-  ASSERT_EQ(ToSk(DlFilterMode::kLinear), SkFilterMode::kLinear);
-  ASSERT_EQ(ToSk(DlFilterMode::kNearest), SkFilterMode::kNearest);
-  ASSERT_EQ(ToSk(DlFilterMode::kLast), SkFilterMode::kLast);
-}
-
-TEST(DisplayListSkConversions, ToSkSrcRectConstraint) {
-  ASSERT_EQ(ToSk(DlCanvas::SrcRectConstraint::kFast),
-            SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
-  ASSERT_EQ(ToSk(DlCanvas::SrcRectConstraint::kStrict),
-            SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint);
-}
-
-TEST(DisplayListSkConversions, ToSkSamplingOptions) {
-  ASSERT_EQ(ToSk(DlImageSampling::kLinear),
-            SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone));
-  ASSERT_EQ(ToSk(DlImageSampling::kMipmapLinear),
-            SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear));
-  ASSERT_EQ(ToSk(DlImageSampling::kNearestNeighbor),
-            SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone));
-  ASSERT_EQ(ToSk(DlImageSampling::kCubic),
-            SkSamplingOptions(SkCubicResampler{1 / 3.0f, 1 / 3.0f}));
-}
-
-#define FOR_EACH_BLEND_MODE_ENUM(FUNC) \
-  FUNC(kSrc)                           \
-  FUNC(kClear)                         \
-  FUNC(kSrc)                           \
-  FUNC(kDst)                           \
-  FUNC(kSrcOver)                       \
-  FUNC(kDstOver)                       \
-  FUNC(kSrcIn)                         \
-  FUNC(kDstIn)                         \
-  FUNC(kSrcOut)                        \
-  FUNC(kDstOut)                        \
-  FUNC(kSrcATop)                       \
-  FUNC(kDstATop)                       \
-  FUNC(kXor)                           \
-  FUNC(kPlus)                          \
-  FUNC(kModulate)                      \
-  FUNC(kScreen)                        \
-  FUNC(kOverlay)                       \
-  FUNC(kDarken)                        \
-  FUNC(kLighten)                       \
-  FUNC(kColorDodge)                    \
-  FUNC(kColorBurn)                     \
-  FUNC(kHardLight)                     \
-  FUNC(kSoftLight)                     \
-  FUNC(kDifference)                    \
-  FUNC(kExclusion)                     \
-  FUNC(kMultiply)                      \
-  FUNC(kHue)                           \
-  FUNC(kSaturation)                    \
-  FUNC(kColor)                         \
-  FUNC(kLuminosity)                    \
-  FUNC(kLastCoeffMode)                 \
-  FUNC(kLastSeparableMode)             \
-  FUNC(kLastMode)
-
-TEST(DisplayListSkConversions, ToSkBlendMode) {
-#define CHECK_TO_SKENUM(V) ASSERT_EQ(ToSk(DlBlendMode::V), SkBlendMode::V);
-  FOR_EACH_BLEND_MODE_ENUM(CHECK_TO_SKENUM)
-#undef CHECK_TO_SKENUM
-}
-
-TEST(DisplayListSkConversions, BlendColorFilterModifiesTransparency) {
-  auto test_mode_color = [](DlBlendMode mode, DlColor color) {
-    std::stringstream desc_str;
-    desc_str << "blend[" << static_cast<int>(mode) << ", " << color << "]";
-    std::string desc = desc_str.str();
-    DlBlendColorFilter filter(color, mode);
-    if (filter.modifies_transparent_black()) {
-      auto dl_filter = DlBlendColorFilter::Make(color, mode);
-      auto sk_filter = ToSk(filter);
-      ASSERT_NE(dl_filter, nullptr) << desc;
-      ASSERT_NE(sk_filter, nullptr) << desc;
-      ASSERT_TRUE(sk_filter->filterColor(0) != 0) << desc;
-    } else {
-      auto dl_filter = DlBlendColorFilter::Make(color, mode);
-      auto sk_filter = ToSk(filter);
-      EXPECT_EQ(dl_filter == nullptr, sk_filter == nullptr) << desc;
-      ASSERT_TRUE(sk_filter == nullptr || sk_filter->filterColor(0) == 0)
-          << desc;
-    }
-  };
-
-  auto test_mode = [&test_mode_color](DlBlendMode mode) {
-    test_mode_color(mode, DlColor::kTransparent());
-    test_mode_color(mode, DlColor::kWhite());
-    test_mode_color(mode, DlColor::kWhite().modulateOpacity(0.5));
-    test_mode_color(mode, DlColor::kBlack());
-    test_mode_color(mode, DlColor::kBlack().modulateOpacity(0.5));
-  };
-
-#define TEST_MODE(V) test_mode(DlBlendMode::V);
-  FOR_EACH_BLEND_MODE_ENUM(TEST_MODE)
-#undef TEST_MODE
-}
-
-#undef FOR_EACH_BLEND_MODE_ENUM
-
-TEST(DisplayListSkConversions, ConvertWithZeroAndNegativeVerticesAndIndices) {
-  std::shared_ptr<const DlVertices> vertices1 = DlVertices::Make(
-      DlVertexMode::kTriangles, 0, nullptr, nullptr, nullptr, 0, nullptr);
-  EXPECT_NE(vertices1, nullptr);
-  EXPECT_NE(ToSk(vertices1), nullptr);
-
-  std::shared_ptr<const DlVertices> vertices2 = DlVertices::Make(
-      DlVertexMode::kTriangles, -1, nullptr, nullptr, nullptr, -1, nullptr);
-  EXPECT_NE(vertices2, nullptr);
-  EXPECT_NE(ToSk(vertices2), nullptr);
-}
-
-TEST(DisplayListVertices, ConvertWithZeroAndNegativeVerticesAndIndices) {
-  DlVertices::Builder builder1(DlVertexMode::kTriangles, 0,
-                               DlVertices::Builder::kNone, 0);
-  EXPECT_TRUE(builder1.is_valid());
-  std::shared_ptr<DlVertices> vertices1 = builder1.build();
-  EXPECT_NE(vertices1, nullptr);
-  EXPECT_NE(ToSk(vertices1), nullptr);
-
-  DlVertices::Builder builder2(DlVertexMode::kTriangles, -1,
-                               DlVertices::Builder::kNone, -1);
-  EXPECT_TRUE(builder2.is_valid());
-  std::shared_ptr<DlVertices> vertices2 = builder2.build();
-  EXPECT_NE(vertices2, nullptr);
-  EXPECT_NE(ToSk(vertices2), nullptr);
-}
-
-TEST(DisplayListColorSource, ConvertRuntimeEffect) {
-  const sk_sp<DlRuntimeEffect> kTestRuntimeEffect1 = DlRuntimeEffect::MakeSkia(
-      SkRuntimeEffect::MakeForShader(
-          SkString("vec4 main(vec2 p) { return vec4(0); }"))
-          .effect);
-  const sk_sp<DlRuntimeEffect> kTestRuntimeEffect2 = DlRuntimeEffect::MakeSkia(
-      SkRuntimeEffect::MakeForShader(
-          SkString("vec4 main(vec2 p) { return vec4(1); }"))
-          .effect);
-  std::shared_ptr<DlRuntimeEffectColorSource> source1 =
-      DlColorSource::MakeRuntimeEffect(
-          kTestRuntimeEffect1, {}, std::make_shared<std::vector<uint8_t>>());
-  std::shared_ptr<DlRuntimeEffectColorSource> source2 =
-      DlColorSource::MakeRuntimeEffect(
-          kTestRuntimeEffect2, {}, std::make_shared<std::vector<uint8_t>>());
-  std::shared_ptr<DlRuntimeEffectColorSource> source3 =
-      DlColorSource::MakeRuntimeEffect(
-          nullptr, {}, std::make_shared<std::vector<uint8_t>>());
-
-  ASSERT_NE(ToSk(source1), nullptr);
-  ASSERT_NE(ToSk(source2), nullptr);
-  ASSERT_EQ(ToSk(source3), nullptr);
-}
-
-TEST(DisplayListColorSource, ConvertRuntimeEffectWithNullSampler) {
-  const sk_sp<DlRuntimeEffect> kTestRuntimeEffect1 = DlRuntimeEffect::MakeSkia(
-      SkRuntimeEffect::MakeForShader(
-          SkString("vec4 main(vec2 p) { return vec4(0); }"))
-          .effect);
-  std::shared_ptr<DlRuntimeEffectColorSource> source1 =
-      DlColorSource::MakeRuntimeEffect(
-          kTestRuntimeEffect1, {nullptr},
-          std::make_shared<std::vector<uint8_t>>());
-
-  ASSERT_EQ(ToSk(source1), nullptr);
-}
-
-TEST(DisplayListSkConversions, MatrixColorFilterModifiesTransparency) {
-  auto test_matrix = [](int element, SkScalar value) {
-    // clang-format off
-    float matrix[] = {
-        1, 0, 0, 0, 0,
-        0, 1, 0, 0, 0,
-        0, 0, 1, 0, 0,
-        0, 0, 0, 1, 0,
-    };
-    // clang-format on
-    std::string desc =
-        "matrix[" + std::to_string(element) + "] = " + std::to_string(value);
-    matrix[element] = value;
-    DlMatrixColorFilter filter(matrix);
-    auto dl_filter = DlMatrixColorFilter::Make(matrix);
-    auto sk_filter = ToSk(filter);
-    EXPECT_EQ(dl_filter == nullptr, sk_filter == nullptr);
-    EXPECT_EQ(filter.modifies_transparent_black(),
-              sk_filter && sk_filter->filterColor(0) != 0);
-  };
-
-  // Tests identity (matrix[0] already == 1 in an identity filter)
-  test_matrix(0, 1);
-  // test_matrix(19, 1);
-  for (int i = 0; i < 20; i++) {
-    test_matrix(i, -0.25);
-    test_matrix(i, 0);
-    test_matrix(i, 0.25);
-    test_matrix(i, 1);
-    test_matrix(i, 1.25);
-    test_matrix(i, SK_ScalarNaN);
-    test_matrix(i, SK_ScalarInfinity);
-    test_matrix(i, -SK_ScalarInfinity);
-  }
-}
-
-}  // namespace testing
-}  // namespace flutter
diff --git a/display_list/skia/dl_sk_dispatcher.cc b/display_list/skia/dl_sk_dispatcher.cc
deleted file mode 100644
index 9d01cc7..0000000
--- a/display_list/skia/dl_sk_dispatcher.cc
+++ /dev/null
@@ -1,305 +0,0 @@
-// 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 "flutter/display_list/skia/dl_sk_dispatcher.h"
-
-#include "flutter/display_list/display_list_blend_mode.h"
-#include "flutter/display_list/skia/dl_sk_conversions.h"
-#include "flutter/fml/trace_event.h"
-#include "third_party/skia/include/utils/SkShadowUtils.h"
-
-namespace flutter {
-
-const SkPaint* DlSkCanvasDispatcher::safe_paint(bool use_attributes) {
-  if (use_attributes) {
-    // The accumulated SkPaint object will already have incorporated
-    // any attribute overrides.
-    return &paint();
-  } else if (has_opacity()) {
-    temp_paint_.setAlphaf(opacity());
-    return &temp_paint_;
-  } else {
-    return nullptr;
-  }
-}
-
-void DlSkCanvasDispatcher::save() {
-  canvas_->save();
-  // save has no impact on attributes, but it needs to register a record
-  // on the restore stack so that the eventual call to restore() will
-  // know what to do at that time. We could annotate the restore record
-  // with a flag that the record came from a save call, but it is simpler
-  // to just pass in the current opacity value as the value to be used by
-  // the children and let the utility calls notice that it didn't change.
-  save_opacity(opacity());
-}
-void DlSkCanvasDispatcher::restore() {
-  canvas_->restore();
-  restore_opacity();
-}
-void DlSkCanvasDispatcher::saveLayer(const SkRect* bounds,
-                                     const SaveLayerOptions options,
-                                     const DlImageFilter* backdrop) {
-  if (bounds == nullptr && options.can_distribute_opacity() &&
-      backdrop == nullptr) {
-    // We know that:
-    // - no bounds is needed for clipping here
-    // - no backdrop filter is used to initialize the layer
-    // - the current attributes only have an alpha
-    // - the children are compatible with individually rendering with
-    //   an inherited opacity
-    // Therefore we can just use a save instead of a saveLayer and pass the
-    // intended opacity to the children.
-    canvas_->save();
-    // If the saveLayer does not use attributes, the children should continue
-    // to render with the inherited opacity unmodified. If attributes are to
-    // be applied, the children should render with the combination of the
-    // inherited opacity combined with the alpha from the current color.
-    save_opacity(options.renders_with_attributes() ? combined_opacity()
-                                                   : opacity());
-  } else {
-    TRACE_EVENT0("flutter", "Canvas::saveLayer");
-    const SkPaint* paint = safe_paint(options.renders_with_attributes());
-    const sk_sp<SkImageFilter> sk_backdrop = ToSk(backdrop);
-    canvas_->saveLayer(
-        SkCanvas::SaveLayerRec(bounds, paint, sk_backdrop.get(), 0));
-    // saveLayer will apply the current opacity on behalf of the children
-    // so they will inherit an opaque opacity.
-    save_opacity(SK_Scalar1);
-  }
-}
-
-void DlSkCanvasDispatcher::translate(SkScalar tx, SkScalar ty) {
-  canvas_->translate(tx, ty);
-}
-void DlSkCanvasDispatcher::scale(SkScalar sx, SkScalar sy) {
-  canvas_->scale(sx, sy);
-}
-void DlSkCanvasDispatcher::rotate(SkScalar degrees) {
-  canvas_->rotate(degrees);
-}
-void DlSkCanvasDispatcher::skew(SkScalar sx, SkScalar sy) {
-  canvas_->skew(sx, sy);
-}
-// clang-format off
-// 2x3 2D affine subset of a 4x4 transform in row major order
-void DlSkCanvasDispatcher::transform2DAffine(
-    SkScalar mxx, SkScalar mxy, SkScalar mxt,
-    SkScalar myx, SkScalar myy, SkScalar myt) {
-  // Internally concat(SkMatrix) gets redirected to concat(SkM44)
-  // so we just jump directly to the SkM44 version
-  canvas_->concat(SkM44(mxx, mxy, 0, mxt,
-                        myx, myy, 0, myt,
-                         0,   0,  1,  0,
-                         0,   0,  0,  1));
-}
-// full 4x4 transform in row major order
-void DlSkCanvasDispatcher::transformFullPerspective(
-    SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
-    SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
-    SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt,
-    SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) {
-  canvas_->concat(SkM44(mxx, mxy, mxz, mxt,
-                        myx, myy, myz, myt,
-                        mzx, mzy, mzz, mzt,
-                        mwx, mwy, mwz, mwt));
-}
-// clang-format on
-void DlSkCanvasDispatcher::transformReset() {
-  canvas_->setMatrix(original_transform_);
-}
-
-void DlSkCanvasDispatcher::clipRect(const SkRect& rect,
-                                    ClipOp clip_op,
-                                    bool is_aa) {
-  canvas_->clipRect(rect, ToSk(clip_op), is_aa);
-}
-void DlSkCanvasDispatcher::clipRRect(const SkRRect& rrect,
-                                     ClipOp clip_op,
-                                     bool is_aa) {
-  canvas_->clipRRect(rrect, ToSk(clip_op), is_aa);
-}
-void DlSkCanvasDispatcher::clipPath(const SkPath& path,
-                                    ClipOp clip_op,
-                                    bool is_aa) {
-  canvas_->clipPath(path, ToSk(clip_op), is_aa);
-}
-
-void DlSkCanvasDispatcher::drawPaint() {
-  const SkPaint& sk_paint = paint();
-  SkImageFilter* filter = sk_paint.getImageFilter();
-  if (filter && !filter->asColorFilter(nullptr)) {
-    // drawPaint does an implicit saveLayer if an SkImageFilter is
-    // present that cannot be replaced by an SkColorFilter.
-    TRACE_EVENT0("flutter", "Canvas::saveLayer");
-  }
-  canvas_->drawPaint(sk_paint);
-}
-void DlSkCanvasDispatcher::drawColor(DlColor color, DlBlendMode mode) {
-  // SkCanvas::drawColor(SkColor) does the following conversion anyway
-  // We do it here manually to increase precision on applying opacity
-  SkColor4f color4f = SkColor4f::FromColor(color);
-  color4f.fA *= opacity();
-  canvas_->drawColor(color4f, ToSk(mode));
-}
-void DlSkCanvasDispatcher::drawLine(const SkPoint& p0, const SkPoint& p1) {
-  canvas_->drawLine(p0, p1, paint());
-}
-void DlSkCanvasDispatcher::drawRect(const SkRect& rect) {
-  canvas_->drawRect(rect, paint());
-}
-void DlSkCanvasDispatcher::drawOval(const SkRect& bounds) {
-  canvas_->drawOval(bounds, paint());
-}
-void DlSkCanvasDispatcher::drawCircle(const SkPoint& center, SkScalar radius) {
-  canvas_->drawCircle(center, radius, paint());
-}
-void DlSkCanvasDispatcher::drawRRect(const SkRRect& rrect) {
-  canvas_->drawRRect(rrect, paint());
-}
-void DlSkCanvasDispatcher::drawDRRect(const SkRRect& outer,
-                                      const SkRRect& inner) {
-  canvas_->drawDRRect(outer, inner, paint());
-}
-void DlSkCanvasDispatcher::drawPath(const SkPath& path) {
-  canvas_->drawPath(path, paint());
-}
-void DlSkCanvasDispatcher::drawArc(const SkRect& bounds,
-                                   SkScalar start,
-                                   SkScalar sweep,
-                                   bool useCenter) {
-  canvas_->drawArc(bounds, start, sweep, useCenter, paint());
-}
-void DlSkCanvasDispatcher::drawPoints(PointMode mode,
-                                      uint32_t count,
-                                      const SkPoint pts[]) {
-  canvas_->drawPoints(ToSk(mode), count, pts, paint());
-}
-void DlSkCanvasDispatcher::drawVertices(const DlVertices* vertices,
-                                        DlBlendMode mode) {
-  canvas_->drawVertices(ToSk(vertices), ToSk(mode), paint());
-}
-void DlSkCanvasDispatcher::drawImage(const sk_sp<DlImage> image,
-                                     const SkPoint point,
-                                     DlImageSampling sampling,
-                                     bool render_with_attributes) {
-  canvas_->drawImage(image ? image->skia_image() : nullptr, point.fX, point.fY,
-                     ToSk(sampling), safe_paint(render_with_attributes));
-}
-void DlSkCanvasDispatcher::drawImageRect(const sk_sp<DlImage> image,
-                                         const SkRect& src,
-                                         const SkRect& dst,
-                                         DlImageSampling sampling,
-                                         bool render_with_attributes,
-                                         SrcRectConstraint constraint) {
-  canvas_->drawImageRect(image ? image->skia_image() : nullptr, src, dst,
-                         ToSk(sampling), safe_paint(render_with_attributes),
-                         ToSk(constraint));
-}
-void DlSkCanvasDispatcher::drawImageNine(const sk_sp<DlImage> image,
-                                         const SkIRect& center,
-                                         const SkRect& dst,
-                                         DlFilterMode filter,
-                                         bool render_with_attributes) {
-  if (!image) {
-    return;
-  }
-  auto skia_image = image->skia_image();
-  if (!skia_image) {
-    return;
-  }
-  canvas_->drawImageNine(skia_image.get(), center, dst, ToSk(filter),
-                         safe_paint(render_with_attributes));
-}
-void DlSkCanvasDispatcher::drawAtlas(const sk_sp<DlImage> atlas,
-                                     const SkRSXform xform[],
-                                     const SkRect tex[],
-                                     const DlColor colors[],
-                                     int count,
-                                     DlBlendMode mode,
-                                     DlImageSampling sampling,
-                                     const SkRect* cullRect,
-                                     bool render_with_attributes) {
-  if (!atlas) {
-    return;
-  }
-  auto skia_atlas = atlas->skia_image();
-  if (!skia_atlas) {
-    return;
-  }
-  const SkColor* sk_colors = reinterpret_cast<const SkColor*>(colors);
-  canvas_->drawAtlas(skia_atlas.get(), xform, tex, sk_colors, count, ToSk(mode),
-                     ToSk(sampling), cullRect,
-                     safe_paint(render_with_attributes));
-}
-void DlSkCanvasDispatcher::drawDisplayList(
-    const sk_sp<DisplayList> display_list,
-    SkScalar opacity) {
-  int restore_count;
-
-  // Compute combined opacity and figure out whether we can apply it
-  // during dispatch or if we need a saveLayer.
-  SkScalar combined_opacity = opacity * this->opacity();
-  if (combined_opacity < SK_Scalar1 &&
-      !display_list->can_apply_group_opacity()) {
-    SkPaint save_paint;
-    save_paint.setAlphaf(combined_opacity);
-    restore_count = canvas_->saveLayer(display_list->bounds(), &save_paint);
-    combined_opacity = SK_Scalar1;
-  } else {
-    restore_count = canvas_->save();
-  }
-
-  // Create a new CanvasDispatcher to isolate the actions of the
-  // display_list from the current environment.
-  DlSkCanvasDispatcher dispatcher(canvas_, combined_opacity);
-  if (display_list->rtree()) {
-    display_list->Dispatch(dispatcher, canvas_->getLocalClipBounds());
-  } else {
-    display_list->Dispatch(dispatcher);
-  }
-
-  // Restore canvas state to what it was before dispatching.
-  canvas_->restoreToCount(restore_count);
-}
-void DlSkCanvasDispatcher::drawTextBlob(const sk_sp<SkTextBlob> blob,
-                                        SkScalar x,
-                                        SkScalar y) {
-  canvas_->drawTextBlob(blob, x, y, paint());
-}
-
-void DlSkCanvasDispatcher::DrawShadow(SkCanvas* canvas,
-                                      const SkPath& path,
-                                      DlColor color,
-                                      float elevation,
-                                      bool transparentOccluder,
-                                      SkScalar dpr) {
-  const SkScalar kAmbientAlpha = 0.039f;
-  const SkScalar kSpotAlpha = 0.25f;
-
-  uint32_t flags = transparentOccluder
-                       ? SkShadowFlags::kTransparentOccluder_ShadowFlag
-                       : SkShadowFlags::kNone_ShadowFlag;
-  flags |= SkShadowFlags::kDirectionalLight_ShadowFlag;
-  SkColor in_ambient = SkColorSetA(color, kAmbientAlpha * SkColorGetA(color));
-  SkColor in_spot = SkColorSetA(color, kSpotAlpha * SkColorGetA(color));
-  SkColor ambient_color, spot_color;
-  SkShadowUtils::ComputeTonalColors(in_ambient, in_spot, &ambient_color,
-                                    &spot_color);
-  SkShadowUtils::DrawShadow(
-      canvas, path, SkPoint3::Make(0, 0, dpr * elevation),
-      SkPoint3::Make(0, -1, 1),
-      DlCanvas::kShadowLightRadius / DlCanvas::kShadowLightHeight,
-      ambient_color, spot_color, flags);
-}
-
-void DlSkCanvasDispatcher::drawShadow(const SkPath& path,
-                                      const DlColor color,
-                                      const SkScalar elevation,
-                                      bool transparent_occluder,
-                                      SkScalar dpr) {
-  DrawShadow(canvas_, path, color, elevation, transparent_occluder, dpr);
-}
-
-}  // namespace flutter
diff --git a/display_list/skia/dl_sk_utils.cc b/display_list/skia/dl_sk_utils.cc
deleted file mode 100644
index 3476ab3..0000000
--- a/display_list/skia/dl_sk_utils.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-// 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 "flutter/display_list/skia/dl_sk_utils.h"
-
-#include <math.h>
-#include <optional>
-#include <type_traits>
-
-#include "flutter/display_list/display_list_blend_mode.h"
-#include "flutter/display_list/skia/dl_sk_conversions.h"
-#include "flutter/fml/logging.h"
-
-namespace flutter {
-
-// clang-format off
-constexpr float kInvertColorMatrix[20] = {
-  -1.0,    0,    0, 1.0, 0,
-     0, -1.0,    0, 1.0, 0,
-     0,    0, -1.0, 1.0, 0,
-   1.0,  1.0,  1.0, 1.0, 0
-};
-// clang-format on
-
-void SkPaintDispatchHelper::save_opacity(SkScalar child_opacity) {
-  save_stack_.emplace_back(opacity_);
-  set_opacity(child_opacity);
-}
-void SkPaintDispatchHelper::restore_opacity() {
-  if (save_stack_.empty()) {
-    return;
-  }
-  set_opacity(save_stack_.back().opacity);
-  save_stack_.pop_back();
-}
-
-void SkPaintDispatchHelper::setAntiAlias(bool aa) {
-  paint_.setAntiAlias(aa);
-}
-void SkPaintDispatchHelper::setDither(bool dither) {
-  paint_.setDither(dither);
-}
-void SkPaintDispatchHelper::setInvertColors(bool invert) {
-  invert_colors_ = invert;
-  paint_.setColorFilter(makeColorFilter());
-}
-void SkPaintDispatchHelper::setStrokeCap(DlStrokeCap cap) {
-  paint_.setStrokeCap(ToSk(cap));
-}
-void SkPaintDispatchHelper::setStrokeJoin(DlStrokeJoin join) {
-  paint_.setStrokeJoin(ToSk(join));
-}
-void SkPaintDispatchHelper::setStyle(DlDrawStyle style) {
-  paint_.setStyle(ToSk(style));
-}
-void SkPaintDispatchHelper::setStrokeWidth(SkScalar width) {
-  paint_.setStrokeWidth(width);
-}
-void SkPaintDispatchHelper::setStrokeMiter(SkScalar limit) {
-  paint_.setStrokeMiter(limit);
-}
-void SkPaintDispatchHelper::setColor(DlColor color) {
-  current_color_ = color;
-  paint_.setColor(color);
-  if (has_opacity()) {
-    paint_.setAlphaf(paint_.getAlphaf() * opacity());
-  }
-}
-void SkPaintDispatchHelper::setBlendMode(DlBlendMode mode) {
-  paint_.setBlendMode(ToSk(mode));
-}
-void SkPaintDispatchHelper::setColorSource(const DlColorSource* source) {
-  paint_.setShader(ToSk(source));
-}
-void SkPaintDispatchHelper::setImageFilter(const DlImageFilter* filter) {
-  paint_.setImageFilter(ToSk(filter));
-}
-void SkPaintDispatchHelper::setColorFilter(const DlColorFilter* filter) {
-  sk_color_filter_ = ToSk(filter);
-  paint_.setColorFilter(makeColorFilter());
-}
-void SkPaintDispatchHelper::setPathEffect(const DlPathEffect* effect) {
-  paint_.setPathEffect(ToSk(effect));
-}
-void SkPaintDispatchHelper::setMaskFilter(const DlMaskFilter* filter) {
-  paint_.setMaskFilter(ToSk(filter));
-}
-
-sk_sp<SkColorFilter> SkPaintDispatchHelper::makeColorFilter() const {
-  if (!invert_colors_) {
-    return sk_color_filter_;
-  }
-  sk_sp<SkColorFilter> invert_filter =
-      SkColorFilters::Matrix(kInvertColorMatrix);
-  if (sk_color_filter_) {
-    invert_filter = invert_filter->makeComposed(sk_color_filter_);
-  }
-  return invert_filter;
-}
-
-}  // namespace flutter
diff --git a/display_list/skia/dl_sk_utils.h b/display_list/skia/dl_sk_utils.h
deleted file mode 100644
index e61848d..0000000
--- a/display_list/skia/dl_sk_utils.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// 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.
-
-#ifndef FLUTTER_DISPLAY_LIST_SKIA_DL_SK_UTILS_H_
-#define FLUTTER_DISPLAY_LIST_SKIA_DL_SK_UTILS_H_
-
-#include "flutter/display_list/dl_op_receiver.h"
-
-namespace flutter {
-
-// A utility class that will monitor the DlOpReceiver methods relating
-// to the rendering attributes and accumulate them into an SkPaint
-// which can be accessed at any time via paint().
-class SkPaintDispatchHelper : public virtual DlOpReceiver {
- public:
-  SkPaintDispatchHelper(SkScalar opacity = SK_Scalar1)
-      : current_color_(SK_ColorBLACK), opacity_(opacity) {
-    if (opacity < SK_Scalar1) {
-      paint_.setAlphaf(opacity);
-    }
-  }
-
-  void setAntiAlias(bool aa) override;
-  void setDither(bool dither) override;
-  void setStyle(DlDrawStyle style) override;
-  void setColor(DlColor color) override;
-  void setStrokeWidth(SkScalar width) override;
-  void setStrokeMiter(SkScalar limit) override;
-  void setStrokeCap(DlStrokeCap cap) override;
-  void setStrokeJoin(DlStrokeJoin join) override;
-  void setColorSource(const DlColorSource* source) override;
-  void setColorFilter(const DlColorFilter* filter) override;
-  void setInvertColors(bool invert) override;
-  void setBlendMode(DlBlendMode mode) override;
-  void setPathEffect(const DlPathEffect* effect) override;
-  void setMaskFilter(const DlMaskFilter* filter) override;
-  void setImageFilter(const DlImageFilter* filter) override;
-
-  const SkPaint& paint() { return paint_; }
-
-  /// Returns the current opacity attribute which is used to reduce
-  /// the alpha of all setColor calls encountered in the streeam
-  SkScalar opacity() { return opacity_; }
-  /// Returns the combined opacity that includes both the current
-  /// opacity attribute and the alpha of the most recent color.
-  /// The most recently set color will have combined the two and
-  /// stored the combined value in the alpha of the paint.
-  SkScalar combined_opacity() { return paint_.getAlphaf(); }
-  /// Returns true iff the current opacity attribute is not opaque,
-  /// irrespective of the alpha of the current color
-  bool has_opacity() { return opacity_ < SK_Scalar1; }
-
- protected:
-  void save_opacity(SkScalar opacity_for_children);
-  void restore_opacity();
-
- private:
-  SkPaint paint_;
-  bool invert_colors_ = false;
-  sk_sp<SkColorFilter> sk_color_filter_;
-
-  sk_sp<SkColorFilter> makeColorFilter() const;
-
-  struct SaveInfo {
-    SaveInfo(SkScalar opacity) : opacity(opacity) {}
-
-    SkScalar opacity;
-  };
-  std::vector<SaveInfo> save_stack_;
-
-  void set_opacity(SkScalar opacity) {
-    if (opacity_ != opacity) {
-      opacity_ = opacity;
-      setColor(current_color_);
-    }
-  }
-
-  SkColor current_color_;
-  SkScalar opacity_;
-};
-
-}  // namespace flutter
-
-#endif  // FLUTTER_DISPLAY_LIST_SKIA_DL_SK_UTILS_H_
diff --git a/display_list/testing/dl_test_snippets.cc b/display_list/testing/dl_test_snippets.cc
index 2116cf9..741b0db 100644
--- a/display_list/testing/dl_test_snippets.cc
+++ b/display_list/testing/dl_test_snippets.cc
@@ -4,35 +4,34 @@
 
 #include "flutter/display_list/testing/dl_test_snippets.h"
 #include "flutter/display_list/display_list_builder.h"
-#include "flutter/display_list/dl_op_receiver.h"
 
 namespace flutter {
 namespace testing {
 
 sk_sp<DisplayList> GetSampleDisplayList() {
   DisplayListBuilder builder(SkRect::MakeWH(150, 100));
-  builder.DrawRect(SkRect::MakeXYWH(10, 10, 80, 80), DlPaint(DlColor::kRed()));
+  builder.setColor(SK_ColorRED);
+  builder.drawRect(SkRect::MakeXYWH(10, 10, 80, 80));
   return builder.Build();
 }
 
 sk_sp<DisplayList> GetSampleNestedDisplayList() {
   DisplayListBuilder builder(SkRect::MakeWH(150, 100));
-  DlPaint paint;
   for (int y = 10; y <= 60; y += 10) {
     for (int x = 10; x <= 60; x += 10) {
-      paint.setColor(((x + y) % 20) == 10 ? SK_ColorRED : SK_ColorBLUE);
-      builder.DrawRect(SkRect::MakeXYWH(x, y, 80, 80), paint);
+      builder.setColor(((x + y) % 20) == 10 ? SK_ColorRED : SK_ColorBLUE);
+      builder.drawRect(SkRect::MakeXYWH(x, y, 80, 80));
     }
   }
   DisplayListBuilder outer_builder(SkRect::MakeWH(150, 100));
-  outer_builder.DrawDisplayList(builder.Build());
+  outer_builder.drawDisplayList(builder.Build());
   return outer_builder.Build();
 }
 
 sk_sp<DisplayList> GetSampleDisplayList(int ops) {
   DisplayListBuilder builder(SkRect::MakeWH(150, 100));
   for (int i = 0; i < ops; i++) {
-    builder.DrawColor(DlColor::kRed(), DlBlendMode::kSrc);
+    builder.drawColor(SK_ColorRED, DlBlendMode::kSrc);
   }
   return builder.Build();
 }
@@ -45,148 +44,192 @@
   return {
       {"SetAntiAlias",
        {
-           {0, 8, 0, 0, [](DlOpReceiver& r) { r.setAntiAlias(true); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.setAntiAlias(false); }},
+           {0, 8, 0, 0, [](DisplayListBuilder& b) { b.setAntiAlias(true); }},
+           {0, 0, 0, 0, [](DisplayListBuilder& b) { b.setAntiAlias(false); }},
        }},
       {"SetDither",
        {
-           {0, 8, 0, 0, [](DlOpReceiver& r) { r.setDither(true); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.setDither(false); }},
+           {0, 8, 0, 0, [](DisplayListBuilder& b) { b.setDither(true); }},
+           {0, 0, 0, 0, [](DisplayListBuilder& b) { b.setDither(false); }},
        }},
       {"SetInvertColors",
        {
-           {0, 8, 0, 0, [](DlOpReceiver& r) { r.setInvertColors(true); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.setInvertColors(false); }},
+           {0, 8, 0, 0, [](DisplayListBuilder& b) { b.setInvertColors(true); }},
+           {0, 0, 0, 0,
+            [](DisplayListBuilder& b) { b.setInvertColors(false); }},
        }},
       {"SetStrokeCap",
        {
            {0, 8, 0, 0,
-            [](DlOpReceiver& r) { r.setStrokeCap(DlStrokeCap::kRound); }},
+            [](DisplayListBuilder& b) { b.setStrokeCap(DlStrokeCap::kRound); }},
            {0, 8, 0, 0,
-            [](DlOpReceiver& r) { r.setStrokeCap(DlStrokeCap::kSquare); }},
+            [](DisplayListBuilder& b) {
+              b.setStrokeCap(DlStrokeCap::kSquare);
+            }},
            {0, 0, 0, 0,
-            [](DlOpReceiver& r) { r.setStrokeCap(DlStrokeCap::kButt); }},
+            [](DisplayListBuilder& b) { b.setStrokeCap(DlStrokeCap::kButt); }},
        }},
       {"SetStrokeJoin",
        {
            {0, 8, 0, 0,
-            [](DlOpReceiver& r) { r.setStrokeJoin(DlStrokeJoin::kBevel); }},
+            [](DisplayListBuilder& b) {
+              b.setStrokeJoin(DlStrokeJoin::kBevel);
+            }},
            {0, 8, 0, 0,
-            [](DlOpReceiver& r) { r.setStrokeJoin(DlStrokeJoin::kRound); }},
+            [](DisplayListBuilder& b) {
+              b.setStrokeJoin(DlStrokeJoin::kRound);
+            }},
            {0, 0, 0, 0,
-            [](DlOpReceiver& r) { r.setStrokeJoin(DlStrokeJoin::kMiter); }},
+            [](DisplayListBuilder& b) {
+              b.setStrokeJoin(DlStrokeJoin::kMiter);
+            }},
        }},
       {"SetStyle",
        {
            {0, 8, 0, 0,
-            [](DlOpReceiver& r) { r.setStyle(DlDrawStyle::kStroke); }},
+            [](DisplayListBuilder& b) { b.setStyle(DlDrawStyle::kStroke); }},
            {0, 8, 0, 0,
-            [](DlOpReceiver& r) { r.setStyle(DlDrawStyle::kStrokeAndFill); }},
+            [](DisplayListBuilder& b) {
+              b.setStyle(DlDrawStyle::kStrokeAndFill);
+            }},
            {0, 0, 0, 0,
-            [](DlOpReceiver& r) { r.setStyle(DlDrawStyle::kFill); }},
+            [](DisplayListBuilder& b) { b.setStyle(DlDrawStyle::kFill); }},
        }},
       {"SetStrokeWidth",
        {
-           {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeWidth(1.0); }},
-           {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeWidth(5.0); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.setStrokeWidth(0.0); }},
+           {0, 8, 0, 0, [](DisplayListBuilder& b) { b.setStrokeWidth(1.0); }},
+           {0, 8, 0, 0, [](DisplayListBuilder& b) { b.setStrokeWidth(5.0); }},
+           {0, 0, 0, 0, [](DisplayListBuilder& b) { b.setStrokeWidth(0.0); }},
        }},
       {"SetStrokeMiter",
        {
-           {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeMiter(0.0); }},
-           {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeMiter(5.0); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.setStrokeMiter(4.0); }},
+           {0, 8, 0, 0, [](DisplayListBuilder& b) { b.setStrokeMiter(0.0); }},
+           {0, 8, 0, 0, [](DisplayListBuilder& b) { b.setStrokeMiter(5.0); }},
+           {0, 0, 0, 0, [](DisplayListBuilder& b) { b.setStrokeMiter(4.0); }},
        }},
       {"SetColor",
        {
-           {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorGREEN); }},
-           {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorBLUE); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorBLACK); }},
+           {0, 8, 0, 0,
+            [](DisplayListBuilder& b) { b.setColor(SK_ColorGREEN); }},
+           {0, 8, 0, 0,
+            [](DisplayListBuilder& b) { b.setColor(SK_ColorBLUE); }},
+           {0, 0, 0, 0,
+            [](DisplayListBuilder& b) { b.setColor(SK_ColorBLACK); }},
        }},
       {"SetBlendMode",
        {
            {0, 8, 0, 0,
-            [](DlOpReceiver& r) { r.setBlendMode(DlBlendMode::kSrcIn); }},
+            [](DisplayListBuilder& b) { b.setBlendMode(DlBlendMode::kSrcIn); }},
            {0, 8, 0, 0,
-            [](DlOpReceiver& r) { r.setBlendMode(DlBlendMode::kDstIn); }},
+            [](DisplayListBuilder& b) { b.setBlendMode(DlBlendMode::kDstIn); }},
            {0, 0, 0, 0,
-            [](DlOpReceiver& r) { r.setBlendMode(DlBlendMode::kSrcOver); }},
+            [](DisplayListBuilder& b) {
+              b.setBlendMode(DlBlendMode::kSrcOver);
+            }},
        }},
       {"SetColorSource",
        {
            {0, 96, 0, 0,
-            [](DlOpReceiver& r) { r.setColorSource(&kTestSource1); }},
+            [](DisplayListBuilder& b) { b.setColorSource(&kTestSource1); }},
            // stop_count * (sizeof(float) + sizeof(uint32_t)) = 80
            {0, 80 + 6 * 4, 0, 0,
-            [](DlOpReceiver& r) { r.setColorSource(kTestSource2.get()); }},
+            [](DisplayListBuilder& b) {
+              b.setColorSource(kTestSource2.get());
+            }},
            {0, 80 + 6 * 4, 0, 0,
-            [](DlOpReceiver& r) { r.setColorSource(kTestSource3.get()); }},
+            [](DisplayListBuilder& b) {
+              b.setColorSource(kTestSource3.get());
+            }},
            {0, 88 + 6 * 4, 0, 0,
-            [](DlOpReceiver& r) { r.setColorSource(kTestSource4.get()); }},
+            [](DisplayListBuilder& b) {
+              b.setColorSource(kTestSource4.get());
+            }},
            {0, 80 + 6 * 4, 0, 0,
-            [](DlOpReceiver& r) { r.setColorSource(kTestSource5.get()); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.setColorSource(nullptr); }},
+            [](DisplayListBuilder& b) {
+              b.setColorSource(kTestSource5.get());
+            }},
+           {0, 0, 0, 0,
+            [](DisplayListBuilder& b) { b.setColorSource(nullptr); }},
        }},
       {"SetImageFilter",
        {
            {0, 32, 0, 0,
-            [](DlOpReceiver& r) { r.setImageFilter(&kTestBlurImageFilter1); }},
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestBlurImageFilter1);
+            }},
            {0, 32, 0, 0,
-            [](DlOpReceiver& r) { r.setImageFilter(&kTestBlurImageFilter2); }},
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestBlurImageFilter2);
+            }},
            {0, 32, 0, 0,
-            [](DlOpReceiver& r) { r.setImageFilter(&kTestBlurImageFilter3); }},
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestBlurImageFilter3);
+            }},
            {0, 32, 0, 0,
-            [](DlOpReceiver& r) { r.setImageFilter(&kTestBlurImageFilter4); }},
-           {0, 24, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setImageFilter(&kTestDilateImageFilter1);
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestBlurImageFilter4);
             }},
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setImageFilter(&kTestDilateImageFilter2);
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestDilateImageFilter1);
             }},
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setImageFilter(&kTestDilateImageFilter3);
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestDilateImageFilter2);
             }},
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) { r.setImageFilter(&kTestErodeImageFilter1); }},
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestDilateImageFilter3);
+            }},
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) { r.setImageFilter(&kTestErodeImageFilter2); }},
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestErodeImageFilter1);
+            }},
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) { r.setImageFilter(&kTestErodeImageFilter3); }},
-           {0, 64, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setImageFilter(&kTestMatrixImageFilter1);
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestErodeImageFilter2);
+            }},
+           {0, 24, 0, 0,
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestErodeImageFilter3);
             }},
            {0, 64, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setImageFilter(&kTestMatrixImageFilter2);
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestMatrixImageFilter1);
             }},
            {0, 64, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setImageFilter(&kTestMatrixImageFilter3);
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestMatrixImageFilter2);
+            }},
+           {0, 64, 0, 0,
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestMatrixImageFilter3);
             }},
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setImageFilter(&kTestComposeImageFilter1);
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestComposeImageFilter1);
             }},
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setImageFilter(&kTestComposeImageFilter2);
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestComposeImageFilter2);
             }},
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setImageFilter(&kTestComposeImageFilter3);
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestComposeImageFilter3);
             }},
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) { r.setImageFilter(&kTestCFImageFilter1); }},
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestCFImageFilter1);
+            }},
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) { r.setImageFilter(&kTestCFImageFilter2); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.setImageFilter(nullptr); }},
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(&kTestCFImageFilter2);
+            }},
+           {0, 0, 0, 0,
+            [](DisplayListBuilder& b) { b.setImageFilter(nullptr); }},
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setImageFilter(
+            [](DisplayListBuilder& b) {
+              b.setImageFilter(
                   kTestBlurImageFilter1
                       .makeWithLocalMatrix(SkMatrix::Translate(2, 2))
                       .get());
@@ -195,51 +238,64 @@
       {"SetColorFilter",
        {
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) { r.setColorFilter(&kTestBlendColorFilter1); }},
+            [](DisplayListBuilder& b) {
+              b.setColorFilter(&kTestBlendColorFilter1);
+            }},
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) { r.setColorFilter(&kTestBlendColorFilter2); }},
+            [](DisplayListBuilder& b) {
+              b.setColorFilter(&kTestBlendColorFilter2);
+            }},
            {0, 24, 0, 0,
-            [](DlOpReceiver& r) { r.setColorFilter(&kTestBlendColorFilter3); }},
-           {0, 96, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setColorFilter(&kTestMatrixColorFilter1);
+            [](DisplayListBuilder& b) {
+              b.setColorFilter(&kTestBlendColorFilter3);
             }},
            {0, 96, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setColorFilter(&kTestMatrixColorFilter2);
+            [](DisplayListBuilder& b) {
+              b.setColorFilter(&kTestMatrixColorFilter1);
+            }},
+           {0, 96, 0, 0,
+            [](DisplayListBuilder& b) {
+              b.setColorFilter(&kTestMatrixColorFilter2);
             }},
            {0, 16, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setColorFilter(DlSrgbToLinearGammaColorFilter::instance.get());
+            [](DisplayListBuilder& b) {
+              b.setColorFilter(DlSrgbToLinearGammaColorFilter::instance.get());
             }},
            {0, 16, 0, 0,
-            [](DlOpReceiver& r) {
-              r.setColorFilter(DlLinearToSrgbGammaColorFilter::instance.get());
+            [](DisplayListBuilder& b) {
+              b.setColorFilter(DlLinearToSrgbGammaColorFilter::instance.get());
             }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.setColorFilter(nullptr); }},
+           {0, 0, 0, 0,
+            [](DisplayListBuilder& b) { b.setColorFilter(nullptr); }},
        }},
       {"SetPathEffect",
        {
            // sizeof(DlDashPathEffect) + 2 * sizeof(SkScalar)
            {0, 32, 0, 0,
-            [](DlOpReceiver& r) { r.setPathEffect(kTestPathEffect1.get()); }},
+            [](DisplayListBuilder& b) {
+              b.setPathEffect(kTestPathEffect1.get());
+            }},
            {0, 32, 0, 0,
-            [](DlOpReceiver& r) { r.setPathEffect(kTestPathEffect2.get()); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.setPathEffect(nullptr); }},
+            [](DisplayListBuilder& b) {
+              b.setPathEffect(kTestPathEffect2.get());
+            }},
+           {0, 0, 0, 0,
+            [](DisplayListBuilder& b) { b.setPathEffect(nullptr); }},
        }},
       {"SetMaskFilter",
        {
            {0, 32, 0, 0,
-            [](DlOpReceiver& r) { r.setMaskFilter(&kTestMaskFilter1); }},
+            [](DisplayListBuilder& b) { b.setMaskFilter(&kTestMaskFilter1); }},
            {0, 32, 0, 0,
-            [](DlOpReceiver& r) { r.setMaskFilter(&kTestMaskFilter2); }},
+            [](DisplayListBuilder& b) { b.setMaskFilter(&kTestMaskFilter2); }},
            {0, 32, 0, 0,
-            [](DlOpReceiver& r) { r.setMaskFilter(&kTestMaskFilter3); }},
+            [](DisplayListBuilder& b) { b.setMaskFilter(&kTestMaskFilter3); }},
            {0, 32, 0, 0,
-            [](DlOpReceiver& r) { r.setMaskFilter(&kTestMaskFilter4); }},
+            [](DisplayListBuilder& b) { b.setMaskFilter(&kTestMaskFilter4); }},
            {0, 32, 0, 0,
-            [](DlOpReceiver& r) { r.setMaskFilter(&kTestMaskFilter5); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.setMaskFilter(nullptr); }},
+            [](DisplayListBuilder& b) { b.setMaskFilter(&kTestMaskFilter5); }},
+           {0, 0, 0, 0,
+            [](DisplayListBuilder& b) { b.setMaskFilter(nullptr); }},
        }},
   };
 }
@@ -249,13 +305,13 @@
       {"Save(Layer)+Restore",
        {
            {5, 112, 5, 112,
-            [](DlOpReceiver& r) {
-              r.saveLayer(nullptr, SaveLayerOptions::kNoAttributes,
+            [](DisplayListBuilder& b) {
+              b.saveLayer(nullptr, SaveLayerOptions::kNoAttributes,
                           &kTestCFImageFilter1);
-              r.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
-              r.drawRect({5, 5, 15, 15});
-              r.drawRect({10, 10, 20, 20});
-              r.restore();
+              b.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
+              b.drawRect({5, 5, 15, 15});
+              b.drawRect({10, 10, 20, 20});
+              b.restore();
             }},
            // There are many reasons that save and restore can elide content,
            // including whether or not there are any draw operations between
@@ -265,80 +321,80 @@
            // cases we include at least one clip operation and 2 overlapping
            // rendering primitives between each save/restore pair.
            {5, 96, 5, 96,
-            [](DlOpReceiver& r) {
-              r.save();
-              r.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
-              r.drawRect({5, 5, 15, 15});
-              r.drawRect({10, 10, 20, 20});
-              r.restore();
+            [](DisplayListBuilder& b) {
+              b.save();
+              b.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
+              b.drawRect({5, 5, 15, 15});
+              b.drawRect({10, 10, 20, 20});
+              b.restore();
             }},
            {5, 96, 5, 96,
-            [](DlOpReceiver& r) {
-              r.saveLayer(nullptr, SaveLayerOptions::kNoAttributes);
-              r.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
-              r.drawRect({5, 5, 15, 15});
-              r.drawRect({10, 10, 20, 20});
-              r.restore();
+            [](DisplayListBuilder& b) {
+              b.saveLayer(nullptr, false);
+              b.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
+              b.drawRect({5, 5, 15, 15});
+              b.drawRect({10, 10, 20, 20});
+              b.restore();
             }},
            {5, 96, 5, 96,
-            [](DlOpReceiver& r) {
-              r.saveLayer(nullptr, SaveLayerOptions::kWithAttributes);
-              r.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
-              r.drawRect({5, 5, 15, 15});
-              r.drawRect({10, 10, 20, 20});
-              r.restore();
+            [](DisplayListBuilder& b) {
+              b.saveLayer(nullptr, true);
+              b.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
+              b.drawRect({5, 5, 15, 15});
+              b.drawRect({10, 10, 20, 20});
+              b.restore();
             }},
            {5, 112, 5, 112,
-            [](DlOpReceiver& r) {
-              r.saveLayer(&kTestBounds, SaveLayerOptions::kNoAttributes);
-              r.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
-              r.drawRect({5, 5, 15, 15});
-              r.drawRect({10, 10, 20, 20});
-              r.restore();
+            [](DisplayListBuilder& b) {
+              b.saveLayer(&kTestBounds, false);
+              b.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
+              b.drawRect({5, 5, 15, 15});
+              b.drawRect({10, 10, 20, 20});
+              b.restore();
             }},
            {5, 112, 5, 112,
-            [](DlOpReceiver& r) {
-              r.saveLayer(&kTestBounds, SaveLayerOptions::kWithAttributes);
-              r.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
-              r.drawRect({5, 5, 15, 15});
-              r.drawRect({10, 10, 20, 20});
-              r.restore();
+            [](DisplayListBuilder& b) {
+              b.saveLayer(&kTestBounds, true);
+              b.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
+              b.drawRect({5, 5, 15, 15});
+              b.drawRect({10, 10, 20, 20});
+              b.restore();
             }},
            // backdrop variants - using the TestCFImageFilter because it can be
            // reconstituted in the DL->SkCanvas->DL stream
-           // {5, 104, 5, 104, [](DlOpReceiver& r) {
-           //   r.saveLayer(nullptr, SaveLayerOptions::kNoAttributes,
-           //   &kTestCFImageFilter1); r.clipRect({0, 0, 25, 25},
-           //   SkClipOp::kIntersect, true); r.drawRect({5, 5, 15, 15});
-           //   r.drawRect({10, 10, 20, 20});
-           //   r.restore();
+           // {5, 104, 5, 104, [](DisplayListBuilder& b) {
+           //   b.saveLayer(nullptr, SaveLayerOptions::kNoAttributes,
+           //   &kTestCFImageFilter1); b.clipRect({0, 0, 25, 25},
+           //   SkClipOp::kIntersect, true); b.drawRect({5, 5, 15, 15});
+           //   b.drawRect({10, 10, 20, 20});
+           //   b.restore();
            // }},
            {5, 112, 5, 112,
-            [](DlOpReceiver& r) {
-              r.saveLayer(nullptr, SaveLayerOptions::kWithAttributes,
+            [](DisplayListBuilder& b) {
+              b.saveLayer(nullptr, SaveLayerOptions::kWithAttributes,
                           &kTestCFImageFilter1);
-              r.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
-              r.drawRect({5, 5, 15, 15});
-              r.drawRect({10, 10, 20, 20});
-              r.restore();
+              b.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
+              b.drawRect({5, 5, 15, 15});
+              b.drawRect({10, 10, 20, 20});
+              b.restore();
             }},
            {5, 128, 5, 128,
-            [](DlOpReceiver& r) {
-              r.saveLayer(&kTestBounds, SaveLayerOptions::kNoAttributes,
+            [](DisplayListBuilder& b) {
+              b.saveLayer(&kTestBounds, SaveLayerOptions::kNoAttributes,
                           &kTestCFImageFilter1);
-              r.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
-              r.drawRect({5, 5, 15, 15});
-              r.drawRect({10, 10, 20, 20});
-              r.restore();
+              b.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
+              b.drawRect({5, 5, 15, 15});
+              b.drawRect({10, 10, 20, 20});
+              b.restore();
             }},
            {5, 128, 5, 128,
-            [](DlOpReceiver& r) {
-              r.saveLayer(&kTestBounds, SaveLayerOptions::kWithAttributes,
+            [](DisplayListBuilder& b) {
+              b.saveLayer(&kTestBounds, SaveLayerOptions::kWithAttributes,
                           &kTestCFImageFilter1);
-              r.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
-              r.drawRect({5, 5, 15, 15});
-              r.drawRect({10, 10, 20, 20});
-              r.restore();
+              b.clipRect({0, 0, 25, 25}, DlCanvas::ClipOp::kIntersect, true);
+              b.drawRect({5, 5, 15, 15});
+              b.drawRect({10, 10, 20, 20});
+              b.restore();
             }},
        }},
   };
@@ -349,61 +405,65 @@
       {"Translate",
        {
            // cv.translate(0, 0) is ignored
-           {1, 16, 1, 16, [](DlOpReceiver& r) { r.translate(10, 10); }},
-           {1, 16, 1, 16, [](DlOpReceiver& r) { r.translate(10, 15); }},
-           {1, 16, 1, 16, [](DlOpReceiver& r) { r.translate(15, 10); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.translate(0, 0); }},
+           {1, 16, 1, 16, [](DisplayListBuilder& b) { b.translate(10, 10); }},
+           {1, 16, 1, 16, [](DisplayListBuilder& b) { b.translate(10, 15); }},
+           {1, 16, 1, 16, [](DisplayListBuilder& b) { b.translate(15, 10); }},
+           {0, 0, 0, 0, [](DisplayListBuilder& b) { b.translate(0, 0); }},
        }},
       {"Scale",
        {
            // cv.scale(1, 1) is ignored
-           {1, 16, 1, 16, [](DlOpReceiver& r) { r.scale(2, 2); }},
-           {1, 16, 1, 16, [](DlOpReceiver& r) { r.scale(2, 3); }},
-           {1, 16, 1, 16, [](DlOpReceiver& r) { r.scale(3, 2); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.scale(1, 1); }},
+           {1, 16, 1, 16, [](DisplayListBuilder& b) { b.scale(2, 2); }},
+           {1, 16, 1, 16, [](DisplayListBuilder& b) { b.scale(2, 3); }},
+           {1, 16, 1, 16, [](DisplayListBuilder& b) { b.scale(3, 2); }},
+           {0, 0, 0, 0, [](DisplayListBuilder& b) { b.scale(1, 1); }},
        }},
       {"Rotate",
        {
            // cv.rotate(0) is ignored, otherwise expressed as concat(rotmatrix)
-           {1, 8, 1, 32, [](DlOpReceiver& r) { r.rotate(30); }},
-           {1, 8, 1, 32, [](DlOpReceiver& r) { r.rotate(45); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.rotate(0); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.rotate(360); }},
+           {1, 8, 1, 32, [](DisplayListBuilder& b) { b.rotate(30); }},
+           {1, 8, 1, 32, [](DisplayListBuilder& b) { b.rotate(45); }},
+           {0, 0, 0, 0, [](DisplayListBuilder& b) { b.rotate(0); }},
+           {0, 0, 0, 0, [](DisplayListBuilder& b) { b.rotate(360); }},
        }},
       {"Skew",
        {
            // cv.skew(0, 0) is ignored, otherwise expressed as
            // concat(skewmatrix)
-           {1, 16, 1, 32, [](DlOpReceiver& r) { r.skew(0.1, 0.1); }},
-           {1, 16, 1, 32, [](DlOpReceiver& r) { r.skew(0.1, 0.2); }},
-           {1, 16, 1, 32, [](DlOpReceiver& r) { r.skew(0.2, 0.1); }},
-           {0, 0, 0, 0, [](DlOpReceiver& r) { r.skew(0, 0); }},
+           {1, 16, 1, 32, [](DisplayListBuilder& b) { b.skew(0.1, 0.1); }},
+           {1, 16, 1, 32, [](DisplayListBuilder& b) { b.skew(0.1, 0.2); }},
+           {1, 16, 1, 32, [](DisplayListBuilder& b) { b.skew(0.2, 0.1); }},
+           {0, 0, 0, 0, [](DisplayListBuilder& b) { b.skew(0, 0); }},
        }},
       {"Transform2DAffine",
        {
            {1, 32, 1, 32,
-            [](DlOpReceiver& r) { r.transform2DAffine(0, 1, 12, 1, 0, 33); }},
-           // r.transform(identity) is ignored
+            [](DisplayListBuilder& b) {
+              b.transform2DAffine(0, 1, 12, 1, 0, 33);
+            }},
+           // b.transform(identity) is ignored
            {0, 0, 0, 0,
-            [](DlOpReceiver& r) { r.transform2DAffine(1, 0, 0, 0, 1, 0); }},
+            [](DisplayListBuilder& b) {
+              b.transform2DAffine(1, 0, 0, 0, 1, 0);
+            }},
        }},
       {"TransformFullPerspective",
        {
            {1, 72, 1, 72,
-            [](DlOpReceiver& r) {
-              r.transformFullPerspective(0, 1, 0, 12, 1, 0, 0, 33, 3, 2, 5, 29,
+            [](DisplayListBuilder& b) {
+              b.transformFullPerspective(0, 1, 0, 12, 1, 0, 0, 33, 3, 2, 5, 29,
                                          0, 0, 0, 12);
             }},
-           // r.transform(2D affine) is reduced to 2x3
+           // b.transform(2D affine) is reduced to 2x3
            {1, 32, 1, 32,
-            [](DlOpReceiver& r) {
-              r.transformFullPerspective(2, 1, 0, 4, 1, 3, 0, 5, 0, 0, 1, 0, 0,
+            [](DisplayListBuilder& b) {
+              b.transformFullPerspective(2, 1, 0, 4, 1, 3, 0, 5, 0, 0, 1, 0, 0,
                                          0, 0, 1);
             }},
-           // r.transform(identity) is ignored
+           // b.transform(identity) is ignored
            {0, 0, 0, 0,
-            [](DlOpReceiver& r) {
-              r.transformFullPerspective(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0,
+            [](DisplayListBuilder& b) {
+              b.transformFullPerspective(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0,
                                          0, 0, 1);
             }},
        }},
@@ -415,86 +475,86 @@
       {"ClipRect",
        {
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.clipRect(kTestBounds, DlCanvas::ClipOp::kIntersect, true);
+            [](DisplayListBuilder& b) {
+              b.clipRect(kTestBounds, DlCanvas::ClipOp::kIntersect, true);
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.clipRect(kTestBounds.makeOffset(1, 1),
+            [](DisplayListBuilder& b) {
+              b.clipRect(kTestBounds.makeOffset(1, 1),
                          DlCanvas::ClipOp::kIntersect, true);
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.clipRect(kTestBounds, DlCanvas::ClipOp::kIntersect, false);
+            [](DisplayListBuilder& b) {
+              b.clipRect(kTestBounds, DlCanvas::ClipOp::kIntersect, false);
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.clipRect(kTestBounds, DlCanvas::ClipOp::kDifference, true);
+            [](DisplayListBuilder& b) {
+              b.clipRect(kTestBounds, DlCanvas::ClipOp::kDifference, true);
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.clipRect(kTestBounds, DlCanvas::ClipOp::kDifference, false);
+            [](DisplayListBuilder& b) {
+              b.clipRect(kTestBounds, DlCanvas::ClipOp::kDifference, false);
             }},
        }},
       {"ClipRRect",
        {
            {1, 64, 1, 64,
-            [](DlOpReceiver& r) {
-              r.clipRRect(kTestRRect, DlCanvas::ClipOp::kIntersect, true);
+            [](DisplayListBuilder& b) {
+              b.clipRRect(kTestRRect, DlCanvas::ClipOp::kIntersect, true);
             }},
            {1, 64, 1, 64,
-            [](DlOpReceiver& r) {
-              r.clipRRect(kTestRRect.makeOffset(1, 1),
+            [](DisplayListBuilder& b) {
+              b.clipRRect(kTestRRect.makeOffset(1, 1),
                           DlCanvas::ClipOp::kIntersect, true);
             }},
            {1, 64, 1, 64,
-            [](DlOpReceiver& r) {
-              r.clipRRect(kTestRRect, DlCanvas::ClipOp::kIntersect, false);
+            [](DisplayListBuilder& b) {
+              b.clipRRect(kTestRRect, DlCanvas::ClipOp::kIntersect, false);
             }},
            {1, 64, 1, 64,
-            [](DlOpReceiver& r) {
-              r.clipRRect(kTestRRect, DlCanvas::ClipOp::kDifference, true);
+            [](DisplayListBuilder& b) {
+              b.clipRRect(kTestRRect, DlCanvas::ClipOp::kDifference, true);
             }},
            {1, 64, 1, 64,
-            [](DlOpReceiver& r) {
-              r.clipRRect(kTestRRect, DlCanvas::ClipOp::kDifference, false);
+            [](DisplayListBuilder& b) {
+              b.clipRRect(kTestRRect, DlCanvas::ClipOp::kDifference, false);
             }},
        }},
       {"ClipPath",
        {
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.clipPath(kTestPath1, DlCanvas::ClipOp::kIntersect, true);
+            [](DisplayListBuilder& b) {
+              b.clipPath(kTestPath1, DlCanvas::ClipOp::kIntersect, true);
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.clipPath(kTestPath2, DlCanvas::ClipOp::kIntersect, true);
+            [](DisplayListBuilder& b) {
+              b.clipPath(kTestPath2, DlCanvas::ClipOp::kIntersect, true);
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.clipPath(kTestPath3, DlCanvas::ClipOp::kIntersect, true);
+            [](DisplayListBuilder& b) {
+              b.clipPath(kTestPath3, DlCanvas::ClipOp::kIntersect, true);
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.clipPath(kTestPath1, DlCanvas::ClipOp::kIntersect, false);
+            [](DisplayListBuilder& b) {
+              b.clipPath(kTestPath1, DlCanvas::ClipOp::kIntersect, false);
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.clipPath(kTestPath1, DlCanvas::ClipOp::kDifference, true);
+            [](DisplayListBuilder& b) {
+              b.clipPath(kTestPath1, DlCanvas::ClipOp::kDifference, true);
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.clipPath(kTestPath1, DlCanvas::ClipOp::kDifference, false);
+            [](DisplayListBuilder& b) {
+              b.clipPath(kTestPath1, DlCanvas::ClipOp::kDifference, false);
             }},
            // clipPath(rect) becomes clipRect
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.clipPath(kTestPathRect, DlCanvas::ClipOp::kIntersect, true);
+            [](DisplayListBuilder& b) {
+              b.clipPath(kTestPathRect, DlCanvas::ClipOp::kIntersect, true);
             }},
            // clipPath(oval) becomes clipRRect
            {1, 64, 1, 64,
-            [](DlOpReceiver& r) {
-              r.clipPath(kTestPathOval, DlCanvas::ClipOp::kIntersect, true);
+            [](DisplayListBuilder& b) {
+              b.clipPath(kTestPathOval, DlCanvas::ClipOp::kIntersect, true);
             }},
        }},
   };
@@ -504,256 +564,268 @@
   return {
       {"DrawPaint",
        {
-           {1, 8, 1, 8, [](DlOpReceiver& r) { r.drawPaint(); }},
+           {1, 8, 1, 8, [](DisplayListBuilder& b) { b.drawPaint(); }},
        }},
       {"DrawColor",
        {
            // cv.drawColor becomes cv.drawPaint(paint)
            {1, 16, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawColor(SK_ColorBLUE, DlBlendMode::kSrcIn);
+            [](DisplayListBuilder& b) {
+              b.drawColor(SK_ColorBLUE, DlBlendMode::kSrcIn);
             }},
            {1, 16, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawColor(SK_ColorBLUE, DlBlendMode::kDstIn);
+            [](DisplayListBuilder& b) {
+              b.drawColor(SK_ColorBLUE, DlBlendMode::kDstIn);
             }},
            {1, 16, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawColor(SK_ColorCYAN, DlBlendMode::kSrcIn);
+            [](DisplayListBuilder& b) {
+              b.drawColor(SK_ColorCYAN, DlBlendMode::kSrcIn);
             }},
        }},
       {"DrawLine",
        {
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawLine({0, 0}, {10, 10});
+            [](DisplayListBuilder& b) {
+              b.drawLine({0, 0}, {10, 10});
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawLine({0, 1}, {10, 10});
+            [](DisplayListBuilder& b) {
+              b.drawLine({0, 1}, {10, 10});
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawLine({0, 0}, {20, 10});
+            [](DisplayListBuilder& b) {
+              b.drawLine({0, 0}, {20, 10});
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawLine({0, 0}, {10, 20});
+            [](DisplayListBuilder& b) {
+              b.drawLine({0, 0}, {10, 20});
             }},
        }},
       {"DrawRect",
        {
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawRect({0, 0, 10, 10});
+            [](DisplayListBuilder& b) {
+              b.drawRect({0, 0, 10, 10});
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawRect({0, 1, 10, 10});
+            [](DisplayListBuilder& b) {
+              b.drawRect({0, 1, 10, 10});
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawRect({0, 0, 20, 10});
+            [](DisplayListBuilder& b) {
+              b.drawRect({0, 0, 20, 10});
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawRect({0, 0, 10, 20});
+            [](DisplayListBuilder& b) {
+              b.drawRect({0, 0, 10, 20});
             }},
        }},
       {"DrawOval",
        {
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawOval({0, 0, 10, 10});
+            [](DisplayListBuilder& b) {
+              b.drawOval({0, 0, 10, 10});
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawOval({0, 1, 10, 10});
+            [](DisplayListBuilder& b) {
+              b.drawOval({0, 1, 10, 10});
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawOval({0, 0, 20, 10});
+            [](DisplayListBuilder& b) {
+              b.drawOval({0, 0, 20, 10});
             }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawOval({0, 0, 10, 20});
+            [](DisplayListBuilder& b) {
+              b.drawOval({0, 0, 10, 20});
             }},
        }},
       {"DrawCircle",
        {
            // cv.drawCircle becomes cv.drawOval
            {1, 16, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawCircle({0, 0}, 10);
+            [](DisplayListBuilder& b) {
+              b.drawCircle({0, 0}, 10);
             }},
            {1, 16, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawCircle({0, 5}, 10);
+            [](DisplayListBuilder& b) {
+              b.drawCircle({0, 5}, 10);
             }},
            {1, 16, 1, 24,
-            [](DlOpReceiver& r) {
-              r.drawCircle({0, 0}, 20);
+            [](DisplayListBuilder& b) {
+              b.drawCircle({0, 0}, 20);
             }},
        }},
       {"DrawRRect",
        {
-           {1, 56, 1, 56, [](DlOpReceiver& r) { r.drawRRect(kTestRRect); }},
            {1, 56, 1, 56,
-            [](DlOpReceiver& r) { r.drawRRect(kTestRRect.makeOffset(5, 5)); }},
+            [](DisplayListBuilder& b) { b.drawRRect(kTestRRect); }},
+           {1, 56, 1, 56,
+            [](DisplayListBuilder& b) {
+              b.drawRRect(kTestRRect.makeOffset(5, 5));
+            }},
        }},
       {"DrawDRRect",
        {
            {1, 112, 1, 112,
-            [](DlOpReceiver& r) { r.drawDRRect(kTestRRect, kTestInnerRRect); }},
+            [](DisplayListBuilder& b) {
+              b.drawDRRect(kTestRRect, kTestInnerRRect);
+            }},
            {1, 112, 1, 112,
-            [](DlOpReceiver& r) {
-              r.drawDRRect(kTestRRect.makeOffset(5, 5),
+            [](DisplayListBuilder& b) {
+              b.drawDRRect(kTestRRect.makeOffset(5, 5),
                            kTestInnerRRect.makeOffset(4, 4));
             }},
        }},
       {"DrawPath",
        {
-           {1, 24, 1, 24, [](DlOpReceiver& r) { r.drawPath(kTestPath1); }},
-           {1, 24, 1, 24, [](DlOpReceiver& r) { r.drawPath(kTestPath2); }},
-           {1, 24, 1, 24, [](DlOpReceiver& r) { r.drawPath(kTestPath3); }},
-           {1, 24, 1, 24, [](DlOpReceiver& r) { r.drawPath(kTestPathRect); }},
-           {1, 24, 1, 24, [](DlOpReceiver& r) { r.drawPath(kTestPathOval); }},
+           {1, 24, 1, 24,
+            [](DisplayListBuilder& b) { b.drawPath(kTestPath1); }},
+           {1, 24, 1, 24,
+            [](DisplayListBuilder& b) { b.drawPath(kTestPath2); }},
+           {1, 24, 1, 24,
+            [](DisplayListBuilder& b) { b.drawPath(kTestPath3); }},
+           {1, 24, 1, 24,
+            [](DisplayListBuilder& b) { b.drawPath(kTestPathRect); }},
+           {1, 24, 1, 24,
+            [](DisplayListBuilder& b) { b.drawPath(kTestPathOval); }},
        }},
       {"DrawArc",
        {
            {1, 32, 1, 32,
-            [](DlOpReceiver& r) { r.drawArc(kTestBounds, 45, 270, false); }},
-           {1, 32, 1, 32,
-            [](DlOpReceiver& r) {
-              r.drawArc(kTestBounds.makeOffset(1, 1), 45, 270, false);
+            [](DisplayListBuilder& b) {
+              b.drawArc(kTestBounds, 45, 270, false);
             }},
            {1, 32, 1, 32,
-            [](DlOpReceiver& r) { r.drawArc(kTestBounds, 30, 270, false); }},
+            [](DisplayListBuilder& b) {
+              b.drawArc(kTestBounds.makeOffset(1, 1), 45, 270, false);
+            }},
            {1, 32, 1, 32,
-            [](DlOpReceiver& r) { r.drawArc(kTestBounds, 45, 260, false); }},
+            [](DisplayListBuilder& b) {
+              b.drawArc(kTestBounds, 30, 270, false);
+            }},
            {1, 32, 1, 32,
-            [](DlOpReceiver& r) { r.drawArc(kTestBounds, 45, 270, true); }},
+            [](DisplayListBuilder& b) {
+              b.drawArc(kTestBounds, 45, 260, false);
+            }},
+           {1, 32, 1, 32,
+            [](DisplayListBuilder& b) {
+              b.drawArc(kTestBounds, 45, 270, true);
+            }},
        }},
       {"DrawPoints",
        {
            {1, 8 + TestPointCount * 8, 1, 8 + TestPointCount * 8,
-            [](DlOpReceiver& r) {
-              r.drawPoints(DlCanvas::PointMode::kPoints, TestPointCount,
+            [](DisplayListBuilder& b) {
+              b.drawPoints(DlCanvas::PointMode::kPoints, TestPointCount,
                            TestPoints);
             }},
            {1, 8 + (TestPointCount - 1) * 8, 1, 8 + (TestPointCount - 1) * 8,
-            [](DlOpReceiver& r) {
-              r.drawPoints(DlCanvas::PointMode::kPoints, TestPointCount - 1,
+            [](DisplayListBuilder& b) {
+              b.drawPoints(DlCanvas::PointMode::kPoints, TestPointCount - 1,
                            TestPoints);
             }},
            {1, 8 + TestPointCount * 8, 1, 8 + TestPointCount * 8,
-            [](DlOpReceiver& r) {
-              r.drawPoints(DlCanvas::PointMode::kLines, TestPointCount,
+            [](DisplayListBuilder& b) {
+              b.drawPoints(DlCanvas::PointMode::kLines, TestPointCount,
                            TestPoints);
             }},
            {1, 8 + TestPointCount * 8, 1, 8 + TestPointCount * 8,
-            [](DlOpReceiver& r) {
-              r.drawPoints(DlCanvas::PointMode::kPolygon, TestPointCount,
+            [](DisplayListBuilder& b) {
+              b.drawPoints(DlCanvas::PointMode::kPolygon, TestPointCount,
                            TestPoints);
             }},
        }},
       {"DrawVertices",
        {
            {1, 112, 1, 16,
-            [](DlOpReceiver& r) {
-              r.drawVertices(TestVertices1.get(), DlBlendMode::kSrcIn);
+            [](DisplayListBuilder& b) {
+              b.drawVertices(TestVertices1, DlBlendMode::kSrcIn);
             }},
            {1, 112, 1, 16,
-            [](DlOpReceiver& r) {
-              r.drawVertices(TestVertices1.get(), DlBlendMode::kDstIn);
+            [](DisplayListBuilder& b) {
+              b.drawVertices(TestVertices1, DlBlendMode::kDstIn);
             }},
            {1, 112, 1, 16,
-            [](DlOpReceiver& r) {
-              r.drawVertices(TestVertices2.get(), DlBlendMode::kSrcIn);
+            [](DisplayListBuilder& b) {
+              b.drawVertices(TestVertices2, DlBlendMode::kSrcIn);
             }},
        }},
       {"DrawImage",
        {
            {1, 24, -1, 48,
-            [](DlOpReceiver& r) {
-              r.drawImage(TestImage1, {10, 10}, kNearestSampling, false);
+            [](DisplayListBuilder& b) {
+              b.drawImage(TestImage1, {10, 10}, kNearestSampling, false);
             }},
            {1, 24, -1, 48,
-            [](DlOpReceiver& r) {
-              r.drawImage(TestImage1, {10, 10}, kNearestSampling, true);
+            [](DisplayListBuilder& b) {
+              b.drawImage(TestImage1, {10, 10}, kNearestSampling, true);
             }},
            {1, 24, -1, 48,
-            [](DlOpReceiver& r) {
-              r.drawImage(TestImage1, {20, 10}, kNearestSampling, false);
+            [](DisplayListBuilder& b) {
+              b.drawImage(TestImage1, {20, 10}, kNearestSampling, false);
             }},
            {1, 24, -1, 48,
-            [](DlOpReceiver& r) {
-              r.drawImage(TestImage1, {10, 20}, kNearestSampling, false);
+            [](DisplayListBuilder& b) {
+              b.drawImage(TestImage1, {10, 20}, kNearestSampling, false);
             }},
            {1, 24, -1, 48,
-            [](DlOpReceiver& r) {
-              r.drawImage(TestImage1, {10, 10}, kLinearSampling, false);
+            [](DisplayListBuilder& b) {
+              b.drawImage(TestImage1, {10, 10}, kLinearSampling, false);
             }},
            {1, 24, -1, 48,
-            [](DlOpReceiver& r) {
-              r.drawImage(TestImage2, {10, 10}, kNearestSampling, false);
+            [](DisplayListBuilder& b) {
+              b.drawImage(TestImage2, {10, 10}, kNearestSampling, false);
             }},
            {1, 24, -1, 48,
-            [](DlOpReceiver& r) {
+            [](DisplayListBuilder& b) {
               auto dl_image = DlImage::Make(TestSkImage);
-              r.drawImage(dl_image, {10, 10}, kNearestSampling, false);
+              b.drawImage(dl_image, {10, 10}, kNearestSampling, false);
             }},
        }},
       {"DrawImageRect",
        {
            {1, 56, -1, 80,
-            [](DlOpReceiver& r) {
-              r.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
-                              kNearestSampling, false,
-                              DlCanvas::SrcRectConstraint::kFast);
+            [](DisplayListBuilder& b) {
+              b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
+                              kNearestSampling, false);
             }},
            {1, 56, -1, 80,
-            [](DlOpReceiver& r) {
-              r.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
-                              kNearestSampling, true,
-                              DlCanvas::SrcRectConstraint::kFast);
+            [](DisplayListBuilder& b) {
+              b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
+                              kNearestSampling, true);
             }},
            {1, 56, -1, 80,
-            [](DlOpReceiver& r) {
-              r.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
-                              kNearestSampling, false,
-                              DlCanvas::SrcRectConstraint::kStrict);
+            [](DisplayListBuilder& b) {
+              b.drawImageRect(
+                  TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
+                  kNearestSampling, false,
+                  SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint);
             }},
            {1, 56, -1, 80,
-            [](DlOpReceiver& r) {
-              r.drawImageRect(TestImage1, {10, 10, 25, 20}, {10, 10, 80, 80},
-                              kNearestSampling, false,
-                              DlCanvas::SrcRectConstraint::kFast);
+            [](DisplayListBuilder& b) {
+              b.drawImageRect(TestImage1, {10, 10, 25, 20}, {10, 10, 80, 80},
+                              kNearestSampling, false);
             }},
            {1, 56, -1, 80,
-            [](DlOpReceiver& r) {
-              r.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 85, 80},
-                              kNearestSampling, false,
-                              DlCanvas::SrcRectConstraint::kFast);
+            [](DisplayListBuilder& b) {
+              b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 85, 80},
+                              kNearestSampling, false);
             }},
            {1, 56, -1, 80,
-            [](DlOpReceiver& r) {
-              r.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
-                              kLinearSampling, false,
-                              DlCanvas::SrcRectConstraint::kFast);
+            [](DisplayListBuilder& b) {
+              b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
+                              kLinearSampling, false);
             }},
            {1, 56, -1, 80,
-            [](DlOpReceiver& r) {
-              r.drawImageRect(TestImage2, {10, 10, 15, 15}, {10, 10, 80, 80},
-                              kNearestSampling, false,
-                              DlCanvas::SrcRectConstraint::kFast);
+            [](DisplayListBuilder& b) {
+              b.drawImageRect(TestImage2, {10, 10, 15, 15}, {10, 10, 80, 80},
+                              kNearestSampling, false);
             }},
            {1, 56, -1, 80,
-            [](DlOpReceiver& r) {
+            [](DisplayListBuilder& b) {
               auto dl_image = DlImage::Make(TestSkImage);
-              r.drawImageRect(dl_image, {10, 10, 15, 15}, {10, 10, 80, 80},
-                              kNearestSampling, false,
-                              DlCanvas::SrcRectConstraint::kFast);
+              b.drawImageRect(dl_image, {10, 10, 15, 15}, {10, 10, 80, 80},
+                              kNearestSampling, false);
             }},
        }},
       {"DrawImageNine",
@@ -761,124 +833,124 @@
            // SkCanvas::drawImageNine is immediately converted to
            // drawImageLattice
            {1, 48, -1, 80,
-            [](DlOpReceiver& r) {
-              r.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
+            [](DisplayListBuilder& b) {
+              b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
                               DlFilterMode::kNearest, false);
             }},
            {1, 48, -1, 80,
-            [](DlOpReceiver& r) {
-              r.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
+            [](DisplayListBuilder& b) {
+              b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
                               DlFilterMode::kNearest, true);
             }},
            {1, 48, -1, 80,
-            [](DlOpReceiver& r) {
-              r.drawImageNine(TestImage1, {10, 10, 25, 20}, {10, 10, 80, 80},
+            [](DisplayListBuilder& b) {
+              b.drawImageNine(TestImage1, {10, 10, 25, 20}, {10, 10, 80, 80},
                               DlFilterMode::kNearest, false);
             }},
            {1, 48, -1, 80,
-            [](DlOpReceiver& r) {
-              r.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 85, 80},
+            [](DisplayListBuilder& b) {
+              b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 85, 80},
                               DlFilterMode::kNearest, false);
             }},
            {1, 48, -1, 80,
-            [](DlOpReceiver& r) {
-              r.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
+            [](DisplayListBuilder& b) {
+              b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
                               DlFilterMode::kLinear, false);
             }},
            {1, 48, -1, 80,
-            [](DlOpReceiver& r) {
-              r.drawImageNine(TestImage2, {10, 10, 15, 15}, {10, 10, 80, 80},
+            [](DisplayListBuilder& b) {
+              b.drawImageNine(TestImage2, {10, 10, 15, 15}, {10, 10, 80, 80},
                               DlFilterMode::kNearest, false);
             }},
            {1, 48, -1, 80,
-            [](DlOpReceiver& r) {
+            [](DisplayListBuilder& b) {
               auto dl_image = DlImage::Make(TestSkImage);
-              r.drawImageNine(dl_image, {10, 10, 15, 15}, {10, 10, 80, 80},
+              b.drawImageNine(dl_image, {10, 10, 15, 15}, {10, 10, 80, 80},
                               DlFilterMode::kNearest, false);
             }},
        }},
       {"DrawAtlas",
        {
            {1, 48 + 32 + 8, -1, 48 + 32 + 32,
-            [](DlOpReceiver& r) {
+            [](DisplayListBuilder& b) {
               static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
               static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}};
-              r.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
+              b.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
                           DlBlendMode::kSrcIn, kNearestSampling, nullptr,
                           false);
             }},
            {1, 48 + 32 + 8, -1, 48 + 32 + 32,
-            [](DlOpReceiver& r) {
+            [](DisplayListBuilder& b) {
               static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
               static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}};
-              r.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
+              b.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
                           DlBlendMode::kSrcIn, kNearestSampling, nullptr, true);
             }},
            {1, 48 + 32 + 8, -1, 48 + 32 + 32,
-            [](DlOpReceiver& r) {
+            [](DisplayListBuilder& b) {
               static SkRSXform xforms[] = {{0, 1, 0, 0}, {0, 1, 0, 0}};
               static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}};
-              r.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
+              b.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
                           DlBlendMode::kSrcIn, kNearestSampling, nullptr,
                           false);
             }},
            {1, 48 + 32 + 8, -1, 48 + 32 + 32,
-            [](DlOpReceiver& r) {
+            [](DisplayListBuilder& b) {
               static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
               static SkRect texs[] = {{10, 10, 20, 20}, {20, 25, 30, 30}};
-              r.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
+              b.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
                           DlBlendMode::kSrcIn, kNearestSampling, nullptr,
                           false);
             }},
            {1, 48 + 32 + 8, -1, 48 + 32 + 32,
-            [](DlOpReceiver& r) {
+            [](DisplayListBuilder& b) {
               static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
               static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}};
-              r.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
+              b.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
                           DlBlendMode::kSrcIn, kLinearSampling, nullptr, false);
             }},
            {1, 48 + 32 + 8, -1, 48 + 32 + 32,
-            [](DlOpReceiver& r) {
+            [](DisplayListBuilder& b) {
               static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
               static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}};
-              r.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
+              b.drawAtlas(TestImage1, xforms, texs, nullptr, 2,
                           DlBlendMode::kDstIn, kNearestSampling, nullptr,
                           false);
             }},
            {1, 64 + 32 + 8, -1, 64 + 32 + 32,
-            [](DlOpReceiver& r) {
+            [](DisplayListBuilder& b) {
               static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
               static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}};
               static SkRect cull_rect = {0, 0, 200, 200};
-              r.drawAtlas(TestImage2, xforms, texs, nullptr, 2,
+              b.drawAtlas(TestImage2, xforms, texs, nullptr, 2,
                           DlBlendMode::kSrcIn, kNearestSampling, &cull_rect,
                           false);
             }},
            {1, 48 + 32 + 8 + 8, -1, 48 + 32 + 32 + 8,
-            [](DlOpReceiver& r) {
+            [](DisplayListBuilder& b) {
               static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
               static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}};
               static DlColor colors[] = {DlColor::kBlue(), DlColor::kGreen()};
-              r.drawAtlas(TestImage1, xforms, texs, colors, 2,
+              b.drawAtlas(TestImage1, xforms, texs, colors, 2,
                           DlBlendMode::kSrcIn, kNearestSampling, nullptr,
                           false);
             }},
            {1, 64 + 32 + 8 + 8, -1, 64 + 32 + 32 + 8,
-            [](DlOpReceiver& r) {
+            [](DisplayListBuilder& b) {
               static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
               static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}};
               static DlColor colors[] = {DlColor::kBlue(), DlColor::kGreen()};
               static SkRect cull_rect = {0, 0, 200, 200};
-              r.drawAtlas(TestImage1, xforms, texs, colors, 2,
+              b.drawAtlas(TestImage1, xforms, texs, colors, 2,
                           DlBlendMode::kSrcIn, kNearestSampling, &cull_rect,
                           false);
             }},
            {1, 48 + 32 + 8, -1, 48 + 32 + 32,
-            [](DlOpReceiver& r) {
+            [](DisplayListBuilder& b) {
               auto dl_image = DlImage::Make(TestSkImage);
               static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}};
               static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}};
-              r.drawAtlas(dl_image, xforms, texs, nullptr, 2,
+              b.drawAtlas(dl_image, xforms, texs, nullptr, 2,
                           DlBlendMode::kSrcIn, kNearestSampling, nullptr,
                           false);
             }},
@@ -887,26 +959,24 @@
        {
            // cv.drawDL does not exist
            {1, 16, -1, 16,
-            [](DlOpReceiver& r) { r.drawDisplayList(TestDisplayList1, 1.0); }},
+            [](DisplayListBuilder& b) { b.drawDisplayList(TestDisplayList1); }},
            {1, 16, -1, 16,
-            [](DlOpReceiver& r) { r.drawDisplayList(TestDisplayList1, 0.5); }},
+            [](DisplayListBuilder& b) { b.drawDisplayList(TestDisplayList2); }},
            {1, 16, -1, 16,
-            [](DlOpReceiver& r) { r.drawDisplayList(TestDisplayList2, 1.0); }},
-           {1, 16, -1, 16,
-            [](DlOpReceiver& r) {
-              r.drawDisplayList(MakeTestDisplayList(10, 10, SK_ColorRED), 1.0);
+            [](DisplayListBuilder& b) {
+              b.drawDisplayList(MakeTestDisplayList(10, 10, SK_ColorRED));
             }},
        }},
       {"DrawTextBlob",
        {
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) { r.drawTextBlob(TestBlob1, 10, 10); }},
+            [](DisplayListBuilder& b) { b.drawTextBlob(TestBlob1, 10, 10); }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) { r.drawTextBlob(TestBlob1, 20, 10); }},
+            [](DisplayListBuilder& b) { b.drawTextBlob(TestBlob1, 20, 10); }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) { r.drawTextBlob(TestBlob1, 10, 20); }},
+            [](DisplayListBuilder& b) { b.drawTextBlob(TestBlob1, 10, 20); }},
            {1, 24, 1, 24,
-            [](DlOpReceiver& r) { r.drawTextBlob(TestBlob2, 10, 10); }},
+            [](DisplayListBuilder& b) { b.drawTextBlob(TestBlob2, 10, 10); }},
        }},
       // The -1 op counts below are to indicate to the framework not to test
       // SkCanvas conversion of these ops as it converts the operation into a
@@ -918,28 +988,28 @@
            // cv shadows are turned into an opaque ShadowRec which is not
            // exposed
            {1, 32, -1, 32,
-            [](DlOpReceiver& r) {
-              r.drawShadow(kTestPath1, SK_ColorGREEN, 1.0, false, 1.0);
+            [](DisplayListBuilder& b) {
+              b.drawShadow(kTestPath1, SK_ColorGREEN, 1.0, false, 1.0);
             }},
            {1, 32, -1, 32,
-            [](DlOpReceiver& r) {
-              r.drawShadow(kTestPath2, SK_ColorGREEN, 1.0, false, 1.0);
+            [](DisplayListBuilder& b) {
+              b.drawShadow(kTestPath2, SK_ColorGREEN, 1.0, false, 1.0);
             }},
            {1, 32, -1, 32,
-            [](DlOpReceiver& r) {
-              r.drawShadow(kTestPath1, SK_ColorBLUE, 1.0, false, 1.0);
+            [](DisplayListBuilder& b) {
+              b.drawShadow(kTestPath1, SK_ColorBLUE, 1.0, false, 1.0);
             }},
            {1, 32, -1, 32,
-            [](DlOpReceiver& r) {
-              r.drawShadow(kTestPath1, SK_ColorGREEN, 2.0, false, 1.0);
+            [](DisplayListBuilder& b) {
+              b.drawShadow(kTestPath1, SK_ColorGREEN, 2.0, false, 1.0);
             }},
            {1, 32, -1, 32,
-            [](DlOpReceiver& r) {
-              r.drawShadow(kTestPath1, SK_ColorGREEN, 1.0, true, 1.0);
+            [](DisplayListBuilder& b) {
+              b.drawShadow(kTestPath1, SK_ColorGREEN, 1.0, true, 1.0);
             }},
            {1, 32, -1, 32,
-            [](DlOpReceiver& r) {
-              r.drawShadow(kTestPath1, SK_ColorGREEN, 1.0, false, 2.5);
+            [](DisplayListBuilder& b) {
+              b.drawShadow(kTestPath1, SK_ColorGREEN, 1.0, false, 2.5);
             }},
        }},
   };
diff --git a/display_list/testing/dl_test_snippets.h b/display_list/testing/dl_test_snippets.h
index 69bab82..35278db 100644
--- a/display_list/testing/dl_test_snippets.h
+++ b/display_list/testing/dl_test_snippets.h
@@ -20,7 +20,7 @@
 sk_sp<DisplayList> GetSampleDisplayList(int ops);
 sk_sp<DisplayList> GetSampleNestedDisplayList();
 
-typedef const std::function<void(DlOpReceiver&)> DlInvoker;
+typedef const std::function<void(DisplayListBuilder&)> DlInvoker;
 
 constexpr SkPoint kEndPoints[] = {
     {0, 0},
@@ -214,7 +214,8 @@
 
 static sk_sp<DisplayList> MakeTestDisplayList(int w, int h, SkColor color) {
   DisplayListBuilder builder;
-  builder.DrawRect(SkRect::MakeWH(w, h), DlPaint(color));
+  builder.setColor(color);
+  builder.drawRect(SkRect::MakeWH(w, h));
   return builder.Build();
 }
 static sk_sp<DisplayList> TestDisplayList1 =
@@ -270,13 +271,13 @@
   // through an SkCanvas interface, comparable to |DisplayList.byte_count().
   size_t sk_byte_count() { return sizeof(DisplayList) + sk_byte_count_; }
 
-  void Invoke(DlOpReceiver& builder) { invoker(builder); }
+  void Invoke(DisplayListBuilder& builder) { invoker(builder); }
 
-  // sk_sp<DisplayList> Build() {
-  //   DisplayListBuilder builder;
-  //   invoker(builder.asReceiver());
-  //   return builder.Build();
-  // }
+  sk_sp<DisplayList> Build() {
+    DisplayListBuilder builder;
+    invoker(builder);
+    return builder.Build();
+  }
 };
 
 struct DisplayListInvocationGroup {
diff --git a/display_list/types.h b/display_list/types.h
index b8737f5..dbe04aa 100644
--- a/display_list/types.h
+++ b/display_list/types.h
@@ -28,4 +28,5 @@
 #include "third_party/skia/include/effects/SkDashPathEffect.h"
 #include "third_party/skia/include/effects/SkDiscretePathEffect.h"
 #include "third_party/skia/include/gpu/GrTypes.h"
+#include "third_party/skia/include/utils/SkShadowUtils.h"
 #endif  // FLUTTER_DISPLAY_LIST_TYPES_H_
diff --git a/flow/layers/checkerboard_layertree_unittests.cc b/flow/layers/checkerboard_layertree_unittests.cc
index 7917b5e..d54abf9 100644
--- a/flow/layers/checkerboard_layertree_unittests.cc
+++ b/flow/layers/checkerboard_layertree_unittests.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "flutter/display_list/display_list_canvas_dispatcher.h"
 #include "flutter/flow/layers/clip_path_layer.h"
 #include "flutter/flow/layers/clip_rect_layer.h"
 #include "flutter/flow/layers/clip_rrect_layer.h"
@@ -234,8 +235,8 @@
   // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and
   // their shadows , so we do not do any painting there.
   EXPECT_EQ(layer->paint_bounds(),
-            DlCanvas::ComputeShadowBounds(layer_path, initial_elevation, 1.0f,
-                                          SkMatrix()));
+            DisplayListCanvasDispatcher::ComputeShadowBounds(
+                layer_path, initial_elevation, 1.0f, SkMatrix()));
   EXPECT_TRUE(layer->needs_painting(paint_context()));
   EXPECT_EQ(layer->elevation(), initial_elevation);
 
diff --git a/flow/layers/clip_path_layer_unittests.cc b/flow/layers/clip_path_layer_unittests.cc
index 1012a4c..4f07f8c 100644
--- a/flow/layers/clip_path_layer_unittests.cc
+++ b/flow/layers/clip_path_layer_unittests.cc
@@ -424,7 +424,7 @@
     {
       expected_builder.Translate(offset.fX, offset.fY);
       /* ClipRectLayer::Paint() */ {
-        expected_builder.Save();
+        expected_builder.save();
         expected_builder.ClipPath(layer_clip, ClipOp::kIntersect, true);
         /* child layer1 paint */ {
           expected_builder.DrawPath(path1, DlPaint().setAlpha(opacity_alpha));
@@ -472,24 +472,25 @@
 
   DisplayListBuilder expected_builder;
   /* OpacityLayer::Paint() */ {
-    expected_builder.Save();
+    expected_builder.save();
     {
-      expected_builder.Translate(offset.fX, offset.fY);
+      expected_builder.translate(offset.fX, offset.fY);
       /* ClipRectLayer::Paint() */ {
-        expected_builder.Save();
-        expected_builder.ClipPath(layer_clip, ClipOp::kIntersect, true);
-        expected_builder.SaveLayer(&children_bounds,
-                                   &DlPaint().setAlpha(opacity_alpha));
+        expected_builder.save();
+        expected_builder.clipPath(layer_clip, ClipOp::kIntersect, true);
+        expected_builder.setColor(opacity_alpha << 24);
+        expected_builder.saveLayer(&children_bounds, true);
         /* child layer1 paint */ {
-          expected_builder.DrawPath(path1, DlPaint());
+          expected_builder.setColor(0xFF000000);
+          expected_builder.drawPath(path1);
         }
         /* child layer2 paint */ {  //
-          expected_builder.DrawPath(path2, DlPaint());
+          expected_builder.drawPath(path2);
         }
-        expected_builder.Restore();
+        expected_builder.restore();
       }
     }
-    expected_builder.Restore();
+    expected_builder.restore();
   }
 
   opacity_layer->Paint(display_list_paint_context());
diff --git a/flow/layers/clip_rect_layer_unittests.cc b/flow/layers/clip_rect_layer_unittests.cc
index 6ddb907..728e487 100644
--- a/flow/layers/clip_rect_layer_unittests.cc
+++ b/flow/layers/clip_rect_layer_unittests.cc
@@ -454,24 +454,25 @@
 
   DisplayListBuilder expected_builder;
   /* OpacityLayer::Paint() */ {
-    expected_builder.Save();
+    expected_builder.save();
     {
-      expected_builder.Translate(offset.fX, offset.fY);
+      expected_builder.translate(offset.fX, offset.fY);
       /* ClipRectLayer::Paint() */ {
-        expected_builder.Save();
-        expected_builder.ClipRect(clip_rect, ClipOp::kIntersect, true);
-        expected_builder.SaveLayer(&children_bounds,
-                                   &DlPaint().setAlpha(opacity_alpha));
+        expected_builder.save();
+        expected_builder.clipRect(clip_rect, ClipOp::kIntersect, true);
+        expected_builder.setColor(opacity_alpha << 24);
+        expected_builder.saveLayer(&children_bounds, true);
         /* child layer1 paint */ {
-          expected_builder.DrawPath(path1, DlPaint());
+          expected_builder.setColor(0xFF000000);
+          expected_builder.drawPath(path1);
         }
         /* child layer2 paint */ {  //
-          expected_builder.DrawPath(path2, DlPaint());
+          expected_builder.drawPath(path2);
         }
-        expected_builder.Restore();
+        expected_builder.restore();
       }
     }
-    expected_builder.Restore();
+    expected_builder.restore();
   }
 
   opacity_layer->Paint(display_list_paint_context());
diff --git a/flow/layers/clip_rrect_layer_unittests.cc b/flow/layers/clip_rrect_layer_unittests.cc
index 75ca41c..9e789ce 100644
--- a/flow/layers/clip_rrect_layer_unittests.cc
+++ b/flow/layers/clip_rrect_layer_unittests.cc
@@ -467,24 +467,25 @@
 
   DisplayListBuilder expected_builder;
   /* OpacityLayer::Paint() */ {
-    expected_builder.Save();
+    expected_builder.save();
     {
-      expected_builder.Translate(offset.fX, offset.fY);
+      expected_builder.translate(offset.fX, offset.fY);
       /* ClipRectLayer::Paint() */ {
-        expected_builder.Save();
-        expected_builder.ClipRRect(clip_rrect, ClipOp::kIntersect, true);
-        expected_builder.SaveLayer(&children_bounds,
-                                   &DlPaint().setAlpha(opacity_alpha));
+        expected_builder.save();
+        expected_builder.clipRRect(clip_rrect, ClipOp::kIntersect, true);
+        expected_builder.setColor(opacity_alpha << 24);
+        expected_builder.saveLayer(&children_bounds, true);
         /* child layer1 paint */ {
-          expected_builder.DrawPath(path1, DlPaint());
+          expected_builder.setColor(0xFF000000);
+          expected_builder.drawPath(path1);
         }
         /* child layer2 paint */ {  //
-          expected_builder.DrawPath(path2, DlPaint());
+          expected_builder.drawPath(path2);
         }
-        expected_builder.Restore();
+        expected_builder.restore();
       }
     }
-    expected_builder.Restore();
+    expected_builder.restore();
   }
 
   opacity_layer->Paint(display_list_paint_context());
diff --git a/flow/layers/display_list_layer_unittests.cc b/flow/layers/display_list_layer_unittests.cc
index 1b2ad31..5d4b633 100644
--- a/flow/layers/display_list_layer_unittests.cc
+++ b/flow/layers/display_list_layer_unittests.cc
@@ -32,7 +32,7 @@
   const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
   const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
   DisplayListBuilder builder;
-  builder.DrawRect(picture_bounds, DlPaint());
+  builder.drawRect(picture_bounds);
   auto display_list = builder.Build();
   auto layer = std::make_shared<DisplayListLayer>(
       layer_offset, SkiaGPUObject<DisplayList>(display_list, unref_queue()),
@@ -47,7 +47,7 @@
   const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
   const SkRect picture_bounds = SkRect::MakeEmpty();
   DisplayListBuilder builder;
-  builder.DrawRect(picture_bounds, DlPaint());
+  builder.drawRect(picture_bounds);
   auto display_list = builder.Build();
   auto layer = std::make_shared<DisplayListLayer>(
       layer_offset, SkiaGPUObject<DisplayList>(display_list, unref_queue()),
@@ -77,7 +77,7 @@
       SkMatrix::Translate(layer_offset.fX, layer_offset.fY);
   const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
   DisplayListBuilder builder;
-  builder.DrawRect(picture_bounds, DlPaint());
+  builder.drawRect(picture_bounds);
   auto display_list = builder.Build();
   auto layer = std::make_shared<DisplayListLayer>(
       layer_offset, SkiaGPUObject(display_list, unref_queue()), false, false);
@@ -102,7 +102,7 @@
 TEST_F(DisplayListLayerTest, CachingDoesNotChangeCullRect) {
   const SkPoint layer_offset = SkPoint::Make(10, 10);
   DisplayListBuilder builder;
-  builder.DrawRect({10, 10, 20, 20}, DlPaint());
+  builder.drawRect({10, 10, 20, 20});
   auto display_list = builder.Build();
   auto layer = std::make_shared<DisplayListLayer>(
       layer_offset, SkiaGPUObject(display_list, unref_queue()), true, false);
@@ -118,7 +118,7 @@
   const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f);
   const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
   DisplayListBuilder builder;
-  builder.DrawRect(picture_bounds, DlPaint());
+  builder.drawRect(picture_bounds);
   auto display_list = builder.Build();
   auto display_list_layer = std::make_shared<DisplayListLayer>(
       layer_offset, SkiaGPUObject(display_list, unref_queue()), false, false);
@@ -130,7 +130,6 @@
             LayerStateStack::kCallerCanApplyOpacity);
 
   int opacity_alpha = 0x7F;
-  SkScalar opacity = opacity_alpha / 255.0;
   SkPoint opacity_offset = SkPoint::Make(10, 10);
   auto opacity_layer =
       std::make_shared<OpacityLayer>(opacity_alpha, opacity_offset);
@@ -139,24 +138,30 @@
   EXPECT_TRUE(opacity_layer->children_can_accept_opacity());
 
   DisplayListBuilder child_builder;
-  child_builder.DrawRect(picture_bounds, DlPaint());
+  child_builder.drawRect(picture_bounds);
   auto child_display_list = child_builder.Build();
 
   DisplayListBuilder expected_builder;
   /* opacity_layer::Paint() */ {
-    expected_builder.Save();
+    expected_builder.save();
     {
-      expected_builder.Translate(opacity_offset.fX, opacity_offset.fY);
+      expected_builder.translate(opacity_offset.fX, opacity_offset.fY);
       /* display_list_layer::Paint() */ {
-        expected_builder.Save();
+        expected_builder.save();
         {
-          expected_builder.Translate(layer_offset.fX, layer_offset.fY);
-          expected_builder.DrawDisplayList(child_display_list, opacity);
+          expected_builder.translate(layer_offset.fX, layer_offset.fY);
+          expected_builder.setColor(opacity_alpha << 24);
+          expected_builder.saveLayer(&picture_bounds, true);
+          /* display_list contents */ {  //
+            expected_builder.drawDisplayList(child_display_list);
+          }
+          expected_builder.restore();
+          expected_builder.setColor(DlColor::kBlack());
         }
-        expected_builder.Restore();
+        expected_builder.restore();
       }
     }
-    expected_builder.Restore();
+    expected_builder.restore();
   }
 
   opacity_layer->Paint(display_list_paint_context());
@@ -169,8 +174,8 @@
   const SkRect picture1_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
   const SkRect picture2_bounds = SkRect::MakeLTRB(10.0f, 15.0f, 30.0f, 35.0f);
   DisplayListBuilder builder;
-  builder.DrawRect(picture1_bounds, DlPaint());
-  builder.DrawRect(picture2_bounds, DlPaint());
+  builder.drawRect(picture1_bounds);
+  builder.drawRect(picture2_bounds);
   auto display_list = builder.Build();
   auto display_list_layer = std::make_shared<DisplayListLayer>(
       layer_offset, SkiaGPUObject(display_list, unref_queue()), false, false);
@@ -189,8 +194,8 @@
   EXPECT_FALSE(opacity_layer->children_can_accept_opacity());
 
   DisplayListBuilder child_builder;
-  child_builder.DrawRect(picture1_bounds, DlPaint());
-  child_builder.DrawRect(picture2_bounds, DlPaint());
+  child_builder.drawRect(picture1_bounds);
+  child_builder.drawRect(picture2_bounds);
   auto child_display_list = child_builder.Build();
 
   auto display_list_bounds = picture1_bounds;
@@ -199,24 +204,24 @@
       display_list_bounds.makeOffset(layer_offset.fX, layer_offset.fY);
   DisplayListBuilder expected_builder;
   /* opacity_layer::Paint() */ {
-    expected_builder.Save();
+    expected_builder.save();
     {
-      expected_builder.Translate(opacity_offset.fX, opacity_offset.fY);
-      expected_builder.SaveLayer(&save_layer_bounds,
-                                 &DlPaint().setAlpha(opacity_alpha));
+      expected_builder.translate(opacity_offset.fX, opacity_offset.fY);
+      expected_builder.setColor(opacity_alpha << 24);
+      expected_builder.saveLayer(&save_layer_bounds, true);
       {
         /* display_list_layer::Paint() */ {
-          expected_builder.Save();
+          expected_builder.save();
           {
-            expected_builder.Translate(layer_offset.fX, layer_offset.fY);
-            expected_builder.DrawDisplayList(child_display_list);
+            expected_builder.translate(layer_offset.fX, layer_offset.fY);
+            expected_builder.drawDisplayList(child_display_list);
           }
-          expected_builder.Restore();
+          expected_builder.restore();
         }
       }
-      expected_builder.Restore();
+      expected_builder.restore();
     }
-    expected_builder.Restore();
+    expected_builder.restore();
   }
 
   opacity_layer->Paint(display_list_paint_context());
@@ -229,8 +234,8 @@
   const SkRect picture1_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
   const SkRect picture2_bounds = SkRect::MakeLTRB(10.0f, 15.0f, 30.0f, 35.0f);
   DisplayListBuilder builder;
-  builder.DrawRect(picture1_bounds, DlPaint());
-  builder.DrawRect(picture2_bounds, DlPaint());
+  builder.drawRect(picture1_bounds);
+  builder.drawRect(picture2_bounds);
   auto display_list = builder.Build();
   auto display_list_layer = std::make_shared<DisplayListLayer>(
       layer_offset, SkiaGPUObject(display_list, unref_queue()), true, false);
@@ -381,7 +386,7 @@
   const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f);
   const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
   DisplayListBuilder builder;
-  builder.DrawRect(picture_bounds, DlPaint());
+  builder.drawRect(picture_bounds);
   auto display_list = builder.Build();
   auto layer = std::make_shared<DisplayListLayer>(
       layer_offset, SkiaGPUObject(display_list, unref_queue()), false, false);
@@ -400,7 +405,7 @@
   const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f);
   const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
   DisplayListBuilder builder;
-  builder.DrawRect(picture_bounds, DlPaint());
+  builder.drawRect(picture_bounds);
   auto display_list = builder.Build();
   auto layer = std::make_shared<DisplayListLayer>(
       layer_offset, SkiaGPUObject(display_list, unref_queue()), false, false);
@@ -418,7 +423,7 @@
   const SkRect missed_cull_rect = SkRect::MakeLTRB(100, 100, 200, 200);
   const SkRect hit_cull_rect = SkRect::MakeLTRB(0, 0, 200, 200);
   DisplayListBuilder builder;
-  builder.DrawRect(picture_bounds, DlPaint());
+  builder.drawRect(picture_bounds);
   auto display_list = builder.Build();
   auto layer = std::make_shared<DisplayListLayer>(
       layer_offset, SkiaGPUObject(display_list, unref_queue()), true, false);
@@ -519,8 +524,8 @@
   std::shared_ptr<DisplayListLayer> layers[layer_count];
   for (int i = 0; i < layer_count; i++) {
     DisplayListBuilder builder(false);
-    builder.DrawRect({0, 0, 100, 100}, DlPaint());
-    builder.DrawRect({50, 50, 100, 100}, DlPaint());
+    builder.drawRect({0, 0, 100, 100});
+    builder.drawRect({50, 50, 100, 100});
     auto display_list = builder.Build();
     ASSERT_FALSE(display_list->can_apply_group_opacity());
     SkPoint offset = {i * 200.0f, 0};
diff --git a/flow/layers/image_filter_layer_unittests.cc b/flow/layers/image_filter_layer_unittests.cc
index bb5ced3..77789a9 100644
--- a/flow/layers/image_filter_layer_unittests.cc
+++ b/flow/layers/image_filter_layer_unittests.cc
@@ -248,7 +248,7 @@
       }
     }
   }
-  expected_builder.Restore();
+  expected_builder.restore();
   auto expected_display_list = expected_builder.Build();
 
   layer->Paint(display_list_paint_context());
diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc
index 1be70be..a5e72e0 100644
--- a/flow/layers/opacity_layer_unittests.cc
+++ b/flow/layers/opacity_layer_unittests.cc
@@ -606,23 +606,24 @@
 
   DisplayListBuilder expected_builder;
   /* opacity_layer_1::Paint */ {
-    expected_builder.Save();
+    expected_builder.save();
     {
-      expected_builder.Translate(offset1.fX, offset1.fY);
+      expected_builder.translate(offset1.fX, offset1.fY);
       /* opacity_layer_2::Paint */ {
-        expected_builder.Save();
+        expected_builder.save();
         {
-          expected_builder.Translate(offset2.fX, offset2.fY);
-          expected_builder.SaveLayer(&mock_layer->paint_bounds(),
-                                     &savelayer_paint);
+          expected_builder.translate(offset2.fX, offset2.fY);
+          expected_builder.setColor(savelayer_paint.getAlpha() << 24);
+          expected_builder.saveLayer(&mock_layer->paint_bounds(), true);
           /* mock_layer::Paint */ {
-            expected_builder.DrawPath(mock_path, DlPaint());
+            expected_builder.setColor(0xFF000000);
+            expected_builder.drawPath(mock_path);
           }
         }
-        expected_builder.Restore();
+        expected_builder.restore();
       }
     }
-    expected_builder.Restore();
+    expected_builder.restore();
   }
 
   opacity_layer_1->Paint(display_list_paint_context());
diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc
index c237114..61b3199 100644
--- a/flow/layers/physical_shape_layer.cc
+++ b/flow/layers/physical_shape_layer.cc
@@ -4,6 +4,7 @@
 
 #include "flutter/flow/layers/physical_shape_layer.h"
 
+#include "flutter/display_list/display_list_canvas_dispatcher.h"
 #include "flutter/flow/paint_utils.h"
 
 namespace flutter {
@@ -35,9 +36,9 @@
   if (elevation_ == 0) {
     bounds = path_.getBounds();
   } else {
-    bounds = DlCanvas::ComputeShadowBounds(path_, elevation_,
-                                           context->frame_device_pixel_ratio(),
-                                           context->GetTransform3x3());
+    bounds = DisplayListCanvasDispatcher::ComputeShadowBounds(
+        path_, elevation_, context->frame_device_pixel_ratio(),
+        context->GetTransform3x3());
   }
 
   context->AddLayerBounds(bounds);
@@ -64,7 +65,7 @@
   } else {
     // We will draw the shadow in Paint(), so add some margin to the paint
     // bounds to leave space for the shadow.
-    paint_bounds = DlCanvas::ComputeShadowBounds(
+    paint_bounds = DisplayListCanvasDispatcher::ComputeShadowBounds(
         path_, elevation_, context->frame_device_pixel_ratio,
         context->state_stack.transform_3x3());
   }
diff --git a/flow/layers/physical_shape_layer_unittests.cc b/flow/layers/physical_shape_layer_unittests.cc
index 6c97b45..daae090 100644
--- a/flow/layers/physical_shape_layer_unittests.cc
+++ b/flow/layers/physical_shape_layer_unittests.cc
@@ -4,6 +4,7 @@
 
 #include "flutter/flow/layers/physical_shape_layer.h"
 
+#include "flutter/display_list/display_list_canvas_dispatcher.h"
 #include "flutter/flow/testing/diff_context_test.h"
 #include "flutter/flow/testing/layer_test.h"
 #include "flutter/flow/testing/mock_layer.h"
@@ -213,8 +214,8 @@
   // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and
   // their shadows , so we do not do any painting there.
   EXPECT_EQ(layer->paint_bounds(),
-            DlCanvas::ComputeShadowBounds(layer_path, initial_elevation, 1.0f,
-                                          SkMatrix()));
+            DisplayListCanvasDispatcher::ComputeShadowBounds(
+                layer_path, initial_elevation, 1.0f, SkMatrix()));
   EXPECT_TRUE(layer->needs_painting(paint_context()));
   EXPECT_EQ(layer->elevation(), initial_elevation);
 
@@ -263,7 +264,7 @@
     // On Fuchsia, the system compositor handles all elevated
     // PhysicalShapeLayers and their shadows , so we do not do any painting
     // there.
-    SkRect paint_bounds = DlCanvas::ComputeShadowBounds(
+    SkRect paint_bounds = DisplayListCanvasDispatcher::ComputeShadowBounds(
         layer_path, initial_elevations[i], 1.0f /* pixel_ratio */, SkMatrix());
 
     // Without clipping the children will be painted as well
@@ -312,15 +313,15 @@
   path.addRect(0, 0, 8, 8).close();
 
   for (SkScalar elevation : elevations) {
-    SkRect baseline_bounds =
-        DlCanvas::ComputeShadowBounds(path, elevation, 1.0f, SkMatrix());
+    SkRect baseline_bounds = DisplayListCanvasDispatcher::ComputeShadowBounds(
+        path, elevation, 1.0f, SkMatrix());
     for (SkScalar scale : scales) {
       for (SkScalar translate_x : translates) {
         for (SkScalar translate_y : translates) {
           SkMatrix ctm;
           ctm.setScaleTranslate(scale, scale, translate_x, translate_y);
-          SkRect bounds =
-              DlCanvas::ComputeShadowBounds(path, elevation, scale, ctm);
+          SkRect bounds = DisplayListCanvasDispatcher::ComputeShadowBounds(
+              path, elevation, scale, ctm);
           EXPECT_FLOAT_EQ(bounds.fLeft, baseline_bounds.fLeft);
           EXPECT_FLOAT_EQ(bounds.fTop, baseline_bounds.fTop);
           EXPECT_FLOAT_EQ(bounds.fRight, baseline_bounds.fRight);
@@ -376,14 +377,14 @@
                   [=](SkCanvas* canvas) {
                     SkPath path;
                     path.addRect(test_case[0]).close();
-                    DlSkCanvasAdapter(canvas).DrawShadow(
-                        path, DlColor::kBlack(), 1.0f, false, 1.0f);
+                    DisplayListCanvasDispatcher::DrawShadow(
+                        canvas, path, DlColor::kBlack(), 1.0f, false, 1.0f);
                   },
                   [=](SkCanvas* canvas) {
                     SkPath path;
                     path.addRect(test_case[1]).close();
-                    DlSkCanvasAdapter(canvas).DrawShadow(
-                        path, DlColor::kBlack(), 1.0f, false, 1.0f);
+                    DisplayListCanvasDispatcher::DrawShadow(
+                        canvas, path, DlColor::kBlack(), 1.0f, false, 1.0f);
                   },
                   SkSize::Make(100, 100)),
               0);
diff --git a/flow/layers/shader_mask_layer_unittests.cc b/flow/layers/shader_mask_layer_unittests.cc
index b08d73c..886f0d0 100644
--- a/flow/layers/shader_mask_layer_unittests.cc
+++ b/flow/layers/shader_mask_layer_unittests.cc
@@ -383,7 +383,7 @@
           /* child layer paint */ {
             expected_builder.DrawPath(child_path, DlPaint());
           }
-          expected_builder.Translate(mask_rect.fLeft, mask_rect.fTop);
+          expected_builder.translate(mask_rect.fLeft, mask_rect.fTop);
           expected_builder.DrawRect(
               SkRect::MakeWH(mask_rect.width(), mask_rect.height()),
               DlPaint().setBlendMode(DlBlendMode::kSrc));
diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc
index 7f1c518..7fc8a5c 100644
--- a/flow/raster_cache_unittests.cc
+++ b/flow/raster_cache_unittests.cc
@@ -449,7 +449,8 @@
 
   SkRect logical_rect = SkRect::MakeLTRB(28, 0, 354.56731, 310.288);
   DisplayListBuilder builder(logical_rect);
-  builder.DrawRect(logical_rect, DlPaint(DlColor::kRed()));
+  builder.setColor(SK_ColorRED);
+  builder.drawRect(logical_rect);
   sk_sp<DisplayList> display_list = builder.Build();
 
   SkMatrix ctm = SkMatrix::MakeAll(1.3312, 0, 233, 0, 1.3312, 206, 0, 0, 1);
diff --git a/flow/testing/diff_context_test.cc b/flow/testing/diff_context_test.cc
index 61629eb..888923a 100644
--- a/flow/testing/diff_context_test.cc
+++ b/flow/testing/diff_context_test.cc
@@ -35,7 +35,8 @@
 sk_sp<DisplayList> DiffContextTest::CreateDisplayList(const SkRect& bounds,
                                                       SkColor color) {
   DisplayListBuilder builder;
-  builder.DrawRect(bounds, DlPaint().setColor(color));
+  builder.setColor(color);
+  builder.drawRect(bounds);
   return builder.Build();
 }
 
diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc
index 8102239..afe8e84 100644
--- a/impeller/display_list/display_list_dispatcher.cc
+++ b/impeller/display_list/display_list_dispatcher.cc
@@ -180,12 +180,12 @@
   };
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setAntiAlias(bool aa) {
   // Nothing to do because AA is implicit.
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setDither(bool dither) {}
 
 static Paint::Style ToStyle(flutter::DlDrawStyle style) {
@@ -201,12 +201,12 @@
   return Paint::Style::kFill;
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setStyle(flutter::DlDrawStyle style) {
   paint_.style = ToStyle(style);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setColor(flutter::DlColor color) {
   paint_.color = {
       color.getRedF(),
@@ -216,17 +216,17 @@
   };
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setStrokeWidth(SkScalar width) {
   paint_.stroke_width = width;
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setStrokeMiter(SkScalar limit) {
   paint_.stroke_miter = limit;
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setStrokeCap(flutter::DlStrokeCap cap) {
   switch (cap) {
     case flutter::DlStrokeCap::kButt:
@@ -241,7 +241,7 @@
   }
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setStrokeJoin(flutter::DlStrokeJoin join) {
   switch (join) {
     case flutter::DlStrokeJoin::kMiter:
@@ -345,7 +345,7 @@
   }
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setColorSource(
     const flutter::DlColorSource* source) {
   if (!source) {
@@ -583,24 +583,24 @@
   return std::nullopt;
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setColorFilter(
     const flutter::DlColorFilter* filter) {
   // Needs https://github.com/flutter/flutter/issues/95434
   paint_.color_filter = ToColorFilterProc(filter);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setInvertColors(bool invert) {
   paint_.invert_colors = invert;
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setBlendMode(flutter::DlBlendMode dl_mode) {
   paint_.blend_mode = ToBlendMode(dl_mode);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setPathEffect(const flutter::DlPathEffect* effect) {
   // Needs https://github.com/flutter/flutter/issues/95434
   UNIMPLEMENTED;
@@ -619,7 +619,7 @@
   }
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setMaskFilter(const flutter::DlMaskFilter* filter) {
   // Needs https://github.com/flutter/flutter/issues/95434
   if (filter == nullptr) {
@@ -704,7 +704,7 @@
       };
       break;
     }
-    case flutter::DlImageFilterType::kCompose: {
+    case flutter::DlImageFilterType::kComposeFilter: {
       auto compose = filter->asCompose();
       FML_DCHECK(compose);
       auto outer = compose->outer();
@@ -741,7 +741,7 @@
       };
       break;
     }
-    case flutter::DlImageFilterType::kLocalMatrix: {
+    case flutter::DlImageFilterType::kLocalMatrixFilter: {
       auto local_matrix_filter = filter->asLocalMatrix();
       FML_DCHECK(local_matrix_filter);
       auto internal_filter = local_matrix_filter->image_filter();
@@ -766,13 +766,13 @@
   }
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::setImageFilter(
     const flutter::DlImageFilter* filter) {
   paint_.image_filter = ToImageFilterProc(filter);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::save() {
   canvas_.Save();
 }
@@ -792,7 +792,7 @@
   return result;
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::saveLayer(const SkRect* bounds,
                                       const flutter::SaveLayerOptions options,
                                       const flutter::DlImageFilter* backdrop) {
@@ -800,32 +800,32 @@
   canvas_.SaveLayer(paint, ToRect(bounds), ToImageFilterProc(backdrop));
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::restore() {
   canvas_.Restore();
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::translate(SkScalar tx, SkScalar ty) {
   canvas_.Translate({tx, ty, 0.0});
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::scale(SkScalar sx, SkScalar sy) {
   canvas_.Scale({sx, sy, 1.0});
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::rotate(SkScalar degrees) {
   canvas_.Rotate(Degrees{degrees});
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::skew(SkScalar sx, SkScalar sy) {
   canvas_.Skew(sx, sy);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::transform2DAffine(SkScalar mxx,
                                               SkScalar mxy,
                                               SkScalar mxt,
@@ -842,7 +842,7 @@
   // clang-format on
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::transformFullPerspective(SkScalar mxx,
                                                      SkScalar mxy,
                                                      SkScalar mxz,
@@ -872,7 +872,7 @@
   canvas_.Transform(xformation);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::transformReset() {
   canvas_.ResetTransform();
   canvas_.Transform(initial_matrix_);
@@ -892,7 +892,7 @@
   }
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::clipRect(const SkRect& rect,
                                      ClipOp clip_op,
                                      bool is_aa) {
@@ -979,7 +979,7 @@
     case SkPathFillType::kInverseWinding:
     case SkPathFillType::kInverseEvenOdd:
       // Flutter doesn't expose these path fill types. These are only visible
-      // via the receiver interface. We should never get here.
+      // via the dispatcher interface. We should never get here.
       fill_type = FillType::kNonZero;
       break;
   }
@@ -992,21 +992,21 @@
       .TakePath();
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::clipRRect(const SkRRect& rrect,
                                       ClipOp clip_op,
                                       bool is_aa) {
   canvas_.ClipPath(ToPath(rrect), ToClipOperation(clip_op));
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::clipPath(const SkPath& path,
                                      ClipOp clip_op,
                                      bool is_aa) {
   canvas_.ClipPath(ToPath(path), ToClipOperation(clip_op));
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawColor(flutter::DlColor color,
                                       flutter::DlBlendMode dl_mode) {
   Paint paint;
@@ -1015,12 +1015,12 @@
   canvas_.DrawPaint(paint);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawPaint() {
   canvas_.DrawPaint(paint_);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawLine(const SkPoint& p0, const SkPoint& p1) {
   auto path = PathBuilder{}.AddLine(ToPoint(p0), ToPoint(p1)).TakePath();
   Paint paint = paint_;
@@ -1028,12 +1028,12 @@
   canvas_.DrawPath(path, paint);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawRect(const SkRect& rect) {
   canvas_.DrawRect(ToRect(rect), paint_);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawOval(const SkRect& bounds) {
   if (bounds.width() == bounds.height()) {
     canvas_.DrawCircle(ToPoint(bounds.center()), bounds.width() * 0.5, paint_);
@@ -1043,12 +1043,12 @@
   }
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawCircle(const SkPoint& center, SkScalar radius) {
   canvas_.DrawCircle(ToPoint(center), radius, paint_);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawRRect(const SkRRect& rrect) {
   if (rrect.isSimple()) {
     canvas_.DrawRRect(ToRect(rrect.rect()), rrect.getSimpleRadii().fX, paint_);
@@ -1057,7 +1057,7 @@
   }
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawDRRect(const SkRRect& outer,
                                        const SkRRect& inner) {
   PathBuilder builder;
@@ -1066,12 +1066,12 @@
   canvas_.DrawPath(builder.TakePath(FillType::kOdd), paint_);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawPath(const SkPath& path) {
   canvas_.DrawPath(ToPath(path), paint_);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawArc(const SkRect& oval_bounds,
                                     SkScalar start_degrees,
                                     SkScalar sweep_degrees,
@@ -1082,7 +1082,7 @@
   canvas_.DrawPath(builder.TakePath(), paint_);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawPoints(PointMode mode,
                                        uint32_t count,
                                        const SkPoint points[]) {
@@ -1121,14 +1121,14 @@
   }
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawVertices(const flutter::DlVertices* vertices,
                                          flutter::DlBlendMode dl_mode) {
   canvas_.DrawVertices(DLVerticesGeometry::MakeVertices(vertices),
                        ToBlendMode(dl_mode), paint_);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawImage(const sk_sp<flutter::DlImage> image,
                                       const SkPoint point,
                                       flutter::DlImageSampling sampling,
@@ -1147,23 +1147,24 @@
   const auto dest =
       SkRect::MakeXYWH(point.fX, point.fY, size.width, size.height);
 
-  drawImageRect(image,                      // image
-                src,                        // source rect
-                dest,                       // destination rect
-                sampling,                   // sampling options
-                render_with_attributes,     // render with attributes
-                SrcRectConstraint::kStrict  // constraint
+  drawImageRect(
+      image,                   // image
+      src,                     // source rect
+      dest,                    // destination rect
+      sampling,                // sampling options
+      render_with_attributes,  // render with attributes
+      SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint  // constraint
   );
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawImageRect(
     const sk_sp<flutter::DlImage> image,
     const SkRect& src,
     const SkRect& dst,
     flutter::DlImageSampling sampling,
     bool render_with_attributes,
-    SrcRectConstraint constraint = SrcRectConstraint::kFast) {
+    SkCanvas::SrcRectConstraint constraint) {
   canvas_.DrawImageRect(
       std::make_shared<Image>(image->impeller_texture()),  // image
       ToRect(src),                                         // source rect
@@ -1173,7 +1174,7 @@
   );
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawImageNine(const sk_sp<flutter::DlImage> image,
                                           const SkIRect& center,
                                           const SkRect& dst,
@@ -1186,7 +1187,7 @@
       ToRect(dst), ToSamplerDescriptor(filter), &canvas_, &paint_);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawAtlas(const sk_sp<flutter::DlImage> atlas,
                                       const SkRSXform xform[],
                                       const SkRect tex[],
@@ -1202,43 +1203,21 @@
                     ToSamplerDescriptor(sampling), ToRect(cull_rect), paint_);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawDisplayList(
-    const sk_sp<flutter::DisplayList> display_list,
-    SkScalar opacity) {
-  // Save all values that must remain untouched after the operation.
-  Paint saved_paint = paint_;
-  Matrix saved_initial_matrix = initial_matrix_;
-  int restore_count = canvas_.GetSaveCount();
-
-  // Establish a new baseline for interpreting the new DL.
-  // Matrix and clip are left untouched, the current
-  // transform is saved as the new base matrix, and paint
-  // values are reset to defaults.
-  initial_matrix_ = canvas_.GetCurrentTransformation();
+    const sk_sp<flutter::DisplayList> display_list) {
+  int saveCount = canvas_.GetSaveCount();
+  Paint savePaint = paint_;
+  Matrix saveMatrix = initial_matrix_;
   paint_ = Paint();
-
-  // Handle passed opacity in the most brute-force way by using
-  // a SaveLayer. If the display_list is able to inherit the
-  // opacity, this could also be handled by modulating all of its
-  // attribute settings (for example, color), by the indicated
-  // opacity.
-  if (opacity < SK_Scalar1) {
-    Paint save_paint;
-    save_paint.color = Color(0, 0, 0, opacity);
-    canvas_.SaveLayer(save_paint);
-  }
-
+  initial_matrix_ = canvas_.GetCurrentTransformation();
   display_list->Dispatch(*this);
-
-  // Restore all saved state back to what it was before we interpreted
-  // the display_list
-  canvas_.RestoreToCount(restore_count);
-  initial_matrix_ = saved_initial_matrix;
-  paint_ = saved_paint;
+  paint_ = savePaint;
+  initial_matrix_ = saveMatrix;
+  canvas_.RestoreToCount(saveCount);
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawTextBlob(const sk_sp<SkTextBlob> blob,
                                          SkScalar x,
                                          SkScalar y) {
@@ -1249,7 +1228,7 @@
   );
 }
 
-// |flutter::DlOpReceiver|
+// |flutter::Dispatcher|
 void DisplayListDispatcher::drawShadow(const SkPath& path,
                                        const flutter::DlColor color,
                                        const SkScalar elevation,
diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h
index 76cf251..2f462f9 100644
--- a/impeller/display_list/display_list_dispatcher.h
+++ b/impeller/display_list/display_list_dispatcher.h
@@ -7,14 +7,14 @@
 #include "display_list/display_list_path_effect.h"
 #include "flutter/display_list/display_list.h"
 #include "flutter/display_list/display_list_blend_mode.h"
-#include "flutter/display_list/dl_op_receiver.h"
+#include "flutter/display_list/display_list_dispatcher.h"
 #include "flutter/fml/macros.h"
 #include "impeller/aiks/canvas.h"
 #include "impeller/aiks/paint.h"
 
 namespace impeller {
 
-class DisplayListDispatcher final : public flutter::DlOpReceiver {
+class DisplayListDispatcher final : public flutter::Dispatcher {
  public:
   DisplayListDispatcher();
 
@@ -22,75 +22,75 @@
 
   Picture EndRecordingAsPicture();
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setAntiAlias(bool aa) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setDither(bool dither) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setStyle(flutter::DlDrawStyle style) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setColor(flutter::DlColor color) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setStrokeWidth(SkScalar width) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setStrokeMiter(SkScalar limit) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setStrokeCap(flutter::DlStrokeCap cap) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setStrokeJoin(flutter::DlStrokeJoin join) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setColorSource(const flutter::DlColorSource* source) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setColorFilter(const flutter::DlColorFilter* filter) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setInvertColors(bool invert) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setBlendMode(flutter::DlBlendMode mode) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setPathEffect(const flutter::DlPathEffect* effect) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setMaskFilter(const flutter::DlMaskFilter* filter) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void setImageFilter(const flutter::DlImageFilter* filter) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void save() override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void saveLayer(const SkRect* bounds,
                  const flutter::SaveLayerOptions options,
                  const flutter::DlImageFilter* backdrop) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void restore() override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void translate(SkScalar tx, SkScalar ty) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void scale(SkScalar sx, SkScalar sy) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void rotate(SkScalar degrees) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void skew(SkScalar sx, SkScalar sy) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void transform2DAffine(SkScalar mxx,
                          SkScalar mxy,
                          SkScalar mxt,
@@ -98,7 +98,7 @@
                          SkScalar myy,
                          SkScalar myt) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void transformFullPerspective(SkScalar mxx,
                                 SkScalar mxy,
                                 SkScalar mxz,
@@ -116,82 +116,82 @@
                                 SkScalar mwz,
                                 SkScalar mwt) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void transformReset() override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void clipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void clipPath(const SkPath& path, ClipOp clip_op, bool is_aa) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawColor(flutter::DlColor color, flutter::DlBlendMode mode) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawPaint() override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawLine(const SkPoint& p0, const SkPoint& p1) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawRect(const SkRect& rect) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawOval(const SkRect& bounds) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawCircle(const SkPoint& center, SkScalar radius) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawRRect(const SkRRect& rrect) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawDRRect(const SkRRect& outer, const SkRRect& inner) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawPath(const SkPath& path) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawArc(const SkRect& oval_bounds,
                SkScalar start_degrees,
                SkScalar sweep_degrees,
                bool use_center) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawPoints(PointMode mode,
                   uint32_t count,
                   const SkPoint points[]) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawVertices(const flutter::DlVertices* vertices,
                     flutter::DlBlendMode dl_mode) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawImage(const sk_sp<flutter::DlImage> image,
                  const SkPoint point,
                  flutter::DlImageSampling sampling,
                  bool render_with_attributes) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawImageRect(const sk_sp<flutter::DlImage> image,
                      const SkRect& src,
                      const SkRect& dst,
                      flutter::DlImageSampling sampling,
                      bool render_with_attributes,
-                     SrcRectConstraint constraint) override;
+                     SkCanvas::SrcRectConstraint constraint) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawImageNine(const sk_sp<flutter::DlImage> image,
                      const SkIRect& center,
                      const SkRect& dst,
                      flutter::DlFilterMode filter,
                      bool render_with_attributes) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawAtlas(const sk_sp<flutter::DlImage> atlas,
                  const SkRSXform xform[],
                  const SkRect tex[],
@@ -202,16 +202,15 @@
                  const SkRect* cull_rect,
                  bool render_with_attributes) override;
 
-  // |flutter::DlOpReceiver|
-  void drawDisplayList(const sk_sp<flutter::DisplayList> display_list,
-                       SkScalar opacity) override;
+  // |flutter::Dispatcher|
+  void drawDisplayList(const sk_sp<flutter::DisplayList> display_list) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawTextBlob(const sk_sp<SkTextBlob> blob,
                     SkScalar x,
                     SkScalar y) override;
 
-  // |flutter::DlOpReceiver|
+  // |flutter::Dispatcher|
   void drawShadow(const SkPath& path,
                   const flutter::DlColor color,
                   const SkScalar elevation,
diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc
index fb94821..0c5c956 100644
--- a/impeller/display_list/display_list_unittests.cc
+++ b/impeller/display_list/display_list_unittests.cc
@@ -44,83 +44,85 @@
 
 TEST_P(DisplayListTest, CanDrawRect) {
   flutter::DisplayListBuilder builder;
-  builder.DrawRect(SkRect::MakeXYWH(10, 10, 100, 100),
-                   flutter::DlPaint(flutter::DlColor::kBlue()));
+  builder.setColor(SK_ColorBLUE);
+  builder.drawRect(SkRect::MakeXYWH(10, 10, 100, 100));
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
 
 TEST_P(DisplayListTest, CanDrawTextBlob) {
   flutter::DisplayListBuilder builder;
-  builder.DrawTextBlob(SkTextBlob::MakeFromString("Hello", CreateTestFont()),
-                       100, 100, flutter::DlPaint(flutter::DlColor::kBlue()));
+  builder.setColor(SK_ColorBLUE);
+  builder.drawTextBlob(SkTextBlob::MakeFromString("Hello", CreateTestFont()),
+                       100, 100);
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
 
 TEST_P(DisplayListTest, CanDrawTextWithSaveLayer) {
   flutter::DisplayListBuilder builder;
-  builder.DrawTextBlob(SkTextBlob::MakeFromString("Hello", CreateTestFont()),
-                       100, 100, flutter::DlPaint(flutter::DlColor::kRed()));
+  builder.setColor(SK_ColorRED);
+  builder.drawTextBlob(SkTextBlob::MakeFromString("Hello", CreateTestFont()),
+                       100, 100);
 
   flutter::DlPaint save_paint;
   float alpha = 0.5;
   save_paint.setAlpha(static_cast<uint8_t>(255 * alpha));
   builder.SaveLayer(nullptr, &save_paint);
-  builder.DrawTextBlob(SkTextBlob::MakeFromString("Hello with half alpha",
+  builder.setColor(SK_ColorRED);
+  builder.drawTextBlob(SkTextBlob::MakeFromString("Hello with half alpha",
                                                   CreateTestFontOfSize(100)),
-                       100, 300, flutter::DlPaint(flutter::DlColor::kRed()));
-  builder.Restore();
+                       100, 300);
+  builder.restore();
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
 
 TEST_P(DisplayListTest, CanDrawImage) {
   auto texture = CreateTextureForFixture("embarcadero.jpg");
   flutter::DisplayListBuilder builder;
-  builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
-                    flutter::DlImageSampling::kNearestNeighbor, nullptr);
+  builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
+                    flutter::DlImageSampling::kNearestNeighbor, true);
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
 
 TEST_P(DisplayListTest, CanDrawCapsAndJoins) {
   flutter::DisplayListBuilder builder;
-  flutter::DlPaint paint;
 
-  paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
-  paint.setStrokeWidth(30);
-  paint.setColor(SK_ColorRED);
+  builder.setStyle(flutter::DlDrawStyle::kStroke);
+  builder.setStrokeWidth(30);
+  builder.setColor(SK_ColorRED);
 
   auto path =
       SkPathBuilder{}.moveTo(-50, 0).lineTo(0, -50).lineTo(50, 0).snapshot();
 
-  builder.Translate(100, 100);
+  builder.translate(100, 100);
   {
-    paint.setStrokeCap(flutter::DlStrokeCap::kButt);
-    paint.setStrokeJoin(flutter::DlStrokeJoin::kMiter);
-    paint.setStrokeMiter(4);
-    builder.DrawPath(path, paint);
+    builder.setStrokeCap(flutter::DlStrokeCap::kButt);
+    builder.setStrokeJoin(flutter::DlStrokeJoin::kMiter);
+    builder.setStrokeMiter(4);
+    builder.drawPath(path);
   }
 
   {
-    builder.Save();
-    builder.Translate(0, 100);
+    builder.save();
+    builder.translate(0, 100);
     // The joint in the path is 45 degrees. A miter length of 1 convert to a
     // bevel in this case.
-    paint.setStrokeMiter(1);
-    builder.DrawPath(path, paint);
-    builder.Restore();
+    builder.setStrokeMiter(1);
+    builder.drawPath(path);
+    builder.restore();
   }
 
-  builder.Translate(150, 0);
+  builder.translate(150, 0);
   {
-    paint.setStrokeCap(flutter::DlStrokeCap::kSquare);
-    paint.setStrokeJoin(flutter::DlStrokeJoin::kBevel);
-    builder.DrawPath(path, paint);
+    builder.setStrokeCap(flutter::DlStrokeCap::kSquare);
+    builder.setStrokeJoin(flutter::DlStrokeJoin::kBevel);
+    builder.drawPath(path);
   }
 
-  builder.Translate(150, 0);
+  builder.translate(150, 0);
   {
-    paint.setStrokeCap(flutter::DlStrokeCap::kRound);
-    paint.setStrokeJoin(flutter::DlStrokeJoin::kRound);
-    builder.DrawPath(path, paint);
+    builder.setStrokeCap(flutter::DlStrokeCap::kRound);
+    builder.setStrokeJoin(flutter::DlStrokeJoin::kRound);
+    builder.drawPath(path);
   }
 
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
@@ -165,21 +167,20 @@
         Point(200, 200), Point(400, 400), 20, Color::White(), Color::White());
 
     flutter::DisplayListBuilder builder;
-    flutter::DlPaint paint;
 
     Vector2 scale = GetContentScale();
-    builder.Scale(scale.x, scale.y);
-    paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
-    paint.setStrokeCap(cap);
-    paint.setStrokeJoin(flutter::DlStrokeJoin::kMiter);
-    paint.setStrokeMiter(10);
+    builder.scale(scale.x, scale.y);
+    builder.setStyle(flutter::DlDrawStyle::kStroke);
+    builder.setStrokeCap(cap);
+    builder.setStrokeJoin(flutter::DlStrokeJoin::kMiter);
+    builder.setStrokeMiter(10);
     auto rect = SkRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y);
-    paint.setColor(SK_ColorGREEN);
-    paint.setStrokeWidth(2);
-    builder.DrawRect(rect, paint);
-    paint.setColor(SK_ColorRED);
-    paint.setStrokeWidth(stroke_width);
-    builder.DrawArc(rect, start_angle, sweep_angle, use_center, paint);
+    builder.setColor(SK_ColorGREEN);
+    builder.setStrokeWidth(2);
+    builder.drawRect(rect);
+    builder.setColor(SK_ColorRED);
+    builder.setStrokeWidth(stroke_width);
+    builder.drawArc(rect, start_angle, sweep_angle, use_center);
 
     return builder.Build();
   };
@@ -188,48 +189,45 @@
 
 TEST_P(DisplayListTest, StrokedPathsDrawCorrectly) {
   flutter::DisplayListBuilder builder;
-  flutter::DlPaint paint;
-
-  paint.setColor(SK_ColorRED);
-  paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
-  paint.setStrokeWidth(10);
+  builder.setColor(SK_ColorRED);
+  builder.setStyle(flutter::DlDrawStyle::kStroke);
+  builder.setStrokeWidth(10);
 
   // Rectangle
-  builder.Translate(100, 100);
-  builder.DrawRect(SkRect::MakeSize({100, 100}), paint);
+  builder.translate(100, 100);
+  builder.drawRect(SkRect::MakeSize({100, 100}));
 
   // Rounded rectangle
-  builder.Translate(150, 0);
-  builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10),
-                    paint);
+  builder.translate(150, 0);
+  builder.drawRRect(SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10));
 
   // Double rounded rectangle
-  builder.Translate(150, 0);
-  builder.DrawDRRect(
+  builder.translate(150, 0);
+  builder.drawDRRect(
       SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10),
-      SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 10, 80, 30), 10, 10), paint);
+      SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 10, 80, 30), 10, 10));
 
   // Contour with duplicate join points
   {
-    builder.Translate(150, 0);
+    builder.translate(150, 0);
     SkPath path;
     path.lineTo({100, 0});
     path.lineTo({100, 0});
     path.lineTo({100, 100});
-    builder.DrawPath(path, paint);
+    builder.drawPath(path);
   }
 
   // Contour with duplicate end points
   {
-    paint.setStrokeCap(flutter::DlStrokeCap::kRound);
-    builder.Translate(150, 0);
+    builder.setStrokeCap(flutter::DlStrokeCap::kRound);
+    builder.translate(150, 0);
     SkPath path;
     path.moveTo(0, 0);
     path.lineTo({0, 0});
     path.lineTo({50, 50});
     path.lineTo({100, 0});
     path.lineTo({100, 0});
-    builder.DrawPath(path, paint);
+    builder.drawPath(path);
   }
 
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
@@ -237,17 +235,15 @@
 
 TEST_P(DisplayListTest, CanDrawWithOddPathWinding) {
   flutter::DisplayListBuilder builder;
-  flutter::DlPaint paint;
+  builder.setColor(SK_ColorRED);
+  builder.setStyle(flutter::DlDrawStyle::kFill);
 
-  paint.setColor(SK_ColorRED);
-  paint.setDrawStyle(flutter::DlDrawStyle::kFill);
-
-  builder.Translate(300, 300);
+  builder.translate(300, 300);
   SkPath path;
   path.setFillType(SkPathFillType::kEvenOdd);
   path.addCircle(0, 0, 100);
   path.addCircle(0, 0, 50);
-  builder.DrawPath(path, paint);
+  builder.drawPath(path);
 
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
@@ -255,31 +251,29 @@
 TEST_P(DisplayListTest, CanDrawWithMaskBlur) {
   auto texture = CreateTextureForFixture("embarcadero.jpg");
   flutter::DisplayListBuilder builder;
-  flutter::DlPaint paint;
 
   // Mask blurred image.
   {
     auto filter = flutter::DlBlurMaskFilter(kNormal_SkBlurStyle, 10.0f);
-    paint.setMaskFilter(&filter);
-    builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
-                      flutter::DlImageSampling::kNearestNeighbor, &paint);
+    builder.setMaskFilter(&filter);
+    builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
+                      flutter::DlImageSampling::kNearestNeighbor, true);
   }
 
   // Mask blurred filled path.
   {
-    paint.setColor(SK_ColorYELLOW);
+    builder.setColor(SK_ColorYELLOW);
     auto filter = flutter::DlBlurMaskFilter(kOuter_SkBlurStyle, 10.0f);
-    paint.setMaskFilter(&filter);
-    builder.DrawArc(SkRect::MakeXYWH(410, 110, 100, 100), 45, 270, true, paint);
+    builder.setMaskFilter(&filter);
+    builder.drawArc(SkRect::MakeXYWH(410, 110, 100, 100), 45, 270, true);
   }
 
   // Mask blurred text.
   {
     auto filter = flutter::DlBlurMaskFilter(kSolid_SkBlurStyle, 10.0f);
-    paint.setMaskFilter(&filter);
-    builder.DrawTextBlob(
-        SkTextBlob::MakeFromString("Testing", CreateTestFont()), 220, 170,
-        paint);
+    builder.setMaskFilter(&filter);
+    builder.drawTextBlob(
+        SkTextBlob::MakeFromString("Testing", CreateTestFont()), 220, 170);
   }
 
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
@@ -301,24 +295,23 @@
 TEST_P(DisplayListTest, CanDrawWithBlendColorFilter) {
   auto texture = CreateTextureForFixture("embarcadero.jpg");
   flutter::DisplayListBuilder builder;
-  flutter::DlPaint paint;
 
   // Pipeline blended image.
   {
     auto filter = flutter::DlBlendColorFilter(SK_ColorYELLOW,
                                               flutter::DlBlendMode::kModulate);
-    paint.setColorFilter(&filter);
-    builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
-                      flutter::DlImageSampling::kNearestNeighbor, &paint);
+    builder.setColorFilter(&filter);
+    builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
+                      flutter::DlImageSampling::kNearestNeighbor, true);
   }
 
   // Advanced blended image.
   {
     auto filter =
         flutter::DlBlendColorFilter(SK_ColorRED, flutter::DlBlendMode::kScreen);
-    paint.setColorFilter(&filter);
-    builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(250, 250),
-                      flutter::DlImageSampling::kNearestNeighbor, &paint);
+    builder.setColorFilter(&filter);
+    builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(250, 250),
+                      flutter::DlImageSampling::kNearestNeighbor, true);
   }
 
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
@@ -333,21 +326,18 @@
   };
   auto texture = CreateTextureForFixture("boston.jpg");
   flutter::DisplayListBuilder builder;
-  flutter::DlPaint paint;
-
   auto color_filter =
       std::make_shared<flutter::DlMatrixColorFilter>(invert_color_matrix);
   auto image_filter =
       std::make_shared<flutter::DlColorFilterImageFilter>(color_filter);
+  builder.setImageFilter(image_filter.get());
+  builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
+                    flutter::DlImageSampling::kNearestNeighbor, true);
 
-  paint.setImageFilter(image_filter.get());
-  builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
-                    flutter::DlImageSampling::kNearestNeighbor, &paint);
-
-  builder.Translate(0, 700);
-  paint.setColorFilter(color_filter.get());
-  builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
-                    flutter::DlImageSampling::kNearestNeighbor, &paint);
+  builder.translate(0, 700);
+  builder.setColorFilter(color_filter.get());
+  builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
+                    flutter::DlImageSampling::kNearestNeighbor, true);
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
 
@@ -362,13 +352,12 @@
     ImGui::End();
 
     flutter::DisplayListBuilder builder;
-    flutter::DlPaint paint;
 
     auto filter = flutter::DlBlurImageFilter(sigma[0], sigma[1],
                                              flutter::DlTileMode::kClamp);
-    paint.setImageFilter(&filter);
-    builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(200, 200),
-                      flutter::DlImageSampling::kNearestNeighbor, &paint);
+    builder.setImageFilter(&filter);
+    builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(200, 200),
+                      flutter::DlImageSampling::kNearestNeighbor, true);
 
     return builder.Build();
   };
@@ -379,20 +368,17 @@
 TEST_P(DisplayListTest, CanDrawWithComposeImageFilter) {
   auto texture = CreateTextureForFixture("boston.jpg");
   flutter::DisplayListBuilder builder;
-  flutter::DlPaint paint;
-
   auto dilate = std::make_shared<flutter::DlDilateImageFilter>(10.0, 10.0);
   auto erode = std::make_shared<flutter::DlErodeImageFilter>(10.0, 10.0);
   auto open = std::make_shared<flutter::DlComposeImageFilter>(dilate, erode);
   auto close = std::make_shared<flutter::DlComposeImageFilter>(erode, dilate);
-
-  paint.setImageFilter(open.get());
-  builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
-                    flutter::DlImageSampling::kNearestNeighbor, &paint);
-  builder.Translate(0, 700);
-  paint.setImageFilter(close.get());
-  builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
-                    flutter::DlImageSampling::kNearestNeighbor, &paint);
+  builder.setImageFilter(open.get());
+  builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
+                    flutter::DlImageSampling::kNearestNeighbor, true);
+  builder.translate(0, 700);
+  builder.setImageFilter(close.get());
+  builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
+                    flutter::DlImageSampling::kNearestNeighbor, true);
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
 
@@ -421,10 +407,9 @@
   auto compose = std::make_shared<flutter::DlComposeImageFilter>(outer, inner);
 
   flutter::DisplayListBuilder builder;
-  flutter::DlPaint paint;
-  paint.setImageFilter(compose.get());
-  builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
-                    flutter::DlImageSampling::kNearestNeighbor, &paint);
+  builder.setImageFilter(compose.get());
+  builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
+                    flutter::DlImageSampling::kNearestNeighbor, true);
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
 
@@ -558,7 +543,7 @@
     flutter::DisplayListBuilder builder;
 
     Vector2 scale = ctm_scale * GetContentScale();
-    builder.Scale(scale.x, scale.y);
+    builder.scale(scale.x, scale.y);
 
     auto filter = flutter::DlBlurImageFilter(sigma[0], sigma[1],
                                              flutter::DlTileMode::kClamp);
@@ -573,12 +558,12 @@
     // Insert a clip to test that the backdrop filter handles stencil depths > 0
     // correctly.
     if (add_clip) {
-      builder.ClipRect(SkRect::MakeLTRB(0, 0, 99999, 99999),
+      builder.clipRect(SkRect::MakeLTRB(0, 0, 99999, 99999),
                        flutter::DlCanvas::ClipOp::kIntersect, true);
     }
 
-    builder.DrawImage(DlImageImpeller::Make(texture), SkPoint::Make(200, 200),
-                      flutter::DlImageSampling::kNearestNeighbor, nullptr);
+    builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(200, 200),
+                      flutter::DlImageSampling::kNearestNeighbor, true);
     builder.SaveLayer(bounds.has_value() ? &bounds.value() : nullptr, nullptr,
                       &filter);
 
@@ -586,13 +571,12 @@
       auto circle_center =
           IMPELLER_PLAYGROUND_POINT(Point(500, 400), 20, Color::Red());
 
-      flutter::DlPaint paint;
-      paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
-      paint.setStrokeCap(flutter::DlStrokeCap::kButt);
-      paint.setStrokeJoin(flutter::DlStrokeJoin::kBevel);
-      paint.setStrokeWidth(10);
-      paint.setColor(flutter::DlColor::kRed().withAlpha(100));
-      builder.DrawCircle({circle_center.x, circle_center.y}, 100, paint);
+      builder.setStyle(flutter::DlDrawStyle::kStroke);
+      builder.setStrokeCap(flutter::DlStrokeCap::kButt);
+      builder.setStrokeJoin(flutter::DlStrokeJoin::kBevel);
+      builder.setStrokeWidth(10);
+      builder.setColor(flutter::DlColor::kRed().withAlpha(100));
+      builder.drawCircle({circle_center.x, circle_center.y}, 100);
     }
 
     return builder.Build();
@@ -606,12 +590,12 @@
   auto texture = CreateTextureForFixture("embarcadero.jpg");
   flutter::DisplayListBuilder builder;
   auto size = texture->GetSize();
-  builder.DrawImageNine(
+  builder.drawImageNine(
       DlImageImpeller::Make(texture),
       SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
                         size.height * 3 / 4),
       SkRect::MakeLTRB(0, 0, size.width * 2, size.height * 2),
-      flutter::DlFilterMode::kNearest, nullptr);
+      flutter::DlFilterMode::kNearest, true);
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
 
@@ -622,12 +606,12 @@
   auto texture = CreateTextureForFixture("embarcadero.jpg");
   flutter::DisplayListBuilder builder;
   auto size = texture->GetSize();
-  builder.DrawImageNine(
+  builder.drawImageNine(
       DlImageImpeller::Make(texture),
       SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
                         size.height * 3 / 4),
       SkRect::MakeLTRB(0, 0, size.width / 2, size.height),
-      flutter::DlFilterMode::kNearest, nullptr);
+      flutter::DlFilterMode::kNearest, true);
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
 
@@ -638,12 +622,12 @@
   auto texture = CreateTextureForFixture("embarcadero.jpg");
   flutter::DisplayListBuilder builder;
   auto size = texture->GetSize();
-  builder.DrawImageNine(
+  builder.drawImageNine(
       DlImageImpeller::Make(texture),
       SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
                         size.height * 3 / 4),
       SkRect::MakeLTRB(0, 0, size.width, size.height / 2),
-      flutter::DlFilterMode::kNearest, nullptr);
+      flutter::DlFilterMode::kNearest, true);
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
 
@@ -653,12 +637,12 @@
   auto texture = CreateTextureForFixture("embarcadero.jpg");
   flutter::DisplayListBuilder builder;
   auto size = texture->GetSize();
-  builder.DrawImageNine(
+  builder.drawImageNine(
       DlImageImpeller::Make(texture),
       SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
                         size.height * 3 / 4),
       SkRect::MakeLTRB(0, 0, size.width / 2, size.height / 2),
-      flutter::DlFilterMode::kNearest, nullptr);
+      flutter::DlFilterMode::kNearest, true);
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
 
@@ -668,12 +652,12 @@
   auto texture = CreateTextureForFixture("embarcadero.jpg");
   flutter::DisplayListBuilder builder;
   auto size = texture->GetSize();
-  builder.DrawImageNine(
+  builder.drawImageNine(
       DlImageImpeller::Make(texture),
       SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
                         size.height * 3 / 4),
       SkRect::MakeLTRB(0, 0, size.width / 4, size.height / 4),
-      flutter::DlFilterMode::kNearest, nullptr);
+      flutter::DlFilterMode::kNearest, true);
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
 
@@ -738,10 +722,9 @@
 
 TEST_P(DisplayListTest, CanDrawShadow) {
   flutter::DisplayListBuilder builder;
-  flutter::DlPaint paint;
 
   auto content_scale = GetContentScale() * 0.8;
-  builder.Scale(content_scale.x, content_scale.y);
+  builder.scale(content_scale.x, content_scale.y);
 
   constexpr size_t star_spikes = 5;
   constexpr SkScalar half_spike_rotation = kPi / star_spikes;
@@ -766,20 +749,20 @@
       SkPath{}.addCircle(100, 50, 50),
       SkPath{}.addPoly(star.data(), star.size(), true),
   };
-  paint.setColor(flutter::DlColor::kWhite());
-  builder.DrawPaint(paint);
-  paint.setColor(flutter::DlColor::kCyan());
-  builder.Translate(100, 50);
+  builder.setColor(flutter::DlColor::kWhite());
+  builder.drawPaint();
+  builder.setColor(flutter::DlColor::kCyan());
+  builder.translate(100, 50);
   for (size_t x = 0; x < paths.size(); x++) {
-    builder.Save();
+    builder.save();
     for (size_t y = 0; y < 6; y++) {
-      builder.DrawShadow(paths[x], flutter::DlColor::kBlack(), 3 + y * 8, false,
+      builder.drawShadow(paths[x], flutter::DlColor::kBlack(), 3 + y * 8, false,
                          1);
-      builder.DrawPath(paths[x], paint);
-      builder.Translate(0, 150);
+      builder.drawPath(paths[x]);
+      builder.translate(0, 150);
     }
-    builder.Restore();
-    builder.Translate(250, 0);
+    builder.restore();
+    builder.translate(250, 0);
   }
 
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
@@ -895,21 +878,20 @@
     ImGui::End();
 
     flutter::DisplayListBuilder builder;
-    flutter::DlPaint paint;
-
+    SkPaint paint;
     if (enable_savelayer) {
       builder.SaveLayer(nullptr, nullptr);
     }
     {
       auto content_scale = GetContentScale();
-      builder.Scale(content_scale.x, content_scale.y);
+      builder.scale(content_scale.x, content_scale.y);
 
       // Set the current transform
       auto ctm_matrix =
           SkMatrix::MakeAll(ctm_scale[0], ctm_skew[0], ctm_translation[0],  //
                             ctm_skew[1], ctm_scale[1], ctm_translation[1],  //
                             0, 0, 1);
-      builder.Transform(ctm_matrix);
+      builder.transform(ctm_matrix);
 
       // Set the matrix filter
       auto filter_matrix =
@@ -922,7 +904,7 @@
           case 0: {
             auto filter = flutter::DlMatrixImageFilter(
                 filter_matrix, flutter::DlImageSampling::kLinear);
-            paint.setImageFilter(&filter);
+            builder.setImageFilter(&filter);
             break;
           }
           case 1: {
@@ -931,17 +913,17 @@
                     .shared();
             auto filter = flutter::DlLocalMatrixImageFilter(filter_matrix,
                                                             internal_filter);
-            paint.setImageFilter(&filter);
+            builder.setImageFilter(&filter);
             break;
           }
         }
       }
 
-      builder.DrawImage(DlImageImpeller::Make(boston), {},
-                        flutter::DlImageSampling::kLinear, &paint);
+      builder.drawImage(DlImageImpeller::Make(boston), {},
+                        flutter::DlImageSampling::kLinear, true);
     }
     if (enable_savelayer) {
-      builder.Restore();
+      builder.restore();
     }
 
     return builder.Build();
diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc
index 03d8385..92f7043 100644
--- a/lib/ui/painting/canvas.cc
+++ b/lib/ui/painting/canvas.cc
@@ -47,7 +47,7 @@
 
 void Canvas::save() {
   if (display_list_builder_) {
-    builder()->Save();
+    builder()->save();
   }
 }
 
@@ -57,11 +57,11 @@
 
   FML_DCHECK(paint.isNotNull());
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    const DlPaint* save_paint = paint.paint(dl_paint, kSaveLayerWithPaintFlags);
-    FML_DCHECK(save_paint);
+    bool restore_with_paint =
+        paint.sync_to(builder(), kSaveLayerWithPaintFlags);
+    FML_DCHECK(restore_with_paint);
     TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)");
-    builder()->SaveLayer(nullptr, save_paint);
+    builder()->saveLayer(nullptr, restore_with_paint);
   }
 }
 
@@ -76,17 +76,17 @@
   FML_DCHECK(paint.isNotNull());
   SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    const DlPaint* save_paint = paint.paint(dl_paint, kSaveLayerWithPaintFlags);
-    FML_DCHECK(save_paint);
+    bool restore_with_paint =
+        paint.sync_to(builder(), kSaveLayerWithPaintFlags);
+    FML_DCHECK(restore_with_paint);
     TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)");
-    builder()->SaveLayer(&bounds, save_paint);
+    builder()->saveLayer(&bounds, restore_with_paint);
   }
 }
 
 void Canvas::restore() {
   if (display_list_builder_) {
-    builder()->Restore();
+    builder()->restore();
   }
 }
 
@@ -100,31 +100,31 @@
 
 void Canvas::restoreToCount(int count) {
   if (display_list_builder_ && count < getSaveCount()) {
-    builder()->RestoreToCount(count);
+    builder()->restoreToCount(count);
   }
 }
 
 void Canvas::translate(double dx, double dy) {
   if (display_list_builder_) {
-    builder()->Translate(dx, dy);
+    builder()->translate(dx, dy);
   }
 }
 
 void Canvas::scale(double sx, double sy) {
   if (display_list_builder_) {
-    builder()->Scale(sx, sy);
+    builder()->scale(sx, sy);
   }
 }
 
 void Canvas::rotate(double radians) {
   if (display_list_builder_) {
-    builder()->Rotate(radians * 180.0 / M_PI);
+    builder()->rotate(radians * 180.0 / M_PI);
   }
 }
 
 void Canvas::skew(double sx, double sy) {
   if (display_list_builder_) {
-    builder()->Skew(sx, sy);
+    builder()->skew(sx, sy);
   }
 }
 
@@ -133,7 +133,7 @@
   // Both DisplayList and SkM44 constructor take row-major matrix order
   if (display_list_builder_) {
     // clang-format off
-    builder()->TransformFullPerspective(
+    builder()->transformFullPerspective(
         matrix4[ 0], matrix4[ 4], matrix4[ 8], matrix4[12],
         matrix4[ 1], matrix4[ 5], matrix4[ 9], matrix4[13],
         matrix4[ 2], matrix4[ 6], matrix4[10], matrix4[14],
@@ -162,14 +162,14 @@
                       DlCanvas::ClipOp clipOp,
                       bool doAntiAlias) {
   if (display_list_builder_) {
-    builder()->ClipRect(SkRect::MakeLTRB(left, top, right, bottom), clipOp,
+    builder()->clipRect(SkRect::MakeLTRB(left, top, right, bottom), clipOp,
                         doAntiAlias);
   }
 }
 
 void Canvas::clipRRect(const RRect& rrect, bool doAntiAlias) {
   if (display_list_builder_) {
-    builder()->ClipRRect(rrect.sk_rrect, DlCanvas::ClipOp::kIntersect,
+    builder()->clipRRect(rrect.sk_rrect, DlCanvas::ClipOp::kIntersect,
                          doAntiAlias);
   }
 }
@@ -181,7 +181,7 @@
     return;
   }
   if (display_list_builder_) {
-    builder()->ClipPath(path->path(), DlCanvas::ClipOp::kIntersect,
+    builder()->clipPath(path->path(), DlCanvas::ClipOp::kIntersect,
                         doAntiAlias);
   }
 }
@@ -210,7 +210,7 @@
 
 void Canvas::drawColor(SkColor color, DlBlendMode blend_mode) {
   if (display_list_builder_) {
-    builder()->DrawColor(color, blend_mode);
+    builder()->drawColor(color, blend_mode);
   }
 }
 
@@ -224,9 +224,8 @@
 
   FML_DCHECK(paint.isNotNull());
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    paint.paint(dl_paint, kDrawLineFlags);
-    builder()->DrawLine(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2), dl_paint);
+    paint.sync_to(builder(), kDrawLineFlags);
+    builder()->drawLine(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2));
   }
 }
 
@@ -235,15 +234,14 @@
 
   FML_DCHECK(paint.isNotNull());
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    paint.paint(dl_paint, kDrawPaintFlags);
-    std::shared_ptr<const DlImageFilter> filter = dl_paint.getImageFilter();
+    paint.sync_to(builder(), kDrawPaintFlags);
+    std::shared_ptr<const DlImageFilter> filter = builder()->getImageFilter();
     if (filter && !filter->asColorFilter()) {
       // drawPaint does an implicit saveLayer if an SkImageFilter is
       // present that cannot be replaced by an SkColorFilter.
       TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)");
     }
-    builder()->DrawPaint(dl_paint);
+    builder()->drawPaint();
   }
 }
 
@@ -257,9 +255,8 @@
 
   FML_DCHECK(paint.isNotNull());
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    paint.paint(dl_paint, kDrawRectFlags);
-    builder()->DrawRect(SkRect::MakeLTRB(left, top, right, bottom), dl_paint);
+    paint.sync_to(builder(), kDrawRectFlags);
+    builder()->drawRect(SkRect::MakeLTRB(left, top, right, bottom));
   }
 }
 
@@ -270,9 +267,8 @@
 
   FML_DCHECK(paint.isNotNull());
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    paint.paint(dl_paint, kDrawRRectFlags);
-    builder()->DrawRRect(rrect.sk_rrect, dl_paint);
+    paint.sync_to(builder(), kDrawRRectFlags);
+    builder()->drawRRect(rrect.sk_rrect);
   }
 }
 
@@ -284,9 +280,8 @@
 
   FML_DCHECK(paint.isNotNull());
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    paint.paint(dl_paint, kDrawDRRectFlags);
-    builder()->DrawDRRect(outer.sk_rrect, inner.sk_rrect, dl_paint);
+    paint.sync_to(builder(), kDrawDRRectFlags);
+    builder()->drawDRRect(outer.sk_rrect, inner.sk_rrect);
   }
 }
 
@@ -300,9 +295,8 @@
 
   FML_DCHECK(paint.isNotNull());
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    paint.paint(dl_paint, kDrawOvalFlags);
-    builder()->DrawOval(SkRect::MakeLTRB(left, top, right, bottom), dl_paint);
+    paint.sync_to(builder(), kDrawOvalFlags);
+    builder()->drawOval(SkRect::MakeLTRB(left, top, right, bottom));
   }
 }
 
@@ -315,9 +309,8 @@
 
   FML_DCHECK(paint.isNotNull());
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    paint.paint(dl_paint, kDrawCircleFlags);
-    builder()->DrawCircle(SkPoint::Make(x, y), radius, dl_paint);
+    paint.sync_to(builder(), kDrawCircleFlags);
+    builder()->drawCircle(SkPoint::Make(x, y), radius);
   }
 }
 
@@ -334,13 +327,13 @@
 
   FML_DCHECK(paint.isNotNull());
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    paint.paint(dl_paint, useCenter  //
-                              ? kDrawArcWithCenterFlags
-                              : kDrawArcNoCenterFlags);
-    builder()->DrawArc(SkRect::MakeLTRB(left, top, right, bottom),
+    paint.sync_to(builder(),
+                  useCenter  //
+                      ? kDrawArcWithCenterFlags
+                      : kDrawArcNoCenterFlags);
+    builder()->drawArc(SkRect::MakeLTRB(left, top, right, bottom),
                        startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI,
-                       useCenter, dl_paint);
+                       useCenter);
   }
 }
 
@@ -356,9 +349,8 @@
     return;
   }
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    paint.paint(dl_paint, kDrawPathFlags);
-    builder()->DrawPath(path->path(), dl_paint);
+    paint.sync_to(builder(), kDrawPathFlags);
+    builder()->drawPath(path->path());
   }
 }
 
@@ -386,9 +378,9 @@
 
   auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex);
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    const DlPaint* opt_paint = paint.paint(dl_paint, kDrawImageWithPaintFlags);
-    builder()->DrawImage(dl_image, SkPoint::Make(x, y), sampling, opt_paint);
+    bool with_attributes = paint.sync_to(builder(), kDrawImageWithPaintFlags);
+    builder()->drawImage(dl_image, SkPoint::Make(x, y), sampling,
+                         with_attributes);
   }
   return Dart_Null();
 }
@@ -425,11 +417,10 @@
   SkRect dst = SkRect::MakeLTRB(dst_left, dst_top, dst_right, dst_bottom);
   auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex);
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    const DlPaint* opt_paint =
-        paint.paint(dl_paint, kDrawImageRectWithPaintFlags);
-    builder()->DrawImageRect(dl_image, src, dst, sampling, opt_paint,
-                             DlCanvas::SrcRectConstraint::kFast);
+    bool with_attributes =
+        paint.sync_to(builder(), kDrawImageRectWithPaintFlags);
+    builder()->drawImageRect(dl_image, src, dst, sampling, with_attributes,
+                             SkCanvas::kFast_SrcRectConstraint);
   }
   return Dart_Null();
 }
@@ -468,10 +459,9 @@
   SkRect dst = SkRect::MakeLTRB(dst_left, dst_top, dst_right, dst_bottom);
   auto filter = ImageFilter::FilterModeFromIndex(bitmapSamplingIndex);
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    const DlPaint* opt_paint =
-        paint.paint(dl_paint, kDrawImageNineWithPaintFlags);
-    builder()->DrawImageNine(dl_image, icenter, dst, filter, opt_paint);
+    bool with_attributes =
+        paint.sync_to(builder(), kDrawImageNineWithPaintFlags);
+    builder()->drawImageNine(dl_image, icenter, dst, filter, with_attributes);
   }
   return Dart_Null();
 }
@@ -484,7 +474,7 @@
   }
   if (picture->display_list()) {
     if (display_list_builder_) {
-      builder()->DrawDisplayList(picture->display_list());
+      builder()->drawDisplayList(picture->display_list());
     }
   } else {
     FML_DCHECK(false);
@@ -502,22 +492,20 @@
 
   FML_DCHECK(paint.isNotNull());
   if (display_list_builder_) {
-    DlPaint dl_paint;
     switch (point_mode) {
       case DlCanvas::PointMode::kPoints:
-        paint.paint(dl_paint, kDrawPointsAsPointsFlags);
+        paint.sync_to(builder(), kDrawPointsAsPointsFlags);
         break;
       case DlCanvas::PointMode::kLines:
-        paint.paint(dl_paint, kDrawPointsAsLinesFlags);
+        paint.sync_to(builder(), kDrawPointsAsLinesFlags);
         break;
       case DlCanvas::PointMode::kPolygon:
-        paint.paint(dl_paint, kDrawPointsAsPolygonFlags);
+        paint.sync_to(builder(), kDrawPointsAsPolygonFlags);
         break;
     }
-    builder()->DrawPoints(point_mode,
+    builder()->drawPoints(point_mode,
                           points.num_elements() / 2,  // SkPoints have 2 floats
-                          reinterpret_cast<const SkPoint*>(points.data()),
-                          dl_paint);
+                          reinterpret_cast<const SkPoint*>(points.data()));
   }
 }
 
@@ -534,9 +522,8 @@
   }
   FML_DCHECK(paint.isNotNull());
   if (display_list_builder_) {
-    DlPaint dl_paint;
-    paint.paint(dl_paint, kDrawVerticesFlags);
-    builder()->DrawVertices(vertices->vertices(), blend_mode, dl_paint);
+    paint.sync_to(builder(), kDrawVerticesFlags);
+    builder()->drawVertices(vertices->vertices(), blend_mode);
   }
 }
 
@@ -577,15 +564,14 @@
     tonic::Int32List colors(colors_handle);
     tonic::Float32List cull_rect(cull_rect_handle);
 
-    DlPaint dl_paint;
-    const DlPaint* opt_paint = paint.paint(dl_paint, kDrawAtlasWithPaintFlags);
-    builder()->DrawAtlas(
+    bool with_attributes = paint.sync_to(builder(), kDrawAtlasWithPaintFlags);
+    builder()->drawAtlas(
         dl_image, reinterpret_cast<const SkRSXform*>(transforms.data()),
         reinterpret_cast<const SkRect*>(rects.data()),
         reinterpret_cast<const DlColor*>(colors.data()),
         rects.num_elements() / 4,  // SkRect have four floats.
         blend_mode, sampling, reinterpret_cast<const SkRect*>(cull_rect.data()),
-        opt_paint);
+        with_attributes);
   }
   return Dart_Null();
 }
@@ -612,7 +598,7 @@
     // that situation we bypass the canvas interface and inject the
     // shadow parameters directly into the underlying DisplayList.
     // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125
-    builder()->DrawShadow(path->path(), color, elevation, transparentOccluder,
+    builder()->drawShadow(path->path(), color, elevation, transparentOccluder,
                           dpr);
   }
 }
diff --git a/lib/ui/painting/color_filter.cc b/lib/ui/painting/color_filter.cc
index 06d421b..12b0db9 100644
--- a/lib/ui/painting/color_filter.cc
+++ b/lib/ui/painting/color_filter.cc
@@ -23,8 +23,8 @@
 }
 
 void ColorFilter::initMode(int color, int blend_mode) {
-  filter_ = DlBlendColorFilter::Make(static_cast<DlColor>(color),
-                                     static_cast<DlBlendMode>(blend_mode));
+  filter_ = std::make_shared<DlBlendColorFilter>(
+      static_cast<DlColor>(color), static_cast<DlBlendMode>(blend_mode));
 }
 
 void ColorFilter::initMatrix(const tonic::Float32List& color_matrix) {
@@ -39,7 +39,7 @@
   matrix[9] *= 1.0f / 255;
   matrix[14] *= 1.0f / 255;
   matrix[19] *= 1.0f / 255;
-  filter_ = DlMatrixColorFilter::Make(matrix);
+  filter_ = std::make_shared<DlMatrixColorFilter>(matrix);
 }
 
 void ColorFilter::initLinearToSrgbGamma() {
diff --git a/lib/ui/painting/color_filter.h b/lib/ui/painting/color_filter.h
index b7aa2e2..f80c48d 100644
--- a/lib/ui/painting/color_filter.h
+++ b/lib/ui/painting/color_filter.h
@@ -31,6 +31,9 @@
   ~ColorFilter() override;
 
   const std::shared_ptr<const DlColorFilter> filter() const { return filter_; }
+  const DlColorFilter* dl_filter() const {
+    return (filter_ && filter_->skia_object()) ? filter_.get() : nullptr;
+  }
 
  private:
   std::shared_ptr<const DlColorFilter> filter_;
diff --git a/lib/ui/painting/gradient.cc b/lib/ui/painting/gradient.cc
index 5f586a3..9651982 100644
--- a/lib/ui/painting/gradient.cc
+++ b/lib/ui/painting/gradient.cc
@@ -25,7 +25,7 @@
 void CanvasGradient::initLinear(const tonic::Float32List& end_points,
                                 const tonic::Int32List& colors,
                                 const tonic::Float32List& color_stops,
-                                DlTileMode tile_mode,
+                                SkTileMode tile_mode,
                                 const tonic::Float64List& matrix4) {
   FML_DCHECK(end_points.num_elements() == 4);
   FML_DCHECK(colors.num_elements() == color_stops.num_elements() ||
@@ -48,7 +48,7 @@
 
   dl_shader_ = DlColorSource::MakeLinear(
       p0, p1, colors.num_elements(), colors_array, color_stops.data(),
-      tile_mode, has_matrix ? &sk_matrix : nullptr);
+      ToDl(tile_mode), has_matrix ? &sk_matrix : nullptr);
 }
 
 void CanvasGradient::initRadial(double center_x,
@@ -56,7 +56,7 @@
                                 double radius,
                                 const tonic::Int32List& colors,
                                 const tonic::Float32List& color_stops,
-                                DlTileMode tile_mode,
+                                SkTileMode tile_mode,
                                 const tonic::Float64List& matrix4) {
   FML_DCHECK(colors.num_elements() == color_stops.num_elements() ||
              color_stops.data() == nullptr);
@@ -74,7 +74,7 @@
 
   dl_shader_ = DlColorSource::MakeRadial(
       SkPoint::Make(center_x, center_y), radius, colors.num_elements(),
-      colors_array, color_stops.data(), tile_mode,
+      colors_array, color_stops.data(), ToDl(tile_mode),
       has_matrix ? &sk_matrix : nullptr);
 }
 
@@ -82,7 +82,7 @@
                                double center_y,
                                const tonic::Int32List& colors,
                                const tonic::Float32List& color_stops,
-                               DlTileMode tile_mode,
+                               SkTileMode tile_mode,
                                double start_angle,
                                double end_angle,
                                const tonic::Float64List& matrix4) {
@@ -103,7 +103,7 @@
   dl_shader_ = DlColorSource::MakeSweep(
       SkPoint::Make(center_x, center_y), start_angle * 180.0 / M_PI,
       end_angle * 180.0 / M_PI, colors.num_elements(), colors_array,
-      color_stops.data(), tile_mode, has_matrix ? &sk_matrix : nullptr);
+      color_stops.data(), ToDl(tile_mode), has_matrix ? &sk_matrix : nullptr);
 }
 
 void CanvasGradient::initTwoPointConical(double start_x,
@@ -114,7 +114,7 @@
                                          double end_radius,
                                          const tonic::Int32List& colors,
                                          const tonic::Float32List& color_stops,
-                                         DlTileMode tile_mode,
+                                         SkTileMode tile_mode,
                                          const tonic::Float64List& matrix4) {
   FML_DCHECK(colors.num_elements() == color_stops.num_elements() ||
              color_stops.data() == nullptr);
@@ -134,7 +134,7 @@
       SkPoint::Make(start_x, start_y), start_radius,            //
       SkPoint::Make(end_x, end_y), end_radius,                  //
       colors.num_elements(), colors_array, color_stops.data(),  //
-      tile_mode, has_matrix ? &sk_matrix : nullptr);
+      ToDl(tile_mode), has_matrix ? &sk_matrix : nullptr);
 }
 
 CanvasGradient::CanvasGradient() = default;
diff --git a/lib/ui/painting/gradient.h b/lib/ui/painting/gradient.h
index 9222fb8..5041ab2 100644
--- a/lib/ui/painting/gradient.h
+++ b/lib/ui/painting/gradient.h
@@ -12,6 +12,9 @@
 
 namespace flutter {
 
+// TODO: update this if/when Skia adds Decal mode skbug.com/7638
+static_assert(kSkTileModeCount >= 3, "Need to update tile mode enum");
+
 class CanvasGradient : public Shader {
   DEFINE_WRAPPERTYPEINFO();
   FML_FRIEND_MAKE_REF_COUNTED(CanvasGradient);
@@ -23,7 +26,7 @@
   void initLinear(const tonic::Float32List& end_points,
                   const tonic::Int32List& colors,
                   const tonic::Float32List& color_stops,
-                  DlTileMode tile_mode,
+                  SkTileMode tile_mode,
                   const tonic::Float64List& matrix4);
 
   void initRadial(double center_x,
@@ -31,14 +34,14 @@
                   double radius,
                   const tonic::Int32List& colors,
                   const tonic::Float32List& color_stops,
-                  DlTileMode tile_mode,
+                  SkTileMode tile_mode,
                   const tonic::Float64List& matrix4);
 
   void initSweep(double center_x,
                  double center_y,
                  const tonic::Int32List& colors,
                  const tonic::Float32List& color_stops,
-                 DlTileMode tile_mode,
+                 SkTileMode tile_mode,
                  double start_angle,
                  double end_angle,
                  const tonic::Float64List& matrix4);
@@ -51,7 +54,7 @@
                            double end_radius,
                            const tonic::Int32List& colors,
                            const tonic::Float32List& color_stops,
-                           DlTileMode tile_mode,
+                           SkTileMode tile_mode,
                            const tonic::Float64List& matrix4);
 
   std::shared_ptr<DlColorSource> shader(DlImageSampling sampling) override {
diff --git a/lib/ui/painting/image_filter.cc b/lib/ui/painting/image_filter.cc
index d92db00..665d3b0 100644
--- a/lib/ui/painting/image_filter.cc
+++ b/lib/ui/painting/image_filter.cc
@@ -52,32 +52,45 @@
 
 void ImageFilter::initBlur(double sigma_x,
                            double sigma_y,
-                           DlTileMode tile_mode) {
-  filter_ = DlBlurImageFilter::Make(sigma_x, sigma_y, tile_mode);
+                           SkTileMode tile_mode) {
+  filter_ =
+      std::make_shared<DlBlurImageFilter>(sigma_x, sigma_y, ToDl(tile_mode));
 }
 
 void ImageFilter::initDilate(double radius_x, double radius_y) {
-  filter_ = DlDilateImageFilter::Make(radius_x, radius_y);
+  filter_ = std::make_shared<DlDilateImageFilter>(radius_x, radius_y);
 }
 
 void ImageFilter::initErode(double radius_x, double radius_y) {
-  filter_ = DlErodeImageFilter::Make(radius_x, radius_y);
+  filter_ = std::make_shared<DlErodeImageFilter>(radius_x, radius_y);
 }
 
 void ImageFilter::initMatrix(const tonic::Float64List& matrix4,
                              int filterQualityIndex) {
   auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex);
-  filter_ = DlMatrixImageFilter::Make(ToSkMatrix(matrix4), sampling);
+  filter_ =
+      std::make_shared<DlMatrixImageFilter>(ToSkMatrix(matrix4), sampling);
 }
 
 void ImageFilter::initColorFilter(ColorFilter* colorFilter) {
   FML_DCHECK(colorFilter);
-  filter_ = DlColorFilterImageFilter::Make(colorFilter->filter());
+  auto dl_filter = colorFilter->dl_filter();
+  // Skia may return nullptr if the colorfilter is a no-op.
+  if (dl_filter) {
+    filter_ = std::make_shared<DlColorFilterImageFilter>(dl_filter);
+  }
 }
 
 void ImageFilter::initComposeFilter(ImageFilter* outer, ImageFilter* inner) {
   FML_DCHECK(outer && inner);
-  filter_ = DlComposeImageFilter::Make(outer->filter(), inner->filter());
+  if (!outer->dl_filter()) {
+    filter_ = inner->filter();
+  } else if (!inner->dl_filter()) {
+    filter_ = outer->filter();
+  } else {
+    filter_ = std::make_shared<DlComposeImageFilter>(outer->dl_filter(),
+                                                     inner->dl_filter());
+  }
 }
 
 }  // namespace flutter
diff --git a/lib/ui/painting/image_filter.h b/lib/ui/painting/image_filter.h
index 66e3012..67d9738 100644
--- a/lib/ui/painting/image_filter.h
+++ b/lib/ui/painting/image_filter.h
@@ -30,7 +30,7 @@
   static DlImageSampling SamplingFromIndex(int filterQualityIndex);
   static DlFilterMode FilterModeFromIndex(int index);
 
-  void initBlur(double sigma_x, double sigma_y, DlTileMode tile_mode);
+  void initBlur(double sigma_x, double sigma_y, SkTileMode tile_mode);
   void initDilate(double radius_x, double radius_y);
   void initErode(double radius_x, double radius_y);
   void initMatrix(const tonic::Float64List& matrix4, int filter_quality_index);
@@ -38,6 +38,9 @@
   void initComposeFilter(ImageFilter* outer, ImageFilter* inner);
 
   const std::shared_ptr<const DlImageFilter> filter() const { return filter_; }
+  const DlImageFilter* dl_filter() const {
+    return (filter_ && filter_->skia_object()) ? filter_.get() : nullptr;
+  }
 
   static void RegisterNatives(tonic::DartLibraryNatives* natives);
 
diff --git a/lib/ui/painting/image_shader.cc b/lib/ui/painting/image_shader.cc
index e2cdae7..bd00fd1 100644
--- a/lib/ui/painting/image_shader.cc
+++ b/lib/ui/painting/image_shader.cc
@@ -24,8 +24,8 @@
 }
 
 Dart_Handle ImageShader::initWithImage(CanvasImage* image,
-                                       DlTileMode tmx,
-                                       DlTileMode tmy,
+                                       SkTileMode tmx,
+                                       SkTileMode tmy,
                                        int filter_quality_index,
                                        Dart_Handle matrix_handle) {
   if (!image) {
@@ -41,7 +41,7 @@
       sampling_is_locked_ ? ImageFilter::SamplingFromIndex(filter_quality_index)
                           : DlImageSampling::kLinear;
   cached_shader_ = UIDartState::CreateGPUObject(sk_make_sp<DlImageColorSource>(
-      image_, tmx, tmy, sampling, &local_matrix));
+      image_, ToDl(tmx), ToDl(tmy), sampling, &local_matrix));
   return Dart_Null();
 }
 
diff --git a/lib/ui/painting/image_shader.h b/lib/ui/painting/image_shader.h
index 7c69d13..d130c5d 100644
--- a/lib/ui/painting/image_shader.h
+++ b/lib/ui/painting/image_shader.h
@@ -25,8 +25,8 @@
   static void Create(Dart_Handle wrapper);
 
   Dart_Handle initWithImage(CanvasImage* image,
-                            DlTileMode tmx,
-                            DlTileMode tmy,
+                            SkTileMode tmx,
+                            SkTileMode tmy,
                             int filter_quality_index,
                             Dart_Handle matrix_handle);
 
diff --git a/lib/ui/painting/paint.cc b/lib/ui/painting/paint.cc
index f4beac1..2849526 100644
--- a/lib/ui/painting/paint.cc
+++ b/lib/ui/painting/paint.cc
@@ -53,17 +53,28 @@
 // default SkPaintDefaults_MiterLimit in Skia (which is not in a public header).
 constexpr double kStrokeMiterLimitDefault = 4.0;
 
+// A color matrix which inverts colors.
+// clang-format off
+constexpr float kInvertColors[20] = {
+  -1.0,    0,    0, 1.0, 0,
+     0, -1.0,    0, 1.0, 0,
+     0,    0, -1.0, 1.0, 0,
+   1.0,  1.0,  1.0, 1.0, 0
+};
+// clang-format on
+
 // Must be kept in sync with the MaskFilter private constants in painting.dart.
 enum MaskFilterType { kNull, kBlur };
 
 Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data)
     : paint_objects_(paint_objects), paint_data_(paint_data) {}
 
-const DlPaint* Paint::paint(DlPaint& paint,
-                            const DisplayListAttributeFlags& flags) const {
+const SkPaint* Paint::paint(SkPaint& paint) const {
   if (isNull()) {
     return nullptr;
   }
+  FML_DCHECK(paint == SkPaint());
+
   tonic::DartByteData byte_data(paint_data_);
   FML_CHECK(byte_data.length_in_bytes() == kDataByteCount);
 
@@ -71,17 +82,7 @@
   const float* float_data = static_cast<const float*>(byte_data.data());
 
   Dart_Handle values[kObjectCount];
-  if (Dart_IsNull(paint_objects_)) {
-    if (flags.applies_shader()) {
-      paint.setColorSource(nullptr);
-    }
-    if (flags.applies_color_filter()) {
-      paint.setColorFilter(nullptr);
-    }
-    if (flags.applies_image_filter()) {
-      paint.setImageFilter(nullptr);
-    }
-  } else {
+  if (!Dart_IsNull(paint_objects_)) {
     FML_DCHECK(Dart_IsList(paint_objects_));
     intptr_t length = 0;
     Dart_ListLength(paint_objects_, &length);
@@ -92,17 +93,147 @@
       return nullptr;
     }
 
+    Dart_Handle shader = values[kShaderIndex];
+    if (!Dart_IsNull(shader)) {
+      if (Shader* decoded = tonic::DartConverter<Shader*>::FromDart(shader)) {
+        auto sampling =
+            ImageFilter::SamplingFromIndex(uint_data[kFilterQualityIndex]);
+        auto color_source = decoded->shader(sampling);
+        // TODO(dnfield): Remove this restriction.
+        // This currently is only used by paragraph code. Once SkParagraph does
+        // not need to take an SkPaint, we won't be restricted in this way
+        // because we will not need to access the shader on the UI task runner.
+        if (color_source->owning_context() != DlImage::OwningContext::kRaster) {
+          paint.setShader(color_source->skia_object());
+        }
+      }
+    }
+
+    Dart_Handle color_filter = values[kColorFilterIndex];
+    if (!Dart_IsNull(color_filter)) {
+      ColorFilter* decoded =
+          tonic::DartConverter<ColorFilter*>::FromDart(color_filter);
+      paint.setColorFilter(decoded->filter()->skia_object());
+    }
+
+    Dart_Handle image_filter = values[kImageFilterIndex];
+    if (!Dart_IsNull(image_filter)) {
+      ImageFilter* decoded =
+          tonic::DartConverter<ImageFilter*>::FromDart(image_filter);
+      paint.setImageFilter(decoded->filter()->skia_object());
+    }
+  }
+
+  paint.setAntiAlias(uint_data[kIsAntiAliasIndex] == 0);
+
+  uint32_t encoded_color = uint_data[kColorIndex];
+  if (encoded_color) {
+    SkColor color = encoded_color ^ kColorDefault;
+    paint.setColor(color);
+  }
+
+  uint32_t encoded_blend_mode = uint_data[kBlendModeIndex];
+  if (encoded_blend_mode) {
+    uint32_t blend_mode = encoded_blend_mode ^ kBlendModeDefault;
+    paint.setBlendMode(static_cast<SkBlendMode>(blend_mode));
+  }
+
+  uint32_t style = uint_data[kStyleIndex];
+  if (style) {
+    paint.setStyle(static_cast<SkPaint::Style>(style));
+  }
+
+  float stroke_width = float_data[kStrokeWidthIndex];
+  if (stroke_width != 0.0) {
+    paint.setStrokeWidth(stroke_width);
+  }
+
+  uint32_t stroke_cap = uint_data[kStrokeCapIndex];
+  if (stroke_cap) {
+    paint.setStrokeCap(static_cast<SkPaint::Cap>(stroke_cap));
+  }
+
+  uint32_t stroke_join = uint_data[kStrokeJoinIndex];
+  if (stroke_join) {
+    paint.setStrokeJoin(static_cast<SkPaint::Join>(stroke_join));
+  }
+
+  float stroke_miter_limit = float_data[kStrokeMiterLimitIndex];
+  if (stroke_miter_limit != 0.0) {
+    paint.setStrokeMiter(stroke_miter_limit + kStrokeMiterLimitDefault);
+  }
+
+  if (uint_data[kInvertColorIndex]) {
+    sk_sp<SkColorFilter> invert_filter = SkColorFilters::Matrix(kInvertColors);
+    sk_sp<SkColorFilter> current_filter = paint.refColorFilter();
+    if (current_filter) {
+      invert_filter = invert_filter->makeComposed(current_filter);
+    }
+    paint.setColorFilter(invert_filter);
+  }
+
+  if (uint_data[kDitherIndex]) {
+    paint.setDither(true);
+  }
+
+  switch (uint_data[kMaskFilterIndex]) {
+    case kNull:
+      break;
+    case kBlur:
+      SkBlurStyle blur_style =
+          static_cast<SkBlurStyle>(uint_data[kMaskFilterBlurStyleIndex]);
+      double sigma = float_data[kMaskFilterSigmaIndex];
+      paint.setMaskFilter(SkMaskFilter::MakeBlur(blur_style, sigma));
+      break;
+  }
+
+  return &paint;
+}
+
+bool Paint::sync_to(DisplayListBuilder* builder,
+                    const DisplayListAttributeFlags& flags) const {
+  if (isNull()) {
+    return false;
+  }
+  tonic::DartByteData byte_data(paint_data_);
+  FML_CHECK(byte_data.length_in_bytes() == kDataByteCount);
+
+  const uint32_t* uint_data = static_cast<const uint32_t*>(byte_data.data());
+  const float* float_data = static_cast<const float*>(byte_data.data());
+
+  Dart_Handle values[kObjectCount];
+  if (Dart_IsNull(paint_objects_)) {
+    if (flags.applies_shader()) {
+      builder->setColorSource(nullptr);
+    }
+    if (flags.applies_color_filter()) {
+      builder->setColorFilter(nullptr);
+    }
+    if (flags.applies_image_filter()) {
+      builder->setImageFilter(nullptr);
+    }
+  } else {
+    FML_DCHECK(Dart_IsList(paint_objects_));
+    intptr_t length = 0;
+    Dart_ListLength(paint_objects_, &length);
+
+    FML_CHECK(length == kObjectCount);
+    if (Dart_IsError(
+            Dart_ListGetRange(paint_objects_, 0, kObjectCount, values))) {
+      return false;
+    }
+
     if (flags.applies_shader()) {
       Dart_Handle shader = values[kShaderIndex];
       if (Dart_IsNull(shader)) {
-        paint.setColorSource(nullptr);
+        builder->setColorSource(nullptr);
       } else {
         if (Shader* decoded = tonic::DartConverter<Shader*>::FromDart(shader)) {
           auto sampling =
               ImageFilter::SamplingFromIndex(uint_data[kFilterQualityIndex]);
-          paint.setColorSource(decoded->shader(sampling).get());
+          builder->setColorSource(decoded->shader(sampling).get());
         } else {
-          paint.setColorSource(nullptr);
+          builder->setColorSource(nullptr);
         }
       }
     }
@@ -110,89 +241,94 @@
     if (flags.applies_color_filter()) {
       Dart_Handle color_filter = values[kColorFilterIndex];
       if (Dart_IsNull(color_filter)) {
-        paint.setColorFilter(nullptr);
+        builder->setColorFilter(nullptr);
       } else {
         ColorFilter* decoded =
             tonic::DartConverter<ColorFilter*>::FromDart(color_filter);
-        paint.setColorFilter(decoded->filter());
+        builder->setColorFilter(decoded->dl_filter());
       }
     }
 
     if (flags.applies_image_filter()) {
       Dart_Handle image_filter = values[kImageFilterIndex];
       if (Dart_IsNull(image_filter)) {
-        paint.setImageFilter(nullptr);
+        builder->setImageFilter(nullptr);
       } else {
         ImageFilter* decoded =
             tonic::DartConverter<ImageFilter*>::FromDart(image_filter);
-        paint.setImageFilter(decoded->filter());
+        builder->setImageFilter(decoded->dl_filter());
       }
     }
   }
 
   if (flags.applies_anti_alias()) {
-    paint.setAntiAlias(uint_data[kIsAntiAliasIndex] == 0);
+    builder->setAntiAlias(uint_data[kIsAntiAliasIndex] == 0);
   }
 
   if (flags.applies_alpha_or_color()) {
     uint32_t encoded_color = uint_data[kColorIndex];
-    paint.setColor(encoded_color ^ kColorDefault);
+    builder->setColor(encoded_color ^ kColorDefault);
   }
 
   if (flags.applies_blend()) {
     uint32_t encoded_blend_mode = uint_data[kBlendModeIndex];
     uint32_t blend_mode = encoded_blend_mode ^ kBlendModeDefault;
-    paint.setBlendMode(static_cast<DlBlendMode>(blend_mode));
+    builder->setBlendMode(static_cast<DlBlendMode>(blend_mode));
   }
 
   if (flags.applies_style()) {
     uint32_t style = uint_data[kStyleIndex];
-    paint.setDrawStyle(static_cast<DlDrawStyle>(style));
+    builder->setStyle(static_cast<DlDrawStyle>(style));
   }
 
-  if (flags.is_stroked(paint.getDrawStyle())) {
+  if (flags.is_stroked(builder->getStyle())) {
     float stroke_width = float_data[kStrokeWidthIndex];
-    paint.setStrokeWidth(stroke_width);
+    builder->setStrokeWidth(stroke_width);
 
     float stroke_miter_limit = float_data[kStrokeMiterLimitIndex];
-    paint.setStrokeMiter(stroke_miter_limit + kStrokeMiterLimitDefault);
+    builder->setStrokeMiter(stroke_miter_limit + kStrokeMiterLimitDefault);
 
     uint32_t stroke_cap = uint_data[kStrokeCapIndex];
-    paint.setStrokeCap(static_cast<DlStrokeCap>(stroke_cap));
+    builder->setStrokeCap(static_cast<DlStrokeCap>(stroke_cap));
 
     uint32_t stroke_join = uint_data[kStrokeJoinIndex];
-    paint.setStrokeJoin(static_cast<DlStrokeJoin>(stroke_join));
+    builder->setStrokeJoin(static_cast<DlStrokeJoin>(stroke_join));
   }
 
   if (flags.applies_color_filter()) {
-    paint.setInvertColors(uint_data[kInvertColorIndex] != 0);
+    builder->setInvertColors(uint_data[kInvertColorIndex] != 0);
   }
 
   if (flags.applies_dither()) {
-    paint.setDither(uint_data[kDitherIndex] != 0);
+    builder->setDither(uint_data[kDitherIndex] != 0);
   }
 
   if (flags.applies_path_effect()) {
     // The paint API exposed to Dart does not support path effects.  But other
     // operations such as text may set a path effect, which must be cleared.
-    paint.setPathEffect(nullptr);
+    builder->setPathEffect(nullptr);
   }
 
   if (flags.applies_mask_filter()) {
     switch (uint_data[kMaskFilterIndex]) {
       case kNull:
-        paint.setMaskFilter(nullptr);
+        builder->setMaskFilter(nullptr);
         break;
       case kBlur:
         SkBlurStyle blur_style =
             static_cast<SkBlurStyle>(uint_data[kMaskFilterBlurStyleIndex]);
         double sigma = float_data[kMaskFilterSigmaIndex];
-        paint.setMaskFilter(DlBlurMaskFilter::Make(blur_style, sigma));
+        DlBlurMaskFilter dl_filter(blur_style, sigma);
+        if (dl_filter.skia_object()) {
+          builder->setMaskFilter(&dl_filter);
+        } else {
+          builder->setMaskFilter(nullptr);
+        }
         break;
     }
   }
 
-  return &paint;
+  return true;
 }
 
 void Paint::toDlPaint(DlPaint& paint) const {
@@ -278,14 +414,11 @@
       SkBlurStyle blur_style =
           static_cast<SkBlurStyle>(uint_data[kMaskFilterBlurStyleIndex]);
       double sigma = float_data[kMaskFilterSigmaIndex];
-      // Make could return a nullptr here if the values are NOP or
-      // do not make sense. We could interpret that as if there was
-      // no value passed from Dart at all (i.e. don't change the
-      // setting in the paint object as in the kNull branch right
-      // above here), but the maskfilter flag was actually set
-      // indicating that the developer "tried" to set a mask, so we
-      // should set the null value rather than do nothing.
-      paint.setMaskFilter(DlBlurMaskFilter::Make(blur_style, sigma));
+      std::shared_ptr<DlBlurMaskFilter> dl_filter =
+          std::make_shared<DlBlurMaskFilter>(blur_style, sigma);
+      if (dl_filter->skia_object()) {
+        paint.setMaskFilter(dl_filter);
+      }
       break;
   }
 }
diff --git a/lib/ui/painting/paint.h b/lib/ui/painting/paint.h
index 466f26a..6e74535 100644
--- a/lib/ui/painting/paint.h
+++ b/lib/ui/painting/paint.h
@@ -17,11 +17,19 @@
   Paint() = default;
   Paint(Dart_Handle paint_objects, Dart_Handle paint_data);
 
-  const DlPaint* paint(DlPaint& paint,
-                       const DisplayListAttributeFlags& flags) const;
+  const SkPaint* paint(SkPaint& paint) const;
 
   void toDlPaint(DlPaint& paint) const;
 
+  /// Synchronize the Dart properties to the display list according
+  /// to the attribute flags that indicate which properties are needed.
+  /// The return value indicates if the paint was non-null and can
+  /// either be DCHECKed or used to indicate to the DisplayList
+  /// draw operation whether or not to use the synchronized attributes
+  /// (mainly the drawImage and saveLayer methods).
+  bool sync_to(DisplayListBuilder* builder,
+               const DisplayListAttributeFlags& flags) const;
+
   bool isNull() const { return Dart_IsNull(paint_data_); }
   bool isNotNull() const { return !Dart_IsNull(paint_data_); }
 
diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc
index ebd2ff2..e1b1789 100644
--- a/shell/common/rasterizer.cc
+++ b/shell/common/rasterizer.cc
@@ -270,9 +270,9 @@
   };
 
   sk_sp<SkSurface> surface = SkSurface::MakeRaster(image_info);
-  auto canvas = DlSkCanvasAdapter(surface->getCanvas());
-  canvas.Clear(DlColor::kTransparent());
-  canvas.DrawDisplayList(display_list);
+  SkCanvas* canvas = surface->getCanvas();
+  canvas->clear(SK_ColorTRANSPARENT);
+  display_list->RenderTo(canvas);
 
   sk_sp<SkImage> image = surface->makeImageSnapshot();
   return std::make_unique<SnapshotDelegate::GpuImageResult>(
@@ -335,9 +335,9 @@
               return;
             }
 
-            auto canvas = DlSkCanvasAdapter(sk_surface->getCanvas());
-            canvas.Clear(DlColor::kTransparent());
-            canvas.DrawDisplayList(display_list);
+            SkCanvas* canvas = sk_surface->getCanvas();
+            canvas->clear(SK_ColorTRANSPARENT);
+            display_list->RenderTo(canvas);
 
             result = std::make_unique<SnapshotDelegate::GpuImageResult>(
                 texture, sk_ref_sp(context), nullptr, "");
diff --git a/shell/common/snapshot_controller_skia.cc b/shell/common/snapshot_controller_skia.cc
index 0e2fdea..27b6700 100644
--- a/shell/common/snapshot_controller_skia.cc
+++ b/shell/common/snapshot_controller_skia.cc
@@ -126,7 +126,7 @@
     sk_sp<DisplayList> display_list,
     SkISize size) {
   return DoMakeRasterSnapshot(size, [display_list](SkCanvas* canvas) {
-    DlSkCanvasAdapter(canvas).DrawDisplayList(display_list);
+    display_list->RenderTo(canvas);
   });
 }
 
diff --git a/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.mm b/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.mm
index 4730fff..6e1cea4 100644
--- a/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.mm
+++ b/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.mm
@@ -61,12 +61,12 @@
   }
 
   if (_externalImage) {
-    context.canvas->DrawImageRect(_externalImage,                                // image
-                                  SkRect::Make(_externalImage->bounds()),        // source rect
-                                  bounds,                                        // destination rect
-                                  sampling,                                      // sampling
-                                  context.paint,                                 // paint
-                                  flutter::DlCanvas::SrcRectConstraint::kStrict  // enforce edges
+    context.canvas->DrawImageRect(_externalImage,                          // image
+                                  SkRect::Make(_externalImage->bounds()),  // source rect
+                                  bounds,                                  // destination rect
+                                  sampling,                                // sampling
+                                  context.paint,                           // paint
+                                  false                                    // enforce edges
     );
   }
 }
diff --git a/testing/display_list_testing.cc b/testing/display_list_testing.cc
index 96a29cf..2caadcb 100644
--- a/testing/display_list_testing.cc
+++ b/testing/display_list_testing.cc
@@ -197,15 +197,8 @@
   switch (op) {
     case DlCanvas::ClipOp::kDifference: return os << "ClipOp::kDifference";
     case DlCanvas::ClipOp::kIntersect:  return os << "ClipOp::kIntersect";
-  }
-}
 
-std::ostream& operator<<(std::ostream& os, const DlCanvas::SrcRectConstraint& constraint) {
-  switch (constraint) {
-    case DlCanvas::SrcRectConstraint::kFast:
-      return os << "SrcRectConstraint::kFast";
-    case DlCanvas::SrcRectConstraint::kStrict:
-      return os << "SrcRectConstraint::kStrict";
+    default: return os << "ClipOp::????";
   }
 }
 
@@ -214,6 +207,8 @@
     case DlStrokeCap::kButt:   return os << "Cap::kButt";
     case DlStrokeCap::kRound:  return os << "Cap::kRound";
     case DlStrokeCap::kSquare: return os << "Cap::kSquare";
+
+    default: return os << "Cap::????";
   }
 }
 
@@ -222,6 +217,8 @@
     case DlStrokeJoin::kMiter: return os << "Join::kMiter";
     case DlStrokeJoin::kRound: return os << "Join::kRound";
     case DlStrokeJoin::kBevel: return os << "Join::kBevel";
+
+    default: return os << "Join::????";
   }
 }
 
@@ -230,6 +227,8 @@
     case DlDrawStyle::kFill:          return os << "Style::kFill";
     case DlDrawStyle::kStroke:        return os << "Style::kStroke";
     case DlDrawStyle::kStrokeAndFill: return os << "Style::kStrokeAnFill";
+
+    default: return os << "Style::????";
   }
 }
 
@@ -239,14 +238,19 @@
     case kSolid_SkBlurStyle:  return os << "BlurStyle::kSolid";
     case kOuter_SkBlurStyle:  return os << "BlurStyle::kOuter";
     case kInner_SkBlurStyle:  return os << "BlurStyle::kInner";
+
+    default: return os << "Style::????";
   }
 }
 
-std::ostream& operator<<(std::ostream& os, const DlCanvas::PointMode& mode) {
+static std::ostream& operator<<(std::ostream& os,
+                                const DlCanvas::PointMode& mode) {
   switch (mode) {
     case DlCanvas::PointMode::kPoints:  return os << "PointMode::kPoints";
     case DlCanvas::PointMode::kLines:   return os << "PointMode::kLines";
     case DlCanvas::PointMode::kPolygon: return os << "PointMode::kPolygon";
+
+    default: return os << "PointMode::????";
   }
 }
 
@@ -444,7 +448,7 @@
       break;
     }
     default:
-      os_ << "?DlUnknownColorSource?()";
+      os_ << "DlUnknownColorSource(" << source->skia_object().get() << ")";
       break;
   }
   os_ << ");" << std::endl;
@@ -486,7 +490,7 @@
       break;
     }
     default:
-      os_ << "?DlUnknownColorFilter?()";
+      os_ << "DlUnknownColorFilter(" << filter.skia_object().get() << ")";
       break;
   }
 }
@@ -526,7 +530,7 @@
       break;
     }
     default:
-      os_ << "?DlUnknownMaskFilter?()";
+      os_ << "DlUnknownMaskFilter(" << filter->skia_object().get() << ")";
       break;
   }
   os_ << ");" << std::endl;
@@ -559,7 +563,7 @@
       os_ << "DlMatrixImageFilter(" << matrix->matrix() << ", " << matrix->sampling() << ")";
       break;
     }
-    case DlImageFilterType::kCompose: {
+    case DlImageFilterType::kComposeFilter: {
       const DlComposeImageFilter* compose = filter.asCompose();
       FML_DCHECK(compose);
       os_ << "DlComposeImageFilter(" << std::endl;
@@ -586,7 +590,7 @@
       os_ << ")";
       break;
     }
-    case DlImageFilterType::kLocalMatrix: {
+    case DlImageFilterType::kLocalMatrixFilter: {
       const DlLocalMatrixImageFilter* local_matrix = filter.asLocalMatrix();
       FML_DCHECK(local_matrix);
       os_ << "DlLocalMatrixImageFilter(" << local_matrix->matrix();
@@ -806,7 +810,7 @@
                                                 const SkRect& dst,
                                                 DlImageSampling sampling,
                                                 bool render_with_attributes,
-                                                SrcRectConstraint constraint) {
+                                                SkCanvas::SrcRectConstraint constraint) {
   startl() << "drawImageRect(" << image.get() << "," << std::endl;
   startl() << "              src: " << src << "," << std::endl;
   startl() << "              dst: " << dst << "," << std::endl;
@@ -845,12 +849,8 @@
            << ");" << std::endl;
 }
 void DisplayListStreamDispatcher::drawDisplayList(
-    const sk_sp<DisplayList> display_list, SkScalar opacity) {
-  startl() << "drawDisplayList("
-           << "ID: " << display_list->unique_id() << ", "
-           << "bounds: " << display_list->bounds() << ", "
-           << "opacity: " << opacity
-           << ");" << std::endl;
+    const sk_sp<DisplayList> display_list) {
+  startl() << "drawDisplayList(ID: " << display_list->unique_id() << ", bounds: " << display_list->bounds() << ");" << std::endl;
 }
 void DisplayListStreamDispatcher::drawTextBlob(const sk_sp<SkTextBlob> blob,
                                                SkScalar x,
diff --git a/testing/display_list_testing.h b/testing/display_list_testing.h
index 60afff2..26d5af0 100644
--- a/testing/display_list_testing.h
+++ b/testing/display_list_testing.h
@@ -8,8 +8,8 @@
 #include <ostream>
 
 #include "flutter/display_list/display_list.h"
+#include "flutter/display_list/display_list_dispatcher.h"
 #include "flutter/display_list/display_list_path_effect.h"
-#include "flutter/display_list/dl_op_receiver.h"
 
 namespace flutter {
 namespace testing {
@@ -36,10 +36,6 @@
 extern std::ostream& operator<<(std::ostream& os, const DlPaint& paint);
 extern std::ostream& operator<<(std::ostream& os, const DlBlendMode& mode);
 extern std::ostream& operator<<(std::ostream& os, const DlCanvas::ClipOp& op);
-extern std::ostream& operator<<(std::ostream& os,
-                                const DlCanvas::PointMode& op);
-extern std::ostream& operator<<(std::ostream& os,
-                                const DlCanvas::SrcRectConstraint& op);
 extern std::ostream& operator<<(std::ostream& os, const DlStrokeCap& cap);
 extern std::ostream& operator<<(std::ostream& os, const DlStrokeJoin& join);
 extern std::ostream& operator<<(std::ostream& os, const DlDrawStyle& style);
@@ -51,7 +47,7 @@
 extern std::ostream& operator<<(std::ostream& os, const DlTileMode& mode);
 extern std::ostream& operator<<(std::ostream& os, const DlImage* image);
 
-class DisplayListStreamDispatcher final : public DlOpReceiver {
+class DisplayListStreamDispatcher final : public Dispatcher {
  public:
   DisplayListStreamDispatcher(std::ostream& os,
                               int cur_indent = 2,
@@ -125,7 +121,7 @@
                      const SkRect& dst,
                      DlImageSampling sampling,
                      bool render_with_attributes,
-                     SrcRectConstraint constraint) override;
+                     SkCanvas::SrcRectConstraint constraint) override;
   void drawImageNine(const sk_sp<DlImage> image,
                      const SkIRect& center,
                      const SkRect& dst,
@@ -140,8 +136,7 @@
                  DlImageSampling sampling,
                  const SkRect* cull_rect,
                  bool render_with_attributes) override;
-  void drawDisplayList(const sk_sp<DisplayList> display_list,
-                       SkScalar opacity) override;
+  void drawDisplayList(const sk_sp<DisplayList> display_list) override;
   void drawTextBlob(const sk_sp<SkTextBlob> blob,
                     SkScalar x,
                     SkScalar y) override;
diff --git a/testing/mock_canvas.cc b/testing/mock_canvas.cc
index e72a27c..837d336 100644
--- a/testing/mock_canvas.cc
+++ b/testing/mock_canvas.cc
@@ -285,7 +285,7 @@
                                const SkRect&,
                                const DlImageSampling,
                                const DlPaint*,
-                               SrcRectConstraint constraint) {
+                               bool enforce_src_edges) {
   FML_DCHECK(false);
 }
 
diff --git a/testing/mock_canvas.h b/testing/mock_canvas.h
index 022793d..5473432 100644
--- a/testing/mock_canvas.h
+++ b/testing/mock_canvas.h
@@ -247,13 +247,12 @@
                  const SkPoint point,
                  DlImageSampling sampling,
                  const DlPaint* paint = nullptr) override;
-  void DrawImageRect(
-      const sk_sp<DlImage>& image,
-      const SkRect& src,
-      const SkRect& dst,
-      DlImageSampling sampling,
-      const DlPaint* paint = nullptr,
-      SrcRectConstraint constraint = SrcRectConstraint::kFast) override;
+  void DrawImageRect(const sk_sp<DlImage>& image,
+                     const SkRect& src,
+                     const SkRect& dst,
+                     DlImageSampling sampling,
+                     const DlPaint* paint = nullptr,
+                     bool enforce_src_edges = false) override;
   void DrawImageNine(const sk_sp<DlImage>& image,
                      const SkIRect& center,
                      const SkRect& dst,