[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);
}