diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index c45bc42a5f63b..ada6d50cb0f5c 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -114,7 +114,7 @@ void Canvas::DrawPath(Path path, Paint paint) { entity.SetPath(std::move(path)); entity.SetStencilDepth(GetStencilDepth()); entity.SetBlendMode(paint.blend_mode); - entity.SetContents(paint.CreateContentsForEntity()); + entity.SetContents(paint.WithFilters(paint.CreateContentsForEntity())); GetCurrentPass().AddEntity(std::move(entity)); } @@ -242,7 +242,7 @@ void Canvas::DrawImageRect(std::shared_ptr image, entity.SetPath(PathBuilder{}.AddRect(dest).TakePath()); entity.SetBlendMode(paint.blend_mode); entity.SetStencilDepth(GetStencilDepth()); - entity.SetContents(contents); + entity.SetContents(paint.WithFilters(contents, false)); entity.SetTransformation(GetCurrentTransformation()); GetCurrentPass().AddEntity(std::move(entity)); @@ -296,7 +296,7 @@ void Canvas::DrawTextFrame(TextFrame text_frame, Point position, Paint paint) { entity.SetPath({}); entity.SetStencilDepth(GetStencilDepth()); entity.SetBlendMode(paint.blend_mode); - entity.SetContents(std::move(text_contents)); + entity.SetContents(paint.WithFilters(std::move(text_contents), true)); GetCurrentPass().AddEntity(std::move(entity)); } diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index 46a14b5ab36ac..e6afd0f419694 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -33,4 +33,24 @@ std::shared_ptr Paint::CreateContentsForEntity() const { return nullptr; } +std::shared_ptr Paint::WithFilters( + std::shared_ptr input, + std::optional is_solid_color) const { + bool is_solid_color_val = is_solid_color.value_or(!contents); + + if (mask_blur.has_value()) { + if (is_solid_color_val) { + input = FilterContents::MakeGaussianBlur( + FilterInput::Make(input), mask_blur->sigma, mask_blur->sigma, + mask_blur->blur_style); + } else { + input = FilterContents::MakeBorderMaskBlur( + FilterInput::Make(input), mask_blur->sigma, mask_blur->sigma, + mask_blur->blur_style); + } + } + + return input; +} + } // namespace impeller diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index be9262d0225ac..07af116b2a2c8 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -8,12 +8,18 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" +#include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" #include "impeller/geometry/color.h" namespace impeller { +struct MaskBlur { + FilterContents::BlurStyle blur_style; + FilterContents::Sigma sigma; +}; + struct Paint { enum class Style { kFill, @@ -27,9 +33,25 @@ struct Paint { Scalar stroke_miter = 4.0; Style style = Style::kFill; Entity::BlendMode blend_mode = Entity::BlendMode::kSourceOver; + std::optional mask_blur; std::shared_ptr contents; std::shared_ptr CreateContentsForEntity() const; + + /// @brief Wrap this paint's configured filters to the given contents. + /// @param[in] input The contents to wrap with paint's filters. + /// @param[in] is_solid_color Affects mask blurring behavior. If false, use + /// the image border for mask blurring. If true, + /// do a Gaussian blur to achieve the mask + /// blurring effect for arbitrary paths. If unset, + /// use the current paint configuration to infer + /// the result. + /// @return The filter-wrapped contents. If there are no filters that need + /// to be wrapped for the current paint configuration, the + /// original contents is returned. + std::shared_ptr WithFilters( + std::shared_ptr input, + std::optional is_solid_color = std::nullopt) const; }; } // namespace impeller diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 2f9abb0010bc3..fc8142dcf16ec 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -7,6 +7,7 @@ #include #include "flutter/fml/trace_event.h" +#include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/linear_gradient_contents.h" #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" @@ -253,15 +254,33 @@ void DisplayListDispatcher::setPathEffect(sk_sp effect) { UNIMPLEMENTED; } +static FilterContents::BlurStyle ToBlurStyle(SkBlurStyle blur_style) { + switch (blur_style) { + case kNormal_SkBlurStyle: + return FilterContents::BlurStyle::kNormal; + case kSolid_SkBlurStyle: + return FilterContents::BlurStyle::kSolid; + case kOuter_SkBlurStyle: + return FilterContents::BlurStyle::kOuter; + case kInner_SkBlurStyle: + return FilterContents::BlurStyle::kInner; + } +} + // |flutter::Dispatcher| void DisplayListDispatcher::setMaskFilter(const flutter::DlMaskFilter* filter) { // Needs https://github.com/flutter/flutter/issues/95434 if (filter == nullptr) { - // Reset everything + paint_.mask_blur = std::nullopt; return; } switch (filter->type()) { - case flutter::DlMaskFilterType::kBlur: + case flutter::DlMaskFilterType::kBlur: { + auto blur = filter->asBlur(); + paint_.mask_blur = {.blur_style = ToBlurStyle(blur->style()), + .sigma = FilterContents::Sigma(blur->sigma())}; + break; + } case flutter::DlMaskFilterType::kUnknown: UNIMPLEMENTED; break; diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index 321832e721393..425e7dfe4af7b 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -8,6 +8,8 @@ #include "third_party/skia/include/core/SkPathBuilder.h" #include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/display_list_mask_filter.h" +#include "flutter/display_list/types.h" #include "flutter/testing/testing.h" #include "impeller/display_list/display_list_image_impeller.h" #include "impeller/display_list/display_list_playground.h" @@ -174,5 +176,36 @@ TEST_P(DisplayListTest, StrokedPathsDrawCorrectly) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } +TEST_F(DisplayListTest, CanDrawWithMaskBlur) { + auto texture = CreateTextureForFixture("embarcadero.jpg"); + flutter::DisplayListBuilder builder; + + // Mask blurred image. + { + auto filter = flutter::DlBlurMaskFilter(kNormal_SkBlurStyle, 10.0f); + builder.setMaskFilter(&filter); + builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100), + SkSamplingOptions{}, true); + } + + // Mask blurred filled path. + { + builder.setColor(SK_ColorYELLOW); + auto filter = flutter::DlBlurMaskFilter(kOuter_SkBlurStyle, 10.0f); + 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); + builder.setMaskFilter(&filter); + builder.drawTextBlob( + SkTextBlob::MakeFromString("Testing", CreateTestFont()), 220, 170); + } + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index a2c0764e23607..98c5a584dcf5a 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -47,7 +47,7 @@ std::optional Contents::RenderToSnapshot( RenderPass& pass) -> bool { Entity sub_entity; sub_entity.SetPath(entity.GetPath()); - sub_entity.SetBlendMode(Entity::BlendMode::kSource); + sub_entity.SetBlendMode(Entity::BlendMode::kSourceOver); sub_entity.SetTransformation( Matrix::MakeTranslation(Vector3(-bounds->origin)) * entity.GetTransformation());