Commit 24c0645d authored by Adam Barth's avatar Adam Barth

Merge pull request #1182 from abarth/composite_blends

Always using OpacityLayer for blending
parents 91551c79 635168d9
...@@ -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