Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit e9d0b4a

Browse files
authored
Revert "remove _getRRect: fixes assertion error (#24688)" (#24718)
This reverts commit e763e56. The commit broke golden tests.
1 parent ef8353b commit e9d0b4a

File tree

3 files changed

+106
-154
lines changed

3 files changed

+106
-154
lines changed

lib/web_ui/lib/src/engine/html/path/path.dart

Lines changed: 22 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,80 +1007,42 @@ class SurfacePath implements ui.Path {
10071007
_addOval(bounds, direction, startIndex ~/ 2);
10081008
} else {
10091009
final double weight = SPath.scalarRoot2Over2;
1010-
final double left = bounds.left;
1011-
final double right = bounds.right;
1012-
final double top = bounds.top;
1013-
final double bottom = bounds.bottom;
1010+
double left = bounds.left;
1011+
double right = bounds.right;
1012+
double top = bounds.top;
1013+
double bottom = bounds.bottom;
10141014
final double width = right - left;
10151015
final double height = bottom - top;
10161016
// Proportionally scale down all radii to fit. Find the minimum ratio
10171017
// of a side and the radii on that side (for all four sides) and use
10181018
// that to scale down _all_ the radii. This algorithm is from the
10191019
// W3 spec (http://www.w3.org/TR/css3-background/) section 5.5
1020-
double tlRadiusX = math.max(0, rrect.tlRadiusX);
1021-
double trRadiusX = math.max(0, rrect.trRadiusX);
1022-
double blRadiusX = math.max(0, rrect.blRadiusX);
1023-
double brRadiusX = math.max(0, rrect.brRadiusX);
1024-
double tlRadiusY = math.max(0, rrect.tlRadiusY);
1025-
double trRadiusY = math.max(0, rrect.trRadiusY);
1026-
double blRadiusY = math.max(0, rrect.blRadiusY);
1027-
double brRadiusY = math.max(0, rrect.brRadiusY);
1020+
final double tlRadiusX = math.max(0, rrect.tlRadiusX);
1021+
final double trRadiusX = math.max(0, rrect.trRadiusX);
1022+
final double blRadiusX = math.max(0, rrect.blRadiusX);
1023+
final double brRadiusX = math.max(0, rrect.brRadiusX);
1024+
final double tlRadiusY = math.max(0, rrect.tlRadiusY);
1025+
final double trRadiusY = math.max(0, rrect.trRadiusY);
1026+
final double blRadiusY = math.max(0, rrect.blRadiusY);
1027+
final double brRadiusY = math.max(0, rrect.brRadiusY);
10281028
double scale = _computeMinScale(tlRadiusX, trRadiusX, width, 1.0);
10291029
scale = _computeMinScale(blRadiusX, brRadiusX, width, scale);
10301030
scale = _computeMinScale(tlRadiusY, trRadiusY, height, scale);
10311031
scale = _computeMinScale(blRadiusY, brRadiusY, height, scale);
10321032

1033-
if (scale != 1.0) {
1034-
tlRadiusX = scale * tlRadiusX;
1035-
trRadiusX = scale * trRadiusX;
1036-
blRadiusX = scale * blRadiusX;
1037-
brRadiusX = scale * brRadiusX;
1038-
tlRadiusY = scale * tlRadiusY;
1039-
trRadiusY = scale * trRadiusY;
1040-
blRadiusY = scale * blRadiusY;
1041-
brRadiusY = scale * brRadiusY;
1042-
}
1043-
1044-
// Whether we had to alter the rrect parameters for correctness.
1045-
final bool isRRectCorrected =
1046-
tlRadiusX != rrect.tlRadiusX ||
1047-
trRadiusX != rrect.trRadiusX ||
1048-
blRadiusX != rrect.blRadiusX ||
1049-
brRadiusX != rrect.brRadiusX ||
1050-
tlRadiusY != rrect.tlRadiusY ||
1051-
trRadiusY != rrect.trRadiusY ||
1052-
blRadiusY != rrect.blRadiusY ||
1053-
brRadiusY != rrect.brRadiusY;
1054-
1055-
// Expand the rrect into a series of path ops.
1056-
moveTo(left, bottom - blRadiusY);
1057-
lineTo(left, top + tlRadiusY);
1058-
conicTo(left, top, left + tlRadiusX, top, weight);
1059-
lineTo(right - trRadiusX, top);
1060-
conicTo(right, top, right, top + trRadiusY, weight);
1061-
lineTo(right, bottom - brRadiusY);
1062-
conicTo(right, bottom, right - brRadiusX, bottom, weight);
1063-
lineTo(left + blRadiusX, bottom);
1064-
conicTo(left, bottom, left, bottom - blRadiusY, weight);
1033+
// Inlined version of:
1034+
moveTo(left, bottom - scale * blRadiusY);
1035+
lineTo(left, top + scale * tlRadiusY);
1036+
conicTo(left, top, left + scale * tlRadiusX, top, weight);
1037+
lineTo(right - scale * trRadiusX, top);
1038+
conicTo(right, top, right, top + scale * trRadiusY, weight);
1039+
lineTo(right, bottom - scale * brRadiusY);
1040+
conicTo(right, bottom, right - scale * brRadiusX, bottom, weight);
1041+
lineTo(left + scale * blRadiusX, bottom);
1042+
conicTo(left, bottom, left, bottom - scale * blRadiusY, weight);
10651043
close();
10661044
// SkAutoDisableDirectionCheck.
10671045
_firstDirection = isRRect ? direction : SPathDirection.kUnknown;
1068-
1069-
// No need to duplicate the rrect if we know the path is not made of a
1070-
// single rrect, or if the original rrect was consistent and didn't have
1071-
// to be corrected.
1072-
if (isRRect && isRRectCorrected) {
1073-
rrect = ui.RRect.fromLTRBAndCorners(
1074-
left,
1075-
top,
1076-
right,
1077-
bottom,
1078-
topLeft: ui.Radius.elliptical(tlRadiusX, tlRadiusY),
1079-
topRight: ui.Radius.elliptical(trRadiusX, trRadiusY),
1080-
bottomLeft: ui.Radius.elliptical(blRadiusX, blRadiusY),
1081-
bottomRight: ui.Radius.elliptical(brRadiusX, brRadiusY),
1082-
);
1083-
}
10841046
pathRef.setIsRRect(
10851047
isRRect, direction == SPathDirection.kCCW, startIndex % 8, rrect);
10861048
}

lib/web_ui/lib/src/engine/html/path/path_ref.dart

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ class PathRef {
6161
fBoundsIsDirty = true; // this also invalidates fIsFinite
6262
fSegmentMask = 0;
6363
fIsOval = false;
64-
rrectRepresentation = null;
64+
fIsRRect = false;
6565
fIsRect = false;
66-
// The next two values don't matter unless fIsOval is true or rrectRepresentation is not null.
66+
// The next two values don't matter unless fIsOval or fIsRRect are true.
6767
fRRectOrOvalIsCCW = false;
6868
fRRectOrOvalStartIdx = 0xAC;
6969
assert(() {
@@ -104,7 +104,7 @@ class PathRef {
104104
}
105105
fSegmentMask = ref.fSegmentMask;
106106
fIsOval = ref.fIsOval;
107-
rrectRepresentation = ref.rrectRepresentation;
107+
fIsRRect = ref.fIsRRect;
108108
fIsRect = ref.fIsRect;
109109
fRRectOrOvalIsCCW = ref.fRRectOrOvalIsCCW;
110110
fRRectOrOvalStartIdx = ref.fRRectOrOvalStartIdx;
@@ -153,9 +153,9 @@ class PathRef {
153153
int get isOval => fIsOval ? fRRectOrOvalStartIdx : -1;
154154
bool get isOvalCCW => fRRectOrOvalIsCCW;
155155

156-
int get isRRect => rrectRepresentation != null ? fRRectOrOvalStartIdx : -1;
156+
int get isRRect => fIsRRect ? fRRectOrOvalStartIdx : -1;
157157
int get isRect => fIsRect ? fRRectOrOvalStartIdx : -1;
158-
ui.RRect? getRRect() => rrectRepresentation;
158+
ui.RRect? getRRect() => fIsRRect ? _getRRect() : null;
159159
ui.Rect? getRect() {
160160
/// Use _detectRect() for detection if explicity addRect was used (fIsRect) or
161161
/// it is a potential due to moveTo + 3 lineTo verbs.
@@ -227,6 +227,70 @@ class PathRef {
227227
return null;
228228
}
229229

230+
/// Reconstructs RRect from path commands.
231+
///
232+
/// Expect 4 Conics and lines between.
233+
/// Use conic points to calculate corner radius.
234+
ui.RRect _getRRect() {
235+
ui.Rect bounds = getBounds();
236+
// Radii x,y of 4 corners
237+
final List<ui.Radius> radii = <ui.Radius>[];
238+
final PathRefIterator iter = PathRefIterator(this);
239+
final Float32List pts = Float32List(PathRefIterator.kMaxBufferSize);
240+
int verb = iter.next(pts);
241+
assert(SPath.kMoveVerb == verb);
242+
int cornerIndex = 0;
243+
while ((verb = iter.next(pts)) != SPath.kDoneVerb) {
244+
if (SPath.kConicVerb == verb) {
245+
final double controlPx = pts[2];
246+
final double controlPy = pts[3];
247+
double vector1_0x = controlPx - pts[0];
248+
double vector1_0y = controlPy - pts[1];
249+
double vector2_1x = pts[4] - pts[2];
250+
double vector2_1y = pts[5] - pts[3];
251+
double dx, dy;
252+
// Depending on the corner we have control point at same
253+
// horizontal position as startpoint or same vertical position.
254+
// The location delta of control point specifies corner radius.
255+
if (vector1_0x != 0.0) {
256+
// For CW : Top right or bottom left corners.
257+
assert(vector2_1x == 0.0 && vector1_0y == 0.0);
258+
dx = vector1_0x.abs();
259+
dy = vector2_1y.abs();
260+
} else if (vector1_0y != 0.0) {
261+
assert(vector2_1x == 0.0 || vector2_1y == 0.0);
262+
dx = vector2_1x.abs();
263+
dy = vector1_0y.abs();
264+
} else {
265+
assert(vector2_1y == 0.0);
266+
dx = vector1_0x.abs();
267+
dy = vector1_0y.abs();
268+
}
269+
if (assertionsEnabled) {
270+
final int checkCornerIndex = _nearlyEqual(controlPx, bounds.left)
271+
? (_nearlyEqual(controlPy, bounds.top)
272+
? _Corner.kUpperLeft
273+
: _Corner.kLowerLeft)
274+
: (_nearlyEqual(controlPy, bounds.top)
275+
? _Corner.kUpperRight
276+
: _Corner.kLowerRight);
277+
assert(checkCornerIndex == cornerIndex);
278+
}
279+
radii.add(ui.Radius.elliptical(dx, dy));
280+
++cornerIndex;
281+
} else {
282+
assert((verb == SPath.kLineVerb &&
283+
((pts[2] - pts[0]) == 0 || (pts[3] - pts[1]) == 0)) ||
284+
verb == SPath.kCloseVerb);
285+
}
286+
}
287+
return ui.RRect.fromRectAndCorners(bounds,
288+
topLeft: radii[_Corner.kUpperLeft],
289+
topRight: radii[_Corner.kUpperRight],
290+
bottomRight: radii[_Corner.kLowerRight],
291+
bottomLeft: radii[_Corner.kLowerLeft]);
292+
}
293+
230294
bool operator ==(Object other) {
231295
if (identical(this, other)) {
232296
return true;
@@ -330,7 +394,7 @@ class PathRef {
330394
}
331395
fSegmentMask = source.fSegmentMask;
332396
fIsOval = source.fIsOval;
333-
rrectRepresentation = source.rrectRepresentation;
397+
fIsRRect = source.fIsRRect;
334398
fIsRect = source.fIsRect;
335399
fRRectOrOvalIsCCW = source.fRRectOrOvalIsCCW;
336400
fRRectOrOvalStartIdx = source.fRRectOrOvalStartIdx;
@@ -363,7 +427,7 @@ class PathRef {
363427
}
364428
fSegmentMask = ref.fSegmentMask;
365429
fIsOval = ref.fIsOval;
366-
rrectRepresentation = ref.rrectRepresentation;
430+
fIsRRect = ref.fIsRRect;
367431
fIsRect = ref.fIsRect;
368432
fRRectOrOvalIsCCW = ref.fRRectOrOvalIsCCW;
369433
fRRectOrOvalStartIdx = ref.fRRectOrOvalStartIdx;
@@ -714,7 +778,7 @@ class PathRef {
714778
/// points are added.
715779
void startEdit() {
716780
fIsOval = false;
717-
rrectRepresentation = null;
781+
fIsRRect = false;
718782
fIsRect = false;
719783
cachedBounds = null;
720784
fBoundsIsDirty = true;
@@ -727,11 +791,7 @@ class PathRef {
727791
}
728792

729793
void setIsRRect(bool isRRect, bool isCCW, int start, ui.RRect rrect) {
730-
if (isRRect) {
731-
rrectRepresentation = rrect;
732-
} else {
733-
rrectRepresentation = null;
734-
}
794+
fIsRRect = isRRect;
735795
fRRectOrOvalIsCCW = isCCW;
736796
fRRectOrOvalStartIdx = start;
737797
}
@@ -753,11 +813,7 @@ class PathRef {
753813
bool fIsFinite = true; // only meaningful if bounds are valid
754814

755815
bool fIsOval = false;
756-
757-
/// If the path is made of a single `RRect`, this field contains the original
758-
/// `RRect` that was added to the path.
759-
ui.RRect? rrectRepresentation;
760-
816+
bool fIsRRect = false;
761817
bool fIsRect = false;
762818
// Both the circle and rrect special cases have a notion of direction and starting point
763819
// The next two variables store that information for either.
@@ -766,9 +822,9 @@ class PathRef {
766822
int fSegmentMask = 0;
767823

768824
bool get isValid {
769-
if (fIsOval || rrectRepresentation != null) {
825+
if (fIsOval || fIsRRect) {
770826
// Currently we don't allow both of these to be set.
771-
if (fIsOval == (rrectRepresentation != null)) {
827+
if (fIsOval == fIsRRect) {
772828
return false;
773829
}
774830
if (fIsOval) {
@@ -782,7 +838,7 @@ class PathRef {
782838
}
783839
}
784840
if (fIsRect) {
785-
if (fIsOval || (rrectRepresentation != null)) {
841+
if (fIsOval || fIsRRect) {
786842
return false;
787843
}
788844
if (fRRectOrOvalStartIdx >= 4) {
@@ -1008,3 +1064,10 @@ class PathRefIterator {
10081064
? pathRef._fVerbs[_verbIndex]
10091065
: SPath.kDoneVerb;
10101066
}
1067+
1068+
class _Corner {
1069+
static const int kUpperLeft = 0;
1070+
static const int kUpperRight = 1;
1071+
static const int kLowerRight = 2;
1072+
static const int kLowerLeft = 3;
1073+
}

lib/web_ui/test/engine/surface/path/path_ref_test.dart

Lines changed: 0 additions & 73 deletions
This file was deleted.

0 commit comments

Comments
 (0)