23
23
namespace impeller {
24
24
25
25
Canvas::Canvas () {
26
- Initialize (Rect::MakeLTRB (- 1e9 , - 1e9 , 1e9 , 1e9 ) );
26
+ Initialize (std::nullopt );
27
27
}
28
28
29
29
Canvas::Canvas (Rect cull_rect) {
@@ -37,11 +37,11 @@ Canvas::Canvas(IRect cull_rect) {
37
37
38
38
Canvas::~Canvas () = default ;
39
39
40
- void Canvas::Initialize (Rect cull_rect) {
40
+ void Canvas::Initialize (std::optional< Rect > cull_rect) {
41
41
initial_cull_rect_ = cull_rect;
42
42
base_pass_ = std::make_unique<EntityPass>();
43
43
current_pass_ = base_pass_.get ();
44
- xformation_stack_.emplace_back (CanvasStackEntry{.clip_bounds = cull_rect});
44
+ xformation_stack_.emplace_back (CanvasStackEntry{.cull_rect = cull_rect});
45
45
lazy_glyph_atlas_ = std::make_shared<LazyGlyphAtlas>();
46
46
FML_DCHECK (GetSaveCount () == 1u );
47
47
FML_DCHECK (base_pass_->GetSubpassesDepth () == 1u );
@@ -64,7 +64,7 @@ void Canvas::Save(
64
64
std::optional<EntityPass::BackdropFilterProc> backdrop_filter) {
65
65
auto entry = CanvasStackEntry{};
66
66
entry.xformation = xformation_stack_.back ().xformation ;
67
- entry.clip_bounds = xformation_stack_.back ().clip_bounds ;
67
+ entry.cull_rect = xformation_stack_.back ().cull_rect ;
68
68
entry.stencil_depth = xformation_stack_.back ().stencil_depth ;
69
69
if (create_subpass) {
70
70
entry.is_subpass = true ;
@@ -120,9 +120,13 @@ const Matrix& Canvas::GetCurrentTransformation() const {
120
120
return xformation_stack_.back ().xformation ;
121
121
}
122
122
123
- const Rect Canvas::GetCurrentLocalClipBounds () const {
124
- Matrix inverse = xformation_stack_.back ().xformation .Invert ();
125
- return xformation_stack_.back ().clip_bounds .TransformBounds (inverse);
123
+ const std::optional<Rect > Canvas::GetCurrentLocalCullingBounds () const {
124
+ auto cull_rect = xformation_stack_.back ().cull_rect ;
125
+ if (cull_rect.has_value ()) {
126
+ Matrix inverse = xformation_stack_.back ().xformation .Invert ();
127
+ cull_rect = cull_rect.value ().TransformBounds (inverse);
128
+ }
129
+ return cull_rect;
126
130
}
127
131
128
132
void Canvas::Translate (const Vector3& offset) {
@@ -273,22 +277,61 @@ void Canvas::DrawCircle(Point center, Scalar radius, const Paint& paint) {
273
277
}
274
278
275
279
void Canvas::ClipPath (const Path& path, Entity::ClipOperation clip_op) {
276
- ClipGeometry (Geometry::MakeFillPath (path), clip_op, path.GetBoundingBox ());
280
+ ClipGeometry (Geometry::MakeFillPath (path), clip_op);
281
+ if (clip_op == Entity::ClipOperation::kIntersect ) {
282
+ auto bounds = path.GetBoundingBox ();
283
+ if (bounds.has_value ()) {
284
+ IntersectCulling (bounds.value ());
285
+ }
286
+ }
277
287
}
278
288
279
289
void Canvas::ClipRect (const Rect & rect, Entity::ClipOperation clip_op) {
280
- ClipGeometry (Geometry::MakeRect (rect), clip_op, rect);
290
+ ClipGeometry (Geometry::MakeRect (rect), clip_op);
291
+ switch (clip_op) {
292
+ case Entity::ClipOperation::kIntersect :
293
+ IntersectCulling (rect);
294
+ break ;
295
+ case Entity::ClipOperation::kDifference :
296
+ SubtractCulling (rect);
297
+ break ;
298
+ }
281
299
}
282
300
283
301
void Canvas::ClipRRect (const Rect & rect,
284
302
Scalar corner_radius,
285
303
Entity::ClipOperation clip_op) {
286
- ClipGeometry (Geometry::MakeRRect (rect, corner_radius), clip_op, rect);
304
+ ClipGeometry (Geometry::MakeRRect (rect, corner_radius), clip_op);
305
+ switch (clip_op) {
306
+ case Entity::ClipOperation::kIntersect :
307
+ IntersectCulling (rect);
308
+ break ;
309
+ case Entity::ClipOperation::kDifference :
310
+ if (corner_radius <= 0 ) {
311
+ SubtractCulling (rect);
312
+ } else {
313
+ // We subtract the inner "tall" and "wide" rectangle pieces
314
+ // that fit inside the corners which cover the greatest area
315
+ // without involving the curved corners
316
+ // Since this is a subtract operation, we can subtract each
317
+ // rectangle piece individually without fear of interference.
318
+ if (corner_radius * 2 < rect.size .width ) {
319
+ SubtractCulling (Rect::MakeLTRB (
320
+ rect.GetLeft () + corner_radius, rect.GetTop (),
321
+ rect.GetRight () - corner_radius, rect.GetBottom ()));
322
+ }
323
+ if (corner_radius * 2 < rect.size .height ) {
324
+ SubtractCulling (Rect::MakeLTRB (
325
+ rect.GetLeft (), rect.GetTop () + corner_radius, //
326
+ rect.GetRight (), rect.GetBottom () - corner_radius));
327
+ }
328
+ }
329
+ break ;
330
+ }
287
331
}
288
332
289
333
void Canvas::ClipGeometry (std::unique_ptr<Geometry> geometry,
290
- Entity::ClipOperation clip_op,
291
- std::optional<Rect > geometry_bounds) {
334
+ Entity::ClipOperation clip_op) {
292
335
auto contents = std::make_shared<ClipContents>();
293
336
contents->SetGeometry (std::move (geometry));
294
337
contents->SetClipOperation (clip_op);
@@ -300,23 +343,35 @@ void Canvas::ClipGeometry(std::unique_ptr<Geometry> geometry,
300
343
301
344
GetCurrentPass ().AddEntity (entity);
302
345
303
- if (geometry_bounds.has_value ()) {
304
- if (clip_op == Entity::ClipOperation::kIntersect ) {
305
- Rect transformed =
306
- geometry_bounds.value ().TransformBounds (GetCurrentTransformation ());
307
- auto new_bounds =
308
- xformation_stack_.back ().clip_bounds .Intersection (transformed);
309
- xformation_stack_.back ().clip_bounds = new_bounds.value_or (Rect {});
310
- }
311
- // else if kDifference we could chop off a side of the clip_bounds if
312
- // the subtracted geometry was a rectangle and it spans the height or
313
- // width of the current bounds.
314
- }
315
-
316
346
++xformation_stack_.back ().stencil_depth ;
317
347
xformation_stack_.back ().contains_clips = true ;
318
348
}
319
349
350
+ void Canvas::IntersectCulling (Rect clip_rect) {
351
+ clip_rect = clip_rect.TransformBounds (GetCurrentTransformation ());
352
+ std::optional<Rect >& cull_rect = xformation_stack_.back ().cull_rect ;
353
+ if (cull_rect.has_value ()) {
354
+ cull_rect = cull_rect
355
+ .value () //
356
+ .Intersection (clip_rect) //
357
+ .value_or (Rect {});
358
+ } else {
359
+ cull_rect = clip_rect;
360
+ }
361
+ }
362
+
363
+ void Canvas::SubtractCulling (Rect clip_rect) {
364
+ std::optional<Rect >& cull_rect = xformation_stack_.back ().cull_rect ;
365
+ if (cull_rect.has_value ()) {
366
+ clip_rect = clip_rect.TransformBounds (GetCurrentTransformation ());
367
+ cull_rect = cull_rect
368
+ .value () //
369
+ .Cutout (clip_rect) //
370
+ .value_or (Rect {});
371
+ }
372
+ // else (no cull) diff (any clip) is non-rectangular
373
+ }
374
+
320
375
void Canvas::RestoreClip () {
321
376
Entity entity;
322
377
entity.SetTransformation (GetCurrentTransformation ());
0 commit comments