Skip to content

Commit 68e9e35

Browse files
Make ui.Canvas.getDestinationClipBounds works with matrix (flutter#34835)
1 parent 91919a2 commit 68e9e35

File tree

4 files changed

+168
-8
lines changed

4 files changed

+168
-8
lines changed

display_list/display_list_builder.cc

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -602,9 +602,7 @@ void DisplayListBuilder::clipRect(const SkRect& rect,
602602
switch (clip_op) {
603603
case SkClipOp::kIntersect:
604604
Push<ClipIntersectRectOp>(0, 1, rect, is_aa);
605-
if (!current_layer_->clip_bounds.intersect(rect)) {
606-
current_layer_->clip_bounds.setEmpty();
607-
}
605+
intersect(rect);
608606
break;
609607
case SkClipOp::kDifference:
610608
Push<ClipDifferenceRectOp>(0, 1, rect, is_aa);
@@ -620,9 +618,7 @@ void DisplayListBuilder::clipRRect(const SkRRect& rrect,
620618
switch (clip_op) {
621619
case SkClipOp::kIntersect:
622620
Push<ClipIntersectRRectOp>(0, 1, rrect, is_aa);
623-
if (!current_layer_->clip_bounds.intersect(rrect.getBounds())) {
624-
current_layer_->clip_bounds.setEmpty();
625-
}
621+
intersect(rrect.getBounds());
626622
break;
627623
case SkClipOp::kDifference:
628624
Push<ClipDifferenceRRectOp>(0, 1, rrect, is_aa);
@@ -653,15 +649,25 @@ void DisplayListBuilder::clipPath(const SkPath& path,
653649
switch (clip_op) {
654650
case SkClipOp::kIntersect:
655651
Push<ClipIntersectPathOp>(0, 1, path, is_aa);
656-
if (!current_layer_->clip_bounds.intersect(path.getBounds())) {
657-
current_layer_->clip_bounds.setEmpty();
652+
if (!path.isInverseFillType()) {
653+
intersect(path.getBounds());
658654
}
659655
break;
660656
case SkClipOp::kDifference:
661657
Push<ClipDifferencePathOp>(0, 1, path, is_aa);
658+
// Map "kDifference of inverse path" to "kIntersect of the original path".
659+
if (path.isInverseFillType()) {
660+
intersect(path.getBounds());
661+
}
662662
break;
663663
}
664664
}
665+
void DisplayListBuilder::intersect(const SkRect& rect) {
666+
SkRect devClipBounds = getTransform().mapRect(rect);
667+
if (!current_layer_->clip_bounds.intersect(devClipBounds)) {
668+
current_layer_->clip_bounds.setEmpty();
669+
}
670+
}
665671
SkRect DisplayListBuilder::getLocalClipBounds() {
666672
SkM44 inverse;
667673
if (current_layer_->matrix.invert(&inverse)) {

display_list/display_list_builder.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ class DisplayListBuilder final : public virtual Dispatcher,
361361

362362
void setAttributesFromDlPaint(const DlPaint& paint,
363363
const DisplayListAttributeFlags flags);
364+
void intersect(const SkRect& rect);
364365

365366
// kInvalidSigma is used to indicate that no MaskBlur is currently set.
366367
static constexpr SkScalar kInvalidSigma = 0.0;

display_list/display_list_unittests.cc

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,6 +2116,25 @@ TEST(DisplayList, ClipRectAffectsClipBounds) {
21162116
ASSERT_EQ(builder.getDestinationClipBounds(), initialDestinationBounds);
21172117
}
21182118

2119+
TEST(DisplayList, ClipRectAffectsClipBoundsWithMatrix) {
2120+
DisplayListBuilder builder;
2121+
SkRect clipBounds1 = SkRect::MakeLTRB(0, 0, 10, 10);
2122+
SkRect clipBounds2 = SkRect::MakeLTRB(10, 10, 20, 20);
2123+
builder.save();
2124+
builder.clipRect(clipBounds1, SkClipOp::kIntersect, false);
2125+
builder.translate(10, 0);
2126+
builder.clipRect(clipBounds1, SkClipOp::kIntersect, false);
2127+
ASSERT_TRUE(builder.getDestinationClipBounds().isEmpty());
2128+
builder.restore();
2129+
2130+
builder.save();
2131+
builder.clipRect(clipBounds1, SkClipOp::kIntersect, false);
2132+
builder.translate(-10, -10);
2133+
builder.clipRect(clipBounds2, SkClipOp::kIntersect, false);
2134+
ASSERT_EQ(builder.getDestinationClipBounds(), clipBounds1);
2135+
builder.restore();
2136+
}
2137+
21192138
TEST(DisplayList, ClipRRectAffectsClipBounds) {
21202139
DisplayListBuilder builder;
21212140
SkRect clipBounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
@@ -2156,6 +2175,28 @@ TEST(DisplayList, ClipRRectAffectsClipBounds) {
21562175
ASSERT_EQ(builder.getDestinationClipBounds(), initialDestinationBounds);
21572176
}
21582177

2178+
TEST(DisplayList, ClipRRectAffectsClipBoundsWithMatrix) {
2179+
DisplayListBuilder builder;
2180+
SkRect clipBounds1 = SkRect::MakeLTRB(0, 0, 10, 10);
2181+
SkRect clipBounds2 = SkRect::MakeLTRB(10, 10, 20, 20);
2182+
SkRRect clip1 = SkRRect::MakeRectXY(clipBounds1, 3, 2);
2183+
SkRRect clip2 = SkRRect::MakeRectXY(clipBounds2, 3, 2);
2184+
2185+
builder.save();
2186+
builder.clipRRect(clip1, SkClipOp::kIntersect, false);
2187+
builder.translate(10, 0);
2188+
builder.clipRRect(clip1, SkClipOp::kIntersect, false);
2189+
ASSERT_TRUE(builder.getDestinationClipBounds().isEmpty());
2190+
builder.restore();
2191+
2192+
builder.save();
2193+
builder.clipRRect(clip1, SkClipOp::kIntersect, false);
2194+
builder.translate(-10, -10);
2195+
builder.clipRRect(clip2, SkClipOp::kIntersect, false);
2196+
ASSERT_EQ(builder.getDestinationClipBounds(), clipBounds1);
2197+
builder.restore();
2198+
}
2199+
21592200
TEST(DisplayList, ClipPathAffectsClipBounds) {
21602201
DisplayListBuilder builder;
21612202
SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
@@ -2196,6 +2237,27 @@ TEST(DisplayList, ClipPathAffectsClipBounds) {
21962237
ASSERT_EQ(builder.getDestinationClipBounds(), initialDestinationBounds);
21972238
}
21982239

2240+
TEST(DisplayList, ClipPathAffectsClipBoundsWithMatrix) {
2241+
DisplayListBuilder builder;
2242+
SkRect clipBounds = SkRect::MakeLTRB(0, 0, 10, 10);
2243+
SkPath clip1 = SkPath().addCircle(2.5, 2.5, 2.5).addCircle(7.5, 7.5, 2.5);
2244+
SkPath clip2 = SkPath().addCircle(12.5, 12.5, 2.5).addCircle(17.5, 17.5, 2.5);
2245+
2246+
builder.save();
2247+
builder.clipPath(clip1, SkClipOp::kIntersect, false);
2248+
builder.translate(10, 0);
2249+
builder.clipPath(clip1, SkClipOp::kIntersect, false);
2250+
ASSERT_TRUE(builder.getDestinationClipBounds().isEmpty());
2251+
builder.restore();
2252+
2253+
builder.save();
2254+
builder.clipPath(clip1, SkClipOp::kIntersect, false);
2255+
builder.translate(-10, -10);
2256+
builder.clipPath(clip2, SkClipOp::kIntersect, false);
2257+
ASSERT_EQ(builder.getDestinationClipBounds(), clipBounds);
2258+
builder.restore();
2259+
}
2260+
21992261
TEST(DisplayList, DiffClipRectDoesNotAffectClipBounds) {
22002262
DisplayListBuilder builder;
22012263
SkRect diff_clip = SkRect::MakeLTRB(0, 0, 15, 15);
@@ -2252,6 +2314,30 @@ TEST(DisplayList, DiffClipPathDoesNotAffectClipBounds) {
22522314
ASSERT_EQ(builder.getDestinationClipBounds(), initialDestinationBounds);
22532315
}
22542316

2317+
TEST(DisplayList, ClipPathWithInvertFillTypeDoesNotAffectClipBounds) {
2318+
SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0);
2319+
DisplayListBuilder builder(cull_rect);
2320+
SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
2321+
clip.setFillType(SkPathFillType::kInverseWinding);
2322+
builder.clipPath(clip, SkClipOp::kIntersect, false);
2323+
2324+
ASSERT_EQ(builder.getLocalClipBounds(), cull_rect);
2325+
ASSERT_EQ(builder.getDestinationClipBounds(), cull_rect);
2326+
}
2327+
2328+
TEST(DisplayList, DiffClipPathWithInvertFillTypeAffectsClipBounds) {
2329+
SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0);
2330+
DisplayListBuilder builder(cull_rect);
2331+
SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
2332+
clip.setFillType(SkPathFillType::kInverseWinding);
2333+
SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7);
2334+
SkRect clip_expanded_bounds = SkRect::MakeLTRB(8, 9, 23, 28);
2335+
builder.clipPath(clip, SkClipOp::kDifference, false);
2336+
2337+
ASSERT_EQ(builder.getLocalClipBounds(), clip_expanded_bounds);
2338+
ASSERT_EQ(builder.getDestinationClipBounds(), clip_bounds);
2339+
}
2340+
22552341
TEST(DisplayList, FlatDrawPointsProducesBounds) {
22562342
SkPoint horizontal_points[2] = {{10, 10}, {20, 10}};
22572343
SkPoint vertical_points[2] = {{10, 10}, {10, 20}};

testing/dart/canvas_test.dart

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,27 @@ void main() {
731731
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
732732
});
733733

734+
test('Canvas.clipRect with matrix affects canvas.getClipBounds', () async {
735+
final PictureRecorder recorder = PictureRecorder();
736+
final Canvas canvas = Canvas(recorder);
737+
const Rect clipBounds1 = Rect.fromLTRB(0.0, 0.0, 10.0, 10.0);
738+
const Rect clipBounds2 = Rect.fromLTRB(10.0, 10.0, 20.0, 20.0);
739+
740+
canvas.save();
741+
canvas.clipRect(clipBounds1);
742+
canvas.translate(0, 10.0);
743+
canvas.clipRect(clipBounds1);
744+
expect(canvas.getDestinationClipBounds().isEmpty, isTrue);
745+
canvas.restore();
746+
747+
canvas.save();
748+
canvas.clipRect(clipBounds1);
749+
canvas.translate(-10.0, -10.0);
750+
canvas.clipRect(clipBounds2);
751+
expect(canvas.getDestinationClipBounds(), clipBounds1);
752+
canvas.restore();
753+
});
754+
734755
test('Canvas.clipRRect affects canvas.getClipBounds', () async {
735756
final PictureRecorder recorder = PictureRecorder();
736757
final Canvas canvas = Canvas(recorder);
@@ -772,6 +793,29 @@ void main() {
772793
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
773794
});
774795

796+
test('Canvas.clipRRect with matrix affects canvas.getClipBounds', () async {
797+
final PictureRecorder recorder = PictureRecorder();
798+
final Canvas canvas = Canvas(recorder);
799+
const Rect clipBounds1 = Rect.fromLTRB(0.0, 0.0, 10.0, 10.0);
800+
const Rect clipBounds2 = Rect.fromLTRB(10.0, 10.0, 20.0, 20.0);
801+
final RRect clip1 = RRect.fromRectAndRadius(clipBounds1, const Radius.circular(3));
802+
final RRect clip2 = RRect.fromRectAndRadius(clipBounds2, const Radius.circular(3));
803+
804+
canvas.save();
805+
canvas.clipRRect(clip1);
806+
canvas.translate(0, 10.0);
807+
canvas.clipRRect(clip1);
808+
expect(canvas.getDestinationClipBounds().isEmpty, isTrue);
809+
canvas.restore();
810+
811+
canvas.save();
812+
canvas.clipRRect(clip1);
813+
canvas.translate(-10.0, -10.0);
814+
canvas.clipRRect(clip2);
815+
expect(canvas.getDestinationClipBounds(), clipBounds1);
816+
canvas.restore();
817+
});
818+
775819
test('Canvas.clipPath affects canvas.getClipBounds', () async {
776820
final PictureRecorder recorder = PictureRecorder();
777821
final Canvas canvas = Canvas(recorder);
@@ -813,6 +857,29 @@ void main() {
813857
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
814858
});
815859

860+
test('Canvas.clipPath with matrix affects canvas.getClipBounds', () async {
861+
final PictureRecorder recorder = PictureRecorder();
862+
final Canvas canvas = Canvas(recorder);
863+
const Rect clipBounds1 = Rect.fromLTRB(0.0, 0.0, 10.0, 10.0);
864+
const Rect clipBounds2 = Rect.fromLTRB(10.0, 10.0, 20.0, 20.0);
865+
final Path clip1 = Path()..addRect(clipBounds1)..addOval(clipBounds1);
866+
final Path clip2 = Path()..addRect(clipBounds2)..addOval(clipBounds2);
867+
868+
canvas.save();
869+
canvas.clipPath(clip1);
870+
canvas.translate(0, 10.0);
871+
canvas.clipPath(clip1);
872+
expect(canvas.getDestinationClipBounds().isEmpty, isTrue);
873+
canvas.restore();
874+
875+
canvas.save();
876+
canvas.clipPath(clip1);
877+
canvas.translate(-10.0, -10.0);
878+
canvas.clipPath(clip2);
879+
expect(canvas.getDestinationClipBounds(), clipBounds1);
880+
canvas.restore();
881+
});
882+
816883
test('Canvas.clipRect(diff) does not affect canvas.getClipBounds', () async {
817884
final PictureRecorder recorder = PictureRecorder();
818885
final Canvas canvas = Canvas(recorder);

0 commit comments

Comments
 (0)