diff --git a/engine/src/flutter/impeller/entity/contents/contents.cc b/engine/src/flutter/impeller/entity/contents/contents.cc index 975b45b77e2..2427125a160 100644 --- a/engine/src/flutter/impeller/entity/contents/contents.cc +++ b/engine/src/flutter/impeller/entity/contents/contents.cc @@ -64,13 +64,20 @@ std::optional Contents::RenderToSnapshot( bool msaa_enabled, const std::string& label) const { auto coverage = GetCoverage(entity); - if (coverage_limit.has_value()) { - coverage = coverage->Intersection(*coverage_limit); - } if (!coverage.has_value()) { return std::nullopt; } + // Pad Contents snapshots with 1 pixel borders to ensure correct sampling + // behavior. Not doing so results in a coverage leak for filters that support + // customizing the input sampling mode. Snapshots of contents should be + // theoretically treated as infinite size just like layers. + coverage = coverage->Expand(1); + + if (coverage_limit.has_value()) { + coverage = coverage->Intersection(*coverage_limit); + } + auto texture = renderer.MakeSubpass( label, ISize::Ceil(coverage->size), [&contents = *this, &entity, &coverage](const ContentContext& renderer, diff --git a/engine/src/flutter/impeller/geometry/geometry_unittests.cc b/engine/src/flutter/impeller/geometry/geometry_unittests.cc index 632e0e6aeb6..fc999fd8039 100644 --- a/engine/src/flutter/impeller/geometry/geometry_unittests.cc +++ b/engine/src/flutter/impeller/geometry/geometry_unittests.cc @@ -1783,6 +1783,34 @@ TEST(GeometryTest, RectMakePointBounds) { } } +TEST(GeometryTest, RectExpand) { + { + auto a = Rect::MakeLTRB(100, 100, 200, 200); + auto b = a.Expand(1); + auto expected = Rect::MakeLTRB(99, 99, 201, 201); + ASSERT_RECT_NEAR(b, expected); + } + { + auto a = Rect::MakeLTRB(100, 100, 200, 200); + auto b = a.Expand(-1); + auto expected = Rect::MakeLTRB(101, 101, 199, 199); + ASSERT_RECT_NEAR(b, expected); + } + + { + auto a = Rect::MakeLTRB(100, 100, 200, 200); + auto b = a.Expand(1, 2, 3, 4); + auto expected = Rect::MakeLTRB(99, 98, 203, 204); + ASSERT_RECT_NEAR(b, expected); + } + { + auto a = Rect::MakeLTRB(100, 100, 200, 200); + auto b = a.Expand(-1, -2, -3, -4); + auto expected = Rect::MakeLTRB(101, 102, 197, 196); + ASSERT_RECT_NEAR(b, expected); + } +} + TEST(GeometryTest, RectGetPositive) { { Rect r{100, 200, 300, 400}; diff --git a/engine/src/flutter/impeller/geometry/rect.h b/engine/src/flutter/impeller/geometry/rect.h index 8b74e5edb40..14c337b43a5 100644 --- a/engine/src/flutter/impeller/geometry/rect.h +++ b/engine/src/flutter/impeller/geometry/rect.h @@ -252,6 +252,24 @@ struct TRect { return TRect(origin.x + offset.x, origin.y + offset.y, size.width, size.height); } + + /// @brief Returns a rectangle with expanded edges. Negative expansion + /// results in shrinking. + constexpr TRect Expand(T left, T top, T right, T bottom) { + return TRect(origin.x - left, // + origin.y - top, // + size.width + left + right, // + size.height + top + bottom); + } + + /// @brief Returns a rectangle with expanded edges in all directions. + /// Negative expansion results in shrinking. + constexpr TRect Expand(T amount) { + return TRect(origin.x - amount, // + origin.y - amount, // + size.width + amount * 2, // + size.height + amount * 2); + } }; using Rect = TRect;