Skip to content

Commit 5b7a3c8

Browse files
authored
[Engine] Add RoundSuperellipse to drawing OP (#160883)
This PR adds support for clipping round superellipse to the engine. For what a rounded superellipse is, see [this design doc](https://docs.google.com/document/d/1CJXULKJGQt22FOFsrlm2TKVjKBtif1yU4U50cMfL6Kc/edit?tab=t.0). Video demos can be found at flutter/engine#56726 and flutter/flutter#161409. Only impeller can actually render it. On Skia and Web, this shape falls back to `RRect`. Part of flutter/flutter#139321 and flutter/flutter#13914, also related to flutter/flutter#91523. ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent 7d23780 commit 5b7a3c8

77 files changed

Lines changed: 3001 additions & 660 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

engine/src/flutter/ci/licenses_golden/excluded_files

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
../../../flutter/flow/layers/clip_path_layer_unittests.cc
5959
../../../flutter/flow/layers/clip_rect_layer_unittests.cc
6060
../../../flutter/flow/layers/clip_rrect_layer_unittests.cc
61+
../../../flutter/flow/layers/clip_rsuperellipse_layer_unittests.cc
6162
../../../flutter/flow/layers/color_filter_layer_unittests.cc
6263
../../../flutter/flow/layers/container_layer_unittests.cc
6364
../../../flutter/flow/layers/display_list_layer_unittests.cc

engine/src/flutter/ci/licenses_golden/licenses_flutter

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41379,6 +41379,8 @@ ORIGIN: ../../../flutter/flow/layers/clip_rect_layer.cc + ../../../flutter/LICEN
4137941379
ORIGIN: ../../../flutter/flow/layers/clip_rect_layer.h + ../../../flutter/LICENSE
4138041380
ORIGIN: ../../../flutter/flow/layers/clip_rrect_layer.cc + ../../../flutter/LICENSE
4138141381
ORIGIN: ../../../flutter/flow/layers/clip_rrect_layer.h + ../../../flutter/LICENSE
41382+
ORIGIN: ../../../flutter/flow/layers/clip_rsuperellipse_layer.cc + ../../../flutter/LICENSE
41383+
ORIGIN: ../../../flutter/flow/layers/clip_rsuperellipse_layer.h + ../../../flutter/LICENSE
4138241384
ORIGIN: ../../../flutter/flow/layers/clip_shape_layer.h + ../../../flutter/LICENSE
4138341385
ORIGIN: ../../../flutter/flow/layers/color_filter_layer.cc + ../../../flutter/LICENSE
4138441386
ORIGIN: ../../../flutter/flow/layers/color_filter_layer.h + ../../../flutter/LICENSE
@@ -42490,6 +42492,8 @@ ORIGIN: ../../../flutter/lib/ui/painting/picture_recorder.cc + ../../../flutter/
4249042492
ORIGIN: ../../../flutter/lib/ui/painting/picture_recorder.h + ../../../flutter/LICENSE
4249142493
ORIGIN: ../../../flutter/lib/ui/painting/rrect.cc + ../../../flutter/LICENSE
4249242494
ORIGIN: ../../../flutter/lib/ui/painting/rrect.h + ../../../flutter/LICENSE
42495+
ORIGIN: ../../../flutter/lib/ui/painting/rsuperellipse.cc + ../../../flutter/LICENSE
42496+
ORIGIN: ../../../flutter/lib/ui/painting/rsuperellipse.h + ../../../flutter/LICENSE
4249342497
ORIGIN: ../../../flutter/lib/ui/painting/shader.cc + ../../../flutter/LICENSE
4249442498
ORIGIN: ../../../flutter/lib/ui/painting/shader.h + ../../../flutter/LICENSE
4249542499
ORIGIN: ../../../flutter/lib/ui/painting/single_frame_codec.cc + ../../../flutter/LICENSE
@@ -44342,6 +44346,8 @@ FILE: ../../../flutter/flow/layers/clip_rect_layer.cc
4434244346
FILE: ../../../flutter/flow/layers/clip_rect_layer.h
4434344347
FILE: ../../../flutter/flow/layers/clip_rrect_layer.cc
4434444348
FILE: ../../../flutter/flow/layers/clip_rrect_layer.h
44349+
FILE: ../../../flutter/flow/layers/clip_rsuperellipse_layer.cc
44350+
FILE: ../../../flutter/flow/layers/clip_rsuperellipse_layer.h
4434544351
FILE: ../../../flutter/flow/layers/clip_shape_layer.h
4434644352
FILE: ../../../flutter/flow/layers/color_filter_layer.cc
4434744353
FILE: ../../../flutter/flow/layers/color_filter_layer.h
@@ -45457,6 +45463,8 @@ FILE: ../../../flutter/lib/ui/painting/picture_recorder.cc
4545745463
FILE: ../../../flutter/lib/ui/painting/picture_recorder.h
4545845464
FILE: ../../../flutter/lib/ui/painting/rrect.cc
4545945465
FILE: ../../../flutter/lib/ui/painting/rrect.h
45466+
FILE: ../../../flutter/lib/ui/painting/rsuperellipse.cc
45467+
FILE: ../../../flutter/lib/ui/painting/rsuperellipse.h
4546045468
FILE: ../../../flutter/lib/ui/painting/shader.cc
4546145469
FILE: ../../../flutter/lib/ui/painting/shader.h
4546245470
FILE: ../../../flutter/lib/ui/painting/single_frame_codec.cc

engine/src/flutter/display_list/benchmarking/dl_complexity_gl.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,12 @@ void DisplayListGLComplexityCalculator::GLHelper::drawDiffRoundRect(
335335
AccumulateComplexity(complexity);
336336
}
337337

338+
void DisplayListGLComplexityCalculator::GLHelper::drawRoundSuperellipse(
339+
const DlRoundSuperellipse& rse) {
340+
// Drawing RSEs on Skia falls back to RRect.
341+
drawRoundRect(rse.ToApproximateRoundRect());
342+
}
343+
338344
void DisplayListGLComplexityCalculator::GLHelper::drawPath(const DlPath& path) {
339345
if (IsComplex()) {
340346
return;

engine/src/flutter/display_list/benchmarking/dl_complexity_gl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class DisplayListGLComplexityCalculator
5151
void drawRoundRect(const DlRoundRect& rrect) override;
5252
void drawDiffRoundRect(const DlRoundRect& outer,
5353
const DlRoundRect& inner) override;
54+
void drawRoundSuperellipse(const DlRoundSuperellipse& rse) override;
5455
void drawPath(const DlPath& path) override;
5556
void drawArc(const DlRect& oval_bounds,
5657
DlScalar start_degrees,

engine/src/flutter/display_list/benchmarking/dl_complexity_metal.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,12 @@ void DisplayListMetalComplexityCalculator::MetalHelper::drawDiffRoundRect(
329329
AccumulateComplexity(complexity);
330330
}
331331

332+
void DisplayListMetalComplexityCalculator::MetalHelper::drawRoundSuperellipse(
333+
const DlRoundSuperellipse& rse) {
334+
// Drawing RSEs on Skia falls back to RRect.
335+
drawRoundRect(rse.ToApproximateRoundRect());
336+
}
337+
332338
void DisplayListMetalComplexityCalculator::MetalHelper::drawPath(
333339
const DlPath& path) {
334340
if (IsComplex()) {

engine/src/flutter/display_list/benchmarking/dl_complexity_metal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class DisplayListMetalComplexityCalculator
5151
void drawRoundRect(const DlRoundRect& rrect) override;
5252
void drawDiffRoundRect(const DlRoundRect& outer,
5353
const DlRoundRect& inner) override;
54+
void drawRoundSuperellipse(const DlRoundSuperellipse& rse) override;
5455
void drawPath(const DlPath& path) override;
5556
void drawArc(const DlRect& oval_bounds,
5657
DlScalar start_degrees,

engine/src/flutter/display_list/display_list.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,12 @@ DisplayListOpCategory DisplayList::GetOpCategory(DisplayListOpType type) {
300300
case DisplayListOpType::kClipIntersectRect:
301301
case DisplayListOpType::kClipIntersectOval:
302302
case DisplayListOpType::kClipIntersectRoundRect:
303+
case DisplayListOpType::kClipIntersectRoundSuperellipse:
303304
case DisplayListOpType::kClipIntersectPath:
304305
case DisplayListOpType::kClipDifferenceRect:
305306
case DisplayListOpType::kClipDifferenceOval:
306307
case DisplayListOpType::kClipDifferenceRoundRect:
308+
case DisplayListOpType::kClipDifferenceRoundSuperellipse:
307309
case DisplayListOpType::kClipDifferencePath:
308310
return DisplayListOpCategory::kClip;
309311

@@ -316,6 +318,7 @@ DisplayListOpCategory DisplayList::GetOpCategory(DisplayListOpType type) {
316318
case DisplayListOpType::kDrawCircle:
317319
case DisplayListOpType::kDrawRoundRect:
318320
case DisplayListOpType::kDrawDiffRoundRect:
321+
case DisplayListOpType::kDrawRoundSuperellipse:
319322
case DisplayListOpType::kDrawArc:
320323
case DisplayListOpType::kDrawPath:
321324
case DisplayListOpType::kDrawPoints:

engine/src/flutter/display_list/display_list.h

Lines changed: 85 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -52,88 +52,91 @@
5252

5353
namespace flutter {
5454

55-
#define FOR_EACH_DISPLAY_LIST_OP(V) \
56-
V(SetAntiAlias) \
57-
V(SetInvertColors) \
58-
\
59-
V(SetStrokeCap) \
60-
V(SetStrokeJoin) \
61-
\
62-
V(SetStyle) \
63-
V(SetStrokeWidth) \
64-
V(SetStrokeMiter) \
65-
\
66-
V(SetColor) \
67-
V(SetBlendMode) \
68-
\
69-
V(ClearColorFilter) \
70-
V(SetPodColorFilter) \
71-
\
72-
V(ClearColorSource) \
73-
V(SetPodColorSource) \
74-
V(SetImageColorSource) \
75-
V(SetRuntimeEffectColorSource) \
76-
\
77-
V(ClearImageFilter) \
78-
V(SetPodImageFilter) \
79-
V(SetSharedImageFilter) \
80-
\
81-
V(ClearMaskFilter) \
82-
V(SetPodMaskFilter) \
83-
\
84-
V(Save) \
85-
V(SaveLayer) \
86-
V(SaveLayerBackdrop) \
87-
V(Restore) \
88-
\
89-
V(Translate) \
90-
V(Scale) \
91-
V(Rotate) \
92-
V(Skew) \
93-
V(Transform2DAffine) \
94-
V(TransformFullPerspective) \
95-
V(TransformReset) \
96-
\
97-
V(ClipIntersectRect) \
98-
V(ClipIntersectOval) \
99-
V(ClipIntersectRoundRect) \
100-
V(ClipIntersectPath) \
101-
V(ClipDifferenceRect) \
102-
V(ClipDifferenceOval) \
103-
V(ClipDifferenceRoundRect) \
104-
V(ClipDifferencePath) \
105-
\
106-
V(DrawPaint) \
107-
V(DrawColor) \
108-
\
109-
V(DrawLine) \
110-
V(DrawDashedLine) \
111-
V(DrawRect) \
112-
V(DrawOval) \
113-
V(DrawCircle) \
114-
V(DrawRoundRect) \
115-
V(DrawDiffRoundRect) \
116-
V(DrawArc) \
117-
V(DrawPath) \
118-
\
119-
V(DrawPoints) \
120-
V(DrawLines) \
121-
V(DrawPolygon) \
122-
V(DrawVertices) \
123-
\
124-
V(DrawImage) \
125-
V(DrawImageWithAttr) \
126-
V(DrawImageRect) \
127-
V(DrawImageNine) \
128-
V(DrawImageNineWithAttr) \
129-
V(DrawAtlas) \
130-
V(DrawAtlasCulled) \
131-
\
132-
V(DrawDisplayList) \
133-
V(DrawTextBlob) \
134-
V(DrawTextFrame) \
135-
\
136-
V(DrawShadow) \
55+
#define FOR_EACH_DISPLAY_LIST_OP(V) \
56+
V(SetAntiAlias) \
57+
V(SetInvertColors) \
58+
\
59+
V(SetStrokeCap) \
60+
V(SetStrokeJoin) \
61+
\
62+
V(SetStyle) \
63+
V(SetStrokeWidth) \
64+
V(SetStrokeMiter) \
65+
\
66+
V(SetColor) \
67+
V(SetBlendMode) \
68+
\
69+
V(ClearColorFilter) \
70+
V(SetPodColorFilter) \
71+
\
72+
V(ClearColorSource) \
73+
V(SetPodColorSource) \
74+
V(SetImageColorSource) \
75+
V(SetRuntimeEffectColorSource) \
76+
\
77+
V(ClearImageFilter) \
78+
V(SetPodImageFilter) \
79+
V(SetSharedImageFilter) \
80+
\
81+
V(ClearMaskFilter) \
82+
V(SetPodMaskFilter) \
83+
\
84+
V(Save) \
85+
V(SaveLayer) \
86+
V(SaveLayerBackdrop) \
87+
V(Restore) \
88+
\
89+
V(Translate) \
90+
V(Scale) \
91+
V(Rotate) \
92+
V(Skew) \
93+
V(Transform2DAffine) \
94+
V(TransformFullPerspective) \
95+
V(TransformReset) \
96+
\
97+
V(ClipIntersectRect) \
98+
V(ClipIntersectOval) \
99+
V(ClipIntersectRoundRect) \
100+
V(ClipIntersectRoundSuperellipse) \
101+
V(ClipIntersectPath) \
102+
V(ClipDifferenceRect) \
103+
V(ClipDifferenceOval) \
104+
V(ClipDifferenceRoundRect) \
105+
V(ClipDifferenceRoundSuperellipse) \
106+
V(ClipDifferencePath) \
107+
\
108+
V(DrawPaint) \
109+
V(DrawColor) \
110+
\
111+
V(DrawLine) \
112+
V(DrawDashedLine) \
113+
V(DrawRect) \
114+
V(DrawOval) \
115+
V(DrawCircle) \
116+
V(DrawRoundRect) \
117+
V(DrawDiffRoundRect) \
118+
V(DrawRoundSuperellipse) \
119+
V(DrawArc) \
120+
V(DrawPath) \
121+
\
122+
V(DrawPoints) \
123+
V(DrawLines) \
124+
V(DrawPolygon) \
125+
V(DrawVertices) \
126+
\
127+
V(DrawImage) \
128+
V(DrawImageWithAttr) \
129+
V(DrawImageRect) \
130+
V(DrawImageNine) \
131+
V(DrawImageNineWithAttr) \
132+
V(DrawAtlas) \
133+
V(DrawAtlasCulled) \
134+
\
135+
V(DrawDisplayList) \
136+
V(DrawTextBlob) \
137+
V(DrawTextFrame) \
138+
\
139+
V(DrawShadow) \
137140
V(DrawShadowTransparentOccluder)
138141

139142
#define DL_OP_TO_ENUM_VALUE(name) k##name,

engine/src/flutter/display_list/display_list_unittests.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4512,7 +4512,7 @@ TEST_F(DisplayListTest, DrawDisplayListForwardsBackdropFlag) {
45124512
#define CLIP_EXPECTOR(name) ClipExpector name(__FILE__, __LINE__)
45134513

45144514
struct ClipExpectation {
4515-
std::variant<DlRect, DlRoundRect, DlPath> shape;
4515+
std::variant<DlRect, DlRoundRect, DlRoundSuperellipse, DlPath> shape;
45164516
bool is_oval;
45174517
DlClipOp clip_op;
45184518
bool is_aa;
@@ -4524,6 +4524,8 @@ struct ClipExpectation {
45244524
case 1:
45254525
return "DlRoundRect";
45264526
case 2:
4527+
return "DlRoundSuperellipse";
4528+
case 3:
45274529
return "DlPath";
45284530
default:
45294531
return "Unknown";
@@ -4632,6 +4634,11 @@ class ClipExpector : public virtual DlOpReceiver,
46324634
bool is_aa) override {
46334635
check(rrect, clip_op, is_aa);
46344636
}
4637+
void clipRoundSuperellipse(const DlRoundSuperellipse& rse,
4638+
DlClipOp clip_op,
4639+
bool is_aa) override {
4640+
check(rse, clip_op, is_aa);
4641+
}
46354642
void clipPath(const DlPath& path, DlClipOp clip_op, bool is_aa) override {
46364643
check(path, clip_op, is_aa);
46374644
}

engine/src/flutter/display_list/dl_builder.cc

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,42 @@ void DisplayListBuilder::ClipRoundRect(const DlRoundRect& rrect,
10251025
break;
10261026
}
10271027
}
1028+
void DisplayListBuilder::ClipRoundSuperellipse(const DlRoundSuperellipse& rse,
1029+
DlClipOp clip_op,
1030+
bool is_aa) {
1031+
if (rse.IsRect()) {
1032+
ClipRect(rse.GetBounds(), clip_op, is_aa);
1033+
return;
1034+
}
1035+
if (rse.IsOval()) {
1036+
ClipOval(rse.GetBounds(), clip_op, is_aa);
1037+
return;
1038+
}
1039+
if (current_info().is_nop) {
1040+
return;
1041+
}
1042+
if (current_info().has_valid_clip && clip_op == DlClipOp::kIntersect &&
1043+
layer_local_state().rsuperellipse_covers_cull(rse)) {
1044+
return;
1045+
}
1046+
global_state().clipRSuperellipse(rse, clip_op, is_aa);
1047+
layer_local_state().clipRSuperellipse(rse, clip_op, is_aa);
1048+
if (global_state().is_cull_rect_empty() ||
1049+
layer_local_state().is_cull_rect_empty()) {
1050+
current_info().is_nop = true;
1051+
return;
1052+
}
1053+
current_info().has_valid_clip = true;
1054+
checkForDeferredSave();
1055+
switch (clip_op) {
1056+
case DlClipOp::kIntersect:
1057+
Push<ClipIntersectRoundSuperellipseOp>(0, rse, is_aa);
1058+
break;
1059+
case DlClipOp::kDifference:
1060+
Push<ClipDifferenceRoundSuperellipseOp>(0, rse, is_aa);
1061+
break;
1062+
}
1063+
}
10281064
void DisplayListBuilder::ClipPath(const DlPath& path,
10291065
DlClipOp clip_op,
10301066
bool is_aa) {
@@ -1214,6 +1250,27 @@ void DisplayListBuilder::DrawDiffRoundRect(const DlRoundRect& outer,
12141250
SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawDRRectFlags);
12151251
drawDiffRoundRect(outer, inner);
12161252
}
1253+
void DisplayListBuilder::drawRoundSuperellipse(const DlRoundSuperellipse& rse) {
1254+
if (rse.IsRect()) {
1255+
drawRect(rse.GetBounds());
1256+
} else if (rse.IsOval()) {
1257+
drawOval(rse.GetBounds());
1258+
} else {
1259+
DisplayListAttributeFlags flags = kDrawRSuperellipseFlags;
1260+
OpResult result = PaintResult(current_, flags);
1261+
if (result != OpResult::kNoEffect &&
1262+
AccumulateOpBounds(rse.GetBounds(), flags)) {
1263+
Push<DrawRoundSuperellipseOp>(0, rse);
1264+
CheckLayerOpacityCompatibility();
1265+
UpdateLayerResult(result);
1266+
}
1267+
}
1268+
}
1269+
void DisplayListBuilder::DrawRoundSuperellipse(const DlRoundSuperellipse& rse,
1270+
const DlPaint& paint) {
1271+
SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRSuperellipseFlags);
1272+
drawRoundSuperellipse(rse);
1273+
}
12171274
void DisplayListBuilder::drawPath(const DlPath& path) {
12181275
DisplayListAttributeFlags flags = kDrawPathFlags;
12191276
OpResult result = PaintResult(current_, flags);

0 commit comments

Comments
 (0)