layer.dart 9.63 KB
Newer Older
1 2 3 4
// 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.

5
import 'dart:ui' as ui;
6

7
import 'package:vector_math/vector_math_64.dart';
8

9 10 11 12
import 'basic_types.dart';

export 'basic_types.dart';

13 14 15 16 17
/// A composited layer
///
/// During painting, the render tree generates a tree of composited layers that
/// are uploaded into the engine and displayed by the compositor. This class is
/// the base class for all composited layers.
18
abstract class Layer {
19
  Layer({ this.offset: Offset.zero });
20

21 22
  /// Offset from parent in the parent's coordinate system.
  Offset offset;
23

24
  /// This layer's parent in the layer tree
25
  ContainerLayer get parent => _parent;
26
  ContainerLayer _parent;
27

28
  /// This layer's next sibling in the parent layer's child list
29
  Layer get nextSibling => _nextSibling;
30
  Layer _nextSibling;
31

32
  /// This layer's previous sibling in the parent layer's child list
33
  Layer get previousSibling => _previousSibling;
34
  Layer _previousSibling;
35

36
  /// Removes this layer from its parent layer's child list
37 38
  void detach() {
    if (_parent != null)
39
      _parent._remove(this);
40
  }
41 42

  /// Replaces this layer with the given layer in the parent layer's child list
Hixie's avatar
Hixie committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
  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;
  }
63

64 65 66 67
  /// Override this function to upload this layer to the engine
  ///
  /// The layerOffset is the accumulated offset of this layer's parent from the
  /// origin of the builder's coordinate system.
68
  void addToScene(ui.SceneBuilder builder, Offset layerOffset);
69 70
}

71
/// A composited layer containing a [Picture]
72
class PictureLayer extends Layer {
Hixie's avatar
Hixie committed
73
  PictureLayer({ Offset offset: Offset.zero, this.paintBounds })
74
    : super(offset: offset);
75

76 77 78 79
  /// The rectangle in this layer's coodinate system that bounds the recording
  ///
  /// The paint bounds are used to decide how much graphics memory to allocate
  /// when rasterizing this layer.
Hixie's avatar
Hixie committed
80
  Rect paintBounds;
81 82 83 84

  /// The picture recorded for this layer
  ///
  /// The picture's coodinate system matches this layer's coodinate system
85
  ui.Picture picture;
86

87
  void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
88
    builder.addPicture(offset + layerOffset, picture, paintBounds);
89 90
  }

91 92
}

93 94 95 96 97 98
/// A layer that indicates to the compositor that it should display
/// certain statistics within it
class StatisticsLayer extends Layer {
  StatisticsLayer({
    Offset offset: Offset.zero,
    this.paintBounds,
99 100
    this.optionsMask,
    this.rasterizerThreshold
101 102 103 104 105 106
  }) : super(offset: offset);

  /// The rectangle in this layer's coodinate system that bounds the recording
  Rect paintBounds;

  /// A mask specifying the statistics to display
107 108 109
  final int optionsMask;

  final int rasterizerThreshold;
110

111
  void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
112 113
    assert(optionsMask != null);
    builder.addStatistics(optionsMask, paintBounds.shift(layerOffset));
114
    builder.setRasterizerTracingThreshold(rasterizerThreshold);
115 116 117 118 119
  }

}


120
/// A composited layer that has a list of children
121
class ContainerLayer extends Layer {
122
  ContainerLayer({ Offset offset: Offset.zero }) : super(offset: offset);
123

124
  /// The first composited layer in this layer's child list
125
  Layer get firstChild => _firstChild;
126
  Layer _firstChild;
127

128
  /// The last composited layer in this layer's child list
129
  Layer get lastChild => _lastChild;
130
  Layer _lastChild;
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

  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;
  }

148 149
  /// Adds the given layer to the end of this layer's child list
  void append(Layer child) {
150 151 152 153 154 155 156
    assert(child != this);
    assert(child != _firstChild);
    assert(child != _lastChild);
    assert(child._parent == null);
    assert(child._nextSibling == null);
    assert(child._previousSibling == null);
    child._parent = this;
157 158 159 160 161 162
    child._previousSibling = _lastChild;
    if (_lastChild != null)
      _lastChild._nextSibling = child;
    _lastChild = child;
    if (_firstChild == null)
      _firstChild = child;
163 164
  }

165
  void _remove(Layer child) {
Hixie's avatar
Hixie committed
166
    assert(child._parent == this);
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
    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;
  }
Hixie's avatar
Hixie committed
185

186
  /// Removes all of this layer's children from its child list
187 188 189 190 191 192 193 194 195 196 197 198 199
  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;
  }

200
  void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
201
    addChildrenToScene(builder, offset + layerOffset);
Hixie's avatar
Hixie committed
202 203
  }

204
  /// Uploads all of this layer's children to the engine
205
  void addChildrenToScene(ui.SceneBuilder builder, Offset layerOffset) {
206
    Layer child = _firstChild;
Hixie's avatar
Hixie committed
207
    while (child != null) {
208
      child.addToScene(builder, layerOffset);
Hixie's avatar
Hixie committed
209 210 211
      child = child.nextSibling;
    }
  }
212

Hixie's avatar
Hixie committed
213 214
}

215
/// A composite layer that clips its children using a rectangle
Hixie's avatar
Hixie committed
216 217 218
class ClipRectLayer extends ContainerLayer {
  ClipRectLayer({ Offset offset: Offset.zero, this.clipRect }) : super(offset: offset);

219
  /// The rectangle to clip in the parent's coordinate system
Hixie's avatar
Hixie committed
220
  Rect clipRect;
221 222
  // TODO(abarth): Why is the rectangle in the parent's coordinate system
  // instead of in the coordinate system of this layer?
Hixie's avatar
Hixie committed
223

224
  void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
225 226 227
    builder.pushClipRect(clipRect.shift(layerOffset));
    addChildrenToScene(builder, offset + layerOffset);
    builder.pop();
Hixie's avatar
Hixie committed
228 229
  }

230
}
Hixie's avatar
Hixie committed
231

232
/// A composite layer that clips its children using a rounded rectangle
Hixie's avatar
Hixie committed
233 234 235
class ClipRRectLayer extends ContainerLayer {
  ClipRRectLayer({ Offset offset: Offset.zero, this.bounds, this.clipRRect }) : super(offset: offset);

236
  /// Unused
Hixie's avatar
Hixie committed
237
  Rect bounds;
238 239 240
  // TODO(abarth): Remove.

  /// The rounded-rect to clip in the parent's coordinate system
241
  ui.RRect clipRRect;
242 243
  // TODO(abarth): Why is the rounded-rect in the parent's coordinate system
  // instead of in the coordinate system of this layer?
Hixie's avatar
Hixie committed
244

245
  void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
246 247 248
    builder.pushClipRRect(clipRRect.shift(layerOffset), bounds.shift(layerOffset));
    addChildrenToScene(builder, offset + layerOffset);
    builder.pop();
Hixie's avatar
Hixie committed
249
  }
250

Hixie's avatar
Hixie committed
251 252
}

253
/// A composite layer that clips its children using a path
Hixie's avatar
Hixie committed
254 255 256
class ClipPathLayer extends ContainerLayer {
  ClipPathLayer({ Offset offset: Offset.zero, this.bounds, this.clipPath }) : super(offset: offset);

257
  /// Unused
Hixie's avatar
Hixie committed
258
  Rect bounds;
259 260 261
  // TODO(abarth): Remove.

  /// The path to clip in the parent's coordinate system
Hixie's avatar
Hixie committed
262
  Path clipPath;
263 264
  // TODO(abarth): Why is the path in the parent's coordinate system instead of
  // in the coordinate system of this layer?
Hixie's avatar
Hixie committed
265

266
  void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
267 268 269
    builder.pushClipPath(clipPath.shift(layerOffset), bounds.shift(layerOffset));
    addChildrenToScene(builder, offset + layerOffset);
    builder.pop();
Hixie's avatar
Hixie committed
270
  }
271

272 273
}

274
/// A composited layer that applies a transformation matrix to its children
275
class TransformLayer extends ContainerLayer {
276
  TransformLayer({ Offset offset: Offset.zero, this.transform }) : super(offset: offset);
277

278
  /// The matrix to apply
279
  Matrix4 transform;
280

281
  void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
282 283 284 285 286
    Matrix4 offsetTransform = new Matrix4.identity();
    offsetTransform.translate(offset.dx + layerOffset.dx, offset.dy + layerOffset.dy);
    builder.pushTransform((offsetTransform * transform).storage);
    addChildrenToScene(builder, Offset.zero);
    builder.pop();
287
  }
288 289
}

290
/// A composited layer that makes its children partially transparent
291 292
class OpacityLayer extends ContainerLayer {
  OpacityLayer({ Offset offset: Offset.zero, this.bounds, this.alpha }) : super(offset: offset);
293

294
  /// Unused
Hixie's avatar
Hixie committed
295
  Rect bounds;
296 297 298 299 300 301
  // TODO(abarth): Remove.

  /// The amount to multiply into the alpha channel
  ///
  /// The opacity is expressed as an integer from 0 to 255, where 0 is fully
  /// transparent and 255 is fully opaque.
302 303
  int alpha;

304
  void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
305
    builder.pushOpacity(alpha, bounds?.shift(layerOffset));
306 307
    addChildrenToScene(builder, offset + layerOffset);
    builder.pop();
308
  }
309
}