Commit 635168d9 authored by Adam Barth's avatar Adam Barth

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.
parent 91551c79
...@@ -372,11 +372,7 @@ class TransformLayer extends ContainerLayer { ...@@ -372,11 +372,7 @@ class TransformLayer extends ContainerLayer {
/// A composited layer that makes its children partially transparent /// A composited layer that makes its children partially transparent
class OpacityLayer extends ContainerLayer { class OpacityLayer extends ContainerLayer {
OpacityLayer({ Offset offset: Offset.zero, this.bounds, this.alpha }) : super(offset: offset); OpacityLayer({ Offset offset: Offset.zero, this.alpha }) : super(offset: offset);
/// Unused
Rect bounds;
// TODO(abarth): Remove.
/// The amount to multiply into the alpha channel /// The amount to multiply into the alpha channel
/// ///
...@@ -386,14 +382,13 @@ class OpacityLayer extends ContainerLayer { ...@@ -386,14 +382,13 @@ class OpacityLayer extends ContainerLayer {
void addToScene(ui.SceneBuilder builder, Offset layerOffset) { void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
Offset childOffset = offset + layerOffset; Offset childOffset = offset + layerOffset;
builder.pushOpacity(alpha, bounds?.shift(childOffset)); builder.pushOpacity(alpha, null);
addChildrenToScene(builder, childOffset); addChildrenToScene(builder, childOffset);
builder.pop(); builder.pop();
} }
void debugDescribeSettings(List<String> settings) { void debugDescribeSettings(List<String> settings) {
super.debugDescribeSettings(settings); super.debugDescribeSettings(settings);
settings.add('bounds: $bounds');
settings.add('alpha: $alpha'); settings.add('alpha: $alpha');
} }
} }
...@@ -285,31 +285,17 @@ class PaintingContext { ...@@ -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. /// Push an opacity layer.
/// ///
/// This function will call painter synchronously with a painting context that /// This function will call painter synchronously with a painting context that
/// will be blended with the given alpha value. /// will be blended with the given alpha value.
void pushOpacity(bool needsCompositing, Offset offset, Rect bounds, int alpha, PaintingContextCallback painter) { void pushOpacity(Offset offset, int alpha, PaintingContextCallback painter) {
if (needsCompositing) { _stopRecordingIfNeeded();
_stopRecordingIfNeeded(); OpacityLayer opacityLayer = new OpacityLayer(alpha: alpha);
OpacityLayer opacityLayer = new OpacityLayer(bounds: bounds, alpha: alpha); _appendLayer(opacityLayer, offset);
_appendLayer(opacityLayer, offset); PaintingContext childContext = new PaintingContext._(opacityLayer, _paintBounds);
PaintingContext childContext = new PaintingContext._(opacityLayer, _paintBounds); painter(childContext, Offset.zero);
painter(childContext, Offset.zero); childContext._stopRecordingIfNeeded();
childContext._stopRecordingIfNeeded();
} else {
// TODO(abarth): pushOpacity should require bounds.
canvas.saveLayer(bounds?.shift(offset), _getPaintForAlpha(alpha));
painter(this, offset);
canvas.restore();
}
} }
static Paint _getPaintForShaderMask(Rect bounds, static Paint _getPaintForShaderMask(Rect bounds,
...@@ -435,7 +421,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -435,7 +421,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
setupParentData(child); setupParentData(child);
super.adoptChild(child); super.adoptChild(child);
markNeedsLayout(); markNeedsLayout();
_markNeedsCompositingBitsUpdate(); markNeedsCompositingBitsUpdate();
} }
/// Called by subclasses when they decide a render object is no longer a child. /// 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 { ...@@ -451,7 +437,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
child.parentData = null; child.parentData = null;
super.dropChild(child); super.dropChild(child);
markNeedsLayout(); markNeedsLayout();
_markNeedsCompositingBitsUpdate(); markNeedsCompositingBitsUpdate();
} }
/// Calls visitor for each immediate child of this render object. /// Calls visitor for each immediate child of this render object.
...@@ -854,7 +840,8 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -854,7 +840,8 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
/// creates at least one composited layer. For example, videos should return /// creates at least one composited layer. For example, videos should return
/// true if they use hardware decoders. /// 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; bool get alwaysNeedsCompositing => false;
ContainerLayer _layer; ContainerLayer _layer;
...@@ -880,7 +867,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -880,7 +867,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
/// This method does not schedule a rendering frame, because since /// This method does not schedule a rendering frame, because since
/// it cannot be the case that _only_ the compositing bits changed, /// it cannot be the case that _only_ the compositing bits changed,
/// something else will have scheduled a frame for us. /// something else will have scheduled a frame for us.
void _markNeedsCompositingBitsUpdate() { void markNeedsCompositingBitsUpdate() {
if (_needsCompositingBitsUpdate) if (_needsCompositingBitsUpdate)
return; return;
_needsCompositingBitsUpdate = true; _needsCompositingBitsUpdate = true;
...@@ -889,7 +876,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -889,7 +876,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
if (parent._needsCompositingBitsUpdate) if (parent._needsCompositingBitsUpdate)
return; return;
if (!isRepaintBoundary && !parent.isRepaintBoundary) { if (!isRepaintBoundary && !parent.isRepaintBoundary) {
parent._markNeedsCompositingBitsUpdate(); parent.markNeedsCompositingBitsUpdate();
return; return;
} }
} }
......
...@@ -525,6 +525,8 @@ class RenderIntrinsicHeight extends RenderProxyBox { ...@@ -525,6 +525,8 @@ class RenderIntrinsicHeight extends RenderProxyBox {
} }
int _getAlphaFromOpacity(double opacity) => (opacity * 255).round();
/// Makes its child partially transparent. /// Makes its child partially transparent.
/// ///
/// This class paints its child into an intermediate buffer and then blends the /// This class paints its child into an intermediate buffer and then blends the
...@@ -534,10 +536,12 @@ class RenderIntrinsicHeight extends RenderProxyBox { ...@@ -534,10 +536,12 @@ class RenderIntrinsicHeight extends RenderProxyBox {
/// into an intermediate buffer. /// into an intermediate buffer.
class RenderOpacity extends RenderProxyBox { class RenderOpacity extends RenderProxyBox {
RenderOpacity({ RenderBox child, double opacity }) RenderOpacity({ RenderBox child, double opacity })
: this._opacity = opacity, super(child) { : _opacity = opacity, _alpha = _getAlphaFromOpacity(opacity), super(child) {
assert(opacity >= 0.0 && opacity <= 1.0); assert(opacity >= 0.0 && opacity <= 1.0);
} }
bool get alwaysNeedsCompositing => _alpha != 0 && _alpha != 255;
/// The fraction to scale the child's alpha value. /// 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 /// An opacity of 1.0 is fully opaque. An opacity of 0.0 is fully transparent
...@@ -550,22 +554,23 @@ class RenderOpacity extends RenderProxyBox { ...@@ -550,22 +554,23 @@ class RenderOpacity extends RenderProxyBox {
if (_opacity == newOpacity) if (_opacity == newOpacity)
return; return;
_opacity = newOpacity; _opacity = newOpacity;
_alpha = _getAlphaFromOpacity(_opacity);
markNeedsCompositingBitsUpdate();
markNeedsPaint(); markNeedsPaint();
} }
int get _alpha => (_opacity * 255).round(); int _alpha;
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
if (child != null) { if (child != null) {
int a = _alpha; if (_alpha == 0)
if (a == 0)
return; return;
if (a == 255) { if (_alpha == 255) {
context.paintChild(child, offset); context.paintChild(child, offset);
return; return;
} }
// TODO(abarth): We should pass bounds here. assert(needsCompositing);
context.pushOpacity(needsCompositing, offset, null, a, super.paint); context.pushOpacity(offset, _alpha, super.paint);
} }
} }
......
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