From 80fbf90cb9ba8585cc038d1b0284dbe6eb557bce Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 11 Jun 2015 09:37:20 -0700 Subject: [PATCH] Factor BoxPainter out of RenderDecoratedBox This factoring will more code use BoxPainter in the future (in particular, PopupMenu). R=ianh@google.com Review URL: https://codereview.chromium.org/1180633004. --- sdk/BUILD.gn | 1 + sdk/lib/framework/painting/box_painter.dart | 252 ++++++++++++++++++++ sdk/lib/framework/rendering/box.dart | 249 +------------------ 3 files changed, 263 insertions(+), 239 deletions(-) create mode 100644 sdk/lib/framework/painting/box_painter.dart diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index 54ab7a94685..aee3614c76d 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -98,6 +98,7 @@ dart_pkg("sdk") { "lib/framework/net/fetch.dart", "lib/framework/net/image_cache.dart", "lib/framework/node.dart", + "lib/framework/painting/box_painter.dart", "lib/framework/painting/shadows.dart", "lib/framework/reflect.dart", "lib/framework/rendering/block.dart", diff --git a/sdk/lib/framework/painting/box_painter.dart b/sdk/lib/framework/painting/box_painter.dart new file mode 100644 index 00000000000..0b5747da502 --- /dev/null +++ b/sdk/lib/framework/painting/box_painter.dart @@ -0,0 +1,252 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:sky' as sky; +import 'dart:sky' show Point, Size, Rect, Color, Paint, Path; +import 'shadows.dart'; + +class BorderSide { + const BorderSide({ + this.color: const Color(0xFF000000), + this.width: 1.0 + }); + final Color color; + final double width; + + static const none = const BorderSide(width: 0.0); + + int get hashCode { + int value = 373; + value = 37 * value * color.hashCode; + value = 37 * value * width.hashCode; + return value; + } + String toString() => 'BorderSide($color, $width)'; +} + +class Border { + const Border({ + this.top: BorderSide.none, + this.right: BorderSide.none, + this.bottom: BorderSide.none, + this.left: BorderSide.none + }); + + const Border.all(BorderSide side) : + top = side, + right = side, + bottom = side, + left = side; + + final BorderSide top; + final BorderSide right; + final BorderSide bottom; + final BorderSide left; + + int get hashCode { + int value = 373; + value = 37 * value * top.hashCode; + value = 37 * value * right.hashCode; + value = 37 * value * bottom.hashCode; + value = 37 * value * left.hashCode; + return value; + } + String toString() => 'Border($top, $right, $bottom, $left)'; +} + +class BoxShadow { + const BoxShadow({ + this.color, + this.offset, + this.blur + }); + + final Color color; + final Size offset; + final double blur; + + String toString() => 'BoxShadow($color, $offset, $blur)'; +} + +abstract class Gradient { + sky.Shader createShader(); +} + +class LinearGradient extends Gradient { + LinearGradient({ + this.endPoints, + this.colors, + this.colorStops, + this.tileMode: sky.TileMode.clamp + }); + + String toString() => + 'LinearGradient($endPoints, $colors, $colorStops, $tileMode)'; + + sky.Shader createShader() { + return new sky.Gradient.Linear(this.endPoints, this.colors, this.colorStops, + this.tileMode); + } + + final List endPoints; + final List colors; + final List colorStops; + final sky.TileMode tileMode; +} + +class RadialGradient extends Gradient { + RadialGradient({ + this.center, + this.radius, + this.colors, + this.colorStops, + this.tileMode: sky.TileMode.clamp + }); + + String toString() => + 'RadialGradient($center, $radius, $colors, $colorStops, $tileMode)'; + + sky.Shader createShader() { + return new sky.Gradient.Radial(this.center, this.radius, this.colors, + this.colorStops, this.tileMode); + } + + final Point center; + final double radius; + final List colors; + final List colorStops; + final sky.TileMode tileMode; +} + +// This must be immutable, because we won't notice when it changes +class BoxDecoration { + const BoxDecoration({ + this.backgroundColor, + this.border, + this.borderRadius, + this.boxShadow, + this.gradient + }); + + final Color backgroundColor; + final double borderRadius; + final Border border; + final List boxShadow; + final Gradient gradient; + + String toString([String prefix = '']) { + List result = []; + if (backgroundColor != null) + result.add('${prefix}backgroundColor: $backgroundColor'); + if (border != null) + result.add('${prefix}border: $border'); + if (borderRadius != null) + result.add('${prefix}borderRadius: $borderRadius'); + if (boxShadow != null) + result.add('${prefix}boxShadow: ${boxShadow.map((shadow) => shadow.toString())}'); + if (gradient != null) + result.add('${prefix}gradient: $gradient'); + if (result.isEmpty) + return '${prefix}'; + return result.join('\n'); + } +} + +class BoxPainter { + BoxPainter(BoxDecoration decoration) : _decoration = decoration { + assert(decoration != null); + } + + BoxDecoration _decoration; + BoxDecoration get decoration => _decoration; + void set decoration (BoxDecoration value) { + assert(value != null); + if (value == _decoration) + return; + _decoration = value; + _cachedBackgroundPaint = null; + } + + Paint _cachedBackgroundPaint; + Paint get _backgroundPaint { + if (_cachedBackgroundPaint == null) { + Paint paint = new Paint(); + + if (_decoration.backgroundColor != null) + paint.color = _decoration.backgroundColor; + + if (_decoration.boxShadow != null) { + var builder = new ShadowDrawLooperBuilder(); + for (BoxShadow boxShadow in _decoration.boxShadow) + builder.addShadow(boxShadow.offset, boxShadow.color, boxShadow.blur); + paint.setDrawLooper(builder.build()); + } + + if (_decoration.gradient != null) + paint.setShader(_decoration.gradient.createShader()); + + _cachedBackgroundPaint = paint; + } + + return _cachedBackgroundPaint; + } + + void paint(sky.Canvas canvas, Rect rect) { + if (_decoration.backgroundColor != null || _decoration.boxShadow != null || + _decoration.gradient != null) { + if (_decoration.borderRadius == null) + canvas.drawRect(rect, _backgroundPaint); + else + canvas.drawRRect(new sky.RRect()..setRectXY(rect, _decoration.borderRadius, _decoration.borderRadius), _backgroundPaint); + } + + if (_decoration.border != null) { + assert(_decoration.borderRadius == null); // TODO(abarth): Implement borders with border radius. + + 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); + } + } +} diff --git a/sdk/lib/framework/rendering/box.dart b/sdk/lib/framework/rendering/box.dart index e1a4545cd95..efbef3d22a0 100644 --- a/sdk/lib/framework/rendering/box.dart +++ b/sdk/lib/framework/rendering/box.dart @@ -6,10 +6,12 @@ import 'dart:math' as math; import 'dart:sky' as sky; import 'dart:typed_data'; import 'object.dart'; -import '../painting/shadows.dart'; +import '../painting/box_painter.dart'; import 'package:vector_math/vector_math.dart'; import 'package:sky/framework/net/image_cache.dart' as image_cache; +export '../painting/box_painter.dart'; + // GENERIC BOX RENDERING // Anything that has a concept of x, y, width, height is going to derive from this @@ -686,262 +688,31 @@ class RenderImage extends RenderBox { String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}url: ${src}\n${prefix}dimensions: ${requestedSize}\n'; } -class BorderSide { - const BorderSide({ - this.color: const Color(0xFF000000), - this.width: 1.0 - }); - final Color color; - final double width; - - static const none = const BorderSide(width: 0.0); - - int get hashCode { - int value = 373; - value = 37 * value * color.hashCode; - value = 37 * value * width.hashCode; - return value; - } - String toString() => 'BorderSide($color, $width)'; -} - -class Border { - const Border({ - this.top: BorderSide.none, - this.right: BorderSide.none, - this.bottom: BorderSide.none, - this.left: BorderSide.none - }); - - const Border.all(BorderSide side) : - top = side, - right = side, - bottom = side, - left = side; - - final BorderSide top; - final BorderSide right; - final BorderSide bottom; - final BorderSide left; - - int get hashCode { - int value = 373; - value = 37 * value * top.hashCode; - value = 37 * value * right.hashCode; - value = 37 * value * bottom.hashCode; - value = 37 * value * left.hashCode; - return value; - } - String toString() => 'Border($top, $right, $bottom, $left)'; -} - -class BoxShadow { - const BoxShadow({ - this.color, - this.offset, - this.blur - }); - - final Color color; - final Size offset; - final double blur; - - String toString() => 'BoxShadow($color, $offset, $blur)'; -} - -abstract class Gradient { - sky.Shader createShader(); -} - -class LinearGradient extends Gradient { - LinearGradient({ - this.endPoints, - this.colors, - this.colorStops, - this.tileMode: sky.TileMode.clamp - }); - - String toString() => - 'LinearGradient($endPoints, $colors, $colorStops, $tileMode)'; - - sky.Shader createShader() { - return new sky.Gradient.Linear(this.endPoints, this.colors, this.colorStops, - this.tileMode); - } - - final List endPoints; - final List colors; - final List colorStops; - final sky.TileMode tileMode; -} - -class RadialGradient extends Gradient { - RadialGradient({ - this.center, - this.radius, - this.colors, - this.colorStops, - this.tileMode: sky.TileMode.clamp - }); - - String toString() => - 'RadialGradient($center, $radius, $colors, $colorStops, $tileMode)'; - - sky.Shader createShader() { - return new sky.Gradient.Radial(this.center, this.radius, this.colors, - this.colorStops, this.tileMode); - } - - final Point center; - final double radius; - final List colors; - final List colorStops; - final sky.TileMode tileMode; -} - -// This must be immutable, because we won't notice when it changes -class BoxDecoration { - const BoxDecoration({ - this.backgroundColor, - this.border, - this.borderRadius, - this.boxShadow, - this.gradient - }); - - final Color backgroundColor; - final double borderRadius; - final Border border; - final List boxShadow; - final Gradient gradient; - - String toString([String prefix = '']) { - List result = []; - if (backgroundColor != null) - result.add('${prefix}backgroundColor: $backgroundColor'); - if (border != null) - result.add('${prefix}border: $border'); - if (borderRadius != null) - result.add('${prefix}borderRadius: $borderRadius'); - if (boxShadow != null) - result.add('${prefix}boxShadow: ${boxShadow.map((shadow) => shadow.toString())}'); - if (gradient != null) - result.add('${prefix}gradient: $gradient'); - if (result.isEmpty) - return '${prefix}'; - return result.join('\n'); - } -} - class RenderDecoratedBox extends RenderProxyBox { RenderDecoratedBox({ BoxDecoration decoration, RenderBox child - }) : _decoration = decoration, super(child) { - assert(_decoration != null); - } + }) : _painter = new BoxPainter(decoration), super(child); - BoxDecoration _decoration; - BoxDecoration get decoration => _decoration; + BoxPainter _painter; + BoxDecoration get decoration => _painter.decoration; void set decoration (BoxDecoration value) { assert(value != null); - if (value == _decoration) + if (value == _painter.decoration) return; - _decoration = value; - _cachedBackgroundPaint = null; + _painter.decoration = value; markNeedsPaint(); } - Paint _cachedBackgroundPaint; - Paint get _backgroundPaint { - if (_cachedBackgroundPaint == null) { - Paint paint = new Paint(); - - if (_decoration.backgroundColor != null) - paint.color = _decoration.backgroundColor; - - if (_decoration.boxShadow != null) { - var builder = new ShadowDrawLooperBuilder(); - for (BoxShadow boxShadow in _decoration.boxShadow) - builder.addShadow(boxShadow.offset, boxShadow.color, boxShadow.blur); - paint.setDrawLooper(builder.build()); - } - - if (_decoration.gradient != null) - paint.setShader(_decoration.gradient.createShader()); - - _cachedBackgroundPaint = paint; - } - - return _cachedBackgroundPaint; - } - void paint(RenderObjectDisplayList canvas) { assert(size.width != null); assert(size.height != null); - - if (_decoration.backgroundColor != null || _decoration.boxShadow != null || - _decoration.gradient != null) { - Rect rect = new Rect.fromLTRB(0.0, 0.0, size.width, size.height); - if (_decoration.borderRadius == null) - canvas.drawRect(rect, _backgroundPaint); - else - canvas.drawRRect(new sky.RRect()..setRectXY(rect, _decoration.borderRadius, _decoration.borderRadius), _backgroundPaint); - } - - if (_decoration.border != null) { - assert(_decoration.borderRadius == null); // TODO(abarth): Implement borders with border radius. - - 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(0.0,0.0); - path.lineTo(_decoration.border.left.width, _decoration.border.top.width); - path.lineTo(size.width - _decoration.border.right.width, _decoration.border.top.width); - path.lineTo(size.width, 0.0); - path.close(); - canvas.drawPath(path, paint); - - paint.color = _decoration.border.right.color; - path = new Path(); - path.moveTo(size.width, 0.0); - path.lineTo(size.width - _decoration.border.right.width, _decoration.border.top.width); - path.lineTo(size.width - _decoration.border.right.width, size.height - _decoration.border.bottom.width); - path.lineTo(size.width, size.height); - path.close(); - canvas.drawPath(path, paint); - - paint.color = _decoration.border.bottom.color; - path = new Path(); - path.moveTo(size.width, size.height); - path.lineTo(size.width - _decoration.border.right.width, size.height - _decoration.border.bottom.width); - path.lineTo(_decoration.border.left.width, size.height - _decoration.border.bottom.width); - path.lineTo(0.0, size.height); - path.close(); - canvas.drawPath(path, paint); - - paint.color = _decoration.border.left.color; - path = new Path(); - path.moveTo(0.0, size.height); - path.lineTo(_decoration.border.left.width, size.height - _decoration.border.bottom.width); - path.lineTo(_decoration.border.left.width, _decoration.border.top.width); - path.lineTo(0.0,0.0); - path.close(); - canvas.drawPath(path, paint); - } - + _painter.paint(canvas, new Rect.fromSize(size)); super.paint(canvas); } - String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}decoration:\n${decoration.toString(prefix + " ")}\n'; + String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}decoration:\n${_painter.decoration.toString(prefix + " ")}\n'; } class RenderTransform extends RenderProxyBox {