Commit 654fc734 authored by Hixie's avatar Hixie

Improvements for Layers logic

- Introduce some new Layer classes.
- Introduce paintChildWith* methods.
- Convert paint() methods to use paintChildWith* where appropriate.
- Fix paintBounds logic in Layer world.
- Introduce Layer.replaceWith(), so that it's clearer what's going on.
- Make RenderObjects have a ContainerLayer, not a PictureLayer.
- Introduce a PaintingContext.replacingLayer() constructor to highlight
  where we are creating a layer just to replace an older one.
- Rename some layer-related methods and fields for clarity:
   requiresCompositing -> hasLayer
   hasCompositedDescendant -> needsCompositing
   updateCompositing -> updateCompositingBits
   _needsCompositingUpdate -> _needsCompositingBitsUpdate
   markNeedsCompositingUpdate -> markNeedsCompositingBitsUpdate
- After updating compositing bits, if we find that the bit changed, we
  now call markNeedsPaint().
- Reorder markNeedsPaint() logic for clarity.
- Make flushPaint() start at the deepest node.
- Make _compositeChild() avoid repainting children with hasLayer that
  aren't dirty, instead it just reuses their existing layer.
- Made RenderView reuse the RenderObject layer instead of having its own.
- Made RenderView have hasLayer set to true.
- Add various asserts and comments.
parent d662f7e6
......@@ -26,15 +26,40 @@ abstract class Layer {
if (_parent != null)
_parent.remove(this);
}
void replaceWith(Layer newLayer) {
assert(_parent != null);
assert(newLayer._parent == null);
assert(newLayer._nextSibling == null);
assert(newLayer._previousSibling == null);
newLayer._nextSibling = _nextSibling;
if (_nextSibling != null)
newLayer._nextSibling._previousSibling = newLayer;
newLayer._previousSibling = _previousSibling;
if (_previousSibling != null)
newLayer._previousSibling._nextSibling = newLayer;
newLayer._parent = _parent;
if (_parent._firstChild == this)
_parent._firstChild = newLayer;
if (_parent._lastChild == this)
_parent._lastChild = newLayer;
_nextSibling = null;
_previousSibling = null;
_parent = null;
}
// The paint() methods are temporary. Eventually, Layers won't have
// a paint() method, the entire Layer hierarchy will be handed over
// to the C++ side for processing. Until we implement that, though,
// we instead have the layers paint themselves into a canvas at
// paint time.
void paint(sky.Canvas canvas);
}
class PictureLayer extends Layer {
PictureLayer({ Offset offset: Offset.zero, this.size })
PictureLayer({ Offset offset: Offset.zero, this.paintBounds })
: super(offset: offset);
Size size;
Rect paintBounds;
sky.Picture picture;
bool _debugPaintLayerBorder(sky.Canvas canvas) {
......@@ -43,12 +68,13 @@ class PictureLayer extends Layer {
..color = debugPaintLayerBordersColor
..strokeWidth = 2.0
..setStyle(sky.PaintingStyle.stroke);
canvas.drawRect(Point.origin & size, border);
canvas.drawRect(paintBounds, border);
}
return true;
}
void paint(sky.Canvas canvas) {
assert(picture != null);
canvas.translate(offset.dx, offset.dy);
canvas.drawPicture(picture);
assert(_debugPaintLayerBorder(canvas));
......@@ -59,17 +85,11 @@ class PictureLayer extends Layer {
class ContainerLayer extends Layer {
ContainerLayer({ Offset offset: Offset.zero }) : super(offset: offset);
void paint(sky.Canvas canvas) {
Layer child = firstChild;
while (child != null) {
child.paint(canvas);
child = child.nextSibling;
}
}
// TODO(ianh): hide firstChild since nobody uses it
Layer _firstChild;
Layer get firstChild => _firstChild;
// TODO(ianh): remove _lastChild since nobody uses it
Layer _lastChild;
Layer get lastChild => _lastChild;
......@@ -89,6 +109,7 @@ class ContainerLayer extends Layer {
return child == equals;
}
// TODO(ianh): Remove 'before' and rename the function to 'append' since nobody uses 'before'
void add(Layer child, { Layer before }) {
assert(child != this);
assert(before != this);
......@@ -126,10 +147,11 @@ class ContainerLayer extends Layer {
}
}
// TODO(ianh): Hide this function since only detach() uses it
void remove(Layer child) {
assert(child._parent == this);
assert(_debugUltimatePreviousSiblingOf(child, equals: _firstChild));
assert(_debugUltimateNextSiblingOf(child, equals: _lastChild));
assert(child._parent == this);
if (child._previousSibling == null) {
assert(_firstChild == child);
_firstChild = child._nextSibling;
......@@ -146,6 +168,69 @@ class ContainerLayer extends Layer {
child._nextSibling = null;
child._parent = null;
}
void paint(sky.Canvas canvas) {
canvas.translate(offset.dx, offset.dy);
paintChildren(canvas);
canvas.translate(-offset.dx, -offset.dy);
}
void paintChildren(sky.Canvas canvas) {
Layer child = firstChild;
while (child != null) {
child.paint(canvas);
child = child.nextSibling;
}
}
}
class ClipRectLayer extends ContainerLayer {
ClipRectLayer({ Offset offset: Offset.zero, this.clipRect }) : super(offset: offset);
// clipRect is _not_ affected by given offset
Rect clipRect;
void paint(sky.Canvas canvas) {
canvas.save();
canvas.clipRect(clipRect);
canvas.translate(offset.dx, offset.dy);
paintChildren(canvas);
canvas.restore();
}
}
final Paint _disableAntialias = new Paint()..isAntiAlias = false;
class ClipRRectLayer extends ContainerLayer {
ClipRRectLayer({ Offset offset: Offset.zero, this.bounds, this.clipRRect }) : super(offset: offset);
// bounds and clipRRect are _not_ affected by given offset
Rect bounds;
sky.RRect clipRRect;
void paint(sky.Canvas canvas) {
canvas.saveLayer(bounds, _disableAntialias);
canvas.clipRRect(clipRRect);
canvas.translate(offset.dx, offset.dy);
paintChildren(canvas);
canvas.restore();
}
}
class ClipPathLayer extends ContainerLayer {
ClipPathLayer({ Offset offset: Offset.zero, this.bounds, this.clipPath }) : super(offset: offset);
// bounds and clipPath are _not_ affected by given offset
Rect bounds;
Path clipPath;
void paint(sky.Canvas canvas) {
canvas.saveLayer(bounds, _disableAntialias);
canvas.clipPath(clipPath);
canvas.translate(offset.dx, offset.dy);
paintChildren(canvas);
canvas.restore();
}
}
class TransformLayer extends ContainerLayer {
......@@ -157,21 +242,22 @@ class TransformLayer extends ContainerLayer {
canvas.save();
canvas.translate(offset.dx, offset.dy);
canvas.concat(transform.storage);
super.paint(canvas);
paintChildren(canvas);
canvas.restore();
}
}
class ClipLayer extends ContainerLayer {
ClipLayer({ Offset offset: Offset.zero, this.clipRect }) : super(offset: offset);
class PaintLayer extends ContainerLayer {
PaintLayer({ Offset offset: Offset.zero, this.bounds, this.paintSettings }) : super(offset: offset);
Rect clipRect;
// bounds is _not_ affected by given offset
Rect bounds;
Paint paintSettings; // TODO(ianh): rename this to 'paint' once paint() is gone
void paint(sky.Canvas canvas) {
canvas.save();
canvas.saveLayer(bounds, paintSettings);
canvas.translate(offset.dx, offset.dy);
canvas.clipRect(clipRect);
super.paint(canvas);
paintChildren(canvas);
canvas.restore();
}
}
......@@ -192,10 +278,9 @@ class ColorFilterLayer extends ContainerLayer {
Paint paint = new Paint()
..color = color
..setTransferMode(transferMode);
canvas.saveLayer(offset & size, paint);
canvas.translate(offset.dx, offset.dy);
super.paint(canvas);
paintChildren(canvas);
canvas.restore();
}
}
......@@ -41,94 +41,211 @@ class PaintingContext {
// 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.withOffset(Offset offset, Rect paintBounds) {
_containerLayer = new ContainerLayer(offset: offset);
_startRecording(paintBounds);
}
PaintingCanvas _canvas;
PaintingCanvas get canvas => _canvas;
PictureLayer _layer;
PictureLayer get layer => _layer;
PaintingContext.withLayer(ContainerLayer containerLayer, Rect paintBounds) {
_containerLayer = containerLayer;
_startRecording(paintBounds);
}
sky.PictureRecorder _recorder;
factory PaintingContext.replacingLayer(ContainerLayer oldLayer, Rect paintBounds) {
PaintingContext newContext = new PaintingContext.withOffset(oldLayer.offset, paintBounds);
if (oldLayer.parent != null)
oldLayer.replaceWith(newContext._containerLayer);
return newContext;
}
PaintingContext.forTesting(this._canvas);
void _startRecording(Offset offset, Size size) {
assert(_layer == null);
ContainerLayer _containerLayer;
ContainerLayer get containerLayer => _containerLayer;
PictureLayer _currentLayer;
sky.PictureRecorder _recorder;
PaintingCanvas _canvas;
PaintingCanvas get canvas => _canvas; // Paint on this.
void _startRecording(Rect paintBounds) {
assert(_currentLayer == null);
assert(_recorder == null);
assert(_canvas == null);
_layer = new PictureLayer(offset: offset, size: size);
_currentLayer = new PictureLayer(paintBounds: paintBounds);
_recorder = new sky.PictureRecorder();
_canvas = new PaintingCanvas(_recorder, Point.origin & size);
_canvas = new PaintingCanvas(_recorder, paintBounds);
_containerLayer.add(_currentLayer);
}
void endRecording() {
assert(_layer != null);
assert(_currentLayer != null);
assert(_recorder != null);
assert(_canvas != null);
_layer.picture = _recorder.endRecording();
_layer = null;
_currentLayer.picture = _recorder.endRecording();
_currentLayer = null;
_recorder = null;
_canvas = null;
}
bool debugCanPaintChild() {
bool debugCanPaintChild(RenderObject child) {
// 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);
assert(canvas.getSaveCount() == 1 || !child.needsCompositing);
return true;
}
void paintChild(RenderObject child, Point point) {
assert(debugCanPaintChild());
final Offset offset = point.toOffset();
if (!child.requiresCompositing) {
child._paintWithContext(this, offset);
void paintChild(RenderObject child, Point childPosition) {
assert(debugCanPaintChild(child));
final Offset childOffset = childPosition.toOffset();
if (!child.hasLayer) {
insertChild(child, childOffset);
} else {
_compositeChild(child, offset, layer.parent, layer.nextSibling);
compositeChild(child, childOffset: childOffset, parentLayer: _containerLayer);
}
}
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.
// Below we have various variants of the paintChild() method, which
// do additional work, such as clipping or transforming, at the same
// time as painting the children.
// If none of the descendants require compositing, then these don't
// need to use a new layer, because at no point will any of the
// children introduce a new layer of their own. In that case, we
// just use regular canvas commands to do the work.
// If at least one of the descendants requires compositing, though,
// we introduce a new layer to do the work, so that when the
// children are split into a new layer, the work (e.g. clip) is not
// lost, as it would if we didn't introduce a new layer.
static final Paint _disableAntialias = new Paint()..isAntiAlias = false;
void paintChildWithClipRect(RenderObject child, Point childPosition, Rect clipRect) {
// clipRect is in the parent's coordinate space
assert(debugCanPaintChild(child));
final Offset childOffset = childPosition.toOffset();
if (!child.needsCompositing) {
canvas.save();
canvas.clipRect(clipRect.shift(offset));
child._paintWithContext(this, offset);
canvas.clipRect(clipRect);
insertChild(child, childOffset);
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);
ClipRectLayer clipLayer = new ClipRectLayer(offset: childOffset, clipRect: clipRect);
_containerLayer.add(clipLayer);
compositeChild(child, parentLayer: clipLayer);
}
}
void paintChildWithClipRRect(RenderObject child, Point childPosition, Rect bounds, sky.RRect clipRRect) {
// clipRRect is in the parent's coordinate space
assert(debugCanPaintChild(child));
final Offset childOffset = childPosition.toOffset();
if (!child.needsCompositing) {
canvas.saveLayer(bounds, _disableAntialias);
canvas.clipRRect(clipRRect);
insertChild(child, childOffset);
canvas.restore();
} else {
ClipRRectLayer clipLayer = new ClipRRectLayer(offset: childOffset, bounds: bounds, clipRRect: clipRRect);
_containerLayer.add(clipLayer);
compositeChild(child, parentLayer: clipLayer);
}
}
void paintChildWithClipPath(RenderObject child, Point childPosition, Rect bounds, Path clipPath) {
// bounds and clipPath are in the parent's coordinate space
assert(debugCanPaintChild(child));
final Offset childOffset = childPosition.toOffset();
if (!child.needsCompositing) {
canvas.saveLayer(bounds, _disableAntialias);
canvas.clipPath(clipPath);
canvas.translate(childOffset.dx, childOffset.dy);
insertChild(child, Offset.zero);
canvas.restore();
} else {
ClipPathLayer clipLayer = new ClipPathLayer(offset: childOffset, bounds: bounds, clipPath: clipPath);
_containerLayer.add(clipLayer);
compositeChild(child, parentLayer: clipLayer);
}
}
void paintChildWithTransform(RenderObject child, Point childPosition, Matrix4 transform) {
assert(debugCanPaintChild(child));
final Offset childOffset = childPosition.toOffset();
if (!child.needsCompositing) {
canvas.save();
canvas.translate(childOffset.dx, childOffset.dy);
canvas.concat(transform.storage);
insertChild(child, Offset.zero);
canvas.restore();
} else {
TransformLayer transformLayer = new TransformLayer(offset: childOffset, transform: transform);
_containerLayer.add(transformLayer);
compositeChild(child, parentLayer: transformLayer);
}
}
void paintChildWithPaint(RenderObject child, Point childPosition, Rect bounds, Paint paint) {
assert(debugCanPaintChild(child));
final Offset childOffset = childPosition.toOffset();
if (!child.needsCompositing) {
canvas.saveLayer(bounds, paint);
canvas.translate(childOffset.dx, childOffset.dy);
insertChild(child, Offset.zero);
canvas.restore();
} else {
PaintLayer paintLayer = new PaintLayer(offset: childOffset, bounds: bounds, paintSettings: paint);
_containerLayer.add(paintLayer);
compositeChild(child, parentLayer: paintLayer);
}
}
// do not call directly
void insertChild(RenderObject child, Offset offset) {
child._paintWithContext(this, offset);
}
void _compositeChild(RenderObject child, Offset offset, ContainerLayer parentLayer, Layer nextSibling) {
final PictureLayer originalLayer = _layer;
// do not call directly
void compositeChild(RenderObject child, { Offset childOffset: Offset.zero, ContainerLayer parentLayer }) {
// This ends the current layer and starts a new layer for the
// remainder of our rendering. It also creates a new layer for the
// child, and inserts that layer into the given parentLayer, which
// must either be our current layer's parent layer, or at least
// must have our current layer's parent layer as an ancestor.
final PictureLayer originalLayer = _currentLayer;
assert(() {
assert(parentLayer != null);
assert(originalLayer != null);
assert(originalLayer.parent != null);
ContainerLayer ancestor = parentLayer;
while (ancestor != null && ancestor != originalLayer.parent)
ancestor = ancestor.parent;
assert(ancestor == originalLayer.parent);
assert(originalLayer.parent == _containerLayer);
return true;
});
// End our current layer.
endRecording();
Rect childBounds = child.paintBounds;
Offset childOffset = childBounds.topLeft.toOffset();
PaintingContext context = new PaintingContext(offset + childOffset, childBounds.size);
parentLayer.add(context.layer, before: nextSibling);
child._layer = context.layer;
child._paintWithContext(context, -childOffset);
// Create a layer for our child, and paint the child into it.
if (child.needsPaint || !child.hasLayer) {
PaintingContext newContext = new PaintingContext.withOffset(childOffset, child.paintBounds);
child._layer = newContext.containerLayer;
child._paintWithContext(newContext, Offset.zero);
newContext.endRecording();
} else {
assert(child._layer != null);
child._layer.detach();
child._layer.offset = childOffset;
}
parentLayer.add(child._layer);
_startRecording(originalLayer.offset, originalLayer.size);
originalLayer.parent.add(layer, before: context.layer.nextSibling);
context.endRecording();
// Start a new layer for anything that remains of our own paint.
_startRecording(originalLayer.paintBounds);
}
}
......@@ -163,7 +280,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
setupParentData(child);
super.adoptChild(child);
markNeedsLayout();
markNeedsCompositingUpdate();
markNeedsCompositingBitsUpdate();
}
void dropChild(RenderObject child) { // only for use by subclasses
assert(debugCanPerformMutations);
......@@ -173,7 +290,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
child.parentData.detach();
super.dropChild(child);
markNeedsLayout();
markNeedsCompositingUpdate();
markNeedsCompositingBitsUpdate();
}
// Override in subclasses with children and call the visitor for each child.
......@@ -254,7 +371,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
}
void scheduleInitialLayout() {
assert(attached);
assert(parent == null);
assert(parent is! RenderObject);
assert(_relayoutSubtreeRoot == null);
_relayoutSubtreeRoot = this;
assert(() {
......@@ -412,71 +529,90 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
static List<RenderObject> _nodesNeedingPaint = new List<RenderObject>();
PictureLayer _layer;
PictureLayer get layer {
assert(requiresCompositing);
return _layer;
}
// Override this in subclasses to indicate that instances of your
// class need to have their own Layer. For example, videos.
bool get hasLayer => false;
bool get requiresCompositing => false;
bool _hasCompositedDescendant = false;
bool get hasCompositedDescendant {
assert(!_needsCompositingUpdate);
return _hasCompositedDescendant;
ContainerLayer _layer;
ContainerLayer get layer {
assert(hasLayer);
assert(!_needsPaint);
return _layer;
}
bool _needsCompositingUpdate = false;
void markNeedsCompositingUpdate() {
if (_needsCompositingUpdate)
// When the subtree is mutated, we need to recompute our
// "needsCompositing" bit, and our ancestors need to do the
// same (in case ours changed). adoptChild() and dropChild() thus
// call markNeedsCompositingBitsUpdate().
bool _needsCompositingBitsUpdate = true;
void markNeedsCompositingBitsUpdate() {
if (_needsCompositingBitsUpdate)
return;
_needsCompositingUpdate = true;
final AbstractNode parent = this.parent;
_needsCompositingBitsUpdate = true;
final AbstractNode parent = this.parent; // TODO(ianh): remove the once the analyzer is cleverer
if (parent is RenderObject)
parent.markNeedsCompositingUpdate();
parent.markNeedsCompositingBitsUpdate();
}
void updateCompositing() {
if (!_needsCompositingUpdate)
bool _needsCompositing = false;
bool get needsCompositing {
// needsCompositing is true if either we have a layer or one of our descendants has a layer
assert(!_needsCompositingBitsUpdate); // make sure we don't use this bit when it is dirty
return _needsCompositing;
}
void updateCompositingBits() {
if (!_needsCompositingBitsUpdate)
return;
bool didHaveCompositedDescendant = _needsCompositing;
visitChildren((RenderObject child) {
child.updateCompositing();
if (child.hasCompositedDescendant)
_hasCompositedDescendant = true;
child.updateCompositingBits();
if (child.needsCompositing)
_needsCompositing = true;
});
if (requiresCompositing)
_hasCompositedDescendant = true;
_needsCompositingUpdate = false;
if (hasLayer)
_needsCompositing = true;
if (didHaveCompositedDescendant != _needsCompositing)
markNeedsPaint();
_needsCompositingBitsUpdate = false;
}
bool _needsPaint = true;
bool get needsPaint => _needsPaint;
void markNeedsPaint() {
assert(!debugDoingPaint);
if (!attached) return; // Don't try painting things that aren't in the hierarchy
if (_needsPaint) return;
if (requiresCompositing) {
if (hasLayer) {
// If we always have our own layer, then we can just repaint
// ourselves without involving any other nodes.
assert(_layer != null);
_needsPaint = true;
_nodesNeedingPaint.add(this);
scheduler.ensureVisualUpdate();
} else if (parent is! RenderObject) {
// we're the root of the render tree (probably a RenderView)
} else if (parent is RenderObject) {
// We don't have our own layer; one of our ancestors will take
// care of updating the layer we're in and when they do that
// we'll get our paint() method called.
assert(_layer == null);
(parent as RenderObject).markNeedsPaint(); // TODO(ianh): remove the cast once the analyzer is cleverer
} else {
// If we're the root of the render tree (probably a RenderView),
// then we have to paint ourselves, since nobody else can paint
// us. We don't add ourselves to _nodesNeedingPaint in this
// case, because the root is always told to paint regardless.
_needsPaint = true;
scheduler.ensureVisualUpdate();
} else {
(parent as RenderObject).markNeedsPaint(); // TODO(ianh): remove the cast once the analyzer is cleverer
}
}
static void flushPaint() {
sky.tracing.begin('RenderObject.flushPaint');
_debugDoingPaint = true;
try {
List<RenderObject> dirtyNodes = _nodesNeedingPaint;
_nodesNeedingPaint = new List<RenderObject>();
for (RenderObject node in dirtyNodes..sort((a, b) => a.depth - b.depth)) {
if (node._needsPaint && node.attached)
// Sort the dirty nodes in reverse order (deepest first).
for (RenderObject node in dirtyNodes..sort((a, b) => b.depth - a.depth)) {
assert(node._needsPaint);
if (node.attached)
node._repaint();
};
assert(_nodesNeedingPaint.length == 0);
......@@ -485,33 +621,36 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
sky.tracing.end('RenderObject.flushPaint');
}
}
void initialPaint(ContainerLayer rootLayer, Size size) {
assert(attached);
assert(parent is! RenderObject);
assert(!_debugDoingPaint);
assert(hasLayer);
PaintingContext newContext = new PaintingContext.withLayer(rootLayer, Point.origin & size);
_paintLayer(newContext);
}
void _repaint() {
assert(!_needsLayout);
assert(requiresCompositing);
assert(hasLayer);
assert(_layer != null);
// TODO(abarth): Using _layer.offset isn't correct if the topLeft of our
// paint bounds has changed since our last repaint.
PaintingContext context = new PaintingContext(_layer.offset, paintBounds.size);
_layer.parent.add(context.layer, before: _layer);
_layer.detach();
_layer = context._layer;
_needsPaint = false;
PaintingContext newContext = new PaintingContext.replacingLayer(_layer, paintBounds);
_paintLayer(newContext);
}
void _paintLayer(PaintingContext context) {
_layer = context._containerLayer;
try {
_paintWithContext(context, Offset.zero);
context.endRecording();
} catch (e) {
print('Exception raised during _repaint:\n${e}\nContext:\n${this}');
print('Exception raised during _paintLayer:\n${e}\nContext:\n${this}');
if (inDebugBuild)
rethrow;
return;
}
assert(!_needsLayout); // check that the paint() method didn't mark us dirty again
assert(!_needsPaint); // check that the paint() method didn't mark us dirty again
}
void _paintWithContext(PaintingContext context, Offset offset) {
_needsPaint = false;
assert(!_debugDoingThisPaint);
assert(!_needsLayout);
assert(!_needsCompositingBitsUpdate);
RenderObject debugLastActivePaint;
assert(() {
_debugDoingThisPaint = true;
......@@ -522,9 +661,13 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
context.canvas.save();
context.canvas.clipRect(paintBounds.shift(offset));
}
assert(!hasLayer || _layer != null);
return true;
});
_needsPaint = false;
paint(context, offset);
assert(!_needsLayout); // check that the paint() method didn't mark us dirty again
assert(!_needsPaint); // check that the paint() method didn't mark us dirty again
assert(() {
if (debugPaintBoundsEnabled)
context.canvas.restore();
......@@ -532,7 +675,6 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
_debugDoingThisPaint = false;
return true;
});
assert(!_needsPaint);
}
Rect get paintBounds;
......
......@@ -295,18 +295,12 @@ class RenderOpacity extends RenderProxyBox {
void paint(PaintingContext context, Offset offset) {
if (child != null) {
int a = _alpha;
if (a == 0)
return;
if (a == 255) {
if (a == 255)
context.paintChild(child, offset.toPoint());
return;
}
context.canvas.saveLayer(null, _paint); // TODO(abarth): layerize
context.paintChild(child, offset.toPoint());
context.canvas.restore();
else
context.paintChildWithPaint(child, offset.toPoint(), null, _paint);
}
}
}
......@@ -349,11 +343,8 @@ class RenderColorFilter extends RenderProxyBox {
}
void paint(PaintingContext context, Offset offset) {
if (child != null) {
context.canvas.saveLayer(offset & size, _paint); // TODO(abarth): layerize
context.paintChild(child, offset.toPoint());
context.canvas.restore();
}
if (child != null)
context.paintChildWithPaint(child, offset.toPoint(), offset & size, _paint);
}
}
......@@ -362,7 +353,7 @@ class RenderClipRect extends RenderProxyBox {
void paint(PaintingContext context, Offset offset) {
if (child != null)
context.paintChildWithClip(child, offset.toPoint(), Offset.zero & size);
context.paintChildWithClipRect(child, offset.toPoint(), offset & size);
}
}
......@@ -393,16 +384,11 @@ class RenderClipRRect extends RenderProxyBox {
markNeedsPaint();
}
final Paint _paint = new Paint()..isAntiAlias = false;
void paint(PaintingContext context, Offset offset) {
if (child != null) {
Rect rect = offset & size;
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());
context.canvas.restore();
context.paintChildWithClipRRect(child, offset.toPoint(), rect, rrect);
}
}
}
......@@ -410,8 +396,6 @@ class RenderClipRRect extends RenderProxyBox {
class RenderClipOval extends RenderProxyBox {
RenderClipOval({ RenderBox child }) : super(child);
final Paint _paint = new Paint()..isAntiAlias = false;
Rect _cachedRect;
Path _cachedPath;
......@@ -426,10 +410,7 @@ class RenderClipOval extends RenderProxyBox {
void paint(PaintingContext context, Offset offset) {
if (child != null) {
Rect rect = offset & size;
context.canvas.saveLayer(rect, _paint); // TODO(abarth): layerize
context.canvas.clipPath(_getPath(rect));
context.paintChild(child, offset.toPoint());
context.canvas.restore();
context.paintChildWithClipPath(child, offset.toPoint(), rect, _getPath(rect));
}
}
}
......@@ -561,11 +542,8 @@ class RenderTransform extends RenderProxyBox {
}
void paint(PaintingContext context, Offset offset) {
context.canvas.save();
context.canvas.translate(offset.dx, offset.dy);
context.canvas.concat(_transform.storage);
super.paint(context, Offset.zero);
context.canvas.restore();
if (child != null)
context.paintChildWithTransform(child, offset.toPoint(), _transform);
}
void applyPaintTransform(Matrix4 transform) {
......
......@@ -72,7 +72,7 @@ class SkyBinding {
}
void beginFrame(double timeStamp) {
RenderObject.flushLayout();
_renderView.updateCompositing();
_renderView.updateCompositingBits();
RenderObject.flushPaint();
_renderView.paintFrame();
_renderView.compositeFrame();
......
......@@ -42,8 +42,6 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
markNeedsLayout();
}
ContainerLayer _rootLayer;
// We never call layout() on this class, so this should never get
// checked. (This class is laid out using scheduleInitialLayout().)
bool debugDoesMeetConstraints() { assert(false); return false; }
......@@ -79,6 +77,8 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
return true;
}
bool get hasLayer => true;
void paint(PaintingContext context, Offset offset) {
if (child != null)
context.paintChild(child, offset.toPoint());
......@@ -88,12 +88,9 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
sky.tracing.begin('RenderView.paintFrame');
try {
final double devicePixelRatio = sky.view.devicePixelRatio;
Matrix4 transform = new Matrix4.diagonal3Values(devicePixelRatio, devicePixelRatio, 1.0);
_rootLayer = new TransformLayer(transform: transform);
PaintingContext context = new PaintingContext(Offset.zero, size);
_rootLayer.add(context.layer);
context.paintChild(child, Point.origin);
context.endRecording();
Matrix4 logicalToDeviceZoom = new Matrix4.diagonal3Values(devicePixelRatio, devicePixelRatio, 1.0);
ContainerLayer rootLayer = new TransformLayer(transform: logicalToDeviceZoom);
initialPaint(rootLayer, size);
} finally {
sky.tracing.end('RenderView.paintFrame');
}
......@@ -102,9 +99,12 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
void compositeFrame() {
sky.tracing.begin('RenderView.compositeFrame');
try {
// Eventually we will want to pass the entire layer tree to the C++ side.
// For now, however, we take the layer tree and paint it into a Canvas,
// which we then hand to the C++ side.
sky.PictureRecorder recorder = new sky.PictureRecorder();
sky.Canvas canvas = new sky.Canvas(recorder, Point.origin & (size * sky.view.devicePixelRatio));
_rootLayer.paint(canvas);
layer.paint(canvas);
sky.view.picture = recorder.endRecording();
} finally {
sky.tracing.end('RenderView.compositeFrame');
......
......@@ -122,13 +122,10 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox
Offset roundedScrollOffset = _scrollOffsetRoundedToIntegerDevicePixels;
bool _needsClip = offset < Offset.zero ||
!(offset & size).contains(((offset - roundedScrollOffset) & child.size).bottomRight);
if (_needsClip) {
context.canvas.save();
context.canvas.clipRect(offset & size);
}
context.paintChild(child, (offset - roundedScrollOffset).toPoint());
if (_needsClip)
context.canvas.restore();
context.paintChildWithClipRect(child, (offset - roundedScrollOffset).toPoint(), offset & size);
else
context.paintChild(child, (offset - roundedScrollOffset).toPoint());
}
}
......
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