From 8b881c634ebbd800cd7c1f2f42353105e6b245cf Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 18 Apr 2022 14:37:49 -0700 Subject: [PATCH] Compute text coverage; use blend mode in savelayer; conservative pass collapse/elision behavior (#129) --- impeller/aiks/aiks_unittests.cc | 21 +++++++++++++++++++ impeller/aiks/canvas.cc | 3 ++- impeller/aiks/paint_pass_delegate.cc | 4 ++-- impeller/entity/contents/text_contents.cc | 10 +++++++++ impeller/entity/contents/text_contents.h | 3 +++ impeller/entity/entity_pass.cc | 5 +++++ impeller/entity/entity_pass.h | 3 +++ .../backends/skia/text_frame_skia.cc | 3 +-- impeller/typographer/text_frame.cc | 15 +++++++++++++ impeller/typographer/text_frame.h | 8 +++++++ 10 files changed, 70 insertions(+), 5 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 0df8ff3ce6cae..13d3160eb7806 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -418,6 +418,27 @@ TEST_F(AiksTest, CanRenderEmojiTextFrame) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanRenderTextInSaveLayer) { + Canvas canvas; + canvas.DrawPaint({.color = Color::White()}); + canvas.Translate({100, 100}); + canvas.Scale(Vector2{0.5, 0.5}); + + // Blend the layer with the parent pass using kClear to expose the coverage. + canvas.SaveLayer({.blend_mode = Entity::BlendMode::kClear}); + ASSERT_TRUE(RenderTextInCanvas( + GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?", + "Roboto-Regular.ttf")); + canvas.Restore(); + + // Render the text again over the cleared coverage rect. + ASSERT_TRUE(RenderTextInCanvas( + GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?", + "Roboto-Regular.ttf")); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + TEST_F(AiksTest, CanDrawPaint) { Paint paint; paint.color = Color::MediumTurquoise(); diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 1949ad8a40d00..c45bc42a5f63b 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -141,9 +141,10 @@ void Canvas::DrawCircle(Point center, Scalar radius, Paint paint) { void Canvas::SaveLayer(Paint paint, std::optional bounds) { GetCurrentPass().SetDelegate( - std::make_unique(std::move(paint), bounds)); + std::make_unique(paint, bounds)); Save(true); + GetCurrentPass().SetBlendMode(paint.blend_mode); if (bounds.has_value()) { // Render target switches due to a save layer can be elided. In such cases diff --git a/impeller/aiks/paint_pass_delegate.cc b/impeller/aiks/paint_pass_delegate.cc index 1e7b38cbd5c89..34791be892086 100644 --- a/impeller/aiks/paint_pass_delegate.cc +++ b/impeller/aiks/paint_pass_delegate.cc @@ -22,12 +22,12 @@ std::optional PaintPassDelegate::GetCoverageRect() { // |EntityPassDelgate| bool PaintPassDelegate::CanElide() { - return paint_.color.IsTransparent(); + return paint_.blend_mode == Entity::BlendMode::kDestination; } // |EntityPassDelgate| bool PaintPassDelegate::CanCollapseIntoParentPass() { - return paint_.color.IsOpaque(); + return false; } // |EntityPassDelgate| diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index f64957f58f14d..f7a435cb913d1 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -4,6 +4,8 @@ #include "impeller/entity/contents/text_contents.h" +#include + #include "impeller/entity/contents/content_context.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" @@ -48,6 +50,14 @@ void TextContents::SetColor(Color color) { color_ = color; } +std::optional TextContents::GetCoverage(const Entity& entity) const { + auto bounds = frame_.GetBounds(); + if (!bounds.has_value()) { + return std::nullopt; + } + return bounds->TransformBounds(entity.GetTransformation()); +} + bool TextContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { diff --git a/impeller/entity/contents/text_contents.h b/impeller/entity/contents/text_contents.h index 238de9a43e77e..1caa38b77ad0b 100644 --- a/impeller/entity/contents/text_contents.h +++ b/impeller/entity/contents/text_contents.h @@ -34,6 +34,9 @@ class TextContents final : public Contents { void SetColor(Color color); + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index a0e4878ece11b..be77768093dc7 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -210,6 +210,7 @@ bool EntityPass::Render(ContentContext& renderer, .TakePath()); entity.SetContents(std::move(offscreen_texture_contents)); entity.SetStencilDepth(stencil_depth_); + entity.SetBlendMode(subpass->blend_mode_); // Once we have filters being applied for SaveLayer, some special sauce // may be needed here (or in PaintPassDelegate) to ensure the filter // parameters are transformed by the `xformation_` matrix, while continuing @@ -257,4 +258,8 @@ void EntityPass::SetStencilDepth(size_t stencil_depth) { stencil_depth_ = stencil_depth; } +void EntityPass::SetBlendMode(Entity::BlendMode blend_mode) { + blend_mode_ = blend_mode; +} + } // namespace impeller diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 07a48e3530ef0..c8b8c117bda2a 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -58,12 +58,15 @@ class EntityPass { void SetStencilDepth(size_t stencil_depth); + void SetBlendMode(Entity::BlendMode blend_mode); + private: Entities entities_; Subpasses subpasses_; EntityPass* superpass_ = nullptr; Matrix xformation_; size_t stencil_depth_ = 0u; + Entity::BlendMode blend_mode_ = Entity::BlendMode::kSourceOver; std::unique_ptr delegate_ = EntityPassDelegate::MakeDefault(); std::shared_ptr lazy_glyph_atlas_ = diff --git a/impeller/typographer/backends/skia/text_frame_skia.cc b/impeller/typographer/backends/skia/text_frame_skia.cc index 4f92b300fb309..13191b556d7c0 100644 --- a/impeller/typographer/backends/skia/text_frame_skia.cc +++ b/impeller/typographer/backends/skia/text_frame_skia.cc @@ -29,8 +29,7 @@ static Font ToFont(const SkFont& font, Scalar scale) { return Font{std::move(typeface), std::move(metrics)}; } -TextFrame TextFrameFromTextBlob(sk_sp blob, - Scalar scale) { +TextFrame TextFrameFromTextBlob(sk_sp blob, Scalar scale) { if (!blob) { return {}; } diff --git a/impeller/typographer/text_frame.cc b/impeller/typographer/text_frame.cc index f6681e5e4671c..4fe00ce615b80 100644 --- a/impeller/typographer/text_frame.cc +++ b/impeller/typographer/text_frame.cc @@ -10,6 +10,21 @@ TextFrame::TextFrame() = default; TextFrame::~TextFrame() = default; +std::optional TextFrame::GetBounds() const { + std::optional result; + + for (const auto& run : runs_) { + const auto glyph_bounds = run.GetFont().GetMetrics().GetBoundingBox(); + for (const auto& glyph_position : run.GetGlyphPositions()) { + Vector2 position = glyph_position.position * Vector2(); + Rect glyph_rect = Rect(position + glyph_bounds.origin, glyph_bounds.size); + result = result.has_value() ? result->Union(glyph_rect) : glyph_rect; + } + } + + return result; +} + bool TextFrame::AddTextRun(TextRun run) { if (!run.IsValid()) { return false; diff --git a/impeller/typographer/text_frame.h b/impeller/typographer/text_frame.h index a6d86b01d068b..854b6a2fb0bb5 100644 --- a/impeller/typographer/text_frame.h +++ b/impeller/typographer/text_frame.h @@ -21,6 +21,14 @@ class TextFrame { ~TextFrame(); + //---------------------------------------------------------------------------- + /// @brief The conservative bounding box for this text frame. + /// + /// @return The bounds rectangle. If there are no glyphs in this text + /// frame, std::nullopt is returned. + /// + std::optional GetBounds() const; + //---------------------------------------------------------------------------- /// @brief The number of runs in this text frame. ///