diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 3af68bf8a3..679275c98a 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -1029,6 +1029,8 @@ ORIGIN: ../../../flutter/impeller/aiks/color_source.cc + ../../../flutter/LICENS ORIGIN: ../../../flutter/impeller/aiks/color_source.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/image.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/image.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/aiks/image_filter.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/aiks/image_filter.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/paint.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/paint.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/aiks/paint_pass_delegate.cc + ../../../flutter/LICENSE @@ -3773,6 +3775,8 @@ FILE: ../../../flutter/impeller/aiks/color_source.cc FILE: ../../../flutter/impeller/aiks/color_source.h FILE: ../../../flutter/impeller/aiks/image.cc FILE: ../../../flutter/impeller/aiks/image.h +FILE: ../../../flutter/impeller/aiks/image_filter.cc +FILE: ../../../flutter/impeller/aiks/image_filter.h FILE: ../../../flutter/impeller/aiks/paint.cc FILE: ../../../flutter/impeller/aiks/paint.h FILE: ../../../flutter/impeller/aiks/paint_pass_delegate.cc diff --git a/engine/src/flutter/impeller/aiks/BUILD.gn b/engine/src/flutter/impeller/aiks/BUILD.gn index 5761cab4a7..ea96309333 100644 --- a/engine/src/flutter/impeller/aiks/BUILD.gn +++ b/engine/src/flutter/impeller/aiks/BUILD.gn @@ -16,6 +16,8 @@ impeller_component("aiks") { "color_source.h", "image.cc", "image.h", + "image_filter.cc", + "image_filter.h", "paint.cc", "paint.h", "paint_pass_delegate.cc", diff --git a/engine/src/flutter/impeller/aiks/aiks_unittests.cc b/engine/src/flutter/impeller/aiks/aiks_unittests.cc index 02388334f4..3438056a0b 100644 --- a/engine/src/flutter/impeller/aiks/aiks_unittests.cc +++ b/engine/src/flutter/impeller/aiks/aiks_unittests.cc @@ -14,6 +14,7 @@ #include "impeller/aiks/aiks_playground.h" #include "impeller/aiks/canvas.h" #include "impeller/aiks/image.h" +#include "impeller/aiks/image_filter.h" #include "impeller/aiks/paint_pass_delegate.h" #include "impeller/aiks/testing/context_spy.h" #include "impeller/core/capture.h" @@ -2110,12 +2111,9 @@ TEST_P(AiksTest, PaintWithFilters) { ASSERT_TRUE(paint.HasColorFilter()); - paint.image_filter = [](const FilterInput::Ref& input, - const Matrix& effect_transform, bool is_subpass) { - return FilterContents::MakeGaussianBlur( - input, Sigma(1.0), Sigma(1.0), FilterContents::BlurStyle::kNormal, - Entity::TileMode::kClamp, effect_transform); - }; + paint.image_filter = ImageFilter::MakeBlur(Sigma(1.0), Sigma(1.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp); ASSERT_TRUE(paint.HasColorFilter()); @@ -2141,12 +2139,9 @@ TEST_P(AiksTest, OpacityPeepHoleApplicationTest) { ASSERT_FALSE(delegate->CanCollapseIntoParentPass(entity_pass.get())); paint.color_filter = nullptr; - paint.image_filter = [](const FilterInput::Ref& input, - const Matrix& effect_transform, bool is_subpass) { - return FilterContents::MakeGaussianBlur( - input, Sigma(1.0), Sigma(1.0), FilterContents::BlurStyle::kNormal, - Entity::TileMode::kClamp, effect_transform); - }; + paint.image_filter = ImageFilter::MakeBlur(Sigma(1.0), Sigma(1.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp); // Paint has image filter, can't elide. delegate = std::make_shared(paint); @@ -2313,11 +2308,9 @@ TEST_P(AiksTest, CollapsedDrawPaintInSubpassBackdropFilter) { canvas.DrawPaint( {.color = Color::Yellow(), .blend_mode = BlendMode::kSource}); canvas.SaveLayer({}, {}, - [](const FilterInput::Ref& input, - const Matrix& effect_transform, bool is_subpass) { - return FilterContents::MakeGaussianBlur(input, Sigma(20.0), - Sigma(20.0)); - }); + ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kDecal)); canvas.DrawPaint( {.color = Color::CornflowerBlue(), .blend_mode = BlendMode::kSourceOver}); @@ -2515,12 +2508,8 @@ TEST_P(AiksTest, TranslucentSaveLayerWithBlendImageFilterDrawsCorrectly) { canvas.SaveLayer({ .color = Color::Black().WithAlpha(0.5), - .image_filter = - [](FilterInput::Ref input, const Matrix& effect_transform, - bool is_subpass) { - return ColorFilterContents::MakeBlend( - BlendMode::kDestinationOver, {std::move(input)}, Color::Red()); - }, + .image_filter = ImageFilter::MakeFromColorFilter( + *ColorFilter::MakeBlend(BlendMode::kDestinationOver, Color::Red())), }); canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()}); @@ -2589,17 +2578,14 @@ TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixImageFilterDrawsCorrectly) { canvas.SaveLayer({ .color = Color::Black().WithAlpha(0.5), - .image_filter = - [](FilterInput::Ref input, const Matrix& effect_transform, - bool is_subpass) { - return ColorFilterContents::MakeColorMatrix({std::move(input)}, - {.array = { - 1, 0, 0, 0, 0, // - 0, 1, 0, 0, 0, // - 0, 0, 1, 0, 0, // - 0, 0, 0, 2, 0 // - }}); - }, + .image_filter = ImageFilter::MakeFromColorFilter( + *ColorFilter::MakeMatrix({.array = + { + 1, 0, 0, 0, 0, // + 0, 1, 0, 0, 0, // + 0, 0, 1, 0, 0, // + 0, 0, 0, 2, 0 // + }})), }); canvas.DrawImage(image, {100, 500}, {}); canvas.Restore(); @@ -2616,17 +2602,14 @@ TEST_P(AiksTest, canvas.SaveLayer({ .color = Color::Black().WithAlpha(0.5), - .image_filter = - [](FilterInput::Ref input, const Matrix& effect_transform, - bool is_subpass) { - return ColorFilterContents::MakeColorMatrix( - {std::move(input)}, {.array = { - 1, 0, 0, 0, 0, // - 0, 1, 0, 0, 0, // - 0, 0.2, 1, 0, 0, // - 0, 0, 0, 0.5, 0 // - }}); - }, + .image_filter = ImageFilter::MakeFromColorFilter( + *ColorFilter::MakeMatrix({.array = + { + 1, 0, 0, 0, 0, // + 0, 1, 0, 0, 0, // + 0, 0.2, 1, 0, 0, // + 0, 0, 0, 0.5, 0 // + }})), .color_filter = ColorFilter::MakeBlend(BlendMode::kModulate, Color::Green()), }); @@ -2758,13 +2741,9 @@ TEST_P(AiksTest, CanRenderBackdropBlurInteractive) { canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()}); canvas.ClipRRect(Rect::MakeLTRB(a.x, a.y, b.x, b.y), 20); canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt, - [](const FilterInput::Ref& input, - const Matrix& effect_transform, bool is_subpass) { - return FilterContents::MakeGaussianBlur( - input, Sigma(20.0), Sigma(20.0), - FilterContents::BlurStyle::kNormal, - Entity::TileMode::kClamp, effect_transform); - }); + ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)); canvas.Restore(); return canvas.EndRecordingAsPicture(); @@ -2781,13 +2760,9 @@ TEST_P(AiksTest, CanRenderBackdropBlur) { canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()}); canvas.ClipRRect(Rect::MakeLTRB(75, 50, 375, 275), 20); canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt, - [](const FilterInput::Ref& input, - const Matrix& effect_transform, bool is_subpass) { - return FilterContents::MakeGaussianBlur( - input, Sigma(30.0), Sigma(30.0), - FilterContents::BlurStyle::kNormal, - Entity::TileMode::kClamp, effect_transform); - }); + ImageFilter::MakeBlur(Sigma(30.0), Sigma(30.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)); canvas.Restore(); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); @@ -2797,13 +2772,9 @@ TEST_P(AiksTest, CanRenderBackdropBlurHugeSigma) { Canvas canvas; canvas.DrawCircle({400, 400}, 300, {.color = Color::Green()}); canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt, - [](const FilterInput::Ref& input, - const Matrix& effect_transform, bool is_subpass) { - return FilterContents::MakeGaussianBlur( - input, Sigma(999999), Sigma(999999), - FilterContents::BlurStyle::kNormal, - Entity::TileMode::kClamp, effect_transform); - }); + ImageFilter::MakeBlur(Sigma(999999), Sigma(999999), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp)); canvas.Restore(); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); @@ -2816,14 +2787,9 @@ TEST_P(AiksTest, CanRenderClippedBlur) { {400, 400}, 200, { .color = Color::Green(), - .image_filter = - [](const FilterInput::Ref& input, const Matrix& effect_transform, - bool is_subpass) { - return FilterContents::MakeGaussianBlur( - input, Sigma(20), Sigma(20), - FilterContents::BlurStyle::kNormal, - Entity::TileMode::kClamp, effect_transform); - }, + .image_filter = ImageFilter::MakeBlur( + Sigma(20.0), Sigma(20.0), FilterContents::BlurStyle::kNormal, + Entity::TileMode::kClamp), }); canvas.Restore(); @@ -3127,11 +3093,9 @@ TEST_P(AiksTest, CanCanvasDrawPictureWithAdvancedBlend) { TEST_P(AiksTest, CanCanvasDrawPictureWithBackdropFilter) { Canvas subcanvas; subcanvas.SaveLayer({}, {}, - [](const FilterInput::Ref& input, - const Matrix& effect_transform, bool is_subpass) { - return FilterContents::MakeGaussianBlur( - input, Sigma(20.0), Sigma(20.0)); - }); + ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0), + FilterContents::BlurStyle::kNormal, + Entity::TileMode::kDecal)); auto image = std::make_shared(CreateTextureForFixture("kalimba.jpg")); Paint paint; paint.color = Color::Red().WithAlpha(0.5); @@ -3206,17 +3170,12 @@ TEST_P(AiksTest, MatrixSaveLayerFilter) { .blend_mode = BlendMode::kPlus}); // Should render a second circle, centered on the bottom-right-most edge of // the circle. - canvas.SaveLayer({.image_filter = - [](const FilterInput::Ref& input, - const Matrix& effect_transform, bool is_subpass) { - Matrix matrix = - Matrix::MakeTranslation( - Vector2(1, 1) * (100 + 100 * k1OverSqrt2)) * - Matrix::MakeScale(Vector2(1, 1) * 0.2) * - Matrix::MakeTranslation(Vector2(-100, -100)); - return FilterContents::MakeMatrixFilter( - input, matrix, {}, Matrix(), true); - }}, + canvas.SaveLayer({.image_filter = ImageFilter::MakeMatrix( + Matrix::MakeTranslation(Vector2(1, 1) * + (200 + 100 * k1OverSqrt2)) * + Matrix::MakeScale(Vector2(1, 1) * 0.2) * + Matrix::MakeTranslation(Vector2(-200, -200)), + SamplerDescriptor{})}, std::nullopt); canvas.DrawCircle(Point(200, 200), 100, {.color = Color::Green().WithAlpha(0.5), @@ -3238,17 +3197,13 @@ TEST_P(AiksTest, MatrixBackdropFilter) { .blend_mode = BlendMode::kPlus}); // Should render a second circle, centered on the bottom-right-most edge of // the circle. - canvas.SaveLayer({}, std::nullopt, - [](const FilterInput::Ref& input, - const Matrix& effect_transform, bool is_subpass) { - Matrix matrix = - Matrix::MakeTranslation(Vector2(1, 1) * - (100 + 100 * k1OverSqrt2)) * - Matrix::MakeScale(Vector2(1, 1) * 0.2) * - Matrix::MakeTranslation(Vector2(-100, -100)); - return FilterContents::MakeMatrixFilter( - input, matrix, {}, Matrix(), true); - }); + canvas.SaveLayer( + {}, std::nullopt, + ImageFilter::MakeMatrix( + Matrix::MakeTranslation(Vector2(1, 1) * (100 + 100 * k1OverSqrt2)) * + Matrix::MakeScale(Vector2(1, 1) * 0.2) * + Matrix::MakeTranslation(Vector2(-100, -100)), + SamplerDescriptor{})); canvas.Restore(); } canvas.Restore(); @@ -3329,14 +3284,14 @@ TEST_P(AiksTest, PipelineBlendSingleParameter) { canvas.Translate(Point(100, 100)); canvas.DrawCircle(Point(200, 200), 200, {.color = Color::Blue()}); canvas.ClipRect(Rect(100, 100, 200, 200)); - canvas.DrawCircle( - Point(200, 200), 200, - {.color = Color::Green(), - .blend_mode = BlendMode::kSourceOver, - .image_filter = [](const FilterInput::Ref& input, - const Matrix& effect_transform, bool is_subpass) { - return ColorFilterContents::MakeBlend(BlendMode::kSource, {input}); - }}); + canvas.DrawCircle(Point(200, 200), 200, + { + .color = Color::Green(), + .blend_mode = BlendMode::kSourceOver, + .image_filter = ImageFilter::MakeFromColorFilter( + *ColorFilter::MakeBlend(BlendMode::kDestination, + Color::White())), + }); canvas.Restore(); } diff --git a/engine/src/flutter/impeller/aiks/canvas.cc b/engine/src/flutter/impeller/aiks/canvas.cc index d2bab026df..f50dd36af7 100644 --- a/engine/src/flutter/impeller/aiks/canvas.cc +++ b/engine/src/flutter/impeller/aiks/canvas.cc @@ -9,6 +9,7 @@ #include #include "flutter/fml/logging.h" +#include "impeller/aiks/image_filter.h" #include "impeller/aiks/paint_pass_delegate.h" #include "impeller/entity/contents/atlas_contents.h" #include "impeller/entity/contents/clip_contents.h" @@ -57,7 +58,7 @@ void Canvas::Save() { void Canvas::Save(bool create_subpass, BlendMode blend_mode, - EntityPass::BackdropFilterProc backdrop_filter) { + const std::shared_ptr& backdrop_filter) { auto entry = CanvasStackEntry{}; entry.xformation = xformation_stack_.back().xformation; entry.cull_rect = xformation_stack_.back().cull_rect; @@ -67,7 +68,18 @@ void Canvas::Save(bool create_subpass, auto subpass = std::make_unique(); subpass->SetEnableOffscreenCheckerboard( debug_options.offscreen_texture_checkerboard); - subpass->SetBackdropFilter(std::move(backdrop_filter)); + if (backdrop_filter) { + EntityPass::BackdropFilterProc backdrop_filter_proc = + [backdrop_filter = backdrop_filter->Clone()]( + const FilterInput::Ref& input, const Matrix& effect_transform, + bool is_subpass) { + auto filter = backdrop_filter->WrapInput(input); + filter->SetEffectTransform(effect_transform); + filter->SetIsForSubpass(is_subpass); + return filter; + }; + subpass->SetBackdropFilter(backdrop_filter_proc); + } subpass->SetBlendMode(blend_mode); current_pass_ = GetCurrentPass().AddSubpass(std::move(subpass)); current_pass_->SetTransformation(xformation_stack_.back().xformation); @@ -518,7 +530,7 @@ size_t Canvas::GetStencilDepth() const { void Canvas::SaveLayer(const Paint& paint, std::optional bounds, - const Paint::ImageFilterProc& backdrop_filter) { + const std::shared_ptr& backdrop_filter) { Save(true, paint.blend_mode, backdrop_filter); auto& new_layer_pass = GetCurrentPass(); diff --git a/engine/src/flutter/impeller/aiks/canvas.h b/engine/src/flutter/impeller/aiks/canvas.h index d36bc3ff58..0ea187c39a 100644 --- a/engine/src/flutter/impeller/aiks/canvas.h +++ b/engine/src/flutter/impeller/aiks/canvas.h @@ -12,6 +12,7 @@ #include "flutter/fml/macros.h" #include "impeller/aiks/image.h" +#include "impeller/aiks/image_filter.h" #include "impeller/aiks/paint.h" #include "impeller/aiks/picture.h" #include "impeller/core/sampler_descriptor.h" @@ -68,7 +69,7 @@ class Canvas { void SaveLayer(const Paint& paint, std::optional bounds = std::nullopt, - const Paint::ImageFilterProc& backdrop_filter = nullptr); + const std::shared_ptr& backdrop_filter = nullptr); bool Restore(); @@ -180,7 +181,7 @@ class Canvas { void Save(bool create_subpass, BlendMode = BlendMode::kSourceOver, - EntityPass::BackdropFilterProc backdrop_filter = nullptr); + const std::shared_ptr& backdrop_filter = nullptr); void RestoreClip(); diff --git a/engine/src/flutter/impeller/aiks/color_filter.cc b/engine/src/flutter/impeller/aiks/color_filter.cc index a684a44643..72868a0c0d 100644 --- a/engine/src/flutter/impeller/aiks/color_filter.cc +++ b/engine/src/flutter/impeller/aiks/color_filter.cc @@ -58,6 +58,10 @@ ColorFilter::ColorFilterProc BlendColorFilter::GetCPUColorFilterProc() const { }; } +std::shared_ptr BlendColorFilter::Clone() const { + return std::make_shared(*this); +} + /******************************************************************************* ******* MatrixColorFilter ******************************************************************************/ @@ -82,6 +86,10 @@ ColorFilter::ColorFilterProc MatrixColorFilter::GetCPUColorFilterProc() const { }; } +std::shared_ptr MatrixColorFilter::Clone() const { + return std::make_shared(*this); +} + /******************************************************************************* ******* SrgbToLinearColorFilter ******************************************************************************/ @@ -104,6 +112,10 @@ ColorFilter::ColorFilterProc SrgbToLinearColorFilter::GetCPUColorFilterProc() return [](Color color) { return color.SRGBToLinear(); }; } +std::shared_ptr SrgbToLinearColorFilter::Clone() const { + return std::make_shared(*this); +} + /******************************************************************************* ******* LinearToSrgbColorFilter ******************************************************************************/ @@ -126,4 +138,8 @@ ColorFilter::ColorFilterProc LinearToSrgbColorFilter::GetCPUColorFilterProc() return [](Color color) { return color.LinearToSRGB(); }; } +std::shared_ptr LinearToSrgbColorFilter::Clone() const { + return std::make_shared(*this); +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/aiks/color_filter.h b/engine/src/flutter/impeller/aiks/color_filter.h index 3ae4d96048..234cbc63c1 100644 --- a/engine/src/flutter/impeller/aiks/color_filter.h +++ b/engine/src/flutter/impeller/aiks/color_filter.h @@ -48,6 +48,8 @@ class ColorFilter { /// @brief Returns a function that can be used to filter unpremultiplied /// Impeller Colors on the CPU. virtual ColorFilterProc GetCPUColorFilterProc() const = 0; + + virtual std::shared_ptr Clone() const = 0; }; /******************************************************************************* @@ -68,6 +70,9 @@ class BlendColorFilter final : public ColorFilter { // |ColorFilter| ColorFilterProc GetCPUColorFilterProc() const override; + // |ColorFilter| + std::shared_ptr Clone() const override; + private: BlendMode blend_mode_; Color color_; @@ -91,6 +96,9 @@ class MatrixColorFilter final : public ColorFilter { // |ColorFilter| ColorFilterProc GetCPUColorFilterProc() const override; + // |ColorFilter| + std::shared_ptr Clone() const override; + private: ColorMatrix color_matrix_; }; @@ -112,6 +120,9 @@ class SrgbToLinearColorFilter final : public ColorFilter { // |ColorFilter| ColorFilterProc GetCPUColorFilterProc() const override; + + // |ColorFilter| + std::shared_ptr Clone() const override; }; /******************************************************************************* @@ -131,6 +142,9 @@ class LinearToSrgbColorFilter final : public ColorFilter { // |ColorFilter| ColorFilterProc GetCPUColorFilterProc() const override; + + // |ColorFilter| + std::shared_ptr Clone() const override; }; } // namespace impeller diff --git a/engine/src/flutter/impeller/aiks/image_filter.cc b/engine/src/flutter/impeller/aiks/image_filter.cc new file mode 100644 index 0000000000..6420e8c293 --- /dev/null +++ b/engine/src/flutter/impeller/aiks/image_filter.cc @@ -0,0 +1,206 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/aiks/image_filter.h" +#include "impeller/entity/contents/filters/filter_contents.h" +#include "impeller/entity/contents/filters/inputs/filter_input.h" + +namespace impeller { + +/******************************************************************************* + ******* ImageFilter + ******************************************************************************/ + +ImageFilter::ImageFilter() = default; + +ImageFilter::~ImageFilter() = default; + +std::shared_ptr ImageFilter::MakeBlur( + Sigma sigma_x, + Sigma sigma_y, + FilterContents::BlurStyle blur_style, + Entity::TileMode tile_mode) { + return std::make_shared(sigma_x, sigma_y, blur_style, + tile_mode); +} + +std::shared_ptr ImageFilter::MakeDilate(Radius radius_x, + Radius radius_y) { + return std::make_shared(radius_x, radius_y); +} + +std::shared_ptr ImageFilter::MakeErode(Radius radius_x, + Radius radius_y) { + return std::make_shared(radius_x, radius_y); +} + +std::shared_ptr ImageFilter::MakeMatrix( + const Matrix& matrix, + SamplerDescriptor sampler_descriptor) { + return std::make_shared(matrix, + std::move(sampler_descriptor)); +} + +std::shared_ptr ImageFilter::MakeCompose( + const ImageFilter& inner, + const ImageFilter& outer) { + return std::make_shared(inner, outer); +} + +std::shared_ptr ImageFilter::MakeFromColorFilter( + const ColorFilter& color_filter) { + return std::make_shared(color_filter); +} + +std::shared_ptr ImageFilter::MakeLocalMatrix( + const Matrix& matrix, + const ImageFilter& internal_filter) { + return std::make_shared(matrix, internal_filter); +} + +std::shared_ptr ImageFilter::GetFilterContents() const { + return WrapInput(FilterInput::Make(Rect())); +} + +/******************************************************************************* + ******* BlurImageFilter + ******************************************************************************/ + +BlurImageFilter::BlurImageFilter(Sigma sigma_x, + Sigma sigma_y, + FilterContents::BlurStyle blur_style, + Entity::TileMode tile_mode) + : sigma_x_(sigma_x), + sigma_y_(sigma_y), + blur_style_(blur_style), + tile_mode_(tile_mode) {} + +BlurImageFilter::~BlurImageFilter() = default; + +std::shared_ptr BlurImageFilter::WrapInput( + const FilterInput::Ref& input) const { + return FilterContents::MakeGaussianBlur(input, sigma_x_, sigma_y_, + blur_style_, tile_mode_); +} + +std::shared_ptr BlurImageFilter::Clone() const { + return std::make_shared(*this); +} + +/******************************************************************************* + ******* DilateImageFilter + ******************************************************************************/ + +DilateImageFilter::DilateImageFilter(Radius radius_x, Radius radius_y) + : radius_x_(radius_x), radius_y_(radius_y) {} + +DilateImageFilter::~DilateImageFilter() = default; + +std::shared_ptr DilateImageFilter::WrapInput( + const FilterInput::Ref& input) const { + return FilterContents::MakeMorphology(input, radius_x_, radius_y_, + FilterContents::MorphType::kDilate); +} + +std::shared_ptr DilateImageFilter::Clone() const { + return std::make_shared(*this); +} + +/******************************************************************************* + ******* ErodeImageFilter + ******************************************************************************/ + +ErodeImageFilter::ErodeImageFilter(Radius radius_x, Radius radius_y) + : radius_x_(radius_x), radius_y_(radius_y) {} + +ErodeImageFilter::~ErodeImageFilter() = default; + +std::shared_ptr ErodeImageFilter::WrapInput( + const FilterInput::Ref& input) const { + return FilterContents::MakeMorphology(input, radius_x_, radius_y_, + FilterContents::MorphType::kErode); +} + +std::shared_ptr ErodeImageFilter::Clone() const { + return std::make_shared(*this); +} + +/******************************************************************************* + ******* MatrixImageFilter + ******************************************************************************/ + +MatrixImageFilter::MatrixImageFilter(const Matrix& matrix, + SamplerDescriptor sampler_descriptor) + : matrix_(matrix), sampler_descriptor_(std::move(sampler_descriptor)) {} + +MatrixImageFilter::~MatrixImageFilter() = default; + +std::shared_ptr MatrixImageFilter::WrapInput( + const FilterInput::Ref& input) const { + return FilterContents::MakeMatrixFilter(input, matrix_, sampler_descriptor_); +} + +std::shared_ptr MatrixImageFilter::Clone() const { + return std::make_shared(*this); +} + +/******************************************************************************* + ******* ComposeImageFilter + ******************************************************************************/ + +ComposeImageFilter::ComposeImageFilter(const ImageFilter& inner, + const ImageFilter& outer) + : inner_(inner.Clone()), outer_(outer.Clone()) {} + +ComposeImageFilter::~ComposeImageFilter() = default; + +std::shared_ptr ComposeImageFilter::WrapInput( + const FilterInput::Ref& input) const { + return outer_->WrapInput(FilterInput::Make(inner_->WrapInput(input))); +} + +std::shared_ptr ComposeImageFilter::Clone() const { + return std::make_shared(*this); +} + +/******************************************************************************* + ******* ColorImageFilter + ******************************************************************************/ + +ColorImageFilter::ColorImageFilter(const ColorFilter& color_filter) + : color_filter_(color_filter.Clone()) {} + +ColorImageFilter::~ColorImageFilter() = default; + +std::shared_ptr ColorImageFilter::WrapInput( + const FilterInput::Ref& input) const { + return color_filter_->WrapWithGPUColorFilter(input, false); +} + +std::shared_ptr ColorImageFilter::Clone() const { + return std::make_shared(*this); +} + +/******************************************************************************* + ******* LocalMatrixImageFilter + ******************************************************************************/ + +LocalMatrixImageFilter::LocalMatrixImageFilter( + const Matrix& matrix, + const ImageFilter& internal_filter) + : matrix_(matrix), internal_filter_(internal_filter.Clone()) {} + +LocalMatrixImageFilter::~LocalMatrixImageFilter() = default; + +std::shared_ptr LocalMatrixImageFilter::WrapInput( + const FilterInput::Ref& input) const { + return FilterContents::MakeLocalMatrixFilter( + FilterInput::Make(internal_filter_->WrapInput(input)), matrix_); +} + +std::shared_ptr LocalMatrixImageFilter::Clone() const { + return std::make_shared(*this); +} + +} // namespace impeller diff --git a/engine/src/flutter/impeller/aiks/image_filter.h b/engine/src/flutter/impeller/aiks/image_filter.h new file mode 100644 index 0000000000..0c5bdd3889 --- /dev/null +++ b/engine/src/flutter/impeller/aiks/image_filter.h @@ -0,0 +1,229 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/aiks/color_filter.h" +#include "impeller/core/sampler_descriptor.h" +#include "impeller/entity/contents/filters/filter_contents.h" +#include "impeller/entity/entity.h" +#include "impeller/geometry/matrix.h" +#include "impeller/geometry/sigma.h" + +namespace impeller { + +struct Paint; + +/******************************************************************************* + ******* ImageFilter + ******************************************************************************/ + +class ImageFilter { + public: + ImageFilter(); + + virtual ~ImageFilter(); + + static std::shared_ptr MakeBlur( + Sigma sigma_x, + Sigma sigma_y, + FilterContents::BlurStyle blur_style, + Entity::TileMode tile_mode); + + static std::shared_ptr MakeDilate(Radius radius_x, + Radius radius_y); + + static std::shared_ptr MakeErode(Radius radius_x, + Radius radius_y); + + static std::shared_ptr MakeMatrix( + const Matrix& matrix, + SamplerDescriptor sampler_descriptor); + + static std::shared_ptr MakeCompose(const ImageFilter& inner, + const ImageFilter& outer); + + static std::shared_ptr MakeFromColorFilter( + const ColorFilter& color_filter); + + static std::shared_ptr MakeLocalMatrix( + const Matrix& matrix, + const ImageFilter& internal_filter); + + /// @brief Generate a new FilterContents using this filter's configuration. + /// + /// This is the same as WrapInput, except no input is set. The input + /// for the filter chain can be set later using. + /// FilterContents::SetLeafInputs(). + /// + /// @see `FilterContents::SetLeafInputs` + std::shared_ptr GetFilterContents() const; + + /// @brief Wraps the given filter input with a GPU-based image filter. + virtual std::shared_ptr WrapInput( + const FilterInput::Ref& input) const = 0; + + virtual std::shared_ptr Clone() const = 0; +}; + +/******************************************************************************* + ******* BlurImageFilter + ******************************************************************************/ + +class BlurImageFilter : public ImageFilter { + public: + BlurImageFilter(Sigma sigma_x, + Sigma sigma_y, + FilterContents::BlurStyle blur_style, + Entity::TileMode tile_mode); + + ~BlurImageFilter() override; + + // |ImageFilter| + std::shared_ptr WrapInput( + const FilterInput::Ref& input) const override; + + // |ImageFilter| + std::shared_ptr Clone() const override; + + private: + Sigma sigma_x_; + Sigma sigma_y_; + FilterContents::BlurStyle blur_style_; + Entity::TileMode tile_mode_; +}; + +/******************************************************************************* + ******* DilateImageFilter + ******************************************************************************/ + +class DilateImageFilter : public ImageFilter { + public: + DilateImageFilter(Radius radius_x, Radius radius_y); + + ~DilateImageFilter() override; + + // |ImageFilter| + std::shared_ptr WrapInput( + const FilterInput::Ref& input) const override; + + // |ImageFilter| + std::shared_ptr Clone() const override; + + private: + Radius radius_x_; + Radius radius_y_; +}; + +/******************************************************************************* + ******* ErodeImageFilter + ******************************************************************************/ + +class ErodeImageFilter : public ImageFilter { + public: + ErodeImageFilter(Radius radius_x, Radius radius_y); + + ~ErodeImageFilter() override; + + // |ImageFilter| + std::shared_ptr WrapInput( + const FilterInput::Ref& input) const override; + + // |ImageFilter| + std::shared_ptr Clone() const override; + + private: + Radius radius_x_; + Radius radius_y_; +}; + +/******************************************************************************* + ******* MatrixImageFilter + ******************************************************************************/ + +class MatrixImageFilter : public ImageFilter { + public: + MatrixImageFilter(const Matrix& matrix, SamplerDescriptor sampler_descriptor); + + ~MatrixImageFilter() override; + + // |ImageFilter| + std::shared_ptr WrapInput( + const FilterInput::Ref& input) const override; + + // |ImageFilter| + std::shared_ptr Clone() const override; + + private: + Matrix matrix_; + SamplerDescriptor sampler_descriptor_; +}; + +/******************************************************************************* + ******* ComposeImageFilter + ******************************************************************************/ + +class ComposeImageFilter : public ImageFilter { + public: + ComposeImageFilter(const ImageFilter& inner, const ImageFilter& outer); + + ~ComposeImageFilter() override; + + // |ImageFilter| + std::shared_ptr WrapInput( + const FilterInput::Ref& input) const override; + + // |ImageFilter| + std::shared_ptr Clone() const override; + + private: + std::shared_ptr inner_; + std::shared_ptr outer_; +}; + +/******************************************************************************* + ******* ColorImageFilter + ******************************************************************************/ + +class ColorImageFilter : public ImageFilter { + public: + explicit ColorImageFilter(const ColorFilter& color_filter); + + ~ColorImageFilter() override; + + // |ImageFilter| + std::shared_ptr WrapInput( + const FilterInput::Ref& input) const override; + + // |ImageFilter| + std::shared_ptr Clone() const override; + + private: + std::shared_ptr color_filter_; +}; + +/******************************************************************************* + ******* LocalMatrixImageFilter + ******************************************************************************/ + +class LocalMatrixImageFilter : public ImageFilter { + public: + LocalMatrixImageFilter(const Matrix& matrix, + const ImageFilter& internal_filter); + + ~LocalMatrixImageFilter() override; + + // |ImageFilter| + std::shared_ptr WrapInput( + const FilterInput::Ref& input) const override; + + // |ImageFilter| + std::shared_ptr Clone() const override; + + private: + Matrix matrix_; + std::shared_ptr internal_filter_; +}; + +} // namespace impeller diff --git a/engine/src/flutter/impeller/aiks/paint.cc b/engine/src/flutter/impeller/aiks/paint.cc index 07751623e2..a7bb6d00d3 100644 --- a/engine/src/flutter/impeller/aiks/paint.cc +++ b/engine/src/flutter/impeller/aiks/paint.cc @@ -85,11 +85,13 @@ std::shared_ptr Paint::WithImageFilter( std::shared_ptr input, const Matrix& effect_transform, bool is_subpass) const { - if (image_filter) { - input = - image_filter(FilterInput::Make(input), effect_transform, is_subpass); + if (!image_filter) { + return input; } - return input; + auto filter = image_filter->WrapInput(FilterInput::Make(input)); + filter->SetIsForSubpass(is_subpass); + filter->SetEffectTransform(effect_transform); + return filter; } std::shared_ptr Paint::WithColorFilter( @@ -146,7 +148,7 @@ std::shared_ptr Paint::MaskBlurDescriptor::CreateMaskBlur( if (color_source_contents->IsSolidColor() && !color_filter) { return FilterContents::MakeGaussianBlur( FilterInput::Make(color_source_contents), sigma, sigma, style, - Entity::TileMode::kDecal, Matrix()); + Entity::TileMode::kDecal); } /// 1. Create an opaque white mask of the original geometry. @@ -158,8 +160,7 @@ std::shared_ptr Paint::MaskBlurDescriptor::CreateMaskBlur( /// 2. Blur the mask. auto blurred_mask = FilterContents::MakeGaussianBlur( - FilterInput::Make(mask), sigma, sigma, style, Entity::TileMode::kDecal, - Matrix()); + FilterInput::Make(mask), sigma, sigma, style, Entity::TileMode::kDecal); /// 3. Replace the geometry of the original color source with a rectangle that /// covers the full region of the blurred mask. Note that geometry is in @@ -193,10 +194,9 @@ std::shared_ptr Paint::MaskBlurDescriptor::CreateMaskBlur( bool is_solid_color) const { if (is_solid_color) { return FilterContents::MakeGaussianBlur(input, sigma, sigma, style, - Entity::TileMode::kDecal, Matrix()); + Entity::TileMode::kDecal); } - return FilterContents::MakeBorderMaskBlur(input, sigma, sigma, style, - Matrix()); + return FilterContents::MakeBorderMaskBlur(input, sigma, sigma, style); } bool Paint::HasColorFilter() const { diff --git a/engine/src/flutter/impeller/aiks/paint.h b/engine/src/flutter/impeller/aiks/paint.h index c8a3434525..d77a298d5b 100644 --- a/engine/src/flutter/impeller/aiks/paint.h +++ b/engine/src/flutter/impeller/aiks/paint.h @@ -9,6 +9,7 @@ #include "flutter/fml/macros.h" #include "impeller/aiks/color_filter.h" #include "impeller/aiks/color_source.h" +#include "impeller/aiks/image_filter.h" #include "impeller/entity/contents/contents.h" #include "impeller/entity/contents/filters/color_filter_contents.h" #include "impeller/entity/contents/filters/filter_contents.h" @@ -62,7 +63,7 @@ struct Paint { BlendMode blend_mode = BlendMode::kSourceOver; bool invert_colors = false; - ImageFilterProc image_filter = nullptr; + std::shared_ptr image_filter; std::shared_ptr color_filter; std::optional mask_blur_descriptor; diff --git a/engine/src/flutter/impeller/display_list/dl_dispatcher.cc b/engine/src/flutter/impeller/display_list/dl_dispatcher.cc index 2ba40a1646..edcd363a7e 100644 --- a/engine/src/flutter/impeller/display_list/dl_dispatcher.cc +++ b/engine/src/flutter/impeller/display_list/dl_dispatcher.cc @@ -560,7 +560,7 @@ void DlDispatcher::setMaskFilter(const flutter::DlMaskFilter* filter) { } } -static Paint::ImageFilterProc ToImageFilterProc( +static std::shared_ptr ToImageFilter( const flutter::DlImageFilter* filter) { if (filter == nullptr) { return nullptr; @@ -572,16 +572,8 @@ static Paint::ImageFilterProc ToImageFilterProc( auto sigma_x = Sigma(blur->sigma_x()); auto sigma_y = Sigma(blur->sigma_y()); auto tile_mode = ToTileMode(blur->tile_mode()); - - return [sigma_x, sigma_y, tile_mode](const FilterInput::Ref& input, - const Matrix& effect_transform, - bool is_subpass) { - return FilterContents::MakeGaussianBlur( - input, sigma_x, sigma_y, FilterContents::BlurStyle::kNormal, - tile_mode, effect_transform); - }; - - break; + return ImageFilter::MakeBlur( + sigma_x, sigma_y, FilterContents::BlurStyle::kNormal, tile_mode); } case flutter::DlImageFilterType::kDilate: { auto dilate = filter->asDilate(); @@ -591,14 +583,7 @@ static Paint::ImageFilterProc ToImageFilterProc( } auto radius_x = Radius(dilate->radius_x()); auto radius_y = Radius(dilate->radius_y()); - return [radius_x, radius_y](FilterInput::Ref input, - const Matrix& effect_transform, - bool is_subpass) { - return FilterContents::MakeMorphology( - std::move(input), radius_x, radius_y, - FilterContents::MorphType::kDilate, effect_transform); - }; - break; + return ImageFilter::MakeDilate(radius_x, radius_y); } case flutter::DlImageFilterType::kErode: { auto erode = filter->asErode(); @@ -608,51 +593,31 @@ static Paint::ImageFilterProc ToImageFilterProc( } auto radius_x = Radius(erode->radius_x()); auto radius_y = Radius(erode->radius_y()); - return [radius_x, radius_y](FilterInput::Ref input, - const Matrix& effect_transform, - bool is_subpass) { - return FilterContents::MakeMorphology( - std::move(input), radius_x, radius_y, - FilterContents::MorphType::kErode, effect_transform); - }; - break; + return ImageFilter::MakeErode(radius_x, radius_y); } case flutter::DlImageFilterType::kMatrix: { auto matrix_filter = filter->asMatrix(); FML_DCHECK(matrix_filter); auto matrix = ToMatrix(matrix_filter->matrix()); auto desc = ToSamplerDescriptor(matrix_filter->sampling()); - return [matrix, desc](FilterInput::Ref input, - const Matrix& effect_transform, bool is_subpass) { - return FilterContents::MakeMatrixFilter(std::move(input), matrix, desc, - effect_transform, is_subpass); - }; - break; + return ImageFilter::MakeMatrix(matrix, desc); } case flutter::DlImageFilterType::kCompose: { auto compose = filter->asCompose(); FML_DCHECK(compose); - auto outer = compose->outer(); - auto inner = compose->inner(); - auto outer_proc = ToImageFilterProc(outer.get()); - auto inner_proc = ToImageFilterProc(inner.get()); - if (!outer_proc) { - return inner_proc; + auto outer_dl_filter = compose->outer(); + auto inner_dl_filter = compose->inner(); + auto outer_filter = ToImageFilter(outer_dl_filter.get()); + auto inner_filter = ToImageFilter(inner_dl_filter.get()); + if (!outer_filter) { + return inner_filter; } - if (!inner_proc) { - return outer_proc; + if (!inner_filter) { + return outer_filter; } - FML_DCHECK(outer_proc && inner_proc); - return [outer_filter = outer_proc, inner_filter = inner_proc]( - FilterInput::Ref input, const Matrix& effect_transform, - bool is_subpass) { - auto contents = - inner_filter(std::move(input), effect_transform, is_subpass); - contents = outer_filter(FilterInput::Make(contents), effect_transform, - is_subpass); - return contents; - }; - break; + FML_DCHECK(outer_filter && inner_filter); + + return ImageFilter::MakeCompose(*inner_filter, *outer_filter); } case flutter::DlImageFilterType::kColorFilter: { auto color_filter_image_filter = filter->asColorFilter(); @@ -662,16 +627,11 @@ static Paint::ImageFilterProc ToImageFilterProc( if (!color_filter) { return nullptr; } - return [filter = color_filter](FilterInput::Ref input, - const Matrix& effect_transform, - bool is_subpass) { - // When color filters are used as image filters, set the color filter's - // "absorb opacity" flag to false. For image filters, the snapshot - // opacity needs to be deferred until the result of the filter chain is - // being blended with the layer. - return filter->WrapWithGPUColorFilter(std::move(input), false); - }; - break; + // When color filters are used as image filters, set the color filter's + // "absorb opacity" flag to false. For image filters, the snapshot + // opacity needs to be deferred until the result of the filter chain is + // being blended with the layer. + return ImageFilter::MakeFromColorFilter(*color_filter); } case flutter::DlImageFilterType::kLocalMatrix: { auto local_matrix_filter = filter->asLocalMatrix(); @@ -679,29 +639,20 @@ static Paint::ImageFilterProc ToImageFilterProc( auto internal_filter = local_matrix_filter->image_filter(); FML_DCHECK(internal_filter); - auto image_filter_proc = ToImageFilterProc(internal_filter.get()); - if (!image_filter_proc) { + auto image_filter = ToImageFilter(internal_filter.get()); + if (!image_filter) { return nullptr; } auto matrix = ToMatrix(local_matrix_filter->matrix()); - - return [matrix, filter_proc = image_filter_proc]( - FilterInput::Ref input, const Matrix& effect_transform, - bool is_subpass) { - std::shared_ptr filter = - filter_proc(std::move(input), effect_transform, is_subpass); - return FilterContents::MakeLocalMatrixFilter(FilterInput::Make(filter), - matrix); - }; - break; + return ImageFilter::MakeLocalMatrix(matrix, *image_filter); } } } // |flutter::DlOpReceiver| void DlDispatcher::setImageFilter(const flutter::DlImageFilter* filter) { - paint_.image_filter = ToImageFilterProc(filter); + paint_.image_filter = ToImageFilter(filter); } // |flutter::DlOpReceiver| @@ -715,7 +666,7 @@ void DlDispatcher::saveLayer(const SkRect* bounds, const flutter::DlImageFilter* backdrop) { auto paint = options.renders_with_attributes() ? paint_ : Paint{}; canvas_.SaveLayer(paint, skia_conversions::ToRect(bounds), - ToImageFilterProc(backdrop)); + ToImageFilter(backdrop)); } // |flutter::DlOpReceiver| diff --git a/engine/src/flutter/impeller/entity/contents/filters/filter_contents.cc b/engine/src/flutter/impeller/entity/contents/filters/filter_contents.cc index 02ba98f022..ae48cec892 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/filter_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/filters/filter_contents.cc @@ -37,8 +37,7 @@ std::shared_ptr FilterContents::MakeDirectionalGaussianBlur( BlurStyle blur_style, Entity::TileMode tile_mode, bool is_second_pass, - Sigma secondary_sigma, - const Matrix& effect_transform) { + Sigma secondary_sigma) { auto blur = std::make_shared(); blur->SetInputs({std::move(input)}); blur->SetSigma(sigma); @@ -47,7 +46,6 @@ std::shared_ptr FilterContents::MakeDirectionalGaussianBlur( blur->SetTileMode(tile_mode); blur->SetIsSecondPass(is_second_pass); blur->SetSecondarySigma(secondary_sigma); - blur->SetEffectTransform(effect_transform); return blur; } @@ -56,14 +54,12 @@ std::shared_ptr FilterContents::MakeGaussianBlur( Sigma sigma_x, Sigma sigma_y, BlurStyle blur_style, - Entity::TileMode tile_mode, - const Matrix& effect_transform) { - auto x_blur = MakeDirectionalGaussianBlur(input, sigma_x, Point(1, 0), - BlurStyle::kNormal, tile_mode, - false, {}, effect_transform); + Entity::TileMode tile_mode) { + auto x_blur = MakeDirectionalGaussianBlur( + input, sigma_x, Point(1, 0), BlurStyle::kNormal, tile_mode, false, {}); auto y_blur = MakeDirectionalGaussianBlur(FilterInput::Make(x_blur), sigma_y, Point(0, 1), blur_style, tile_mode, - true, sigma_x, effect_transform); + true, sigma_x); return y_blur; } @@ -71,13 +67,11 @@ std::shared_ptr FilterContents::MakeBorderMaskBlur( FilterInput::Ref input, Sigma sigma_x, Sigma sigma_y, - BlurStyle blur_style, - const Matrix& effect_transform) { + BlurStyle blur_style) { auto filter = std::make_shared(); filter->SetInputs({std::move(input)}); filter->SetSigma(sigma_x, sigma_y); filter->SetBlurStyle(blur_style); - filter->SetEffectTransform(effect_transform); return filter; } @@ -85,14 +79,12 @@ std::shared_ptr FilterContents::MakeDirectionalMorphology( FilterInput::Ref input, Radius radius, Vector2 direction, - MorphType morph_type, - const Matrix& effect_transform) { + MorphType morph_type) { auto filter = std::make_shared(); filter->SetInputs({std::move(input)}); filter->SetRadius(radius); filter->SetDirection(direction); filter->SetMorphType(morph_type); - filter->SetEffectTransform(effect_transform); return filter; } @@ -100,28 +92,22 @@ std::shared_ptr FilterContents::MakeMorphology( FilterInput::Ref input, Radius radius_x, Radius radius_y, - MorphType morph_type, - const Matrix& effect_transform) { - auto x_morphology = MakeDirectionalMorphology( - std::move(input), radius_x, Point(1, 0), morph_type, effect_transform); - auto y_morphology = - MakeDirectionalMorphology(FilterInput::Make(x_morphology), radius_y, - Point(0, 1), morph_type, effect_transform); + MorphType morph_type) { + auto x_morphology = MakeDirectionalMorphology(std::move(input), radius_x, + Point(1, 0), morph_type); + auto y_morphology = MakeDirectionalMorphology( + FilterInput::Make(x_morphology), radius_y, Point(0, 1), morph_type); return y_morphology; } std::shared_ptr FilterContents::MakeMatrixFilter( FilterInput::Ref input, const Matrix& matrix, - const SamplerDescriptor& desc, - const Matrix& effect_transform, - bool is_subpass) { + const SamplerDescriptor& desc) { auto filter = std::make_shared(); filter->SetInputs({std::move(input)}); filter->SetMatrix(matrix); filter->SetSamplerDescriptor(desc); - filter->SetEffectTransform(effect_transform); - filter->SetIsSubpass(is_subpass); return filter; } @@ -153,8 +139,12 @@ void FilterContents::SetInputs(FilterInput::Vector inputs) { inputs_ = std::move(inputs); } -void FilterContents::SetEffectTransform(Matrix effect_transform) { +void FilterContents::SetEffectTransform(const Matrix& effect_transform) { effect_transform_ = effect_transform; + + for (auto& input : inputs_) { + input->SetEffectTransform(effect_transform); + } } bool FilterContents::Render(const ContentContext& renderer, @@ -296,4 +286,10 @@ void FilterContents::SetLeafInputs(const FilterInput::Vector& inputs) { } } +void FilterContents::SetIsForSubpass(bool is_subpass) { + for (auto& input : inputs_) { + input->SetIsForSubpass(is_subpass); + } +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/entity/contents/filters/filter_contents.h b/engine/src/flutter/impeller/entity/contents/filters/filter_contents.h index 35d135ab9d..e5e7604434 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/filter_contents.h +++ b/engine/src/flutter/impeller/entity/contents/filters/filter_contents.h @@ -38,44 +38,36 @@ class FilterContents : public Contents { BlurStyle blur_style = BlurStyle::kNormal, Entity::TileMode tile_mode = Entity::TileMode::kDecal, bool is_second_pass = false, - Sigma secondary_sigma = {}, - const Matrix& effect_transform = Matrix()); + Sigma secondary_sigma = {}); static std::shared_ptr MakeGaussianBlur( const FilterInput::Ref& input, Sigma sigma_x, Sigma sigma_y, BlurStyle blur_style = BlurStyle::kNormal, - Entity::TileMode tile_mode = Entity::TileMode::kDecal, - const Matrix& effect_transform = Matrix()); + Entity::TileMode tile_mode = Entity::TileMode::kDecal); static std::shared_ptr MakeBorderMaskBlur( FilterInput::Ref input, Sigma sigma_x, Sigma sigma_y, - BlurStyle blur_style = BlurStyle::kNormal, - const Matrix& effect_transform = Matrix()); + BlurStyle blur_style = BlurStyle::kNormal); static std::shared_ptr MakeDirectionalMorphology( FilterInput::Ref input, Radius radius, Vector2 direction, - MorphType morph_type, - const Matrix& effect_transform = Matrix()); + MorphType morph_type); - static std::shared_ptr MakeMorphology( - FilterInput::Ref input, - Radius radius_x, - Radius radius_y, - MorphType morph_type, - const Matrix& effect_transform = Matrix()); + static std::shared_ptr MakeMorphology(FilterInput::Ref input, + Radius radius_x, + Radius radius_y, + MorphType morph_type); static std::shared_ptr MakeMatrixFilter( FilterInput::Ref input, const Matrix& matrix, - const SamplerDescriptor& desc, - const Matrix& effect_transform, - bool is_subpass); + const SamplerDescriptor& desc); static std::shared_ptr MakeLocalMatrixFilter( FilterInput::Ref input, @@ -99,7 +91,11 @@ class FilterContents : public Contents { /// @brief Sets the transform which gets appended to the effect of this /// filter. Note that this is in addition to the entity's transform. - void SetEffectTransform(Matrix effect_transform); + /// + /// This is useful for subpass rendering scenarios where it's + /// difficult to encode the current transform of the layer into the + /// Entity being rendered. + void SetEffectTransform(const Matrix& effect_transform); /// @brief Create an Entity that renders this filter's output. std::optional GetEntity( @@ -137,11 +133,19 @@ class FilterContents : public Contents { /// children. bool IsLeaf() const; - /// @brief Replaces the leaf of all leaf `FilterContents` with a new set - /// of `inputs`. + /// @brief Replaces the set of all leaf `FilterContents` with a new set + /// of `FilterInput`s. /// @see `FilterContents::IsLeaf` void SetLeafInputs(const FilterInput::Vector& inputs); + /// @brief Marks this filter chain as applying in a subpass scenario. + /// + /// Subpasses render in screenspace, and this setting informs filters + /// that the current transformation matrix of the entity is not stored + /// in the Entity transformation matrix. Instead, the effect transform + /// is used in this case. + virtual void SetIsForSubpass(bool is_subpass); + private: virtual std::optional GetFilterCoverage( const FilterInput::Vector& inputs, diff --git a/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc b/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc index 5861247b96..e1af31f8be 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc +++ b/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc @@ -67,4 +67,12 @@ void FilterContentsFilterInput::SetLeafInputs( filter_->SetLeafInputs(inputs); } +void FilterContentsFilterInput::SetEffectTransform(const Matrix& matrix) { + filter_->SetEffectTransform(matrix); +} + +void FilterContentsFilterInput::SetIsForSubpass(bool is_for_subpass) { + filter_->SetIsForSubpass(is_for_subpass); +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h b/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h index a10a5e5afe..7aaaa3aebd 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h +++ b/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h @@ -42,6 +42,12 @@ class FilterContentsFilterInput final : public FilterInput { // |FilterInput| void SetLeafInputs(const FilterInput::Vector& inputs) override; + // |FilterInput| + virtual void SetEffectTransform(const Matrix& matrix) override; + + // |FilterInput| + virtual void SetIsForSubpass(bool is_for_subpass) override; + private: explicit FilterContentsFilterInput(std::shared_ptr filter); diff --git a/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input.cc b/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input.cc index 612a1f7221..1cb4744468 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input.cc +++ b/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input.cc @@ -82,4 +82,8 @@ bool FilterInput::IsLeaf() const { void FilterInput::SetLeafInputs(const FilterInput::Vector& inputs) {} +void FilterInput::SetEffectTransform(const Matrix& matrix) {} + +void FilterInput::SetIsForSubpass(bool is_for_subpass) {} + } // namespace impeller diff --git a/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input.h b/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input.h index 3e84f1e761..598ca1b560 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input.h +++ b/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input.h @@ -77,6 +77,12 @@ class FilterInput { /// of `inputs`. /// @see `FilterInput::IsLeaf` virtual void SetLeafInputs(const FilterInput::Vector& inputs); + + /// @brief Sets the effect transform of filter inputs. + virtual void SetEffectTransform(const Matrix& matrix); + + /// @brief Turns on subpass mode for filter inputs. + virtual void SetIsForSubpass(bool is_for_subpass); }; } // namespace impeller diff --git a/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input_unittests.cc b/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input_unittests.cc index 1f3d55fbc9..a4683664eb 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input_unittests.cc +++ b/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input_unittests.cc @@ -32,8 +32,8 @@ TEST(FilterInputTest, IsLeaf) { ColorFilterContents::MakeBlend(BlendMode::kSource, {}); ASSERT_TRUE(leaf->IsLeaf()); - auto base = ColorFilterContents::MakeMatrixFilter( - FilterInput::Make(leaf), Matrix(), {}, Matrix(), false); + auto base = ColorFilterContents::MakeMatrixFilter(FilterInput::Make(leaf), + Matrix(), {}); ASSERT_TRUE(leaf->IsLeaf()); ASSERT_FALSE(base->IsLeaf()); @@ -44,8 +44,8 @@ TEST(FilterInputTest, SetCoverageInputs) { ColorFilterContents::MakeBlend(BlendMode::kSource, {}); ASSERT_TRUE(leaf->IsLeaf()); - auto base = ColorFilterContents::MakeMatrixFilter( - FilterInput::Make(leaf), Matrix(), {}, Matrix(), false); + auto base = ColorFilterContents::MakeMatrixFilter(FilterInput::Make(leaf), + Matrix(), {}); { auto result = base->GetCoverage({}); diff --git a/engine/src/flutter/impeller/entity/contents/filters/matrix_filter_contents.cc b/engine/src/flutter/impeller/entity/contents/filters/matrix_filter_contents.cc index 55bc05f28a..5483b4d7d3 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/matrix_filter_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/filters/matrix_filter_contents.cc @@ -14,8 +14,9 @@ void MatrixFilterContents::SetMatrix(Matrix matrix) { matrix_ = matrix; } -void MatrixFilterContents::SetIsSubpass(bool is_subpass) { - is_subpass_ = is_subpass; +void MatrixFilterContents::SetIsForSubpass(bool is_subpass) { + is_for_subpass_ = is_subpass; + FilterContents::SetIsForSubpass(is_subpass); } void MatrixFilterContents::SetSamplerDescriptor(SamplerDescriptor desc) { @@ -49,7 +50,8 @@ std::optional MatrixFilterContents::RenderFilter( // mentioned above). And so we sneak the subpass's captured CTM in through the // effect transform. - auto transform = is_subpass_ ? effect_transform : entity.GetTransformation(); + auto transform = + is_for_subpass_ ? effect_transform : entity.GetTransformation(); snapshot->transform = transform * // matrix_ * // transform.Invert() * // @@ -72,7 +74,8 @@ std::optional MatrixFilterContents::GetFilterCoverage( if (!coverage.has_value()) { return std::nullopt; } - auto& m = is_subpass_ ? effect_transform : inputs[0]->GetTransform(entity); + auto& m = + is_for_subpass_ ? effect_transform : inputs[0]->GetTransform(entity); auto transform = m * // matrix_ * // m.Invert(); // diff --git a/engine/src/flutter/impeller/entity/contents/filters/matrix_filter_contents.h b/engine/src/flutter/impeller/entity/contents/filters/matrix_filter_contents.h index 902f6a8c98..a2e4c5f239 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/matrix_filter_contents.h +++ b/engine/src/flutter/impeller/entity/contents/filters/matrix_filter_contents.h @@ -17,7 +17,8 @@ class MatrixFilterContents final : public FilterContents { void SetMatrix(Matrix matrix); - void SetIsSubpass(bool is_subpass); + // |FilterContents| + void SetIsForSubpass(bool is_for_subpass) override; void SetSamplerDescriptor(SamplerDescriptor desc); @@ -39,7 +40,7 @@ class MatrixFilterContents final : public FilterContents { Matrix matrix_; SamplerDescriptor sampler_descriptor_ = {}; - bool is_subpass_ = false; + bool is_for_subpass_ = false; FML_DISALLOW_COPY_AND_ASSIGN(MatrixFilterContents); }; diff --git a/engine/src/flutter/impeller/entity/entity_pass.cc b/engine/src/flutter/impeller/entity/entity_pass.cc index 873a1db8ec..29a737e8f5 100644 --- a/engine/src/flutter/impeller/entity/entity_pass.cc +++ b/engine/src/flutter/impeller/entity/entity_pass.cc @@ -604,13 +604,15 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( // right thing. return EntityPass::EntityResult::Failure(); } - - element_entity.SetCapture(capture.CreateChild("Entity (Subpass texture)")); + Capture subpass_texture_capture = + capture.CreateChild("Entity (Subpass texture)"); + element_entity.SetCapture(subpass_texture_capture); element_entity.SetContents(std::move(offscreen_texture_contents)); element_entity.SetStencilDepth(subpass->stencil_depth_); element_entity.SetBlendMode(subpass->blend_mode_); - element_entity.SetTransformation(Matrix::MakeTranslation( - Vector3(subpass_coverage->origin - global_pass_position))); + element_entity.SetTransformation(subpass_texture_capture.AddMatrix( + "Transform", Matrix::MakeTranslation(Vector3(subpass_coverage->origin - + global_pass_position)))); } else { FML_UNREACHABLE(); } diff --git a/engine/src/flutter/impeller/entity/entity_unittests.cc b/engine/src/flutter/impeller/entity/entity_unittests.cc index 127d1c0bf9..b5d36ca2ab 100644 --- a/engine/src/flutter/impeller/entity/entity_unittests.cc +++ b/engine/src/flutter/impeller/entity/entity_unittests.cc @@ -1191,12 +1191,11 @@ TEST_P(EntityTest, MorphologyFilter) { input = texture; input_size = input_rect.size; - auto effect_transform = Matrix::MakeScale( - Vector2{effect_transform_scale, effect_transform_scale}); - auto contents = FilterContents::MakeMorphology( FilterInput::Make(input), Radius{radius[0]}, Radius{radius[1]}, - morphology_types[selected_morphology_type], effect_transform); + morphology_types[selected_morphology_type]); + contents->SetEffectTransform(Matrix::MakeScale( + Vector2{effect_transform_scale, effect_transform_scale})); auto ctm = Matrix::MakeScale(GetContentScale()) * Matrix::MakeTranslation(Vector3(offset[0], offset[1])) *