Commit 82bb3bd4 authored by Adam Barth's avatar Adam Barth

ShaderMask needs to play nicely with compositing

This patch adds ShaderLayer and makes ShaderMask use it.

Fixes #1155
parent 875da1f5
...@@ -393,8 +393,8 @@ class CardCollectionState extends State<CardCollection> { ...@@ -393,8 +393,8 @@ class CardCollectionState extends State<CardCollection> {
ui.Shader _createShader(Rect bounds) { ui.Shader _createShader(Rect bounds) {
return new LinearGradient( return new LinearGradient(
begin: Point.origin, begin: bounds.topLeft,
end: new Point(0.0, bounds.height), end: bounds.bottomLeft,
colors: <Color>[const Color(0x00FFFFFF), const Color(0xFFFFFFFF)], colors: <Color>[const Color(0x00FFFFFF), const Color(0xFFFFFFFF)],
stops: <double>[0.1, 0.35] stops: <double>[0.1, 0.35]
) )
......
...@@ -392,3 +392,31 @@ class OpacityLayer extends ContainerLayer { ...@@ -392,3 +392,31 @@ class OpacityLayer extends ContainerLayer {
settings.add('alpha: $alpha'); settings.add('alpha: $alpha');
} }
} }
/// A composited layer that applies a shader to hits children.
class ShaderMaskLayer extends ContainerLayer {
ShaderMaskLayer({ Offset offset: Offset.zero, this.shader, this.maskRect, this.transferMode }) : super(offset: offset);
/// The shader to apply to the children.
ui.Shader shader;
/// The size of the shader.
Rect maskRect;
/// The tranfer mode to apply when blending the shader with the children.
TransferMode transferMode;
void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
Offset childOffset = offset + layerOffset;
builder.pushShaderMask(shader, maskRect.shift(childOffset), transferMode);
addChildrenToScene(builder, childOffset);
builder.pop();
}
void debugDescribeSettings(List<String> settings) {
super.debugDescribeSettings(settings);
settings.add('shader: $shader');
settings.add('maskRect: $maskRect');
settings.add('transferMode: $transferMode');
}
}
...@@ -18,8 +18,6 @@ import 'node.dart'; ...@@ -18,8 +18,6 @@ import 'node.dart';
export 'layer.dart'; export 'layer.dart';
export 'package:flutter/gestures.dart' show HitTestEntry, HitTestResult; export 'package:flutter/gestures.dart' show HitTestEntry, HitTestResult;
typedef ui.Shader ShaderCallback(Rect bounds);
/// Base class for data associated with a [RenderObject] by its parent. /// Base class for data associated with a [RenderObject] by its parent.
/// ///
/// Some render objects wish to store data on their children, such as their /// Some render objects wish to store data on their children, such as their
...@@ -298,27 +296,21 @@ class PaintingContext { ...@@ -298,27 +296,21 @@ class PaintingContext {
childContext._stopRecordingIfNeeded(); childContext._stopRecordingIfNeeded();
} }
static Paint _getPaintForShaderMask(Rect bounds,
ShaderCallback shaderCallback,
TransferMode transferMode) {
return new Paint()
..transferMode = transferMode
..shader = shaderCallback(bounds);
}
/// Push a shader mask. /// Push a shader mask.
/// ///
/// This function will call painter synchronously with a painting context that /// This function will call painter synchronously with a painting context that
/// will be masked with the given shader. /// will be masked with the given shader.
/// void pushShaderMask(Offset offset, ui.Shader shader, Rect maskRect, TransferMode transferMode, PaintingContextCallback painter) {
/// WARNING: This function does not yet support compositing. _stopRecordingIfNeeded();
void pushShaderMask(bool needsCompositing, Offset offset, Rect bounds, ShaderCallback shaderCallback, TransferMode transferMode, PaintingContextCallback painter) { ShaderMaskLayer shaderLayer = new ShaderMaskLayer(
assert(!needsCompositing); // TODO(abarth): Implement compositing for shader masks. shader: shader,
canvas.saveLayer(bounds.shift(offset), _disableAntialias); maskRect: maskRect,
painter(this, offset); transferMode: transferMode
Paint shaderPaint = _getPaintForShaderMask(bounds, shaderCallback, transferMode); );
canvas.drawRect(bounds, shaderPaint); _appendLayer(shaderLayer, offset);
canvas.restore(); PaintingContext childContext = new PaintingContext._(shaderLayer, _paintBounds);
painter(childContext, Offset.zero);
childContext._stopRecordingIfNeeded();
} }
} }
......
...@@ -540,7 +540,7 @@ class RenderOpacity extends RenderProxyBox { ...@@ -540,7 +540,7 @@ class RenderOpacity extends RenderProxyBox {
assert(opacity >= 0.0 && opacity <= 1.0); assert(opacity >= 0.0 && opacity <= 1.0);
} }
bool get alwaysNeedsCompositing => _alpha != 0 && _alpha != 255; bool get alwaysNeedsCompositing => child != null && (_alpha != 0 && _alpha != 255);
/// The fraction to scale the child's alpha value. /// The fraction to scale the child's alpha value.
/// ///
...@@ -580,6 +580,8 @@ class RenderOpacity extends RenderProxyBox { ...@@ -580,6 +580,8 @@ class RenderOpacity extends RenderProxyBox {
} }
} }
typedef ui.Shader ShaderCallback(Rect bounds);
class RenderShaderMask extends RenderProxyBox { class RenderShaderMask extends RenderProxyBox {
RenderShaderMask({ RenderBox child, ShaderCallback shaderCallback, TransferMode transferMode }) RenderShaderMask({ RenderBox child, ShaderCallback shaderCallback, TransferMode transferMode })
: _shaderCallback = shaderCallback, _transferMode = transferMode, super(child); : _shaderCallback = shaderCallback, _transferMode = transferMode, super(child);
...@@ -604,9 +606,14 @@ class RenderShaderMask extends RenderProxyBox { ...@@ -604,9 +606,14 @@ class RenderShaderMask extends RenderProxyBox {
markNeedsPaint(); markNeedsPaint();
} }
bool get alwaysNeedsCompositing => child != null;
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
if (child != null) if (child != null) {
context.pushShaderMask(needsCompositing, offset, Point.origin & size, _shaderCallback, _transferMode, super.paint); assert(needsCompositing);
Rect rect = Point.origin & size;
context.pushShaderMask(offset, _shaderCallback(rect), rect, _transferMode, super.paint);
}
} }
} }
......
...@@ -11,8 +11,8 @@ import 'package:test/test.dart'; ...@@ -11,8 +11,8 @@ import 'package:test/test.dart';
ui.Shader createShader(Rect bounds) { ui.Shader createShader(Rect bounds) {
return new LinearGradient( return new LinearGradient(
begin: Point.origin, begin: bounds.topLeft,
end: new Point(0.0, bounds.height), end: bounds.bottomLeft,
colors: <Color>[const Color(0x00FFFFFF), const Color(0xFFFFFFFF)], colors: <Color>[const Color(0x00FFFFFF), const Color(0xFFFFFFFF)],
stops: <double>[0.1, 0.35] stops: <double>[0.1, 0.35]
) )
......
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