Commit 98b1f19d authored by Ian Hickson's avatar Ian Hickson

Merge pull request #2875 from Hixie/border-paint

Make the border painting code reusable.
parents f5c3b75d 32f7cbcf
......@@ -13,6 +13,18 @@ import 'edge_insets.dart';
export 'edge_insets.dart' show EdgeInsets;
double _getEffectiveBorderRadius(Rect rect, double borderRadius) {
assert(rect != null);
assert(borderRadius != null);
double shortestSide = rect.shortestSide;
// In principle, we should use shortestSide / 2.0, but we don't want to
// run into floating point rounding errors. Instead, we just use
// shortestSide and let Canvas do any remaining clamping.
// The right long-term fix is to do layout using fixed precision
// arithmetic. (see also "_applyFloatingPointHack")
return borderRadius > shortestSide ? shortestSide : borderRadius;
}
/// A side of a border of a box.
class BorderSide {
const BorderSide({
......@@ -151,6 +163,93 @@ class Border {
);
}
void paint(Canvas canvas, Rect rect, {
BoxShape shape: BoxShape.rectangle,
double borderRadius: null
}) {
if (isUniform) {
if (borderRadius != null) {
_paintBorderWithRadius(canvas, rect, borderRadius);
return;
}
if (shape == BoxShape.circle) {
_paintBorderWithCircle(canvas, rect);
return;
}
}
assert(borderRadius == null); // TODO(abarth): Support non-uniform rounded borders.
assert(shape == BoxShape.rectangle); // TODO(ianh): Support non-uniform borders on circles.
assert(top != null);
assert(right != null);
assert(bottom != null);
assert(left != null);
Paint paint = new Paint();
Path path;
// TODO(ianh): Handle hairline border by drawing a single line instead of a wedge
paint.color = top.color;
path = new Path();
path.moveTo(rect.left, rect.top);
path.lineTo(rect.left + left.width, rect.top + top.width);
path.lineTo(rect.right - right.width, rect.top + top.width);
path.lineTo(rect.right, rect.top);
path.close();
canvas.drawPath(path, paint);
paint.color = right.color;
path = new Path();
path.moveTo(rect.right, rect.top);
path.lineTo(rect.right - right.width, rect.top + top.width);
path.lineTo(rect.right - right.width, rect.bottom - bottom.width);
path.lineTo(rect.right, rect.bottom);
path.close();
canvas.drawPath(path, paint);
paint.color = bottom.color;
path = new Path();
path.moveTo(rect.right, rect.bottom);
path.lineTo(rect.right - right.width, rect.bottom - bottom.width);
path.lineTo(rect.left + left.width, rect.bottom - bottom.width);
path.lineTo(rect.left, rect.bottom);
path.close();
canvas.drawPath(path, paint);
paint.color = left.color;
path = new Path();
path.moveTo(rect.left, rect.bottom);
path.lineTo(rect.left + left.width, rect.bottom - bottom.width);
path.lineTo(rect.left + left.width, rect.top + top.width);
path.lineTo(rect.left, rect.top);
path.close();
canvas.drawPath(path, paint);
}
void _paintBorderWithRadius(Canvas canvas, Rect rect, double borderRadius) {
assert(isUniform);
Color color = top.color;
double width = top.width;
double radius = _getEffectiveBorderRadius(rect, borderRadius);
// TODO(ianh): Handle hairline borders by just drawing an RRect instead
RRect outer = new RRect.fromRectXY(rect, radius, radius);
RRect inner = new RRect.fromRectXY(rect.deflate(width), radius - width, radius - width);
canvas.drawDRRect(outer, inner, new Paint()..color = color);
}
void _paintBorderWithCircle(Canvas canvas, Rect rect) {
assert(isUniform);
double width = top.width;
Paint paint = new Paint()
..color = top.color
..strokeWidth = width
..style = PaintingStyle.stroke;
double radius = (rect.shortestSide - width) / 2.0;
canvas.drawCircle(rect.center, radius, paint);
}
@override
bool operator ==(dynamic other) {
if (identical(this, other))
......@@ -981,16 +1080,6 @@ class BoxDecoration extends Decoration {
backgroundImage?._removeChangeListener(listener);
}
double getEffectiveBorderRadius(Rect rect) {
double shortestSide = rect.shortestSide;
// In principle, we should use shortestSide / 2.0, but we don't want to
// run into floating point rounding errors. Instead, we just use
// shortestSide and let Canvas do any remaining clamping.
// The right long-term fix is to do layout using fixed precision
// arithmetic. (see also "_applyFloatingPointHack")
return borderRadius > shortestSide ? shortestSide : borderRadius;
}
@override
bool hitTest(Size size, Point position) {
assert(shape != null);
......@@ -1059,7 +1148,7 @@ class _BoxDecorationPainter extends BoxPainter {
if (_decoration.borderRadius == null) {
canvas.drawRect(rect, paint);
} else {
double radius = _decoration.getEffectiveBorderRadius(rect);
double radius = _getEffectiveBorderRadius(rect, _decoration.borderRadius);
canvas.drawRRect(new RRect.fromRectXY(rect, radius, radius), paint);
}
break;
......@@ -1101,103 +1190,17 @@ class _BoxDecorationPainter extends BoxPainter {
);
}
void _paintBorder(Canvas canvas, Rect rect) {
if (_decoration.border == null)
return;
if (_decoration.border.isUniform) {
if (_decoration.borderRadius != null) {
_paintBorderWithRadius(canvas, rect);
return;
}
if (_decoration.shape == BoxShape.circle) {
_paintBorderWithCircle(canvas, rect);
return;
}
}
assert(_decoration.borderRadius == null); // TODO(abarth): Support non-uniform rounded borders.
assert(_decoration.shape == BoxShape.rectangle); // TODO(ianh): Support non-uniform borders on circles.
assert(_decoration.border.top != null);
assert(_decoration.border.right != null);
assert(_decoration.border.bottom != null);
assert(_decoration.border.left != null);
Paint paint = new Paint();
Path path;
paint.color = _decoration.border.top.color;
path = new Path();
path.moveTo(rect.left, rect.top);
path.lineTo(rect.left + _decoration.border.left.width, rect.top + _decoration.border.top.width);
path.lineTo(rect.right - _decoration.border.right.width, rect.top + _decoration.border.top.width);
path.lineTo(rect.right, rect.top);
path.close();
canvas.drawPath(path, paint);
paint.color = _decoration.border.right.color;
path = new Path();
path.moveTo(rect.right, rect.top);
path.lineTo(rect.right - _decoration.border.right.width, rect.top + _decoration.border.top.width);
path.lineTo(rect.right - _decoration.border.right.width, rect.bottom - _decoration.border.bottom.width);
path.lineTo(rect.right, rect.bottom);
path.close();
canvas.drawPath(path, paint);
paint.color = _decoration.border.bottom.color;
path = new Path();
path.moveTo(rect.right, rect.bottom);
path.lineTo(rect.right - _decoration.border.right.width, rect.bottom - _decoration.border.bottom.width);
path.lineTo(rect.left + _decoration.border.left.width, rect.bottom - _decoration.border.bottom.width);
path.lineTo(rect.left, rect.bottom);
path.close();
canvas.drawPath(path, paint);
paint.color = _decoration.border.left.color;
path = new Path();
path.moveTo(rect.left, rect.bottom);
path.lineTo(rect.left + _decoration.border.left.width, rect.bottom - _decoration.border.bottom.width);
path.lineTo(rect.left + _decoration.border.left.width, rect.top + _decoration.border.top.width);
path.lineTo(rect.left, rect.top);
path.close();
canvas.drawPath(path, paint);
}
void _paintBorderWithRadius(Canvas canvas, Rect rect) {
assert(_decoration.border.isUniform);
assert(_decoration.shape == BoxShape.rectangle);
Color color = _decoration.border.top.color;
double width = _decoration.border.top.width;
double radius = _decoration.getEffectiveBorderRadius(rect);
RRect outer = new RRect.fromRectXY(rect, radius, radius);
RRect inner = new RRect.fromRectXY(rect.deflate(width), radius - width, radius - width);
canvas.drawDRRect(outer, inner, new Paint()..color = color);
}
void _paintBorderWithCircle(Canvas canvas, Rect rect) {
assert(_decoration.border.isUniform);
assert(_decoration.shape == BoxShape.circle);
assert(_decoration.borderRadius == null);
double width = _decoration.border.top.width;
if (width <= 0.0)
return;
Paint paint = new Paint()
..color = _decoration.border.top.color
..strokeWidth = width
..style = PaintingStyle.stroke;
Point center = rect.center;
double radius = (rect.shortestSide - width) / 2.0;
canvas.drawCircle(center, radius, paint);
}
/// Paint the box decoration into the given location on the given canvas
@override
void paint(Canvas canvas, Rect rect) {
_paintShadows(canvas, rect);
_paintBackgroundColor(canvas, rect);
_paintBackgroundImage(canvas, rect);
_paintBorder(canvas, rect);
_decoration.border?.paint(
canvas,
rect,
shape: _decoration.shape,
borderRadius: _decoration.borderRadius
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment