diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 05fc556df41df..8182261890aa8 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -103,6 +103,30 @@ TEST_F(AiksTest, CanRenderClips) { canvas.ClipPath( PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 500, 500)).CreatePath()); canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint); + // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_F(AiksTest, CanRenderGroupOpacity) { + Canvas canvas; + + Paint red; + red.color = Color::Red(); //.WithAlpha(0.5); + Paint green; + green.color = Color::Green(); //.WithAlpha(0.5); + Paint blue; + blue.color = Color::Blue(); //.WithAlpha(0.5); + + Paint alpha; + alpha.color = Color::Red().WithAlpha(0.5); + + canvas.SaveLayer(alpha); + + canvas.DrawRect({100, 100, 100, 100}, red); + canvas.DrawRect({120, 120, 100, 100}, green); + canvas.DrawRect({140, 140, 100, 100}, blue); + + canvas.Restore(); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index c5d401b2a2814..4d9c3d99a5f64 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -12,15 +12,13 @@ namespace impeller { Canvas::Canvas() { - xformation_stack_.push({}); - passes_.emplace_back(CanvasPass{}); + Save(true); } Canvas::~Canvas() = default; void Canvas::Save() { - FML_DCHECK(xformation_stack_.size() > 0); - xformation_stack_.push(xformation_stack_.top()); + Save(false); } bool Canvas::Restore() { @@ -28,17 +26,16 @@ bool Canvas::Restore() { if (xformation_stack_.size() == 1) { return false; } - xformation_stack_.pop(); + xformation_stack_.pop_back(); return true; } void Canvas::Concat(const Matrix& xformation) { - const auto current_xformation = xformation_stack_.top().xformation; - xformation_stack_.top().xformation = xformation * current_xformation; + xformation_stack_.back().xformation = xformation * GetCurrentTransformation(); } const Matrix& Canvas::GetCurrentTransformation() const { - return xformation_stack_.top().xformation; + return xformation_stack_.back().xformation; } void Canvas::Translate(const Vector3& offset) { @@ -71,6 +68,7 @@ void Canvas::DrawPath(Path path, Paint paint) { entity.SetPath(std::move(path)); entity.SetStencilDepth(GetStencilDepth()); entity.SetContents(paint.CreateContentsForEntity()); + GetCurrentPass().PushEntity(std::move(entity)); } @@ -86,21 +84,23 @@ void Canvas::ClipPath(Path path) { entity.SetPath(std::move(path)); entity.SetContents(std::make_shared()); entity.SetStencilDepth(GetStencilDepth()); + GetCurrentPass().PushEntity(std::move(entity)); } void Canvas::DrawShadow(Path path, Color color, Scalar elevation) {} void Canvas::DrawPicture(const Picture& picture) { - for (const auto& pass : picture.passes) { - CanvasPass new_pass; - for (const auto& entity : pass.GetPassEntities()) { - auto new_entity = entity; - new_entity.SetTransformation(GetCurrentTransformation() * - entity.GetTransformation()); - new_pass.PushEntity(std::move(new_entity)); + for (const auto& stack_entry : picture.entries) { + auto new_stack_entry = stack_entry; + if (auto pass = new_stack_entry.pass) { + for (auto entity : pass->GetPassEntities()) { + entity.IncrementStencilDepth(GetStencilDepth()); + entity.SetTransformation(GetCurrentTransformation() * + entity.GetTransformation()); + } } - passes_.emplace_back(std::move(new_pass)); + xformation_stack_.emplace_back(std::move(new_stack_entry)); } } @@ -145,21 +145,49 @@ void Canvas::DrawImageRect(std::shared_ptr image, Picture Canvas::EndRecordingAsPicture() { Picture picture; - picture.passes = std::move(passes_); + picture.entries = std::move(xformation_stack_); return picture; } CanvasPass& Canvas::GetCurrentPass() { - FML_DCHECK(!passes_.empty()); - return passes_.back(); + for (auto i = xformation_stack_.rbegin(), end = xformation_stack_.rend(); + i < end; i++) { + if (i->pass.has_value()) { + return i->pass.value(); + } + } + FML_UNREACHABLE(); } void Canvas::IncrementStencilDepth() { - ++xformation_stack_.top().stencil_depth; + ++xformation_stack_.back().stencil_depth; } size_t Canvas::GetStencilDepth() const { - return xformation_stack_.top().stencil_depth; + return xformation_stack_.back().stencil_depth; +} + +void Canvas::DrawRect(Rect rect, Paint paint) { + DrawPath(PathBuilder{}.AddRect(rect).CreatePath(), std::move(paint)); +} + +void Canvas::Save(bool create_subpass) { + // Check if called from the ctor. + if (xformation_stack_.empty()) { + FML_DCHECK(create_subpass) << "Base entries must have a pass."; + CanvasStackEntry entry; + entry.pass = CanvasPass{}; + xformation_stack_.emplace_back(std::move(entry)); + } + + auto entry = CanvasStackEntry{}; + + entry.xformation = xformation_stack_.back().xformation; + entry.stencil_depth = xformation_stack_.back().stencil_depth; + if (create_subpass) { + entry.pass = CanvasPass{}; + } + xformation_stack_.emplace_back(std::move(entry)); } } // namespace impeller diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 162d3428f9ec2..b2a5d8c3ff1fa 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -4,9 +4,9 @@ #pragma once +#include #include #include -#include #include #include "flutter/fml/macros.h" @@ -23,11 +23,6 @@ namespace impeller { class Entity; -struct CanvasStackEntry { - Matrix xformation; - size_t stencil_depth = 0u; -}; - class Canvas { public: Canvas(); @@ -56,6 +51,8 @@ class Canvas { void DrawPath(Path path, Paint paint); + void DrawRect(Rect rect, Paint paint); + void DrawImage(std::shared_ptr image, Point offset, Paint paint); void DrawImageRect(std::shared_ptr image, @@ -72,8 +69,7 @@ class Canvas { Picture EndRecordingAsPicture(); private: - std::stack xformation_stack_; - std::vector passes_; + std::deque xformation_stack_; CanvasPass& GetCurrentPass(); @@ -81,6 +77,8 @@ class Canvas { size_t GetStencilDepth() const; + void Save(bool create_subpass); + FML_DISALLOW_COPY_AND_ASSIGN(Canvas); }; diff --git a/impeller/aiks/canvas_pass.cc b/impeller/aiks/canvas_pass.cc index e861e923e5b78..d7e3773f2a73a 100644 --- a/impeller/aiks/canvas_pass.cc +++ b/impeller/aiks/canvas_pass.cc @@ -26,4 +26,27 @@ const Entity& CanvasPass::GetPostProcessingEntity() const { return post_processing_entity_; } +Rect CanvasPass::GetCoverageRect() const { + std::optional min, max; + for (const auto& entity : ops_) { + auto coverage = entity.GetPath().GetMinMaxCoveragePoints(); + if (!coverage.has_value()) { + continue; + } + if (!min.has_value()) { + min = coverage->first; + } + if (!max.has_value()) { + max = coverage->second; + } + min = min->Min(coverage->first); + max = max->Max(coverage->second); + } + if (!min.has_value() || !max.has_value()) { + return {}; + } + const auto diff = *max - *min; + return {min->x, min->y, diff.x, diff.y}; +} + } // namespace impeller diff --git a/impeller/aiks/canvas_pass.h b/impeller/aiks/canvas_pass.h index 0074492bd4625..5d7494bac133b 100644 --- a/impeller/aiks/canvas_pass.h +++ b/impeller/aiks/canvas_pass.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "flutter/fml/macros.h" #include "impeller/entity/contents.h" @@ -20,6 +21,8 @@ class CanvasPass { void PushEntity(Entity entity); + Rect GetCoverageRect() const; + const std::vector& GetPassEntities() const; void SetPostProcessingEntity(Entity entity); @@ -31,4 +34,10 @@ class CanvasPass { Entity post_processing_entity_; }; +struct CanvasStackEntry { + Matrix xformation; + size_t stencil_depth = 0u; + std::optional pass; +}; + } // namespace impeller diff --git a/impeller/aiks/picture.h b/impeller/aiks/picture.h index 048a81b98a300..4b5fed22a6e40 100644 --- a/impeller/aiks/picture.h +++ b/impeller/aiks/picture.h @@ -4,8 +4,8 @@ #pragma once +#include #include -#include #include "flutter/fml/macros.h" #include "impeller/aiks/canvas_pass.h" @@ -14,7 +14,7 @@ namespace impeller { struct Picture { - std::vector passes; + std::deque entries; }; } // namespace impeller diff --git a/impeller/aiks/picture_renderer.cc b/impeller/aiks/picture_renderer.cc index a37330bfb3cb3..85f9da018fd47 100644 --- a/impeller/aiks/picture_renderer.cc +++ b/impeller/aiks/picture_renderer.cc @@ -29,10 +29,12 @@ bool PictureRenderer::Render(const Surface& surface, return false; } - for (const auto& pass : picture.passes) { - if (!entity_renderer_.RenderEntities(surface, parent_pass, - pass.GetPassEntities())) { - return false; + for (const auto& entry : picture.entries) { + if (auto pass = entry.pass) { + if (!entity_renderer_.RenderEntities(surface, parent_pass, + pass->GetPassEntities())) { + return false; + } } } diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 6e315f95ebd1c..9ff908721cb2d 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -42,4 +42,8 @@ uint32_t Entity::GetStencilDepth() const { return stencil_depth_; } +void Entity::IncrementStencilDepth(uint32_t increment) { + stencil_depth_ += increment; +} + } // namespace impeller diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 89b82d4fe5d01..ef5285c2dbc2e 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -33,6 +33,8 @@ class Entity { void SetStencilDepth(uint32_t stencil_depth); + void IncrementStencilDepth(uint32_t increment); + uint32_t GetStencilDepth() const; private: diff --git a/impeller/geometry/color.h b/impeller/geometry/color.h index dea534b2a3e10..30706da4c678a 100644 --- a/impeller/geometry/color.h +++ b/impeller/geometry/color.h @@ -62,6 +62,10 @@ struct Color { static constexpr Color Blue() { return {0.0, 0.0, 1.0, 1.0}; } + constexpr Color WithAlpha(Scalar new_alpha) const { + return {red, green, blue, new_alpha}; + } + static constexpr Color AliceBlue() { return {240.0 / 255.0, 248.0 / 255.0, 255.0 / 255.0, 1.0}; } diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index facb85173cef7..9959488bdfdf2 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -174,9 +174,20 @@ std::vector Path::CreatePolyline( } Rect Path::GetBoundingBox() const { - if (linears_.empty() && quads_.empty() && cubics_.empty()) { + auto min_max = GetMinMaxCoveragePoints(); + if (!min_max.has_value()) { return {}; } + auto min = min_max->first; + auto max = min_max->second; + const auto difference = max - min; + return {min.x, min.y, difference.x, difference.y}; +} + +std::optional> Path::GetMinMaxCoveragePoints() const { + if (linears_.empty() && quads_.empty() && cubics_.empty()) { + return std::nullopt; + } std::optional min, max; @@ -209,9 +220,7 @@ Rect Path::GetBoundingBox() const { clamp(cubic.Extrema()); } - const auto difference = *max - *min; - - return {min->x, min->y, difference.x, difference.y}; + return std::make_pair(min.value(), max.value()); } } // namespace impeller diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 1eda541179294..78bee57798deb 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include "impeller/geometry/path_component.h" @@ -68,6 +69,8 @@ class Path { Rect GetBoundingBox() const; + std::optional> GetMinMaxCoveragePoints() const; + private: struct ComponentIndexPair { ComponentType type = ComponentType::kLinear; diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index b015a2ee44ba7..ba12338557c52 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -83,6 +84,14 @@ struct TPoint { return dx * dx + dy * dy; } + constexpr TPoint Min(const TPoint& p) const { + return {std::min(x, p.x), std::min(y, p.y)}; + } + + constexpr TPoint Max(const TPoint& p) const { + return {std::max(x, p.x), std::max(y, p.y)}; + } + constexpr Type GetDistance(const TPoint& p) const { return sqrt(GetDistanceSquared(p)); }