Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 22 additions & 60 deletions lib/web_ui/lib/src/engine/html/path/path.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1007,80 +1007,42 @@ class SurfacePath implements ui.Path {
_addOval(bounds, direction, startIndex ~/ 2);
} else {
final double weight = SPath.scalarRoot2Over2;
final double left = bounds.left;
final double right = bounds.right;
final double top = bounds.top;
final double bottom = bounds.bottom;
double left = bounds.left;
double right = bounds.right;
double top = bounds.top;
double bottom = bounds.bottom;
final double width = right - left;
final double height = bottom - top;
// Proportionally scale down all radii to fit. Find the minimum ratio
// of a side and the radii on that side (for all four sides) and use
// that to scale down _all_ the radii. This algorithm is from the
// W3 spec (http://www.w3.org/TR/css3-background/) section 5.5
double tlRadiusX = math.max(0, rrect.tlRadiusX);
double trRadiusX = math.max(0, rrect.trRadiusX);
double blRadiusX = math.max(0, rrect.blRadiusX);
double brRadiusX = math.max(0, rrect.brRadiusX);
double tlRadiusY = math.max(0, rrect.tlRadiusY);
double trRadiusY = math.max(0, rrect.trRadiusY);
double blRadiusY = math.max(0, rrect.blRadiusY);
double brRadiusY = math.max(0, rrect.brRadiusY);
final double tlRadiusX = math.max(0, rrect.tlRadiusX);
final double trRadiusX = math.max(0, rrect.trRadiusX);
final double blRadiusX = math.max(0, rrect.blRadiusX);
final double brRadiusX = math.max(0, rrect.brRadiusX);
final double tlRadiusY = math.max(0, rrect.tlRadiusY);
final double trRadiusY = math.max(0, rrect.trRadiusY);
final double blRadiusY = math.max(0, rrect.blRadiusY);
final double brRadiusY = math.max(0, rrect.brRadiusY);
double scale = _computeMinScale(tlRadiusX, trRadiusX, width, 1.0);
scale = _computeMinScale(blRadiusX, brRadiusX, width, scale);
scale = _computeMinScale(tlRadiusY, trRadiusY, height, scale);
scale = _computeMinScale(blRadiusY, brRadiusY, height, scale);

if (scale != 1.0) {
tlRadiusX = scale * tlRadiusX;
trRadiusX = scale * trRadiusX;
blRadiusX = scale * blRadiusX;
brRadiusX = scale * brRadiusX;
tlRadiusY = scale * tlRadiusY;
trRadiusY = scale * trRadiusY;
blRadiusY = scale * blRadiusY;
brRadiusY = scale * brRadiusY;
}

// Whether we had to alter the rrect parameters for correctness.
final bool isRRectCorrected =
tlRadiusX != rrect.tlRadiusX ||
trRadiusX != rrect.trRadiusX ||
blRadiusX != rrect.blRadiusX ||
brRadiusX != rrect.brRadiusX ||
tlRadiusY != rrect.tlRadiusY ||
trRadiusY != rrect.trRadiusY ||
blRadiusY != rrect.blRadiusY ||
brRadiusY != rrect.brRadiusY;

// Expand the rrect into a series of path ops.
moveTo(left, bottom - blRadiusY);
lineTo(left, top + tlRadiusY);
conicTo(left, top, left + tlRadiusX, top, weight);
lineTo(right - trRadiusX, top);
conicTo(right, top, right, top + trRadiusY, weight);
lineTo(right, bottom - brRadiusY);
conicTo(right, bottom, right - brRadiusX, bottom, weight);
lineTo(left + blRadiusX, bottom);
conicTo(left, bottom, left, bottom - blRadiusY, weight);
// Inlined version of:
moveTo(left, bottom - scale * blRadiusY);
lineTo(left, top + scale * tlRadiusY);
conicTo(left, top, left + scale * tlRadiusX, top, weight);
lineTo(right - scale * trRadiusX, top);
conicTo(right, top, right, top + scale * trRadiusY, weight);
lineTo(right, bottom - scale * brRadiusY);
conicTo(right, bottom, right - scale * brRadiusX, bottom, weight);
lineTo(left + scale * blRadiusX, bottom);
conicTo(left, bottom, left, bottom - scale * blRadiusY, weight);
close();
// SkAutoDisableDirectionCheck.
_firstDirection = isRRect ? direction : SPathDirection.kUnknown;

// No need to duplicate the rrect if we know the path is not made of a
// single rrect, or if the original rrect was consistent and didn't have
// to be corrected.
if (isRRect && isRRectCorrected) {
rrect = ui.RRect.fromLTRBAndCorners(
left,
top,
right,
bottom,
topLeft: ui.Radius.elliptical(tlRadiusX, tlRadiusY),
topRight: ui.Radius.elliptical(trRadiusX, trRadiusY),
bottomLeft: ui.Radius.elliptical(blRadiusX, blRadiusY),
bottomRight: ui.Radius.elliptical(brRadiusX, brRadiusY),
);
}
pathRef.setIsRRect(
isRRect, direction == SPathDirection.kCCW, startIndex % 8, rrect);
}
Expand Down
105 changes: 84 additions & 21 deletions lib/web_ui/lib/src/engine/html/path/path_ref.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ class PathRef {
fBoundsIsDirty = true; // this also invalidates fIsFinite
fSegmentMask = 0;
fIsOval = false;
rrectRepresentation = null;
fIsRRect = false;
fIsRect = false;
// The next two values don't matter unless fIsOval is true or rrectRepresentation is not null.
// The next two values don't matter unless fIsOval or fIsRRect are true.
fRRectOrOvalIsCCW = false;
fRRectOrOvalStartIdx = 0xAC;
assert(() {
Expand Down Expand Up @@ -104,7 +104,7 @@ class PathRef {
}
fSegmentMask = ref.fSegmentMask;
fIsOval = ref.fIsOval;
rrectRepresentation = ref.rrectRepresentation;
fIsRRect = ref.fIsRRect;
fIsRect = ref.fIsRect;
fRRectOrOvalIsCCW = ref.fRRectOrOvalIsCCW;
fRRectOrOvalStartIdx = ref.fRRectOrOvalStartIdx;
Expand Down Expand Up @@ -153,9 +153,9 @@ class PathRef {
int get isOval => fIsOval ? fRRectOrOvalStartIdx : -1;
bool get isOvalCCW => fRRectOrOvalIsCCW;

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

/// Reconstructs RRect from path commands.
///
/// Expect 4 Conics and lines between.
/// Use conic points to calculate corner radius.
ui.RRect _getRRect() {
ui.Rect bounds = getBounds();
// Radii x,y of 4 corners
final List<ui.Radius> radii = <ui.Radius>[];
final PathRefIterator iter = PathRefIterator(this);
final Float32List pts = Float32List(PathRefIterator.kMaxBufferSize);
int verb = iter.next(pts);
assert(SPath.kMoveVerb == verb);
int cornerIndex = 0;
while ((verb = iter.next(pts)) != SPath.kDoneVerb) {
if (SPath.kConicVerb == verb) {
final double controlPx = pts[2];
final double controlPy = pts[3];
double vector1_0x = controlPx - pts[0];
double vector1_0y = controlPy - pts[1];
double vector2_1x = pts[4] - pts[2];
double vector2_1y = pts[5] - pts[3];
double dx, dy;
// Depending on the corner we have control point at same
// horizontal position as startpoint or same vertical position.
// The location delta of control point specifies corner radius.
if (vector1_0x != 0.0) {
// For CW : Top right or bottom left corners.
assert(vector2_1x == 0.0 && vector1_0y == 0.0);
dx = vector1_0x.abs();
dy = vector2_1y.abs();
} else if (vector1_0y != 0.0) {
assert(vector2_1x == 0.0 || vector2_1y == 0.0);
dx = vector2_1x.abs();
dy = vector1_0y.abs();
} else {
assert(vector2_1y == 0.0);
dx = vector1_0x.abs();
dy = vector1_0y.abs();
}
if (assertionsEnabled) {
final int checkCornerIndex = _nearlyEqual(controlPx, bounds.left)
? (_nearlyEqual(controlPy, bounds.top)
? _Corner.kUpperLeft
: _Corner.kLowerLeft)
: (_nearlyEqual(controlPy, bounds.top)
? _Corner.kUpperRight
: _Corner.kLowerRight);
assert(checkCornerIndex == cornerIndex);
}
radii.add(ui.Radius.elliptical(dx, dy));
++cornerIndex;
} else {
assert((verb == SPath.kLineVerb &&
((pts[2] - pts[0]) == 0 || (pts[3] - pts[1]) == 0)) ||
verb == SPath.kCloseVerb);
}
}
return ui.RRect.fromRectAndCorners(bounds,
topLeft: radii[_Corner.kUpperLeft],
topRight: radii[_Corner.kUpperRight],
bottomRight: radii[_Corner.kLowerRight],
bottomLeft: radii[_Corner.kLowerLeft]);
}

bool operator ==(Object other) {
if (identical(this, other)) {
return true;
Expand Down Expand Up @@ -330,7 +394,7 @@ class PathRef {
}
fSegmentMask = source.fSegmentMask;
fIsOval = source.fIsOval;
rrectRepresentation = source.rrectRepresentation;
fIsRRect = source.fIsRRect;
fIsRect = source.fIsRect;
fRRectOrOvalIsCCW = source.fRRectOrOvalIsCCW;
fRRectOrOvalStartIdx = source.fRRectOrOvalStartIdx;
Expand Down Expand Up @@ -363,7 +427,7 @@ class PathRef {
}
fSegmentMask = ref.fSegmentMask;
fIsOval = ref.fIsOval;
rrectRepresentation = ref.rrectRepresentation;
fIsRRect = ref.fIsRRect;
fIsRect = ref.fIsRect;
fRRectOrOvalIsCCW = ref.fRRectOrOvalIsCCW;
fRRectOrOvalStartIdx = ref.fRRectOrOvalStartIdx;
Expand Down Expand Up @@ -714,7 +778,7 @@ class PathRef {
/// points are added.
void startEdit() {
fIsOval = false;
rrectRepresentation = null;
fIsRRect = false;
fIsRect = false;
cachedBounds = null;
fBoundsIsDirty = true;
Expand All @@ -727,11 +791,7 @@ class PathRef {
}

void setIsRRect(bool isRRect, bool isCCW, int start, ui.RRect rrect) {
if (isRRect) {
rrectRepresentation = rrect;
} else {
rrectRepresentation = null;
}
fIsRRect = isRRect;
fRRectOrOvalIsCCW = isCCW;
fRRectOrOvalStartIdx = start;
}
Expand All @@ -753,11 +813,7 @@ class PathRef {
bool fIsFinite = true; // only meaningful if bounds are valid

bool fIsOval = false;

/// If the path is made of a single `RRect`, this field contains the original
/// `RRect` that was added to the path.
ui.RRect? rrectRepresentation;

bool fIsRRect = false;
bool fIsRect = false;
// Both the circle and rrect special cases have a notion of direction and starting point
// The next two variables store that information for either.
Expand All @@ -766,9 +822,9 @@ class PathRef {
int fSegmentMask = 0;

bool get isValid {
if (fIsOval || rrectRepresentation != null) {
if (fIsOval || fIsRRect) {
// Currently we don't allow both of these to be set.
if (fIsOval == (rrectRepresentation != null)) {
if (fIsOval == fIsRRect) {
return false;
}
if (fIsOval) {
Expand All @@ -782,7 +838,7 @@ class PathRef {
}
}
if (fIsRect) {
if (fIsOval || (rrectRepresentation != null)) {
if (fIsOval || fIsRRect) {
return false;
}
if (fRRectOrOvalStartIdx >= 4) {
Expand Down Expand Up @@ -1008,3 +1064,10 @@ class PathRefIterator {
? pathRef._fVerbs[_verbIndex]
: SPath.kDoneVerb;
}

class _Corner {
static const int kUpperLeft = 0;
static const int kUpperRight = 1;
static const int kLowerRight = 2;
static const int kLowerLeft = 3;
}
73 changes: 0 additions & 73 deletions lib/web_ui/test/engine/surface/path/path_ref_test.dart

This file was deleted.