-
Notifications
You must be signed in to change notification settings - Fork 6k
Make ui.Canvas.getDestinationClipBounds works with matrix #34835
Changes from 4 commits
31d44f5
739bdfb
56f9eb1
230f9d6
fb1a872
e0b1a0a
1869882
cab83b1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -602,9 +602,7 @@ void DisplayListBuilder::clipRect(const SkRect& rect, | |
| switch (clip_op) { | ||
| case SkClipOp::kIntersect: | ||
| Push<ClipIntersectRectOp>(0, 1, rect, is_aa); | ||
| if (!current_layer_->clip_bounds.intersect(rect)) { | ||
| current_layer_->clip_bounds.setEmpty(); | ||
| } | ||
| intersect(rect); | ||
| break; | ||
| case SkClipOp::kDifference: | ||
| Push<ClipDifferenceRectOp>(0, 1, rect, is_aa); | ||
|
|
@@ -620,9 +618,7 @@ void DisplayListBuilder::clipRRect(const SkRRect& rrect, | |
| switch (clip_op) { | ||
| case SkClipOp::kIntersect: | ||
| Push<ClipIntersectRRectOp>(0, 1, rrect, is_aa); | ||
| if (!current_layer_->clip_bounds.intersect(rrect.getBounds())) { | ||
| current_layer_->clip_bounds.setEmpty(); | ||
| } | ||
| intersect(rrect.getBounds()); | ||
| break; | ||
| case SkClipOp::kDifference: | ||
| Push<ClipDifferenceRRectOp>(0, 1, rrect, is_aa); | ||
|
|
@@ -653,15 +649,24 @@ void DisplayListBuilder::clipPath(const SkPath& path, | |
| switch (clip_op) { | ||
| case SkClipOp::kIntersect: | ||
| Push<ClipIntersectPathOp>(0, 1, path, is_aa); | ||
| if (!current_layer_->clip_bounds.intersect(path.getBounds())) { | ||
| current_layer_->clip_bounds.setEmpty(); | ||
| if (!path.isInverseFillType()) { | ||
| intersect(path.getBounds()); | ||
| } | ||
| break; | ||
| case SkClipOp::kDifference: | ||
| Push<ClipDifferencePathOp>(0, 1, path, is_aa); | ||
| if (path.isInverseFillType()) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a reminder comment here about how this works. Nothing too detailed, just reminding the reader (i.e. me?) that inverted difference is like intersection...
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| intersect(path.getBounds()); | ||
| } | ||
| break; | ||
| } | ||
| } | ||
| void DisplayListBuilder::intersect(const SkRect& rect) { | ||
| SkRect devClipBounds = getTransform().mapRect(rect); | ||
| if (!current_layer_->clip_bounds.intersect(devClipBounds)) { | ||
| current_layer_->clip_bounds.setEmpty(); | ||
| } | ||
| } | ||
| SkRect DisplayListBuilder::getLocalClipBounds() { | ||
| SkM44 inverse; | ||
| if (current_layer_->matrix.invert(&inverse)) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2116,6 +2116,25 @@ TEST(DisplayList, ClipRectAffectsClipBounds) { | |
| ASSERT_EQ(builder.getDestinationClipBounds(), initialDestinationBounds); | ||
| } | ||
|
|
||
| TEST(DisplayList, ClipRectAffectsClipBoundsWithMatrix) { | ||
| DisplayListBuilder builder; | ||
| SkRect clipBounds1 = SkRect::MakeLTRB(0, 0, 10, 10); | ||
| SkRect clipBounds2 = SkRect::MakeLTRB(10, 10, 20, 20); | ||
| builder.save(); | ||
| builder.clipRect(clipBounds1, SkClipOp::kIntersect, false); | ||
| builder.translate(10, 0); | ||
| builder.clipRect(clipBounds1, SkClipOp::kIntersect, false); | ||
| ASSERT_EQ(builder.getDestinationClipBounds(), SkRect::MakeEmpty()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps we should relax these to "get...Bounds().isEmpty()" rather than force a specific empty return value? Are they documented to return exactly that rectangle for an empty condition? I suppose we could just modify the test if we ever return a different value, but an implementation might decide to just max/min the 8 values and let their natural "emptiness" be a return value. We only get a hard-coded 0,0,0,0 rect because we use Skia for the intersections and they decided to return a false boolean with no changes to the rectangle for an empty intersection and so we have to manually force the answer to an empty answer (using MakeEmpty() for convenience).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| builder.restore(); | ||
|
|
||
| builder.save(); | ||
| builder.clipRect(clipBounds1, SkClipOp::kIntersect, false); | ||
| builder.translate(-10, -10); | ||
| builder.clipRect(clipBounds2, SkClipOp::kIntersect, false); | ||
| ASSERT_EQ(builder.getDestinationClipBounds(), clipBounds1); | ||
| builder.restore(); | ||
| } | ||
|
|
||
| TEST(DisplayList, ClipRRectAffectsClipBounds) { | ||
| DisplayListBuilder builder; | ||
| SkRect clipBounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7); | ||
|
|
@@ -2156,6 +2175,28 @@ TEST(DisplayList, ClipRRectAffectsClipBounds) { | |
| ASSERT_EQ(builder.getDestinationClipBounds(), initialDestinationBounds); | ||
| } | ||
|
|
||
| TEST(DisplayList, ClipRRectAffectsClipBoundsWithMatrix) { | ||
| DisplayListBuilder builder; | ||
| SkRect clipBounds1 = SkRect::MakeLTRB(0, 0, 10, 10); | ||
| SkRect clipBounds2 = SkRect::MakeLTRB(10, 10, 20, 20); | ||
| SkRRect clip1 = SkRRect::MakeRectXY(clipBounds1, 3, 2); | ||
| SkRRect clip2 = SkRRect::MakeRectXY(clipBounds2, 3, 2); | ||
|
|
||
| builder.save(); | ||
| builder.clipRRect(clip1, SkClipOp::kIntersect, false); | ||
| builder.translate(10, 0); | ||
| builder.clipRRect(clip1, SkClipOp::kIntersect, false); | ||
| ASSERT_EQ(builder.getDestinationClipBounds(), SkRect::MakeEmpty()); | ||
| builder.restore(); | ||
|
|
||
| builder.save(); | ||
| builder.clipRRect(clip1, SkClipOp::kIntersect, false); | ||
| builder.translate(-10, -10); | ||
| builder.clipRRect(clip2, SkClipOp::kIntersect, false); | ||
| ASSERT_EQ(builder.getDestinationClipBounds(), clipBounds1); | ||
| builder.restore(); | ||
| } | ||
|
|
||
| TEST(DisplayList, ClipPathAffectsClipBounds) { | ||
| DisplayListBuilder builder; | ||
| SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2); | ||
|
|
@@ -2196,6 +2237,27 @@ TEST(DisplayList, ClipPathAffectsClipBounds) { | |
| ASSERT_EQ(builder.getDestinationClipBounds(), initialDestinationBounds); | ||
| } | ||
|
|
||
| TEST(DisplayList, ClipPathAffectsClipBoundsWithMatrix) { | ||
| DisplayListBuilder builder; | ||
| SkRect clipBounds = SkRect::MakeLTRB(0, 0, 10, 10); | ||
| SkPath clip1 = SkPath().addCircle(2.5, 2.5, 2.5).addCircle(7.5, 7.5, 2.5); | ||
| SkPath clip2 = SkPath().addCircle(12.5, 12.5, 2.5).addCircle(17.5, 17.5, 2.5); | ||
|
|
||
| builder.save(); | ||
| builder.clipPath(clip1, SkClipOp::kIntersect, false); | ||
| builder.translate(10, 0); | ||
| builder.clipPath(clip1, SkClipOp::kIntersect, false); | ||
| ASSERT_EQ(builder.getDestinationClipBounds(), SkRect::MakeEmpty()); | ||
| builder.restore(); | ||
|
|
||
| builder.save(); | ||
| builder.clipPath(clip1, SkClipOp::kIntersect, false); | ||
| builder.translate(-10, -10); | ||
| builder.clipPath(clip2, SkClipOp::kIntersect, false); | ||
| ASSERT_EQ(builder.getDestinationClipBounds(), clipBounds); | ||
| builder.restore(); | ||
| } | ||
|
|
||
| TEST(DisplayList, DiffClipRectDoesNotAffectClipBounds) { | ||
| DisplayListBuilder builder; | ||
| SkRect diff_clip = SkRect::MakeLTRB(0, 0, 15, 15); | ||
|
|
@@ -2252,6 +2314,34 @@ TEST(DisplayList, DiffClipPathDoesNotAffectClipBounds) { | |
| ASSERT_EQ(builder.getDestinationClipBounds(), initialDestinationBounds); | ||
| } | ||
|
|
||
| TEST(DisplayList, ClipPathWithInvertFillTypeDoesNotAffectClipBounds) { | ||
| SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0); | ||
| DisplayListBuilder builder(cull_rect); | ||
| SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2); | ||
| clip.setFillType(SkPathFillType::kInverseWinding); | ||
| builder.clipPath(clip, SkClipOp::kIntersect, false); | ||
|
|
||
| SkRect initial_local_bounds = builder.getLocalClipBounds(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "initial_"? That modifier would make sense if you were going to take measurements later on under a different name. It might be reasonable to just inline the getBounds calls into the ASSERT macros here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| SkRect initial_destination_bounds = builder.getDestinationClipBounds(); | ||
| ASSERT_EQ(initial_local_bounds, cull_rect); | ||
| ASSERT_EQ(initial_destination_bounds, cull_rect); | ||
| } | ||
|
|
||
| TEST(DisplayList, DiffClipPathWithInvertFillTypeAffectsClipBounds) { | ||
| SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0); | ||
| DisplayListBuilder builder(cull_rect); | ||
| SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2); | ||
| clip.setFillType(SkPathFillType::kInverseWinding); | ||
| SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7); | ||
| SkRect clip_expanded_bounds = SkRect::MakeLTRB(8, 9, 23, 28); | ||
| builder.clipPath(clip, SkClipOp::kDifference, false); | ||
|
|
||
| SkRect initial_local_bounds = builder.getLocalClipBounds(); | ||
| SkRect initial_destination_bounds = builder.getDestinationClipBounds(); | ||
| ASSERT_EQ(initial_local_bounds, clip_expanded_bounds); | ||
| ASSERT_EQ(initial_destination_bounds, clip_bounds); | ||
| } | ||
|
|
||
| TEST(DisplayList, FlatDrawPointsProducesBounds) { | ||
| SkPoint horizontal_points[2] = {{10, 10}, {20, 10}}; | ||
| SkPoint vertical_points[2] = {{10, 10}, {10, 20}}; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -731,6 +731,27 @@ void main() { | |
| expect(canvas.getDestinationClipBounds(), initialDestinationBounds); | ||
| }); | ||
|
|
||
| test('Canvas.clipRect with matrix affects canvas.getClipBounds', () async { | ||
| final PictureRecorder recorder = PictureRecorder(); | ||
| final Canvas canvas = Canvas(recorder); | ||
| const Rect clipBounds1 = Rect.fromLTRB(0.0, 0.0, 10.0, 10.0); | ||
| const Rect clipBounds2 = Rect.fromLTRB(10.0, 10.0, 20.0, 20.0); | ||
|
|
||
| canvas.save(); | ||
| canvas.clipRect(clipBounds1); | ||
| canvas.translate(0, 10.0); | ||
| canvas.clipRect(clipBounds1); | ||
| expect(canvas.getDestinationClipBounds(), Rect.fromLTRB(0.0, 0.0, 0.0, 0.0)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment about measuring expectations against the "isEmpty" property rather than requiring a specific answer as I made above for the native tests.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| canvas.restore(); | ||
|
|
||
| canvas.save(); | ||
| canvas.clipRect(clipBounds1); | ||
| canvas.translate(-10.0, -10.0); | ||
| canvas.clipRect(clipBounds2); | ||
| expect(canvas.getDestinationClipBounds(), clipBounds1); | ||
| canvas.restore(); | ||
| }); | ||
|
|
||
| test('Canvas.clipRRect affects canvas.getClipBounds', () async { | ||
| final PictureRecorder recorder = PictureRecorder(); | ||
| final Canvas canvas = Canvas(recorder); | ||
|
|
@@ -772,6 +793,29 @@ void main() { | |
| expect(canvas.getDestinationClipBounds(), initialDestinationBounds); | ||
| }); | ||
|
|
||
| test('Canvas.clipRRect with matrix affects canvas.getClipBounds', () async { | ||
| final PictureRecorder recorder = PictureRecorder(); | ||
| final Canvas canvas = Canvas(recorder); | ||
| const Rect clipBounds1 = Rect.fromLTRB(0.0, 0.0, 10.0, 10.0); | ||
| const Rect clipBounds2 = Rect.fromLTRB(10.0, 10.0, 20.0, 20.0); | ||
| final RRect clip1 = RRect.fromRectAndRadius(clipBounds1, const Radius.circular(3)); | ||
| final RRect clip2 = RRect.fromRectAndRadius(clipBounds2, const Radius.circular(3)); | ||
|
|
||
| canvas.save(); | ||
| canvas.clipRRect(clip1); | ||
| canvas.translate(0, 10.0); | ||
| canvas.clipRRect(clip1); | ||
| expect(canvas.getDestinationClipBounds(), Rect.fromLTRB(0.0, 0.0, 0.0, 0.0)); | ||
| canvas.restore(); | ||
|
|
||
| canvas.save(); | ||
| canvas.clipRRect(clip1); | ||
| canvas.translate(-10.0, -10.0); | ||
| canvas.clipRRect(clip2); | ||
| expect(canvas.getDestinationClipBounds(), clipBounds1); | ||
| canvas.restore(); | ||
| }); | ||
|
|
||
| test('Canvas.clipPath affects canvas.getClipBounds', () async { | ||
| final PictureRecorder recorder = PictureRecorder(); | ||
| final Canvas canvas = Canvas(recorder); | ||
|
|
@@ -813,6 +857,29 @@ void main() { | |
| expect(canvas.getDestinationClipBounds(), initialDestinationBounds); | ||
| }); | ||
|
|
||
| test('Canvas.clipPath with matrix affects canvas.getClipBounds', () async { | ||
| final PictureRecorder recorder = PictureRecorder(); | ||
| final Canvas canvas = Canvas(recorder); | ||
| const Rect clipBounds1 = Rect.fromLTRB(0.0, 0.0, 10.0, 10.0); | ||
| const Rect clipBounds2 = Rect.fromLTRB(10.0, 10.0, 20.0, 20.0); | ||
| final Path clip1 = Path()..addRect(clipBounds1)..addOval(clipBounds1); | ||
| final Path clip2 = Path()..addRect(clipBounds2)..addOval(clipBounds2); | ||
|
|
||
| canvas.save(); | ||
| canvas.clipPath(clip1); | ||
| canvas.translate(0, 10.0); | ||
| canvas.clipPath(clip1); | ||
| expect(canvas.getDestinationClipBounds(), Rect.fromLTRB(0.0, 0.0, 0.0, 0.0)); | ||
| canvas.restore(); | ||
|
|
||
| canvas.save(); | ||
| canvas.clipPath(clip1); | ||
| canvas.translate(-10.0, -10.0); | ||
| canvas.clipPath(clip2); | ||
| expect(canvas.getDestinationClipBounds(), clipBounds1); | ||
| canvas.restore(); | ||
| }); | ||
|
|
||
| test('Canvas.clipRect(diff) does not affect canvas.getClipBounds', () async { | ||
| final PictureRecorder recorder = PictureRecorder(); | ||
| final Canvas canvas = Canvas(recorder); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.