未验证 提交 e9d0b4a5 编写于 作者: Y Yegor 提交者: GitHub

Revert "remove _getRRect: fixes assertion error (#24688)" (#24718)

This reverts commit e763e567.

The commit broke golden tests.
上级 ef8353b1
......@@ -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);
}
......
......@@ -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(() {
......@@ -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;
......@@ -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.
......@@ -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;
......@@ -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;
......@@ -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;
......@@ -714,7 +778,7 @@ class PathRef {
/// points are added.
void startEdit() {
fIsOval = false;
rrectRepresentation = null;
fIsRRect = false;
fIsRect = false;
cachedBounds = null;
fBoundsIsDirty = true;
......@@ -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;
}
......@@ -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.
......@@ -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) {
......@@ -782,7 +838,7 @@ class PathRef {
}
}
if (fIsRect) {
if (fIsOval || (rrectRepresentation != null)) {
if (fIsOval || fIsRRect) {
return false;
}
if (fRRectOrOvalStartIdx >= 4) {
......@@ -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;
}
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.12
import 'package:ui/src/engine.dart';
import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/ui.dart';
void main() {
internalBootstrapBrowserTest(() => testMain);
}
void testMain() {
test('PathRef.getRRect with radius', () {
final SurfacePath path = SurfacePath();
final RRect rrect = RRect.fromLTRBR(0, 0, 10, 10, Radius.circular(2));
path.addRRect(rrect);
expect(path.toRoundedRect(), rrect);
});
test('PathRef.getRRect with radius larger than rect', () {
final SurfacePath path = SurfacePath();
final RRect rrect = RRect.fromLTRBR(0, 0, 10, 10, Radius.circular(20));
path.addRRect(rrect);
expect(
path.toRoundedRect(),
// Path.addRRect will correct the radius to fit the dimensions, so when
// extracting the rrect out of the path we don't get the original.
RRect.fromLTRBR(0, 0, 10, 10, Radius.circular(5)),
);
});
test('PathRef.getRRect with zero radius', () {
final SurfacePath path = SurfacePath();
final RRect rrect = RRect.fromLTRBR(0, 0, 10, 10, Radius.circular(0));
path.addRRect(rrect);
expect(path.toRoundedRect(), isNull);
expect(path.toRect(), rrect.outerRect);
});
test('PathRef.getRRect elliptical', () {
final SurfacePath path = SurfacePath();
final RRect rrect = RRect.fromLTRBR(0, 0, 10, 10, Radius.elliptical(2, 4));
path.addRRect(rrect);
expect(path.toRoundedRect(), rrect);
});
test('PathRef.getRRect elliptical zero x', () {
final SurfacePath path = SurfacePath();
final RRect rrect = RRect.fromLTRBR(0, 0, 10, 10, Radius.elliptical(0, 3));
path.addRRect(rrect);
expect(path.toRoundedRect(), isNull);
expect(path.toRect(), rrect.outerRect);
});
test('PathRef.getRRect elliptical zero y', () {
final SurfacePath path = SurfacePath();
final RRect rrect = RRect.fromLTRBR(0, 0, 10, 10, Radius.elliptical(3, 0));
path.addRRect(rrect);
expect(path.toRoundedRect(), isNull);
expect(path.toRect(), rrect.outerRect);
});
test('PathRef.getRRect with nearly zero corner', () {
final SurfacePath path = SurfacePath();
final RRect original = RRect.fromLTRBR(0, 0, 10, 10, Radius.elliptical(0.00000001, 5));
path.addRRect(original);
expect(path.toRoundedRect(), original);
});
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册