[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();