[Impeller] Fix unsafe access for clip stencil coverage (#39595) (#39728)
Co-authored-by: Brandon DeRosier <bdero@google.com>
diff --git a/impeller/entity/contents/clip_contents.cc b/impeller/entity/contents/clip_contents.cc
index d958822..5fbd10e 100644
--- a/impeller/entity/contents/clip_contents.cc
+++ b/impeller/entity/contents/clip_contents.cc
@@ -47,10 +47,18 @@
return {.type = StencilCoverage::Type::kAppend,
.coverage = current_stencil_coverage};
case Entity::ClipOperation::kIntersect:
+ if (!geometry_) {
+ return {.type = StencilCoverage::Type::kAppend,
+ .coverage = std::nullopt};
+ }
+ auto coverage = geometry_->GetCoverage(entity.GetTransformation());
+ if (!coverage.has_value() || !current_stencil_coverage.has_value()) {
+ return {.type = StencilCoverage::Type::kAppend,
+ .coverage = std::nullopt};
+ }
return {
.type = StencilCoverage::Type::kAppend,
- .coverage = current_stencil_coverage->Intersection(
- geometry_->GetCoverage(entity.GetTransformation()).value()),
+ .coverage = current_stencil_coverage->Intersection(coverage.value()),
};
}
FML_UNREACHABLE();
diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc
index 4114c8a..0dde60a 100644
--- a/impeller/entity/entity_unittests.cc
+++ b/impeller/entity/entity_unittests.cc
@@ -1463,6 +1463,66 @@
}
}
+TEST_P(EntityTest, ClipContentsGetStencilCoverageIsCorrect) {
+ // Intersection: No stencil coverage, no geometry.
+ {
+ auto clip = std::make_shared<ClipContents>();
+ clip->SetClipOperation(Entity::ClipOperation::kIntersect);
+ auto result = clip->GetStencilCoverage(Entity{}, Rect{});
+
+ ASSERT_FALSE(result.coverage.has_value());
+ }
+
+ // Intersection: No stencil coverage, with geometry.
+ {
+ auto clip = std::make_shared<ClipContents>();
+ clip->SetClipOperation(Entity::ClipOperation::kIntersect);
+ clip->SetGeometry(Geometry::MakeFillPath(
+ PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 100, 100)).TakePath()));
+ auto result = clip->GetStencilCoverage(Entity{}, Rect{});
+
+ ASSERT_FALSE(result.coverage.has_value());
+ }
+
+ // Intersection: With stencil coverage, no geometry.
+ {
+ auto clip = std::make_shared<ClipContents>();
+ clip->SetClipOperation(Entity::ClipOperation::kIntersect);
+ auto result =
+ clip->GetStencilCoverage(Entity{}, Rect::MakeLTRB(0, 0, 100, 100));
+
+ ASSERT_FALSE(result.coverage.has_value());
+ }
+
+ // Intersection: With stencil coverage, with geometry.
+ {
+ auto clip = std::make_shared<ClipContents>();
+ clip->SetClipOperation(Entity::ClipOperation::kIntersect);
+ clip->SetGeometry(Geometry::MakeFillPath(
+ PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 50, 50)).TakePath()));
+ auto result =
+ clip->GetStencilCoverage(Entity{}, Rect::MakeLTRB(0, 0, 100, 100));
+
+ ASSERT_TRUE(result.coverage.has_value());
+ ASSERT_RECT_NEAR(result.coverage.value(), Rect::MakeLTRB(0, 0, 50, 50));
+ ASSERT_EQ(result.type, Contents::StencilCoverage::Type::kAppend);
+ }
+
+ // Difference: With stencil coverage, with geometry.
+ {
+ auto clip = std::make_shared<ClipContents>();
+ clip->SetClipOperation(Entity::ClipOperation::kDifference);
+ clip->SetGeometry(Geometry::MakeFillPath(
+ PathBuilder{}.AddRect(Rect::MakeLTRB(0, 0, 50, 50)).TakePath()));
+ auto result =
+ clip->GetStencilCoverage(Entity{}, Rect::MakeLTRB(0, 0, 100, 100));
+
+ ASSERT_TRUE(result.coverage.has_value());
+ ASSERT_RECT_NEAR(result.coverage.value(), Rect::MakeLTRB(0, 0, 100, 100));
+ ASSERT_EQ(result.type, Contents::StencilCoverage::Type::kAppend);
+ }
+}
+
TEST_P(EntityTest, RRectShadowTest) {
auto callback = [&](ContentContext& context, RenderPass& pass) {
static Color color = Color::Red();