Commit 167fbced authored by Adam Barth's avatar Adam Barth

Viewport fails to clip its AssetImage child

We were getting confused about our layer tree offsets, which caused us
to set an incorrect cull rect for the recording inside a viewport.

This patch does away with layer tree offsets almost entirely. We now use
them only at repaint boundaries, which is where we want the layer tree
to be mutable.

Fixes #1460
parent b89d81a9
...@@ -17,11 +17,6 @@ export 'basic_types.dart'; ...@@ -17,11 +17,6 @@ export 'basic_types.dart';
/// are uploaded into the engine and displayed by the compositor. This class is /// are uploaded into the engine and displayed by the compositor. This class is
/// the base class for all composited layers. /// the base class for all composited layers.
abstract class Layer { abstract class Layer {
Layer({ this.offset: Offset.zero });
/// Offset from parent in the parent's coordinate system.
Offset offset;
/// This layer's parent in the layer tree /// This layer's parent in the layer tree
ContainerLayer get parent => _parent; ContainerLayer get parent => _parent;
ContainerLayer _parent; ContainerLayer _parent;
...@@ -95,7 +90,6 @@ abstract class Layer { ...@@ -95,7 +90,6 @@ abstract class Layer {
void debugDescribeSettings(List<String> settings) { void debugDescribeSettings(List<String> settings) {
if (debugOwner != null) if (debugOwner != null)
settings.add('owner: $debugOwner'); settings.add('owner: $debugOwner');
settings.add('offset: $offset');
} }
String debugDescribeChildren(String prefix) => ''; String debugDescribeChildren(String prefix) => '';
...@@ -103,16 +97,13 @@ abstract class Layer { ...@@ -103,16 +97,13 @@ abstract class Layer {
/// A composited layer containing a [Picture] /// A composited layer containing a [Picture]
class PictureLayer extends Layer { class PictureLayer extends Layer {
PictureLayer({ Offset offset: Offset.zero })
: super(offset: offset);
/// The picture recorded for this layer /// The picture recorded for this layer
/// ///
/// The picture's coodinate system matches this layer's coodinate system /// The picture's coodinate system matches this layer's coodinate system
ui.Picture picture; ui.Picture picture;
void addToScene(ui.SceneBuilder builder, Offset layerOffset) { void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
builder.addPicture(offset + layerOffset, picture); builder.addPicture(layerOffset, picture);
} }
} }
...@@ -120,11 +111,10 @@ class PictureLayer extends Layer { ...@@ -120,11 +111,10 @@ class PictureLayer extends Layer {
/// certain performance statistics within it. /// certain performance statistics within it.
class PerformanceOverlayLayer extends Layer { class PerformanceOverlayLayer extends Layer {
PerformanceOverlayLayer({ PerformanceOverlayLayer({
Offset offset: Offset.zero,
this.overlayRect, this.overlayRect,
this.optionsMask, this.optionsMask,
this.rasterizerThreshold this.rasterizerThreshold
}) : super(offset: offset); });
/// The rectangle in this layer's coodinate system that the overlay should occupy. /// The rectangle in this layer's coodinate system that the overlay should occupy.
Rect overlayRect; Rect overlayRect;
...@@ -136,7 +126,7 @@ class PerformanceOverlayLayer extends Layer { ...@@ -136,7 +126,7 @@ class PerformanceOverlayLayer extends Layer {
void addToScene(ui.SceneBuilder builder, Offset layerOffset) { void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
assert(optionsMask != null); assert(optionsMask != null);
builder.addPerformanceOverlay(optionsMask, overlayRect.shift(offset + layerOffset)); builder.addPerformanceOverlay(optionsMask, overlayRect.shift(layerOffset));
builder.setRasterizerTracingThreshold(rasterizerThreshold); builder.setRasterizerTracingThreshold(rasterizerThreshold);
} }
} }
...@@ -144,8 +134,6 @@ class PerformanceOverlayLayer extends Layer { ...@@ -144,8 +134,6 @@ class PerformanceOverlayLayer extends Layer {
/// A composited layer that has a list of children /// A composited layer that has a list of children
class ContainerLayer extends Layer { class ContainerLayer extends Layer {
ContainerLayer({ Offset offset: Offset.zero }) : super(offset: offset);
/// The first composited layer in this layer's child list /// The first composited layer in this layer's child list
Layer get firstChild => _firstChild; Layer get firstChild => _firstChild;
Layer _firstChild; Layer _firstChild;
...@@ -230,7 +218,7 @@ class ContainerLayer extends Layer { ...@@ -230,7 +218,7 @@ class ContainerLayer extends Layer {
} }
void addToScene(ui.SceneBuilder builder, Offset layerOffset) { void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
addChildrenToScene(builder, offset + layerOffset); addChildrenToScene(builder, layerOffset);
} }
/// Uploads all of this layer's children to the engine /// Uploads all of this layer's children to the engine
...@@ -261,9 +249,26 @@ class ContainerLayer extends Layer { ...@@ -261,9 +249,26 @@ class ContainerLayer extends Layer {
} }
} }
class OffsetLayer extends ContainerLayer {
OffsetLayer({ this.offset: Offset.zero });
/// Offset from parent in the parent's coordinate system.
Offset offset;
void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
addChildrenToScene(builder, offset + layerOffset);
}
void debugDescribeSettings(List<String> settings) {
super.debugDescribeSettings(settings);
settings.add('offset: $offset');
}
}
/// A composite layer that clips its children using a rectangle /// A composite layer that clips its children using a rectangle
class ClipRectLayer extends ContainerLayer { class ClipRectLayer extends ContainerLayer {
ClipRectLayer({ Offset offset: Offset.zero, this.clipRect }) : super(offset: offset); ClipRectLayer({ this.clipRect });
/// The rectangle to clip in the parent's coordinate system /// The rectangle to clip in the parent's coordinate system
Rect clipRect; Rect clipRect;
...@@ -271,9 +276,8 @@ class ClipRectLayer extends ContainerLayer { ...@@ -271,9 +276,8 @@ class ClipRectLayer extends ContainerLayer {
// instead of in the coordinate system of this layer? // instead of in the coordinate system of this layer?
void addToScene(ui.SceneBuilder builder, Offset layerOffset) { void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
Offset childOffset = offset + layerOffset; builder.pushClipRect(clipRect.shift(layerOffset));
builder.pushClipRect(clipRect.shift(childOffset)); addChildrenToScene(builder, layerOffset);
addChildrenToScene(builder, childOffset);
builder.pop(); builder.pop();
} }
...@@ -285,7 +289,7 @@ class ClipRectLayer extends ContainerLayer { ...@@ -285,7 +289,7 @@ class ClipRectLayer extends ContainerLayer {
/// A composite layer that clips its children using a rounded rectangle /// A composite layer that clips its children using a rounded rectangle
class ClipRRectLayer extends ContainerLayer { class ClipRRectLayer extends ContainerLayer {
ClipRRectLayer({ Offset offset: Offset.zero, this.clipRRect }) : super(offset: offset); ClipRRectLayer({ this.clipRRect });
/// The rounded-rect to clip in the parent's coordinate system /// The rounded-rect to clip in the parent's coordinate system
ui.RRect clipRRect; ui.RRect clipRRect;
...@@ -293,9 +297,8 @@ class ClipRRectLayer extends ContainerLayer { ...@@ -293,9 +297,8 @@ class ClipRRectLayer extends ContainerLayer {
// instead of in the coordinate system of this layer? // instead of in the coordinate system of this layer?
void addToScene(ui.SceneBuilder builder, Offset layerOffset) { void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
Offset childOffset = offset + layerOffset; builder.pushClipRRect(clipRRect.shift(layerOffset));
builder.pushClipRRect(clipRRect.shift(childOffset)); addChildrenToScene(builder, layerOffset);
addChildrenToScene(builder, childOffset);
builder.pop(); builder.pop();
} }
...@@ -307,7 +310,7 @@ class ClipRRectLayer extends ContainerLayer { ...@@ -307,7 +310,7 @@ class ClipRRectLayer extends ContainerLayer {
/// A composite layer that clips its children using a path /// A composite layer that clips its children using a path
class ClipPathLayer extends ContainerLayer { class ClipPathLayer extends ContainerLayer {
ClipPathLayer({ Offset offset: Offset.zero, this.clipPath }) : super(offset: offset); ClipPathLayer({ this.clipPath });
/// The path to clip in the parent's coordinate system /// The path to clip in the parent's coordinate system
Path clipPath; Path clipPath;
...@@ -315,9 +318,8 @@ class ClipPathLayer extends ContainerLayer { ...@@ -315,9 +318,8 @@ class ClipPathLayer extends ContainerLayer {
// in the coordinate system of this layer? // in the coordinate system of this layer?
void addToScene(ui.SceneBuilder builder, Offset layerOffset) { void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
Offset childOffset = offset + layerOffset; builder.pushClipPath(clipPath.shift(layerOffset));
builder.pushClipPath(clipPath.shift(childOffset)); addChildrenToScene(builder, layerOffset);
addChildrenToScene(builder, childOffset);
builder.pop(); builder.pop();
} }
...@@ -328,8 +330,8 @@ class ClipPathLayer extends ContainerLayer { ...@@ -328,8 +330,8 @@ class ClipPathLayer extends ContainerLayer {
} }
/// A composited layer that applies a transformation matrix to its children /// A composited layer that applies a transformation matrix to its children
class TransformLayer extends ContainerLayer { class TransformLayer extends OffsetLayer {
TransformLayer({ Offset offset: Offset.zero, this.transform }) : super(offset: offset); TransformLayer({ Offset offset: Offset.zero, this.transform }): super(offset: offset);
/// The matrix to apply /// The matrix to apply
Matrix4 transform; Matrix4 transform;
...@@ -351,7 +353,7 @@ class TransformLayer extends ContainerLayer { ...@@ -351,7 +353,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.alpha }) : super(offset: offset); OpacityLayer({ this.alpha });
/// The amount to multiply into the alpha channel /// The amount to multiply into the alpha channel
/// ///
...@@ -360,9 +362,8 @@ class OpacityLayer extends ContainerLayer { ...@@ -360,9 +362,8 @@ class OpacityLayer extends ContainerLayer {
int alpha; int alpha;
void addToScene(ui.SceneBuilder builder, Offset layerOffset) { void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
Offset childOffset = offset + layerOffset;
builder.pushOpacity(alpha); builder.pushOpacity(alpha);
addChildrenToScene(builder, childOffset); addChildrenToScene(builder, layerOffset);
builder.pop(); builder.pop();
} }
...@@ -374,7 +375,7 @@ class OpacityLayer extends ContainerLayer { ...@@ -374,7 +375,7 @@ class OpacityLayer extends ContainerLayer {
/// A composited layer that applies a shader to hits children. /// A composited layer that applies a shader to hits children.
class ShaderMaskLayer extends ContainerLayer { class ShaderMaskLayer extends ContainerLayer {
ShaderMaskLayer({ Offset offset: Offset.zero, this.shader, this.maskRect, this.transferMode }) : super(offset: offset); ShaderMaskLayer({ this.shader, this.maskRect, this.transferMode });
/// The shader to apply to the children. /// The shader to apply to the children.
ui.Shader shader; ui.Shader shader;
...@@ -386,9 +387,8 @@ class ShaderMaskLayer extends ContainerLayer { ...@@ -386,9 +387,8 @@ class ShaderMaskLayer extends ContainerLayer {
TransferMode transferMode; TransferMode transferMode;
void addToScene(ui.SceneBuilder builder, Offset layerOffset) { void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
Offset childOffset = offset + layerOffset; builder.pushShaderMask(shader, maskRect.shift(layerOffset), transferMode);
builder.pushShaderMask(shader, maskRect.shift(childOffset), transferMode); addChildrenToScene(builder, layerOffset);
addChildrenToScene(builder, childOffset);
builder.pop(); builder.pop();
} }
......
...@@ -69,7 +69,7 @@ class PaintingContext { ...@@ -69,7 +69,7 @@ class PaintingContext {
static void repaintCompositedChild(RenderObject child) { static void repaintCompositedChild(RenderObject child) {
assert(child.isRepaintBoundary); assert(child.isRepaintBoundary);
assert(child.needsPaint); assert(child.needsPaint);
child._layer ??= new ContainerLayer(); child._layer ??= new OffsetLayer();
child._layer.removeAllChildren(); child._layer.removeAllChildren();
assert(() { assert(() {
child._layer.debugOwner = child.debugOwner ?? child.runtimeType; child._layer.debugOwner = child.debugOwner ?? child.runtimeType;
...@@ -110,12 +110,12 @@ class PaintingContext { ...@@ -110,12 +110,12 @@ class PaintingContext {
return true; return true;
}); });
} }
_appendLayer(child._layer, offset); child._layer.offset = offset;
_appendLayer(child._layer);
} }
void _appendLayer(Layer layer, Offset offset) { void _appendLayer(Layer layer) {
assert(!_isRecording); assert(!_isRecording);
layer.offset = offset;
_containerLayer.append(layer); _containerLayer.append(layer);
} }
...@@ -190,11 +190,11 @@ class PaintingContext { ...@@ -190,11 +190,11 @@ class PaintingContext {
void pushPerformanceOverlay(Offset offset, int optionsMask, int rasterizerThreshold, Size size) { void pushPerformanceOverlay(Offset offset, int optionsMask, int rasterizerThreshold, Size size) {
_stopRecordingIfNeeded(); _stopRecordingIfNeeded();
PerformanceOverlayLayer performanceOverlayLayer = new PerformanceOverlayLayer( PerformanceOverlayLayer performanceOverlayLayer = new PerformanceOverlayLayer(
overlayRect: new Rect.fromLTWH(0.0, 0.0, size.width, size.height), overlayRect: new Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height),
optionsMask: optionsMask, optionsMask: optionsMask,
rasterizerThreshold: rasterizerThreshold rasterizerThreshold: rasterizerThreshold
); );
_appendLayer(performanceOverlayLayer, offset); _appendLayer(performanceOverlayLayer);
} }
/// Push a rectangular clip rect. /// Push a rectangular clip rect.
...@@ -203,12 +203,13 @@ class PaintingContext { ...@@ -203,12 +203,13 @@ class PaintingContext {
/// is clipped by the given clip. The given clip should not incorporate the /// is clipped by the given clip. The given clip should not incorporate the
/// painting offset. /// painting offset.
void pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter) { void pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter) {
Rect offsetClipRect = clipRect.shift(offset);
if (needsCompositing) { if (needsCompositing) {
_stopRecordingIfNeeded(); _stopRecordingIfNeeded();
ClipRectLayer clipLayer = new ClipRectLayer(clipRect: clipRect); ClipRectLayer clipLayer = new ClipRectLayer(clipRect: offsetClipRect);
_appendLayer(clipLayer, offset); _appendLayer(clipLayer);
PaintingContext childContext = new PaintingContext._(clipLayer, clipRect); PaintingContext childContext = new PaintingContext._(clipLayer, offsetClipRect);
painter(childContext, Offset.zero); painter(childContext, offset);
childContext._stopRecordingIfNeeded(); childContext._stopRecordingIfNeeded();
} else { } else {
canvas.save(); canvas.save();
...@@ -224,16 +225,18 @@ class PaintingContext { ...@@ -224,16 +225,18 @@ class PaintingContext {
/// is clipped by the given clip. The given clip should not incorporate the /// is clipped by the given clip. The given clip should not incorporate the
/// painting offset. /// painting offset.
void pushClipRRect(bool needsCompositing, Offset offset, Rect bounds, ui.RRect clipRRect, PaintingContextCallback painter) { void pushClipRRect(bool needsCompositing, Offset offset, Rect bounds, ui.RRect clipRRect, PaintingContextCallback painter) {
Rect offsetBounds = bounds.shift(offset);
ui.RRect offsetClipRRect = clipRRect.shift(offset);
if (needsCompositing) { if (needsCompositing) {
_stopRecordingIfNeeded(); _stopRecordingIfNeeded();
ClipRRectLayer clipLayer = new ClipRRectLayer(clipRRect: clipRRect); ClipRRectLayer clipLayer = new ClipRRectLayer(clipRRect: offsetClipRRect);
_appendLayer(clipLayer, offset); _appendLayer(clipLayer);
PaintingContext childContext = new PaintingContext._(clipLayer, bounds); PaintingContext childContext = new PaintingContext._(clipLayer, offsetBounds);
painter(childContext, Offset.zero); painter(childContext, offset);
childContext._stopRecordingIfNeeded(); childContext._stopRecordingIfNeeded();
} else { } else {
canvas.saveLayer(bounds.shift(offset), _disableAntialias); canvas.saveLayer(offsetBounds, _disableAntialias);
canvas.clipRRect(clipRRect.shift(offset)); canvas.clipRRect(offsetClipRRect);
painter(this, offset); painter(this, offset);
canvas.restore(); canvas.restore();
} }
...@@ -245,12 +248,14 @@ class PaintingContext { ...@@ -245,12 +248,14 @@ class PaintingContext {
/// is clipped by the given clip. The given clip should not incorporate the /// is clipped by the given clip. The given clip should not incorporate the
/// painting offset. /// painting offset.
void pushClipPath(bool needsCompositing, Offset offset, Rect bounds, Path clipPath, PaintingContextCallback painter) { void pushClipPath(bool needsCompositing, Offset offset, Rect bounds, Path clipPath, PaintingContextCallback painter) {
Rect offsetBounds = bounds.shift(offset);
Path offsetClipPath = clipPath.shift(offset);
if (needsCompositing) { if (needsCompositing) {
_stopRecordingIfNeeded(); _stopRecordingIfNeeded();
ClipPathLayer clipLayer = new ClipPathLayer(clipPath: clipPath); ClipPathLayer clipLayer = new ClipPathLayer(clipPath: offsetClipPath);
_appendLayer(clipLayer, offset); _appendLayer(clipLayer);
PaintingContext childContext = new PaintingContext._(clipLayer, bounds); PaintingContext childContext = new PaintingContext._(clipLayer, offsetBounds);
painter(childContext, Offset.zero); painter(childContext, offset);
childContext._stopRecordingIfNeeded(); childContext._stopRecordingIfNeeded();
} else { } else {
canvas.saveLayer(bounds.shift(offset), _disableAntialias); canvas.saveLayer(bounds.shift(offset), _disableAntialias);
...@@ -268,8 +273,9 @@ class PaintingContext { ...@@ -268,8 +273,9 @@ class PaintingContext {
void pushTransform(bool needsCompositing, Offset offset, Matrix4 transform, PaintingContextCallback painter) { void pushTransform(bool needsCompositing, Offset offset, Matrix4 transform, PaintingContextCallback painter) {
if (needsCompositing) { if (needsCompositing) {
_stopRecordingIfNeeded(); _stopRecordingIfNeeded();
TransformLayer transformLayer = new TransformLayer(transform: transform); TransformLayer transformLayer = new TransformLayer(offset: offset, transform: transform);
_appendLayer(transformLayer, offset); _appendLayer(transformLayer);
// TODO(abarth): We need to run _paintBounds through the inverse of transform.
PaintingContext childContext = new PaintingContext._(transformLayer, _paintBounds); PaintingContext childContext = new PaintingContext._(transformLayer, _paintBounds);
painter(childContext, Offset.zero); painter(childContext, Offset.zero);
childContext._stopRecordingIfNeeded(); childContext._stopRecordingIfNeeded();
...@@ -290,9 +296,9 @@ class PaintingContext { ...@@ -290,9 +296,9 @@ class PaintingContext {
void pushOpacity(Offset offset, int alpha, PaintingContextCallback painter) { void pushOpacity(Offset offset, int alpha, PaintingContextCallback painter) {
_stopRecordingIfNeeded(); _stopRecordingIfNeeded();
OpacityLayer opacityLayer = new OpacityLayer(alpha: alpha); OpacityLayer opacityLayer = new OpacityLayer(alpha: alpha);
_appendLayer(opacityLayer, offset); _appendLayer(opacityLayer);
PaintingContext childContext = new PaintingContext._(opacityLayer, _paintBounds); PaintingContext childContext = new PaintingContext._(opacityLayer, _paintBounds);
painter(childContext, Offset.zero); painter(childContext, offset);
childContext._stopRecordingIfNeeded(); childContext._stopRecordingIfNeeded();
} }
...@@ -307,9 +313,9 @@ class PaintingContext { ...@@ -307,9 +313,9 @@ class PaintingContext {
maskRect: maskRect, maskRect: maskRect,
transferMode: transferMode transferMode: transferMode
); );
_appendLayer(shaderLayer, offset); _appendLayer(shaderLayer);
PaintingContext childContext = new PaintingContext._(shaderLayer, _paintBounds); PaintingContext childContext = new PaintingContext._(shaderLayer, _paintBounds);
painter(childContext, Offset.zero); painter(childContext, offset);
childContext._stopRecordingIfNeeded(); childContext._stopRecordingIfNeeded();
} }
} }
...@@ -1156,11 +1162,11 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -1156,11 +1162,11 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
/// getter changes. /// getter changes.
bool get alwaysNeedsCompositing => false; bool get alwaysNeedsCompositing => false;
ContainerLayer _layer; OffsetLayer _layer;
/// The compositing layer that this render object uses to repaint. /// The compositing layer that this render object uses to repaint.
/// ///
/// Call only when [isRepaintBoundary] is true. /// Call only when [isRepaintBoundary] is true.
ContainerLayer get layer { OffsetLayer get layer {
assert(isRepaintBoundary); assert(isRepaintBoundary);
assert(!_needsPaint); assert(!_needsPaint);
return _layer; return _layer;
...@@ -1522,7 +1528,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -1522,7 +1528,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
if (!node._needsSemanticsUpdate) { if (!node._needsSemanticsUpdate) {
node._needsSemanticsUpdate = true; node._needsSemanticsUpdate = true;
_nodesNeedingSemantics.add(node); _nodesNeedingSemantics.add(node);
} }
} else { } else {
// The shape of the semantics tree around us may have changed. // The shape of the semantics tree around us may have changed.
// The worst case is that we may have removed a branch of the // The worst case is that we may have removed a branch of the
...@@ -1541,7 +1547,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -1541,7 +1547,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
if (!node._needsSemanticsUpdate) { if (!node._needsSemanticsUpdate) {
node._needsSemanticsUpdate = true; node._needsSemanticsUpdate = true;
_nodesNeedingSemantics.add(node); _nodesNeedingSemantics.add(node);
} }
} }
} }
......
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