diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 4b59ba2a32d69..e91c705f68a12 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -1989,11 +1989,13 @@ TEST_P(AiksTest, OpacityPeepHoleApplicationTest) { TEST_P(AiksTest, DrawPaintAbsorbsClears) { Canvas canvas; canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource}); - canvas.DrawPaint( - {.color = Color::CornflowerBlue(), .blend_mode = BlendMode::kSource}); + canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75), + .blend_mode = BlendMode::kSourceOver}); Picture picture = canvas.EndRecordingAsPicture(); - ASSERT_EQ(picture.pass->GetClearColor(), Color::CornflowerBlue()); + auto expected = Color::Red().Blend(Color::CornflowerBlue().WithAlpha(0.75), + BlendMode::kSourceOver); + ASSERT_EQ(picture.pass->GetClearColor(), expected); std::shared_ptr spy = ContextSpy::Make(); std::shared_ptr real_context = GetContext(); @@ -2010,8 +2012,9 @@ TEST_P(AiksTest, DrawRectAbsorbsClears) { Canvas canvas; canvas.DrawRect({0, 0, 300, 300}, {.color = Color::Red(), .blend_mode = BlendMode::kSource}); - canvas.DrawRect({0, 0, 300, 300}, {.color = Color::CornflowerBlue(), - .blend_mode = BlendMode::kSource}); + canvas.DrawRect({0, 0, 300, 300}, + {.color = Color::CornflowerBlue().WithAlpha(0.75), + .blend_mode = BlendMode::kSourceOver}); std::shared_ptr spy = ContextSpy::Make(); Picture picture = canvas.EndRecordingAsPicture(); @@ -2029,9 +2032,9 @@ TEST_P(AiksTest, DrawRectAbsorbsClearsNegativeRRect) { Canvas canvas; canvas.DrawRRect({0, 0, 300, 300}, 5.0, {.color = Color::Red(), .blend_mode = BlendMode::kSource}); - canvas.DrawRRect( - {0, 0, 300, 300}, 5.0, - {.color = Color::CornflowerBlue(), .blend_mode = BlendMode::kSource}); + canvas.DrawRRect({0, 0, 300, 300}, 5.0, + {.color = Color::CornflowerBlue().WithAlpha(0.75), + .blend_mode = BlendMode::kSourceOver}); std::shared_ptr spy = ContextSpy::Make(); Picture picture = canvas.EndRecordingAsPicture(); @@ -2069,8 +2072,9 @@ TEST_P(AiksTest, DrawRectAbsorbsClearsNegative) { Canvas canvas; canvas.DrawRect({0, 0, 300, 300}, {.color = Color::Red(), .blend_mode = BlendMode::kSource}); - canvas.DrawRect({0, 0, 300, 300}, {.color = Color::CornflowerBlue(), - .blend_mode = BlendMode::kSource}); + canvas.DrawRect({0, 0, 300, 300}, + {.color = Color::CornflowerBlue().WithAlpha(0.75), + .blend_mode = BlendMode::kSourceOver}); std::shared_ptr spy = ContextSpy::Make(); Picture picture = canvas.EndRecordingAsPicture(); @@ -2089,7 +2093,8 @@ TEST_P(AiksTest, CollapsedDrawPaintInSubpass) { canvas.DrawPaint( {.color = Color::Yellow(), .blend_mode = BlendMode::kSource}); canvas.SaveLayer({.blend_mode = BlendMode::kMultiply}); - canvas.DrawPaint({.color = Color::Red(), .blend_mode = BlendMode::kSource}); + canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75), + .blend_mode = BlendMode::kSourceOver}); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } @@ -2171,7 +2176,7 @@ static Picture BlendModeTest(BlendMode blend_mode, canvas.DrawPaint({.color = Color::Black()}); //---------------------------------------------------------------------------- - /// 1. Save layer blending (top left). + /// 1. Save layer blending (top squares). /// canvas.Save(); @@ -2201,7 +2206,7 @@ static Picture BlendModeTest(BlendMode blend_mode, canvas.RestoreToCount(0); //---------------------------------------------------------------------------- - /// 2. CPU blend modes (top left). + /// 2. CPU blend modes (bottom squares). /// canvas.Save(); diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index 3a9ce204c478b..28abe929732ed 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -118,6 +118,11 @@ void Contents::SetInheritedOpacity(Scalar opacity) { "Contents::CanAcceptOpacity returns false."; } +std::optional Contents::AsBackgroundColor(const Entity& entity, + ISize target_size) const { + return {}; +} + bool Contents::ShouldRender(const Entity& entity, const std::optional& stencil_coverage) const { if (!stencil_coverage.has_value()) { diff --git a/impeller/entity/contents/contents.h b/impeller/entity/contents/contents.h index 09d45a5b67b7c..afbd259d1849d 100644 --- a/impeller/entity/contents/contents.h +++ b/impeller/entity/contents/contents.h @@ -117,10 +117,14 @@ class Contents { /// Use of this method is invalid if CanAcceptOpacity returns false. virtual void SetInheritedOpacity(Scalar opacity); + /// @brief Returns a color if this Contents will flood the given `target_size` + /// with a color. This output color is the "Source" color that will be + /// used for the Entity's blend operation. + /// + /// This is useful for absorbing full screen solid color draws into + /// subpass clear colors. virtual std::optional AsBackgroundColor(const Entity& entity, - ISize target_size) const { - return {}; - } + ISize target_size) const; private: std::optional coverage_hint_; diff --git a/impeller/entity/contents/solid_color_contents.cc b/impeller/entity/contents/solid_color_contents.cc index 8814e177b8e58..91cf094f0d5b2 100644 --- a/impeller/entity/contents/solid_color_contents.cc +++ b/impeller/entity/contents/solid_color_contents.cc @@ -104,12 +104,6 @@ std::unique_ptr SolidColorContents::Make(const Path& path, std::optional SolidColorContents::AsBackgroundColor( const Entity& entity, ISize target_size) const { - if (!(GetColor().IsOpaque() && - (entity.GetBlendMode() == BlendMode::kSource || - entity.GetBlendMode() == BlendMode::kSourceOver))) { - return {}; - } - Rect target_rect = Rect::MakeSize(target_size); return GetGeometry()->CoversArea(entity.GetTransformation(), target_rect) ? GetColor() diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index ec1e39be8ecfd..d0b2226735fc5 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -124,6 +124,10 @@ bool Entity::SetInheritedOpacity(Scalar alpha) { return true; } +std::optional Entity::AsBackgroundColor(ISize target_size) const { + return contents_->AsBackgroundColor(*this, target_size); +} + /// @brief Returns true if the blend mode is "destructive", meaning that even /// fully transparent source colors would result in the destination /// getting changed. diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 5577c486c4c8a..2063213b18072 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -92,6 +92,8 @@ class Entity { bool SetInheritedOpacity(Scalar alpha); + std::optional AsBackgroundColor(ISize target_size) const; + private: Matrix transformation_; std::shared_ptr contents_; diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 3ba003272212f..8b1aac582550e 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -37,13 +37,13 @@ namespace impeller { namespace { -std::optional AsBackgroundColor(const EntityPass::Element& element, - ISize target_size) { +std::tuple, BlendMode> ElementAsBackgroundColor( + const EntityPass::Element& element, + ISize target_size) { if (const Entity* entity = std::get_if(&element)) { - std::optional entity_color = - entity->GetContents()->AsBackgroundColor(*entity, target_size); + std::optional entity_color = entity->AsBackgroundColor(target_size); if (entity_color.has_value()) { - return entity_color.value(); + return {entity_color.value(), entity->GetBlendMode()}; } } return {}; @@ -251,7 +251,7 @@ bool EntityPass::Render(ContentContext& renderer, if (!supports_onscreen_backdrop_reads && reads_from_onscreen_backdrop) { auto offscreen_target = CreateRenderTarget( renderer, root_render_target.GetRenderTargetSize(), true, - GetClearColor(render_target.GetRenderTargetSize()).Premultiply()); + GetClearColor(render_target.GetRenderTargetSize())); if (!OnRender(renderer, // renderer offscreen_target.GetRenderTarget() @@ -356,8 +356,7 @@ bool EntityPass::Render(ContentContext& renderer, } // Set up the clear color of the root pass. - color0.clear_color = - GetClearColor(render_target.GetRenderTargetSize()).Premultiply(); + color0.clear_color = GetClearColor(render_target.GetRenderTargetSize()); root_render_target.SetColorAttachment(color0, 0); EntityPassTarget pass_target( @@ -723,13 +722,12 @@ bool EntityPass::OnRender( for (const auto& element : elements_) { // Skip elements that are incorporated into the clear color. if (is_collapsing_clear_colors) { - std::optional entity_color = - AsBackgroundColor(element, root_pass_size); + auto [entity_color, _] = + ElementAsBackgroundColor(element, root_pass_size); if (entity_color.has_value()) { continue; - } else { - is_collapsing_clear_colors = false; } + is_collapsing_clear_colors = false; } EntityResult result = @@ -922,14 +920,14 @@ void EntityPass::SetBlendMode(BlendMode blend_mode) { Color EntityPass::GetClearColor(ISize target_size) const { Color result = Color::BlackTransparent(); for (const Element& element : elements_) { - std::optional entity_color = AsBackgroundColor(element, target_size); - if (entity_color.has_value()) { - result = entity_color.value(); - } else { + auto [entity_color, blend_mode] = + ElementAsBackgroundColor(element, target_size); + if (!entity_color.has_value()) { break; } + result = result.Blend(entity_color.value(), blend_mode); } - return result; + return result.Premultiply(); } void EntityPass::SetBackdropFilter(BackdropFilterProc proc) { diff --git a/impeller/entity/geometry/geometry.cc b/impeller/entity/geometry/geometry.cc index 708d8b1f5f9d7..a252af0c46a21 100644 --- a/impeller/entity/geometry/geometry.cc +++ b/impeller/entity/geometry/geometry.cc @@ -138,4 +138,8 @@ std::unique_ptr Geometry::MakeRect(Rect rect) { return std::make_unique(rect); } +bool Geometry::CoversArea(const Matrix& transform, const Rect& rect) const { + return false; +} + } // namespace impeller diff --git a/impeller/entity/geometry/geometry.h b/impeller/entity/geometry/geometry.h index fa428aca24643..0f80e5456b0c1 100644 --- a/impeller/entity/geometry/geometry.h +++ b/impeller/entity/geometry/geometry.h @@ -90,9 +90,7 @@ class Geometry { /// @return `true` if this geometry will completely cover all fragments in /// `rect` when the `transform` is applied to it. - virtual bool CoversArea(const Matrix& transform, const Rect& rect) const { - return false; - } + virtual bool CoversArea(const Matrix& transform, const Rect& rect) const; }; } // namespace impeller