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 {
/// 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<String> settings) {
super.debugDescribeSettings(settings);
settings.add('bounds: $bounds');
settings.add('alpha: $alpha');
}
}
......@@ -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) {
void pushOpacity(Offset offset, int alpha, PaintingContextCallback painter) {
_stopRecordingIfNeeded();
OpacityLayer opacityLayer = new OpacityLayer(bounds: bounds, alpha: alpha);
OpacityLayer opacityLayer = new OpacityLayer(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();
}
}
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;
}
}
......
......@@ -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);
}
}
......
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