[Impeller] Redo simplify invert colors. (#46416)

Now with fewer state bugs.

Fixes https://github.com/flutter/flutter/issues/135699
diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc
index 4950aae..849cf64 100644
--- a/impeller/aiks/aiks_unittests.cc
+++ b/impeller/aiks/aiks_unittests.cc
@@ -13,6 +13,7 @@
 #include "flutter/testing/testing.h"
 #include "impeller/aiks/aiks_playground.h"
 #include "impeller/aiks/canvas.h"
+#include "impeller/aiks/color_filter.h"
 #include "impeller/aiks/image.h"
 #include "impeller/aiks/image_filter.h"
 #include "impeller/aiks/paint_pass_delegate.h"
@@ -123,16 +124,43 @@
   ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
 }
 
-TEST_P(AiksTest, CanRenderInvertedImage) {
+TEST_P(AiksTest, CanRenderInvertedImageWithColorFilter) {
   Canvas canvas;
   Paint paint;
   auto image = std::make_shared<Image>(CreateTextureForFixture("kalimba.jpg"));
   paint.color = Color::Red();
+  paint.color_filter =
+      ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Yellow());
   paint.invert_colors = true;
+
   canvas.DrawImage(image, Point::MakeXY(100.0, 100.0), paint);
   ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
 }
 
+TEST_P(AiksTest, CanRenderColorFilterWithInvertColors) {
+  Canvas canvas;
+  Paint paint;
+  paint.color = Color::Red();
+  paint.color_filter =
+      ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Yellow());
+  paint.invert_colors = true;
+
+  canvas.DrawRect(Rect::MakeLTRB(0, 0, 100, 100), paint);
+  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
+}
+
+TEST_P(AiksTest, CanRenderColorFilterWithInvertColorsDrawPaint) {
+  Canvas canvas;
+  Paint paint;
+  paint.color = Color::Red();
+  paint.color_filter =
+      ColorFilter::MakeBlend(BlendMode::kSourceOver, Color::Yellow());
+  paint.invert_colors = true;
+
+  canvas.DrawPaint(paint);
+  ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
+}
+
 namespace {
 bool GenerateMipmap(const std::shared_ptr<Context>& context,
                     std::shared_ptr<Texture> texture,
diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc
index ae4c9d5..80f0cc7 100644
--- a/impeller/aiks/canvas.cc
+++ b/impeller/aiks/canvas.cc
@@ -4,7 +4,6 @@
 
 #include "impeller/aiks/canvas.h"
 
-#include <algorithm>
 #include <optional>
 #include <utility>
 
diff --git a/impeller/aiks/color_filter.cc b/impeller/aiks/color_filter.cc
index 87f7022..b5498a1 100644
--- a/impeller/aiks/color_filter.cc
+++ b/impeller/aiks/color_filter.cc
@@ -3,7 +3,10 @@
 // found in the LICENSE file.
 
 #include "impeller/aiks/color_filter.h"
+
+#include <utility>
 #include "impeller/entity/contents/filters/color_filter_contents.h"
+#include "impeller/entity/contents/filters/filter_contents.h"
 #include "impeller/entity/contents/filters/inputs/filter_input.h"
 #include "impeller/geometry/color.h"
 
@@ -34,6 +37,12 @@
   return std::make_shared<LinearToSrgbColorFilter>();
 }
 
+std::shared_ptr<ColorFilter> ColorFilter::MakeComposed(
+    const std::shared_ptr<ColorFilter>& outer,
+    const std::shared_ptr<ColorFilter>& inner) {
+  return std::make_shared<ComposedColorFilter>(outer, inner);
+}
+
 /*******************************************************************************
  ******* BlendColorFilter
  ******************************************************************************/
@@ -142,4 +151,40 @@
   return std::make_shared<LinearToSrgbColorFilter>(*this);
 }
 
+/*******************************************************************************
+ ******* ComposedColorFilter
+ ******************************************************************************/
+
+ComposedColorFilter::ComposedColorFilter(
+    const std::shared_ptr<ColorFilter>& outer,
+    const std::shared_ptr<ColorFilter>& inner)
+    : outer_(outer), inner_(inner) {}
+
+ComposedColorFilter::~ComposedColorFilter() = default;
+
+std::shared_ptr<ColorFilterContents>
+ComposedColorFilter::WrapWithGPUColorFilter(
+    std::shared_ptr<FilterInput> input,
+    ColorFilterContents::AbsorbOpacity absorb_opacity) const {
+  std::shared_ptr<FilterContents> inner = inner_->WrapWithGPUColorFilter(
+      input, ColorFilterContents::AbsorbOpacity::kNo);
+  return outer_->WrapWithGPUColorFilter(FilterInput::Make(inner),
+                                        absorb_opacity);
+}
+
+// |ColorFilter|
+ColorFilter::ColorFilterProc ComposedColorFilter::GetCPUColorFilterProc()
+    const {
+  return [inner = inner_, outer = outer_](Color color) {
+    auto inner_proc = inner->GetCPUColorFilterProc();
+    auto outer_proc = outer->GetCPUColorFilterProc();
+    return outer_proc(inner_proc(color));
+  };
+}
+
+// |ColorFilter|
+std::shared_ptr<ColorFilter> ComposedColorFilter::Clone() const {
+  return std::make_shared<ComposedColorFilter>(outer_, inner_);
+}
+
 }  // namespace impeller
diff --git a/impeller/aiks/color_filter.h b/impeller/aiks/color_filter.h
index 1c5bf32..84dfbbd 100644
--- a/impeller/aiks/color_filter.h
+++ b/impeller/aiks/color_filter.h
@@ -34,6 +34,10 @@
 
   static std::shared_ptr<ColorFilter> MakeLinearToSrgb();
 
+  static std::shared_ptr<ColorFilter> MakeComposed(
+      const std::shared_ptr<ColorFilter>& outer,
+      const std::shared_ptr<ColorFilter>& inner);
+
   /// @brief  Wraps the given filter input with a GPU-based filter that will
   ///         perform the color operation. The given input will first be
   ///         rendered to a texture and then filtered.
@@ -147,4 +151,28 @@
   std::shared_ptr<ColorFilter> Clone() const override;
 };
 
+/// @brief Applies color filters as f(g(x)), where x is the input color.
+class ComposedColorFilter final : public ColorFilter {
+ public:
+  ComposedColorFilter(const std::shared_ptr<ColorFilter>& outer,
+                      const std::shared_ptr<ColorFilter>& inner);
+
+  ~ComposedColorFilter() override;
+
+  // |ColorFilter|
+  std::shared_ptr<ColorFilterContents> WrapWithGPUColorFilter(
+      std::shared_ptr<FilterInput> input,
+      ColorFilterContents::AbsorbOpacity absorb_opacity) const override;
+
+  // |ColorFilter|
+  ColorFilterProc GetCPUColorFilterProc() const override;
+
+  // |ColorFilter|
+  std::shared_ptr<ColorFilter> Clone() const override;
+
+ private:
+  std::shared_ptr<ColorFilter> outer_;
+  std::shared_ptr<ColorFilter> inner_;
+};
+
 }  // namespace impeller
diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc
index 66a3f72..332973e 100644
--- a/impeller/aiks/paint.cc
+++ b/impeller/aiks/paint.cc
@@ -14,6 +14,18 @@
 
 namespace impeller {
 
+/// A color matrix which inverts colors.
+// clang-format off
+constexpr ColorMatrix kColorInversion = {
+  .array = {
+    -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
+
 std::shared_ptr<Contents> Paint::CreateContentsForEntity(const Path& path,
                                                          bool cover) const {
   std::unique_ptr<Geometry> geometry;
@@ -38,6 +50,7 @@
   // Attempt to apply the color filter on the CPU first.
   // Note: This is not just an optimization; some color sources rely on
   //       CPU-applied color filters to behave properly.
+  auto color_filter = GetColorFilter();
   bool needs_color_filter = !!color_filter;
   if (color_filter &&
       contents->ApplyColorFilter(color_filter->GetCPUColorFilterProc())) {
@@ -59,7 +72,6 @@
 std::shared_ptr<Contents> Paint::WithFilters(
     std::shared_ptr<Contents> input) const {
   input = WithColorFilter(input, ColorFilterContents::AbsorbOpacity::kYes);
-  input = WithInvertFilter(input);
   auto image_filter =
       WithImageFilter(input, Matrix(), Entity::RenderingMode::kDirect);
   if (image_filter) {
@@ -111,6 +123,7 @@
     return input;
   }
 
+  auto color_filter = GetColorFilter();
   if (!color_filter) {
     return input;
   }
@@ -121,33 +134,10 @@
   if (input->ApplyColorFilter(color_filter->GetCPUColorFilterProc())) {
     return input;
   }
-
   return color_filter->WrapWithGPUColorFilter(FilterInput::Make(input),
                                               absorb_opacity);
 }
 
-/// A color matrix which inverts colors.
-// clang-format off
-constexpr ColorMatrix kColorInversion = {
-  .array = {
-    -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
-
-std::shared_ptr<Contents> Paint::WithInvertFilter(
-    std::shared_ptr<Contents> input) const {
-  if (!invert_colors) {
-    return input;
-  }
-
-  return ColorFilterContents::MakeColorMatrix(
-      {FilterInput::Make(std::move(input))}, kColorInversion);
-}
-
 std::shared_ptr<FilterContents> Paint::MaskBlurDescriptor::CreateMaskBlur(
     std::shared_ptr<ColorSourceContents> color_source_contents,
     const std::shared_ptr<ColorFilter>& color_filter) const {
@@ -208,8 +198,22 @@
   return FilterContents::MakeBorderMaskBlur(input, sigma, sigma, style);
 }
 
+std::shared_ptr<ColorFilter> Paint::GetColorFilter() const {
+  if (invert_colors && color_filter) {
+    auto filter = ColorFilter::MakeMatrix(kColorInversion);
+    return ColorFilter::MakeComposed(filter, color_filter);
+  }
+  if (invert_colors) {
+    return ColorFilter::MakeMatrix(kColorInversion);
+  }
+  if (color_filter) {
+    return color_filter;
+  }
+  return nullptr;
+}
+
 bool Paint::HasColorFilter() const {
-  return !!color_filter;
+  return !!color_filter || invert_colors;
 }
 
 }  // namespace impeller
diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h
index c2e907a..bf96842 100644
--- a/impeller/aiks/paint.h
+++ b/impeller/aiks/paint.h
@@ -67,6 +67,8 @@
   std::shared_ptr<ColorFilter> color_filter;
   std::optional<MaskBlurDescriptor> mask_blur_descriptor;
 
+  std::shared_ptr<ColorFilter> GetColorFilter() const;
+
   /// @brief      Wrap this paint's configured filters to the given contents.
   /// @param[in]  input           The contents to wrap with paint's filters.
   /// @return     The filter-wrapped contents. If there are no filters that need
@@ -108,9 +110,6 @@
       std::shared_ptr<Contents> input,
       ColorFilterContents::AbsorbOpacity absorb_opacity =
           ColorFilterContents::AbsorbOpacity::kNo) const;
-
-  std::shared_ptr<Contents> WithInvertFilter(
-      std::shared_ptr<Contents> input) const;
 };
 
 }  // namespace impeller
diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc
index 0b19cef..7796efc 100644
--- a/impeller/display_list/dl_dispatcher.cc
+++ b/impeller/display_list/dl_dispatcher.cc
@@ -8,7 +8,6 @@
 #include <cstring>
 #include <memory>
 #include <optional>
-#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -16,20 +15,13 @@
 #include "flutter/fml/trace_event.h"
 #include "impeller/aiks/color_filter.h"
 #include "impeller/core/formats.h"
-#include "impeller/display_list/dl_image_impeller.h"
 #include "impeller/display_list/dl_vertices_geometry.h"
 #include "impeller/display_list/nine_patch_converter.h"
 #include "impeller/display_list/skia_conversions.h"
-#include "impeller/entity/contents/conical_gradient_contents.h"
 #include "impeller/entity/contents/filters/filter_contents.h"
 #include "impeller/entity/contents/filters/inputs/filter_input.h"
-#include "impeller/entity/contents/linear_gradient_contents.h"
-#include "impeller/entity/contents/radial_gradient_contents.h"
 #include "impeller/entity/contents/runtime_effect_contents.h"
-#include "impeller/entity/contents/sweep_gradient_contents.h"
-#include "impeller/entity/contents/tiled_texture_contents.h"
 #include "impeller/entity/entity.h"
-#include "impeller/entity/geometry/geometry.h"
 #include "impeller/geometry/path.h"
 #include "impeller/geometry/path_builder.h"
 #include "impeller/geometry/scalar.h"
@@ -483,7 +475,6 @@
 
 // |flutter::DlOpReceiver|
 void DlDispatcher::setColorFilter(const flutter::DlColorFilter* filter) {
-  // Needs https://github.com/flutter/flutter/issues/95434
   paint_.color_filter = ToColorFilter(filter);
 }