From 635168d9f9b8e1b30114cdba889f7aa061b58816 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 11 Jan 2016 00:16:42 -0800 Subject: [PATCH] Always using OpacityLayer for blending We don't know how to accuately compute paint bounds in the render tree. Instead, we can rely on the compositor to compute the paint bounds for us if we use OpacityLayer to do our opacity blends. Fixes the shadow when closing the menu in the stocks app. --- packages/flutter/lib/src/rendering/layer.dart | 9 +---- .../flutter/lib/src/rendering/object.dart | 39 +++++++------------ .../flutter/lib/src/rendering/proxy_box.dart | 19 +++++---- 3 files changed, 27 insertions(+), 40 deletions(-) diff --git a/packages/flutter/lib/src/rendering/layer.dart b/packages/flutter/lib/src/rendering/layer.dart index 279ddcbae60..d2e9082fa6c 100644 --- a/packages/flutter/lib/src/rendering/layer.dart +++ b/packages/flutter/lib/src/rendering/layer.dart @@ -372,11 +372,7 @@ class TransformLayer extends ContainerLayer { /// A composited layer that makes its children partially transparent class OpacityLayer extends ContainerLayer { - OpacityLayer({ Offset offset: Offset.zero, this.bounds, this.alpha }) : super(offset: offset); - - /// Unused - Rect bounds; - // TODO(abarth): Remove. + OpacityLayer({ Offset offset: Offset.zero, this.alpha }) : super(offset: offset); /// The amount to multiply into the alpha channel /// @@ -386,14 +382,13 @@ class OpacityLayer extends ContainerLayer { void addToScene(ui.SceneBuilder builder, Offset layerOffset) { Offset childOffset = offset + layerOffset; - builder.pushOpacity(alpha, bounds?.shift(childOffset)); + builder.pushOpacity(alpha, null); addChildrenToScene(builder, childOffset); builder.pop(); } void debugDescribeSettings(List settings) { super.debugDescribeSettings(settings); - settings.add('bounds: $bounds'); settings.add('alpha: $alpha'); } } diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart index 6877a087815..fb16aa0ebe7 100644 --- a/packages/flutter/lib/src/rendering/object.dart +++ b/packages/flutter/lib/src/rendering/object.dart @@ -285,31 +285,17 @@ class PaintingContext { } } - static Paint _getPaintForAlpha(int alpha) { - return new Paint() - ..color = new Color.fromARGB(alpha, 0, 0, 0) - ..transferMode = TransferMode.srcOver - ..isAntiAlias = false; - } - /// Push an opacity layer. /// /// This function will call painter synchronously with a painting context that /// will be blended with the given alpha value. - void pushOpacity(bool needsCompositing, Offset offset, Rect bounds, int alpha, PaintingContextCallback painter) { - if (needsCompositing) { - _stopRecordingIfNeeded(); - OpacityLayer opacityLayer = new OpacityLayer(bounds: bounds, alpha: alpha); - _appendLayer(opacityLayer, offset); - PaintingContext childContext = new PaintingContext._(opacityLayer, _paintBounds); - painter(childContext, Offset.zero); - childContext._stopRecordingIfNeeded(); - } else { - // TODO(abarth): pushOpacity should require bounds. - canvas.saveLayer(bounds?.shift(offset), _getPaintForAlpha(alpha)); - painter(this, offset); - canvas.restore(); - } + void pushOpacity(Offset offset, int alpha, PaintingContextCallback painter) { + _stopRecordingIfNeeded(); + OpacityLayer opacityLayer = new OpacityLayer(alpha: alpha); + _appendLayer(opacityLayer, offset); + PaintingContext childContext = new PaintingContext._(opacityLayer, _paintBounds); + painter(childContext, Offset.zero); + childContext._stopRecordingIfNeeded(); } static Paint _getPaintForShaderMask(Rect bounds, @@ -435,7 +421,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { setupParentData(child); super.adoptChild(child); markNeedsLayout(); - _markNeedsCompositingBitsUpdate(); + markNeedsCompositingBitsUpdate(); } /// Called by subclasses when they decide a render object is no longer a child. @@ -451,7 +437,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { child.parentData = null; super.dropChild(child); markNeedsLayout(); - _markNeedsCompositingBitsUpdate(); + markNeedsCompositingBitsUpdate(); } /// Calls visitor for each immediate child of this render object. @@ -854,7 +840,8 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { /// creates at least one composited layer. For example, videos should return /// true if they use hardware decoders. /// - /// Warning: This getter must not change value over the lifetime of this object. + /// You must call markNeedsCompositingBitsUpdate() if the value of this + /// getter changes. bool get alwaysNeedsCompositing => false; ContainerLayer _layer; @@ -880,7 +867,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { /// This method does not schedule a rendering frame, because since /// it cannot be the case that _only_ the compositing bits changed, /// something else will have scheduled a frame for us. - void _markNeedsCompositingBitsUpdate() { + void markNeedsCompositingBitsUpdate() { if (_needsCompositingBitsUpdate) return; _needsCompositingBitsUpdate = true; @@ -889,7 +876,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { if (parent._needsCompositingBitsUpdate) return; if (!isRepaintBoundary && !parent.isRepaintBoundary) { - parent._markNeedsCompositingBitsUpdate(); + parent.markNeedsCompositingBitsUpdate(); return; } } diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart index a24a69dacca..abf1d0eab51 100644 --- a/packages/flutter/lib/src/rendering/proxy_box.dart +++ b/packages/flutter/lib/src/rendering/proxy_box.dart @@ -525,6 +525,8 @@ class RenderIntrinsicHeight extends RenderProxyBox { } +int _getAlphaFromOpacity(double opacity) => (opacity * 255).round(); + /// Makes its child partially transparent. /// /// This class paints its child into an intermediate buffer and then blends the @@ -534,10 +536,12 @@ class RenderIntrinsicHeight extends RenderProxyBox { /// into an intermediate buffer. class RenderOpacity extends RenderProxyBox { RenderOpacity({ RenderBox child, double opacity }) - : this._opacity = opacity, super(child) { + : _opacity = opacity, _alpha = _getAlphaFromOpacity(opacity), super(child) { assert(opacity >= 0.0 && opacity <= 1.0); } + bool get alwaysNeedsCompositing => _alpha != 0 && _alpha != 255; + /// The fraction to scale the child's alpha value. /// /// An opacity of 1.0 is fully opaque. An opacity of 0.0 is fully transparent @@ -550,22 +554,23 @@ class RenderOpacity extends RenderProxyBox { if (_opacity == newOpacity) return; _opacity = newOpacity; + _alpha = _getAlphaFromOpacity(_opacity); + markNeedsCompositingBitsUpdate(); markNeedsPaint(); } - int get _alpha => (_opacity * 255).round(); + int _alpha; void paint(PaintingContext context, Offset offset) { if (child != null) { - int a = _alpha; - if (a == 0) + if (_alpha == 0) return; - if (a == 255) { + if (_alpha == 255) { context.paintChild(child, offset); return; } - // TODO(abarth): We should pass bounds here. - context.pushOpacity(needsCompositing, offset, null, a, super.paint); + assert(needsCompositing); + context.pushOpacity(offset, _alpha, super.paint); } }