From 5ceb52a0e138efba966c8fe197295142832fa6cb Mon Sep 17 00:00:00 2001 From: Hixie Date: Mon, 17 Aug 2015 14:36:46 -0700 Subject: [PATCH] Port clipping to the new layer world. - Add Canvas.getSaveCount() - Make RenderClipRect call context.paintChildWithClip instead of doing the clipping itself - Make ClipLayer take a Rect instead of a Size - Make PaintingContext.canvas read-only - Add PaintingContext.paintChildWithClip() - Minor rearrangings of code and style tweaks --- sky/engine/core/painting/Canvas.cpp | 7 ++ sky/engine/core/painting/Canvas.h | 1 + sky/engine/core/painting/Canvas.idl | 1 + sky/packages/sky/lib/rendering/box.dart | 18 ++--- sky/packages/sky/lib/rendering/layer.dart | 6 +- sky/packages/sky/lib/rendering/object.dart | 81 ++++++++++++++++------ sky/tests/resources/display_list.dart | 7 ++ 7 files changed, 87 insertions(+), 34 deletions(-) diff --git a/sky/engine/core/painting/Canvas.cpp b/sky/engine/core/painting/Canvas.cpp index b0c81a7ec5a..b373cb02110 100644 --- a/sky/engine/core/painting/Canvas.cpp +++ b/sky/engine/core/painting/Canvas.cpp @@ -47,6 +47,13 @@ void Canvas::restore() m_canvas->restore(); } +int Canvas::getSaveCount() +{ + if (!m_canvas) + return 0; + return m_canvas->getSaveCount(); +} + void Canvas::translate(float dx, float dy) { if (!m_canvas) diff --git a/sky/engine/core/painting/Canvas.h b/sky/engine/core/painting/Canvas.h index e2ed9f2bc2e..f027d2f46c3 100644 --- a/sky/engine/core/painting/Canvas.h +++ b/sky/engine/core/painting/Canvas.h @@ -63,6 +63,7 @@ public: void save(); void saveLayer(const Rect& bounds, const Paint& paint); void restore(); + int getSaveCount(); void translate(float dx, float dy); void scale(float sx, float sy); diff --git a/sky/engine/core/painting/Canvas.idl b/sky/engine/core/painting/Canvas.idl index fef62130ec6..7b93a20341a 100644 --- a/sky/engine/core/painting/Canvas.idl +++ b/sky/engine/core/painting/Canvas.idl @@ -11,6 +11,7 @@ // TODO(jackson): Paint should be optional, but making it optional causes crash void saveLayer(Rect bounds, /* optional */ Paint paint); void restore(); + int getSaveCount(); // returns 1 for a clean canvas; each call to save() or saveLayer() increments it, and each call to restore() decrements it. void translate(float dx, float dy); void scale(float sx, float sy); diff --git a/sky/packages/sky/lib/rendering/box.dart b/sky/packages/sky/lib/rendering/box.dart index d53d515225f..afd7566b220 100644 --- a/sky/packages/sky/lib/rendering/box.dart +++ b/sky/packages/sky/lib/rendering/box.dart @@ -805,7 +805,7 @@ class RenderOpacity extends RenderProxyBox { Paint _cachedPaint; Paint get _paint { if (_cachedPaint == null) { - _cachedPaint = new Paint() + _cachedPaint = new Paint() ..color = new Color.fromARGB(_alpha, 0, 0, 0) ..setTransferMode(sky.TransferMode.srcOver) ..isAntiAlias = false; @@ -825,7 +825,7 @@ class RenderOpacity extends RenderProxyBox { return; } - context.canvas.saveLayer(null, _paint); + context.canvas.saveLayer(null, _paint); // TODO(abarth): layerize context.paintChild(child, offset.toPoint()); context.canvas.restore(); } @@ -871,7 +871,7 @@ class RenderColorFilter extends RenderProxyBox { void paint(PaintingContext context, Offset offset) { if (child != null) { - context.canvas.saveLayer(offset & size, _paint); + context.canvas.saveLayer(offset & size, _paint); // TODO(abarth): layerize context.paintChild(child, offset.toPoint()); context.canvas.restore(); } @@ -882,12 +882,8 @@ class RenderClipRect extends RenderProxyBox { RenderClipRect({ RenderBox child }) : super(child); void paint(PaintingContext context, Offset offset) { - if (child != null) { - context.canvas.save(); - context.canvas.clipRect(offset & size); - context.paintChild(child, offset.toPoint()); - context.canvas.restore(); - } + if (child != null) + context.paintChildWithClip(child, offset.toPoint(), Offset.zero & size); } } @@ -923,7 +919,7 @@ class RenderClipRRect extends RenderProxyBox { void paint(PaintingContext context, Offset offset) { if (child != null) { Rect rect = offset & size; - context.canvas.saveLayer(rect, _paint); + context.canvas.saveLayer(rect, _paint); // TODO(abarth): layerize sky.RRect rrect = new sky.RRect()..setRectXY(rect, xRadius, yRadius); context.canvas.clipRRect(rrect); context.paintChild(child, offset.toPoint()); @@ -951,7 +947,7 @@ class RenderClipOval extends RenderProxyBox { void paint(PaintingContext context, Offset offset) { if (child != null) { Rect rect = offset & size; - context.canvas.saveLayer(rect, _paint); + context.canvas.saveLayer(rect, _paint); // TODO(abarth): layerize context.canvas.clipPath(_getPath(rect)); context.paintChild(child, offset.toPoint()); context.canvas.restore(); diff --git a/sky/packages/sky/lib/rendering/layer.dart b/sky/packages/sky/lib/rendering/layer.dart index c11a9733741..368554b7502 100644 --- a/sky/packages/sky/lib/rendering/layer.dart +++ b/sky/packages/sky/lib/rendering/layer.dart @@ -163,14 +163,14 @@ class TransformLayer extends ContainerLayer { } class ClipLayer extends ContainerLayer { - ClipLayer({ Offset offset: Offset.zero, this.size }) : super(offset: offset); + ClipLayer({ Offset offset: Offset.zero, this.clipRect }) : super(offset: offset); - Size size; + Rect clipRect; void paint(sky.Canvas canvas) { canvas.save(); canvas.translate(offset.dx, offset.dy); - canvas.clipRect(Point.origin & size); + canvas.clipRect(clipRect); super.paint(canvas); canvas.restore(); } diff --git a/sky/packages/sky/lib/rendering/object.dart b/sky/packages/sky/lib/rendering/object.dart index af09d2dee4f..6121aa74ef5 100644 --- a/sky/packages/sky/lib/rendering/object.dart +++ b/sky/packages/sky/lib/rendering/object.dart @@ -30,50 +30,92 @@ class ParentData { class PaintingCanvas extends sky.Canvas { PaintingCanvas(sky.PictureRecorder recorder, Rect bounds) : super(recorder, bounds); + // TODO(ianh): Just use sky.Canvas everywhere instead } class PaintingContext { - PaintingCanvas canvas; - PictureLayer get layer => _layer; - PictureLayer _layer; + // A PaintingContext wraps a canvas, so that the canvas can be + // hot-swapped whenever we need to start a new layer. - sky.PictureRecorder _recorder; + // Don't keep a reference to the PaintingContext.canvas, since it + // can change dynamically after any call to this object's methods. PaintingContext(Offset offest, Size size) { _startRecording(offest, size); } - PaintingContext.forTesting(this.canvas); + PaintingCanvas _canvas; + PaintingCanvas get canvas => _canvas; + + PictureLayer _layer; + PictureLayer get layer => _layer; + + sky.PictureRecorder _recorder; + + PaintingContext.forTesting(this._canvas); void _startRecording(Offset offset, Size size) { assert(_layer == null); assert(_recorder == null); - assert(canvas == null); + assert(_canvas == null); _layer = new PictureLayer(offset: offset, size: size); _recorder = new sky.PictureRecorder(); - canvas = new PaintingCanvas(_recorder, Point.origin & size); + _canvas = new PaintingCanvas(_recorder, Point.origin & size); } void endRecording() { assert(_layer != null); assert(_recorder != null); - assert(canvas != null); - canvas = null; + assert(_canvas != null); _layer.picture = _recorder.endRecording(); - _recorder = null; _layer = null; + _recorder = null; + _canvas = null; + } + + bool debugCanPaintChild() { + // You need to use layers if you are applying transforms, clips, + // or similar, to a child. To do so, use the paintChildWith*() + // methods below. + // (commented out for now because we haven't ported everything yet) + // assert(canvas.getSaveCount() == 1); + return true; } void paintChild(RenderObject child, Point point) { + assert(debugCanPaintChild()); final Offset offset = point.toOffset(); - - if (!child.requiresCompositing) - return child._paintWithContext(this, offset); - _compositChild(child, offset, layer.parent, layer.nextSibling); + if (!child.requiresCompositing) { + child._paintWithContext(this, offset); + } else { + _compositeChild(child, offset, layer.parent, layer.nextSibling); + } } - void _compositChild(RenderObject child, Offset offset, ContainerLayer parentLayer, Layer nextSibling) { + void paintChildWithClip(RenderObject child, Point point, Rect clipRect) { + assert(debugCanPaintChild()); + final Offset offset = point.toOffset(); + if (!child.hasCompositedDescendant) { + // If none of the descendants require compositing, then we don't + // need to use a new layer here, because at no point will any of + // the children introduce a new layer of their own. + canvas.save(); + canvas.clipRect(clipRect.shift(offset)); + child._paintWithContext(this, offset); + canvas.restore(); + } else { + // At least one of the descendants requires compositing. We + // therefore introduce a new layer to do the clipping, so that + // when the children are split into a new layer, the clip is not + // lost, as it would if we didn't introduce a new layer. + ClipLayer clip = new ClipLayer(offset: offset, clipRect: clipRect); + layer.parent.add(clip, before: layer.nextSibling); + _compositeChild(child, Offset.zero, clip, null); + } + } + + void _compositeChild(RenderObject child, Offset offset, ContainerLayer parentLayer, Layer nextSibling) { final PictureLayer originalLayer = _layer; endRecording(); @@ -88,6 +130,7 @@ class PaintingContext { originalLayer.parent.add(layer, before: context.layer.nextSibling); context.endRecording(); } + } abstract class Constraints { @@ -417,14 +460,12 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { _needsPaint = true; _nodesNeedingPaint.add(this); scheduler.ensureVisualUpdate(); - } else if (parent == null) { + } else if (parent is! RenderObject) { + // we're the root of the render tree (probably a RenderView) _needsPaint = true; scheduler.ensureVisualUpdate(); } else { - assert(parent != null); // parent always exists on this path because the root node is a RenderView, which sets createNewDisplayList. - if (parent is RenderObject) { - (parent as RenderObject).markNeedsPaint(); // TODO(ianh): remove the cast once the analyzer is cleverer - } + (parent as RenderObject).markNeedsPaint(); // TODO(ianh): remove the cast once the analyzer is cleverer } } diff --git a/sky/tests/resources/display_list.dart b/sky/tests/resources/display_list.dart index 0b64100b479..3cd862468a7 100644 --- a/sky/tests/resources/display_list.dart +++ b/sky/tests/resources/display_list.dart @@ -26,18 +26,25 @@ class TestPaintingCanvas extends PaintingCanvas { logger("${indent} ${s}"); } + int _saveCount = 1; + void save() { log("save"); + _saveCount += 1; } void saveLayer(Rect bounds, Paint paint) { log("saveLayer($bounds, $paint)"); + _saveCount += 1; } void restore() { log("restore"); + _saveCount -= 1; } + int getSaveCount() => _saveCount; + void translate(double dx, double dy) { log("translate($dx, $dy)"); }