Skip to content

Commit 18399a5

Browse files
chinmaygardednfield
authored andcommitted
Implement Canvas::SaveLayer with bounds.
1 parent 6d87108 commit 18399a5

9 files changed

+107
-4
lines changed

impeller/aiks/aiks_unittests.cc

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,5 +153,59 @@ TEST_F(AiksTest, CanPerformSkew) {
153153
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
154154
}
155155

156+
TEST_F(AiksTest, CanPerformSaveLayerWithBounds) {
157+
Canvas canvas;
158+
159+
Paint red;
160+
red.color = Color::Red();
161+
162+
Paint green;
163+
green.color = Color::Green();
164+
165+
Paint blue;
166+
blue.color = Color::Blue();
167+
168+
Paint save;
169+
save.color = Color::Black();
170+
171+
canvas.SaveLayer(save, Rect{0, 0, 50, 50});
172+
173+
canvas.DrawRect({0, 0, 100, 100}, red);
174+
canvas.DrawRect({10, 10, 100, 100}, green);
175+
canvas.DrawRect({20, 20, 100, 100}, blue);
176+
177+
canvas.Restore();
178+
179+
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
180+
}
181+
182+
TEST_F(
183+
AiksTest,
184+
DISABLED_CanPerformSaveLayerWithBoundsAndLargerIntermediateIsNotAllocated) {
185+
Canvas canvas;
186+
187+
Paint red;
188+
red.color = Color::Red();
189+
190+
Paint green;
191+
green.color = Color::Green();
192+
193+
Paint blue;
194+
blue.color = Color::Blue();
195+
196+
Paint save;
197+
save.color = Color::Black().WithAlpha(0.5);
198+
199+
canvas.SaveLayer(save, Rect{0, 0, 100000, 100000});
200+
201+
canvas.DrawRect({0, 0, 100, 100}, red);
202+
canvas.DrawRect({10, 10, 100, 100}, green);
203+
canvas.DrawRect({20, 20, 100, 100}, blue);
204+
205+
canvas.Restore();
206+
207+
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
208+
}
209+
156210
} // namespace testing
157211
} // namespace impeller

impeller/aiks/canvas.cc

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,18 @@ void Canvas::DrawCircle(Point center, Scalar radius, Paint paint) {
106106

107107
void Canvas::SaveLayer(Paint paint, std::optional<Rect> bounds) {
108108
GetCurrentPass().SetDelegate(
109-
std::make_unique<PaintPassDelegate>(std::move(paint)));
109+
std::make_unique<PaintPassDelegate>(std::move(paint), bounds));
110+
110111
Save(true);
112+
113+
if (bounds.has_value()) {
114+
// Render target switches due to a save layer can be elided. In such cases
115+
// where passes are collapsed into their parent, the clipping effect to
116+
// the size of the render target that would have been allocated will be
117+
// absent. Explicitly add back a clip to reproduce that behavior. Since
118+
// clips never require a render target switch, this is a cheap operation.
119+
ClipPath(PathBuilder{}.AddRect(bounds.value()).CreatePath());
120+
}
111121
}
112122

113123
void Canvas::ClipPath(Path path) {

impeller/aiks/paint_pass_delegate.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@
88

99
namespace impeller {
1010

11-
PaintPassDelegate::PaintPassDelegate(Paint paint) : paint_(std::move(paint)) {}
11+
PaintPassDelegate::PaintPassDelegate(Paint paint, std::optional<Rect> coverage)
12+
: paint_(std::move(paint)), coverage_(std::move(coverage)) {}
1213

1314
// |EntityPassDelgate|
1415
PaintPassDelegate::~PaintPassDelegate() = default;
1516

17+
// |EntityPassDelgate|
18+
std::optional<Rect> PaintPassDelegate::GetCoverageRect() {
19+
return coverage_;
20+
}
21+
1622
// |EntityPassDelgate|
1723
bool PaintPassDelegate::CanElide() {
1824
return paint_.color.IsTransparent();

impeller/aiks/paint_pass_delegate.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#pragma once
66

7+
#include <optional>
8+
79
#include "flutter/fml/macros.h"
810
#include "impeller/aiks/paint.h"
911
#include "impeller/entity/entity_pass_delegate.h"
@@ -12,11 +14,14 @@ namespace impeller {
1214

1315
class PaintPassDelegate final : public EntityPassDelegate {
1416
public:
15-
PaintPassDelegate(Paint paint);
17+
PaintPassDelegate(Paint paint, std::optional<Rect> coverage);
1618

1719
// |EntityPassDelgate|
1820
~PaintPassDelegate() override;
1921

22+
// |EntityPassDelegate|
23+
std::optional<Rect> GetCoverageRect() override;
24+
2025
// |EntityPassDelgate|
2126
bool CanElide() override;
2227

@@ -29,6 +34,7 @@ class PaintPassDelegate final : public EntityPassDelegate {
2934

3035
private:
3136
const Paint paint_;
37+
const std::optional<Rect> coverage_;
3238

3339
FML_DISALLOW_COPY_AND_ASSIGN(PaintPassDelegate);
3440
};

impeller/entity/entity_pass.cc

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ const EntityPass::Subpasses& EntityPass::GetSubpasses() const {
7474
return subpasses_;
7575
}
7676

77+
Rect EntityPass::GetSubpassCoverage(const EntityPass& subpass) const {
78+
auto subpass_coverage = subpass.GetCoverageRect();
79+
auto delegate_coverage =
80+
delegate_->GetCoverageRect().value_or(subpass_coverage);
81+
Rect coverage;
82+
coverage.origin = subpass_coverage.origin;
83+
// TODO(csg): This must still be restricted to the max texture size. Or,
84+
// decide if this must be done by the allocator.
85+
coverage.size = subpass_coverage.size.Min(delegate_coverage.size);
86+
return coverage;
87+
}
88+
7789
EntityPass* EntityPass::AddSubpass(std::unique_ptr<EntityPass> pass) {
7890
if (!pass) {
7991
return nullptr;
@@ -103,7 +115,7 @@ bool EntityPass::Render(ContentRenderer& renderer,
103115
continue;
104116
}
105117

106-
const auto subpass_coverage = subpass->GetCoverageRect();
118+
const auto subpass_coverage = GetSubpassCoverage(*subpass);
107119

108120
if (subpass_coverage.IsEmpty()) {
109121
// It is not an error to have an empty subpass. But subpasses that can't

impeller/entity/entity_pass.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ class EntityPass {
6666
std::unique_ptr<EntityPassDelegate> delegate_ =
6767
EntityPassDelegate::MakeDefault();
6868

69+
Rect GetSubpassCoverage(const EntityPass& subpass) const;
70+
6971
FML_DISALLOW_COPY_AND_ASSIGN(EntityPass);
7072
};
7173

impeller/entity/entity_pass_delegate.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,19 @@ class DefaultEntityPassDelegate final : public EntityPassDelegate {
1414
public:
1515
DefaultEntityPassDelegate() = default;
1616

17+
// |EntityPassDelegate|
1718
~DefaultEntityPassDelegate() override = default;
1819

20+
// |EntityPassDelegate|
21+
std::optional<Rect> GetCoverageRect() override { return std::nullopt; }
22+
23+
// |EntityPassDelegate|
1924
bool CanElide() override { return false; }
2025

26+
// |EntityPassDelegate|
2127
bool CanCollapseIntoParentPass() override { return true; }
2228

29+
// |EntityPassDelegate|
2330
std::shared_ptr<Contents> CreateContentsForSubpassTarget(
2431
std::shared_ptr<Texture> target) override {
2532
// Not possible since this pass always collapses into its parent.

impeller/entity/entity_pass_delegate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class EntityPassDelegate {
1818

1919
EntityPassDelegate();
2020

21+
virtual std::optional<Rect> GetCoverageRect() = 0;
22+
2123
virtual ~EntityPassDelegate();
2224

2325
virtual bool CanElide() = 0;

impeller/geometry/size.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ struct TSize {
5959
return {width - s.width, height - s.height};
6060
}
6161

62+
constexpr TSize Min(const TSize& s) const { return Intersection(s); }
63+
64+
constexpr TSize Max(const TSize& s) const { return Union(s); }
65+
6266
constexpr TSize Union(const TSize& o) const {
6367
return {
6468
std::max(width, o.width),

0 commit comments

Comments
 (0)