diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 4950aae8496fc..849cf648b1f8e 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 @@ TEST_P(AiksTest, CanRenderImage) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_P(AiksTest, CanRenderInvertedImage) { +TEST_P(AiksTest, CanRenderInvertedImageWithColorFilter) { Canvas canvas; Paint paint; auto image = std::make_shared(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, std::shared_ptr texture, diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index ae4c9d512037f..80f0cc734809e 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -4,7 +4,6 @@ #include "impeller/aiks/canvas.h" -#include #include #include diff --git a/impeller/aiks/color_filter.cc b/impeller/aiks/color_filter.cc index 87f7022e1cd1f..b5498a195408b 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 #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 @@ std::shared_ptr ColorFilter::MakeLinearToSrgb() { return std::make_shared(); } +std::shared_ptr ColorFilter::MakeComposed( + const std::shared_ptr& outer, + const std::shared_ptr& inner) { + return std::make_shared(outer, inner); +} + /******************************************************************************* ******* BlendColorFilter ******************************************************************************/ @@ -142,4 +151,40 @@ std::shared_ptr LinearToSrgbColorFilter::Clone() const { return std::make_shared(*this); } +/******************************************************************************* + ******* ComposedColorFilter + ******************************************************************************/ + +ComposedColorFilter::ComposedColorFilter( + const std::shared_ptr& outer, + const std::shared_ptr& inner) + : outer_(outer), inner_(inner) {} + +ComposedColorFilter::~ComposedColorFilter() = default; + +std::shared_ptr +ComposedColorFilter::WrapWithGPUColorFilter( + std::shared_ptr input, + ColorFilterContents::AbsorbOpacity absorb_opacity) const { + std::shared_ptr 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 ComposedColorFilter::Clone() const { + return std::make_shared(outer_, inner_); +} + } // namespace impeller diff --git a/impeller/aiks/color_filter.h b/impeller/aiks/color_filter.h index 1c5bf32dd9f8a..84dfbbd19b40d 100644 --- a/impeller/aiks/color_filter.h +++ b/impeller/aiks/color_filter.h @@ -34,6 +34,10 @@ class ColorFilter { static std::shared_ptr MakeLinearToSrgb(); + static std::shared_ptr MakeComposed( + const std::shared_ptr& outer, + const std::shared_ptr& 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 @@ class LinearToSrgbColorFilter final : public ColorFilter { std::shared_ptr 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& outer, + const std::shared_ptr& inner); + + ~ComposedColorFilter() override; + + // |ColorFilter| + std::shared_ptr WrapWithGPUColorFilter( + std::shared_ptr input, + ColorFilterContents::AbsorbOpacity absorb_opacity) const override; + + // |ColorFilter| + ColorFilterProc GetCPUColorFilterProc() const override; + + // |ColorFilter| + std::shared_ptr Clone() const override; + + private: + std::shared_ptr outer_; + std::shared_ptr inner_; +}; + } // namespace impeller diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index 66a3f7280f70a..332973e3c2a99 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 Paint::CreateContentsForEntity(const Path& path, bool cover) const { std::unique_ptr geometry; @@ -38,6 +50,7 @@ std::shared_ptr Paint::CreateContentsForGeometry( // 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 Paint::CreateContentsForGeometry( std::shared_ptr Paint::WithFilters( std::shared_ptr 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 @@ std::shared_ptr Paint::WithColorFilter( return input; } + auto color_filter = GetColorFilter(); if (!color_filter) { return input; } @@ -121,33 +134,10 @@ std::shared_ptr Paint::WithColorFilter( 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 Paint::WithInvertFilter( - std::shared_ptr input) const { - if (!invert_colors) { - return input; - } - - return ColorFilterContents::MakeColorMatrix( - {FilterInput::Make(std::move(input))}, kColorInversion); -} - std::shared_ptr Paint::MaskBlurDescriptor::CreateMaskBlur( std::shared_ptr color_source_contents, const std::shared_ptr& color_filter) const { @@ -208,8 +198,22 @@ std::shared_ptr Paint::MaskBlurDescriptor::CreateMaskBlur( return FilterContents::MakeBorderMaskBlur(input, sigma, sigma, style); } +std::shared_ptr 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 c2e907a1cc5b0..bf96842b783c9 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -67,6 +67,8 @@ struct Paint { std::shared_ptr color_filter; std::optional mask_blur_descriptor; + std::shared_ptr 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 @@ struct Paint { std::shared_ptr input, ColorFilterContents::AbsorbOpacity absorb_opacity = ColorFilterContents::AbsorbOpacity::kNo) const; - - std::shared_ptr WithInvertFilter( - std::shared_ptr input) const; }; } // namespace impeller diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index 0b19cefb5b79b..7796efc26b8b6 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -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 @@ static std::shared_ptr ToColorFilter( // |flutter::DlOpReceiver| void DlDispatcher::setColorFilter(const flutter::DlColorFilter* filter) { - // Needs https://github.com/flutter/flutter/issues/95434 paint_.color_filter = ToColorFilter(filter); }