mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
We now use the repaint system to do all the painting. During initialization, we set up a root layer that applies the device pixel ratio. Fixes #706
311 lines
8.9 KiB
Dart
311 lines
8.9 KiB
Dart
// Copyright 2015 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:sky' as sky;
|
|
import 'dart:sky' show Point, Offset, Size, Rect, Color, Paint, Path;
|
|
|
|
import 'package:sky/base/debug.dart';
|
|
import 'package:vector_math/vector_math.dart';
|
|
|
|
abstract class Layer {
|
|
Layer({ this.offset: Offset.zero });
|
|
|
|
Offset offset; // From parent, in parent's coordinate system.
|
|
|
|
ContainerLayer _parent;
|
|
ContainerLayer get parent => _parent;
|
|
|
|
Layer _nextSibling;
|
|
Layer get nextSibling => _nextSibling;
|
|
|
|
Layer _previousSibling;
|
|
Layer get previousSibling => _previousSibling;
|
|
|
|
void detach() {
|
|
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.paintBounds })
|
|
: super(offset: offset);
|
|
|
|
Rect paintBounds;
|
|
sky.Picture picture;
|
|
|
|
bool _debugPaintLayerBorder(sky.Canvas canvas) {
|
|
if (debugPaintLayerBordersEnabled) {
|
|
Paint border = new Paint()
|
|
..color = debugPaintLayerBordersColor
|
|
..strokeWidth = 2.0
|
|
..setStyle(sky.PaintingStyle.stroke);
|
|
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));
|
|
canvas.translate(-offset.dx, -offset.dy);
|
|
}
|
|
}
|
|
|
|
class ContainerLayer extends Layer {
|
|
ContainerLayer({ Offset offset: Offset.zero }) : super(offset: offset);
|
|
|
|
// 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;
|
|
|
|
bool _debugUltimatePreviousSiblingOf(Layer child, { Layer equals }) {
|
|
while (child._previousSibling != null) {
|
|
assert(child._previousSibling != child);
|
|
child = child._previousSibling;
|
|
}
|
|
return child == equals;
|
|
}
|
|
|
|
bool _debugUltimateNextSiblingOf(Layer child, { Layer equals }) {
|
|
while (child._nextSibling != null) {
|
|
assert(child._nextSibling != child);
|
|
child = child._nextSibling;
|
|
}
|
|
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);
|
|
assert(child != before);
|
|
assert(child != _firstChild);
|
|
assert(child != _lastChild);
|
|
assert(child._parent == null);
|
|
assert(child._nextSibling == null);
|
|
assert(child._previousSibling == null);
|
|
child._parent = this;
|
|
if (before == null) {
|
|
child._previousSibling = _lastChild;
|
|
if (_lastChild != null)
|
|
_lastChild._nextSibling = child;
|
|
_lastChild = child;
|
|
if (_firstChild == null)
|
|
_firstChild = child;
|
|
} else {
|
|
assert(_firstChild != null);
|
|
assert(_lastChild != null);
|
|
assert(_debugUltimatePreviousSiblingOf(before, equals: _firstChild));
|
|
assert(_debugUltimateNextSiblingOf(before, equals: _lastChild));
|
|
if (before._previousSibling == null) {
|
|
assert(before == _firstChild);
|
|
child._nextSibling = before;
|
|
before._previousSibling = child;
|
|
_firstChild = child;
|
|
} else {
|
|
child._previousSibling = before._previousSibling;
|
|
child._nextSibling = before;
|
|
child._previousSibling._nextSibling = child;
|
|
child._nextSibling._previousSibling = child;
|
|
assert(before._previousSibling == child);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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));
|
|
if (child._previousSibling == null) {
|
|
assert(_firstChild == child);
|
|
_firstChild = child._nextSibling;
|
|
} else {
|
|
child._previousSibling._nextSibling = child._nextSibling;
|
|
}
|
|
if (child._nextSibling == null) {
|
|
assert(_lastChild == child);
|
|
_lastChild = child._previousSibling;
|
|
} else {
|
|
child._nextSibling._previousSibling = child._previousSibling;
|
|
}
|
|
child._previousSibling = null;
|
|
child._nextSibling = null;
|
|
child._parent = null;
|
|
}
|
|
|
|
void removeAllChildren() {
|
|
Layer child = _firstChild;
|
|
while (child != null) {
|
|
Layer next = child.nextSibling;
|
|
child._previousSibling = null;
|
|
child._nextSibling = null;
|
|
child._parent = null;
|
|
child = next;
|
|
}
|
|
_firstChild = null;
|
|
_lastChild = 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 {
|
|
TransformLayer({ Offset offset: Offset.zero, this.transform }) : super(offset: offset);
|
|
|
|
Matrix4 transform;
|
|
|
|
void paint(sky.Canvas canvas) {
|
|
canvas.save();
|
|
canvas.translate(offset.dx, offset.dy);
|
|
canvas.concat(transform.storage);
|
|
paintChildren(canvas);
|
|
canvas.restore();
|
|
}
|
|
}
|
|
|
|
class OpacityLayer extends ContainerLayer {
|
|
OpacityLayer({ Offset offset: Offset.zero, this.bounds, this.alpha }) : super(offset: offset);
|
|
|
|
// bounds is _not_ affected by given offset
|
|
Rect bounds;
|
|
int alpha;
|
|
|
|
static Paint paintForAlpha(int alpha) {
|
|
return new Paint()
|
|
..color = new Color.fromARGB(alpha, 0, 0, 0)
|
|
..setTransferMode(sky.TransferMode.srcOver)
|
|
..isAntiAlias = false;
|
|
}
|
|
|
|
void paint(sky.Canvas canvas) {
|
|
canvas.saveLayer(bounds, paintForAlpha(alpha));
|
|
canvas.translate(offset.dx, offset.dy);
|
|
paintChildren(canvas);
|
|
canvas.restore();
|
|
}
|
|
}
|
|
|
|
class ColorFilterLayer extends ContainerLayer {
|
|
ColorFilterLayer({
|
|
Offset offset: Offset.zero,
|
|
this.bounds,
|
|
this.color,
|
|
this.transferMode
|
|
}) : super(offset: offset);
|
|
|
|
// bounds is _not_ affected by given offset
|
|
Rect bounds;
|
|
Color color;
|
|
sky.TransferMode transferMode;
|
|
|
|
static paintForColorFilter(Color color, sky.TransferMode transferMode) {
|
|
new Paint()
|
|
..setColorFilter(new sky.ColorFilter.mode(color, transferMode))
|
|
..isAntiAlias = false;
|
|
}
|
|
|
|
void paint(sky.Canvas canvas) {
|
|
canvas.saveLayer(bounds, paintForColorFilter(color, transferMode));
|
|
canvas.translate(offset.dx, offset.dy);
|
|
paintChildren(canvas);
|
|
canvas.restore();
|
|
}
|
|
}
|