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 84488213be0..70b83a9613b 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/filter_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/filters/filter_contents.cc @@ -116,6 +116,10 @@ void FilterContents::SetInputs(FilterInput::Vector inputs) { inputs_ = std::move(inputs); } +void FilterContents::SetCoverageCrop(std::optional coverage_crop) { + coverage_crop_ = coverage_crop; +} + bool FilterContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { @@ -146,11 +150,22 @@ bool FilterContents::Render(const ContentContext& renderer, return contents->Render(renderer, e, pass); } +std::optional FilterContents::GetLocalCoverage( + const Entity& local_entity) const { + auto coverage = GetFilterCoverage(inputs_, local_entity); + if (coverage_crop_.has_value() && coverage.has_value()) { + coverage = coverage->Intersection(coverage_crop_.value()); + } + + return coverage; +} + std::optional FilterContents::GetCoverage(const Entity& entity) const { Entity entity_with_local_transform = entity; entity_with_local_transform.SetTransformation( GetTransform(entity.GetTransformation())); - return GetFilterCoverage(inputs_, entity_with_local_transform); + + return GetLocalCoverage(entity_with_local_transform); } std::optional FilterContents::GetFilterCoverage( @@ -186,7 +201,7 @@ std::optional FilterContents::RenderToSnapshot( entity_with_local_transform.SetTransformation( GetTransform(entity.GetTransformation())); - auto coverage = GetFilterCoverage(inputs_, entity_with_local_transform); + auto coverage = GetLocalCoverage(entity_with_local_transform); if (!coverage.has_value() || coverage->IsEmpty()) { return std::nullopt; } 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 4d16b1873fb..43d2af2512b 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/filter_contents.h +++ b/engine/src/flutter/impeller/entity/contents/filters/filter_contents.h @@ -109,13 +109,16 @@ class FilterContents : public Contents { ~FilterContents() override; - /// @brief The input texture sources for this filter. Each input's emitted - /// texture is expected to have premultiplied alpha colors. + /// @brief The input texture sources for this filter. Each input's emitted + /// texture is expected to have premultiplied alpha colors. /// - /// The number of required or optional textures depends on the - /// particular filter's implementation. + /// The number of required or optional textures depends on the + /// particular filter's implementation. void SetInputs(FilterInput::Vector inputs); + /// @brief Screen space bounds to use for cropping the filter output. + void SetCoverageCrop(std::optional coverage_crop); + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, @@ -145,7 +148,10 @@ class FilterContents : public Contents { RenderPass& pass, const Rect& coverage) const = 0; + std::optional GetLocalCoverage(const Entity& local_entity) const; + FilterInput::Vector inputs_; + std::optional coverage_crop_; FML_DISALLOW_COPY_AND_ASSIGN(FilterContents); }; diff --git a/engine/src/flutter/impeller/entity/entity_pass.cc b/engine/src/flutter/impeller/entity/entity_pass.cc index 071ac71c6f4..04231efc5b8 100644 --- a/engine/src/flutter/impeller/entity/entity_pass.cc +++ b/engine/src/flutter/impeller/entity/entity_pass.cc @@ -65,7 +65,7 @@ const std::shared_ptr& EntityPass::GetLazyGlyphAtlas() const { } std::optional EntityPass::GetElementsCoverage( - std::optional coverage_clip) const { + std::optional coverage_crop) const { std::optional result; for (const auto& element : elements_) { std::optional coverage; @@ -73,12 +73,12 @@ std::optional EntityPass::GetElementsCoverage( if (auto entity = std::get_if(&element)) { coverage = entity->GetCoverage(); - if (coverage.has_value() && coverage_clip.has_value()) { - coverage = coverage->Intersection(coverage_clip.value()); + if (coverage.has_value() && coverage_crop.has_value()) { + coverage = coverage->Intersection(coverage_crop.value()); } } else if (auto subpass = std::get_if>(&element)) { - coverage = GetSubpassCoverage(*subpass->get(), coverage_clip); + coverage = GetSubpassCoverage(*subpass->get(), coverage_crop); } else { FML_UNREACHABLE(); } @@ -427,8 +427,10 @@ bool EntityPass::OnRender(ContentContext& renderer, FilterInput::Make(result.entity.GetContents()), FilterInput::Make(texture, result.entity.GetTransformation().Invert())}; - result.entity.SetContents( - FilterContents::MakeBlend(result.entity.GetBlendMode(), inputs)); + auto contents = + FilterContents::MakeBlend(result.entity.GetBlendMode(), inputs); + contents->SetCoverageCrop(result.entity.GetCoverage()); + result.entity.SetContents(std::move(contents)); result.entity.SetBlendMode(Entity::BlendMode::kSourceOver); } diff --git a/engine/src/flutter/impeller/entity/entity_pass.h b/engine/src/flutter/impeller/entity/entity_pass.h index 621f7b57a3a..87c7bddd4cd 100644 --- a/engine/src/flutter/impeller/entity/entity_pass.h +++ b/engine/src/flutter/impeller/entity/entity_pass.h @@ -62,10 +62,10 @@ class EntityPass { std::optional GetSubpassCoverage( const EntityPass& subpass, - std::optional coverage_clip) const; + std::optional coverage_crop) const; std::optional GetElementsCoverage( - std::optional coverage_clip) const; + std::optional coverage_crop) const; private: struct EntityResult { diff --git a/engine/src/flutter/impeller/entity/entity_unittests.cc b/engine/src/flutter/impeller/entity/entity_unittests.cc index 2b96ce49849..85249f6a099 100644 --- a/engine/src/flutter/impeller/entity/entity_unittests.cc +++ b/engine/src/flutter/impeller/entity/entity_unittests.cc @@ -131,6 +131,31 @@ TEST_P(EntityTest, EntityPassCoverageRespectsCoverageLimit) { } } +TEST_P(EntityTest, FilterCoverageRespectsCropRect) { + auto image = CreateTextureForFixture("boston.jpg"); + auto filter = FilterContents::MakeBlend(Entity::BlendMode::kSoftLight, + FilterInput::Make({image})); + + // Without the crop rect (default behavior). + { + auto actual = filter->GetCoverage({}); + auto expected = Rect::MakeSize(Size(image->GetSize())); + + ASSERT_TRUE(actual.has_value()); + ASSERT_RECT_NEAR(actual.value(), expected); + } + + // With the crop rect. + { + auto expected = Rect::MakeLTRB(50, 50, 100, 100); + filter->SetCoverageCrop(expected); + auto actual = filter->GetCoverage({}); + + ASSERT_TRUE(actual.has_value()); + ASSERT_RECT_NEAR(actual.value(), expected); + } +} + TEST_P(EntityTest, CanDrawRect) { Entity entity; entity.SetTransformation(Matrix::MakeScale(GetContentScale()));