layer.dart 71.7 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:async';
6
import 'dart:collection';
7 8
import 'dart:ui' as ui show EngineLayer, Image, ImageFilter, PathMetric,
                            Picture, PictureRecorder, Scene, SceneBuilder;
9

10
import 'package:flutter/foundation.dart';
11
import 'package:flutter/gestures.dart';
12
import 'package:flutter/painting.dart';
13
import 'package:vector_math/vector_math_64.dart';
14

15 16
import 'debug.dart';

17
/// A composited layer.
18 19 20 21
///
/// 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.
22 23 24 25 26
///
/// Most layers can have their properties mutated, and layers can be moved to
/// different parents. The scene must be explicitly recomposited after such
/// changes are made; the layer tree does not maintain its own dirty state.
///
27
/// To composite the tree, create a [SceneBuilder] object, pass it to the
28
/// root [Layer] object's [addToScene] method, and then call
29 30
/// [SceneBuilder.build] to obtain a [Scene]. A [Scene] can then be painted
/// using [Window.render].
31 32 33 34
///
/// See also:
///
///  * [RenderView.compositeFrame], which implements this recomposition protocol
35
///    for painting [RenderObject] trees on the display.
36
abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
37 38 39 40 41 42 43 44
  /// This layer's parent in the layer tree.
  ///
  /// The [parent] of the root node in the layer tree is null.
  ///
  /// Only subclasses of [ContainerLayer] can have children in the layer tree.
  /// All other layer classes are used for leaves in the layer tree.
  @override
  ContainerLayer get parent => super.parent;
45

46 47 48 49 50 51 52
  // Whether this layer has any changes since its last call to [addToScene].
  //
  // Initialized to true as a new layer has never called [addToScene].
  bool _needsAddToScene = true;

  /// Mark that this layer has changed and [addToScene] needs to be called.
  @protected
53
  @visibleForTesting
54 55 56 57 58 59 60 61 62
  void markNeedsAddToScene() {
    _needsAddToScene = true;
  }

  /// Mark that this layer is in sync with engine.
  ///
  /// This is only for debug and test purpose only.
  @visibleForTesting
  void debugMarkClean() {
63
    assert(() {
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
      _needsAddToScene = false;
      return true;
    }());
  }

  /// Subclasses may override this to true to disable retained rendering.
  @protected
  bool get alwaysNeedsAddToScene => false;

  bool _subtreeNeedsAddToScene;

  /// Whether any layer in the subtree needs [addToScene].
  ///
  /// This is for debug and test purpose only. It only becomes valid after
  /// calling [updateSubtreeNeedsAddToScene].
  @visibleForTesting
  bool get debugSubtreeNeedsAddToScene {
    bool result;
82
    assert(() {
83 84 85 86 87 88 89 90 91 92 93 94 95
      result = _subtreeNeedsAddToScene;
      return true;
    }());
    return result;
  }

  ui.EngineLayer _engineLayer;

  /// Traverse the layer tree and compute if any subtree needs [addToScene].
  ///
  /// A subtree needs [addToScene] if any of its layer needs [addToScene].
  /// The [ContainerLayer] will override this to respect its children.
  @protected
96
  @visibleForTesting
97 98 99 100
  void updateSubtreeNeedsAddToScene() {
    _subtreeNeedsAddToScene = _needsAddToScene || alwaysNeedsAddToScene;
  }

101
  /// This layer's next sibling in the parent layer's child list.
102
  Layer get nextSibling => _nextSibling;
103
  Layer _nextSibling;
104

105
  /// This layer's previous sibling in the parent layer's child list.
106
  Layer get previousSibling => _previousSibling;
107
  Layer _previousSibling;
108

109 110 111 112 113 114 115 116 117 118 119 120
  @override
  void dropChild(AbstractNode child) {
    markNeedsAddToScene();
    super.dropChild(child);
  }

  @override
  void adoptChild(AbstractNode child) {
    markNeedsAddToScene();
    super.adoptChild(child);
  }

121
  /// Removes this layer from its parent layer's child list.
122 123
  ///
  /// This has no effect if the layer's parent is already null.
124
  @mustCallSuper
125 126
  void remove() {
    parent?._removeChild(this);
127
  }
128

129 130 131 132 133
  /// Returns the value of [S] that corresponds to the point described by
  /// [regionOffset].
  ///
  /// Returns null if no matching region is found.
  ///
134
  /// The main way for a value to be found here is by pushing an
135 136 137 138
  /// [AnnotatedRegionLayer] into the layer tree.
  ///
  /// See also:
  ///
139
  ///  * [AnnotatedRegionLayer], for placing values in the layer tree.
140 141
  S find<S>(Offset regionOffset);

142 143 144 145 146 147 148 149 150 151 152 153 154
  /// Returns an iterable of [S] values that corresponds to the point described
  /// by [regionOffset] on all layers under the point.
  ///
  /// Returns an empty list if no matching region is found.
  ///
  /// The main way for a value to be found here is by pushing an
  /// [AnnotatedRegionLayer] into the layer tree.
  ///
  /// See also:
  ///
  ///  * [AnnotatedRegionLayer], for placing values in the layer tree.
  Iterable<S> findAll<S>(Offset regionOffset);

155
  /// Override this method to upload this layer to the engine.
156 157 158 159
  ///
  /// Return the engine layer for retained rendering. When there's no
  /// corresponding engine layer, null is returned.
  @protected
160
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]);
161 162 163 164 165 166 167 168

  void _addToSceneWithRetainedRendering(ui.SceneBuilder builder) {
    // There can't be a loop by adding a retained layer subtree whose
    // _subtreeNeedsAddToScene is false.
    //
    // Proof by contradiction:
    //
    // If we introduce a loop, this retained layer must be appended to one of
xster's avatar
xster committed
169
    // its descendant layers, say A. That means the child structure of A has
170 171 172 173 174 175 176 177 178
    // changed so A's _needsAddToScene is true. This contradicts
    // _subtreeNeedsAddToScene being false.
    if (!_subtreeNeedsAddToScene && _engineLayer != null) {
      builder.addRetained(_engineLayer);
      return;
    }
    _engineLayer = addToScene(builder);
    _needsAddToScene = false;
  }
179

180 181 182 183
  /// The object responsible for creating this layer.
  ///
  /// Defaults to the value of [RenderObject.debugCreator] for the render object
  /// that created this layer. Used in debug messages.
184
  dynamic debugCreator;
185

186
  @override
187
  String toStringShort() => '${super.toStringShort()}${ owner == null ? " DETACHED" : ""}';
188

189
  @override
190 191
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
192 193
    properties.add(DiagnosticsProperty<Object>('owner', owner, level: parent != null ? DiagnosticLevel.hidden : DiagnosticLevel.info, defaultValue: null));
    properties.add(DiagnosticsProperty<dynamic>('creator', debugCreator, defaultValue: null, level: DiagnosticLevel.debug));
194
  }
195 196
}

197 198 199
/// A composited layer containing a [Picture].
///
/// Picture layers are always leaves in the layer tree.
200
class PictureLayer extends Layer {
201
  /// Creates a leaf layer for the layer tree.
202 203 204 205 206
  PictureLayer(this.canvasBounds);

  /// The bounds that were used for the canvas that drew this layer's [picture].
  ///
  /// This is purely advisory. It is included in the information dumped with
207
  /// [debugDumpLayerTree] (which can be triggered by pressing "L" when using
208 209 210 211
  /// "flutter run" at the console), which can help debug why certain drawing
  /// commands are being culled.
  final Rect canvasBounds;

212
  /// The picture recorded for this layer.
213
  ///
214
  /// The picture's coordinate system matches this layer's coordinate system.
215 216 217
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
218 219 220 221 222 223
  ui.Picture get picture => _picture;
  ui.Picture _picture;
  set picture(ui.Picture picture) {
    _needsAddToScene = true;
    _picture = picture;
  }
224

225 226 227 228 229
  /// Hints that the painting in this layer is complex and would benefit from
  /// caching.
  ///
  /// If this hint is not set, the compositor will apply its own heuristics to
  /// decide whether the this layer is complex enough to benefit from caching.
230 231 232
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
233 234 235 236 237 238 239 240
  bool get isComplexHint => _isComplexHint;
  bool _isComplexHint = false;
  set isComplexHint(bool value) {
    if (value != _isComplexHint) {
      _isComplexHint = value;
      markNeedsAddToScene();
    }
  }
241 242 243 244 245 246 247

  /// Hints that the painting in this layer is likely to change next frame.
  ///
  /// This hint tells the compositor not to cache this layer because the cache
  /// will not be used in the future. If this hint is not set, the compositor
  /// will apply its own heuristics to decide whether this layer is likely to be
  /// reused in the future.
248 249 250
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
251 252 253 254 255 256 257 258
  bool get willChangeHint => _willChangeHint;
  bool _willChangeHint = false;
  set willChangeHint(bool value) {
    if (value != _willChangeHint) {
      _willChangeHint = value;
      markNeedsAddToScene();
    }
  }
259

260
  @override
261
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
262
    builder.addPicture(layerOffset, picture, isComplexHint: isComplexHint, willChangeHint: willChangeHint);
263
    return null; // this does not return an engine layer yet.
264
  }
265 266

  @override
267 268
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
269
    properties.add(DiagnosticsProperty<Rect>('paint bounds', canvasBounds));
270
  }
271 272 273

  @override
  S find<S>(Offset regionOffset) => null;
274 275 276

  @override
  Iterable<S> findAll<S>(Offset regionOffset) => <S>[];
277 278
}

279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
/// A composited layer that maps a backend texture to a rectangle.
///
/// Backend textures are images that can be applied (mapped) to an area of the
/// Flutter view. They are created, managed, and updated using a
/// platform-specific texture registry. This is typically done by a plugin
/// that integrates with host platform video player, camera, or OpenGL APIs,
/// or similar image sources.
///
/// A texture layer refers to its backend texture using an integer ID. Texture
/// IDs are obtained from the texture registry and are scoped to the Flutter
/// view. Texture IDs may be reused after deregistration, at the discretion
/// of the registry. The use of texture IDs currently unknown to the registry
/// will silently result in a blank rectangle.
///
/// Once inserted into the layer tree, texture layers are repainted autonomously
/// as dictated by the backend (e.g. on arrival of a video frame). Such
/// repainting generally does not involve executing Dart code.
///
/// Texture layers are always leaves in the layer tree.
///
/// See also:
///
301
///  * <https://api.flutter.dev/javadoc/io/flutter/view/TextureRegistry.html>
302
///    for how to create and manage backend textures on Android.
303
///  * <https://api.flutter.dev/objcdoc/Protocols/FlutterTextureRegistry.html>
304
///    for how to create and manage backend textures on iOS.
305 306
class TextureLayer extends Layer {
  /// Creates a texture layer bounded by [rect] and with backend texture
307 308
  /// identified by [textureId], if [freeze] is true new texture frames will not be
  /// populated to the texture.
309 310 311
  TextureLayer({
    @required this.rect,
    @required this.textureId,
312
    this.freeze = false,
313 314
  }) : assert(rect != null),
       assert(textureId != null);
315 316 317 318 319 320 321

  /// Bounding rectangle of this layer.
  final Rect rect;

  /// The identity of the backend texture.
  final int textureId;

322 323 324 325 326 327 328
  /// When true the texture that will not be updated with new frames.
  ///
  /// This is used when resizing an embedded  Android views: When resizing
  /// there is a short period during which the framework cannot tell
  /// if the newest texture frame has the previous or new size, to workaround this
  /// the framework "freezes" the texture just before resizing the Android view and unfreezes
  /// it when it is certain that a frame with the new size is ready.
329 330
  final bool freeze;

331
  @override
332
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
333
    final Rect shiftedRect = layerOffset == Offset.zero ? rect : rect.shift(layerOffset);
334 335 336 337 338
    builder.addTexture(
      textureId,
      offset: shiftedRect.topLeft,
      width: shiftedRect.width,
      height: shiftedRect.height,
339
      freeze: freeze,
340
    );
341
    return null; // this does not return an engine layer yet.
342
  }
343 344 345

  @override
  S find<S>(Offset regionOffset) => null;
346 347 348

  @override
  Iterable<S> findAll<S>(Offset regionOffset) => <S>[];
349 350
}

351 352 353 354 355 356 357 358 359
/// A layer that shows an embedded [UIView](https://developer.apple.com/documentation/uikit/uiview)
/// on iOS.
class PlatformViewLayer extends Layer {
  /// Creates a platform view layer.
  ///
  /// The `rect` and `viewId` parameters must not be null.
  PlatformViewLayer({
    @required this.rect,
    @required this.viewId,
360 361
  }) : assert(rect != null),
       assert(viewId != null);
362 363 364 365 366 367 368 369 370 371

  /// Bounding rectangle of this layer in the global coordinate space.
  final Rect rect;

  /// The unique identifier of the UIView displayed on this layer.
  ///
  /// A UIView with this identifier must have been created by [PlatformViewsServices.initUiKitView].
  final int viewId;

  @override
372
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
373
    final Rect shiftedRect = layerOffset == Offset.zero ? rect : rect.shift(layerOffset);
374 375 376 377 378 379 380 381 382 383 384
    builder.addPlatformView(
      viewId,
      offset: shiftedRect.topLeft,
      width: shiftedRect.width,
      height: shiftedRect.height,
    );
    return null;
  }

  @override
  S find<S>(Offset regionOffset) => null;
385 386 387

  @override
  Iterable<S> findAll<S>(Offset regionOffset) => <S>[];
388 389
}

390
/// A layer that indicates to the compositor that it should display
391
/// certain performance statistics within it.
392 393
///
/// Performance overlay layers are always leaves in the layer tree.
394
class PerformanceOverlayLayer extends Layer {
395
  /// Creates a layer that displays a performance overlay.
396
  PerformanceOverlayLayer({
397
    @required Rect overlayRect,
398 399 400
    @required this.optionsMask,
    @required this.rasterizerThreshold,
    @required this.checkerboardRasterCacheImages,
401
    @required this.checkerboardOffscreenLayers,
402
  }) : _overlayRect = overlayRect;
403

404
  /// The rectangle in this layer's coordinate system that the overlay should occupy.
405 406 407
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
408 409 410 411 412 413 414 415
  Rect get overlayRect => _overlayRect;
  Rect _overlayRect;
  set overlayRect(Rect value) {
    if (value != _overlayRect) {
      _overlayRect = value;
      markNeedsAddToScene();
    }
  }
416

417 418
  /// The mask is created by shifting 1 by the index of the specific
  /// [PerformanceOverlayOption] to enable.
419 420
  final int optionsMask;

421 422 423
  /// The rasterizer threshold is an integer specifying the number of frame
  /// intervals that the rasterizer must miss before it decides that the frame
  /// is suitable for capturing an SkPicture trace for further analysis.
424
  final int rasterizerThreshold;
425

426 427 428 429 430 431 432 433 434 435 436 437 438
  /// Whether the raster cache should checkerboard cached entries.
  ///
  /// The compositor can sometimes decide to cache certain portions of the
  /// widget hierarchy. Such portions typically don't change often from frame to
  /// frame and are expensive to render. This can speed up overall rendering. However,
  /// there is certain upfront cost to constructing these cache entries. And, if
  /// the cache entries are not used very often, this cost may not be worth the
  /// speedup in rendering of subsequent frames. If the developer wants to be certain
  /// that populating the raster cache is not causing stutters, this option can be
  /// set. Depending on the observations made, hints can be provided to the compositor
  /// that aid it in making better decisions about caching.
  final bool checkerboardRasterCacheImages;

439 440 441 442 443 444 445 446 447 448
  /// Whether the compositor should checkerboard layers that are rendered to offscreen
  /// bitmaps. This can be useful for debugging rendering performance.
  ///
  /// Render target switches are caused by using opacity layers (via a [FadeTransition] or
  /// [Opacity] widget), clips, shader mask layers, etc. Selecting a new render target
  /// and merging it with the rest of the scene has a performance cost. This can sometimes
  /// be avoided by using equivalent widgets that do not require these layers (for example,
  /// replacing an [Opacity] widget with an [widgets.Image] using a [BlendMode]).
  final bool checkerboardOffscreenLayers;

449
  @override
450
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
451
    assert(optionsMask != null);
452 453
    final Rect shiftedOverlayRect = layerOffset == Offset.zero ? overlayRect : overlayRect.shift(layerOffset);
    builder.addPerformanceOverlay(optionsMask, shiftedOverlayRect);
454
    builder.setRasterizerTracingThreshold(rasterizerThreshold);
455
    builder.setCheckerboardRasterCacheImages(checkerboardRasterCacheImages);
456
    builder.setCheckerboardOffscreenLayers(checkerboardOffscreenLayers);
457
    return null; // this does not return an engine layer yet.
458
  }
459 460 461

  @override
  S find<S>(Offset regionOffset) => null;
462 463 464

  @override
  Iterable<S> findAll<S>(Offset regionOffset) => <S>[];
465 466
}

467 468 469 470 471
/// A composited layer that has a list of children.
///
/// A [ContainerLayer] instance merely takes a list of children and inserts them
/// into the composited rendering in order. There are subclasses of
/// [ContainerLayer] which apply more elaborate effects in the process.
472
class ContainerLayer extends Layer {
473
  /// The first composited layer in this layer's child list.
474
  Layer get firstChild => _firstChild;
475
  Layer _firstChild;
476

477
  /// The last composited layer in this layer's child list.
478
  Layer get lastChild => _lastChild;
479
  Layer _lastChild;
480 481

  bool _debugUltimatePreviousSiblingOf(Layer child, { Layer equals }) {
482 483 484 485 486
    assert(child.attached == attached);
    while (child.previousSibling != null) {
      assert(child.previousSibling != child);
      child = child.previousSibling;
      assert(child.attached == attached);
487 488 489 490 491
    }
    return child == equals;
  }

  bool _debugUltimateNextSiblingOf(Layer child, { Layer equals }) {
492
    assert(child.attached == attached);
493 494 495
    while (child._nextSibling != null) {
      assert(child._nextSibling != child);
      child = child._nextSibling;
496
      assert(child.attached == attached);
497 498 499 500
    }
    return child == equals;
  }

501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
  PictureLayer _highlightConflictingLayer(PhysicalModelLayer child) {
    final ui.PictureRecorder recorder = ui.PictureRecorder();
    final Canvas canvas = Canvas(recorder);
    canvas.drawPath(
      child.clipPath,
      Paint()
        ..color = const Color(0xFFAA0000)
        ..style = PaintingStyle.stroke
        // The elevation may be 0 or otherwise too small to notice.
        // Adding 10 to it makes it more visually obvious.
        ..strokeWidth = child.elevation + 10.0,
    );
    final PictureLayer pictureLayer = PictureLayer(child.clipPath.getBounds())
      ..picture = recorder.endRecording()
      ..debugCreator = child;
    child.append(pictureLayer);
    return pictureLayer;
  }

  List<PictureLayer> _processConflictingPhysicalLayers(PhysicalModelLayer predecessor, PhysicalModelLayer child) {
    FlutterError.reportError(FlutterErrorDetails(
      exception: FlutterError('Painting order is out of order with respect to elevation.\n'
523
                              'See https://api.flutter.dev/flutter/rendering/debugCheckElevationsEnabled.html '
524 525
                              'for more details.'),
      library: 'rendering library',
526
      context: ErrorDescription('during compositing'),
527 528 529 530 531 532
      informationCollector: () {
        return <DiagnosticsNode>[
          child.toDiagnosticsNode(name: 'Attempted to composite layer', style: DiagnosticsTreeStyle.errorProperty),
          predecessor.toDiagnosticsNode(name: 'after layer', style: DiagnosticsTreeStyle.errorProperty),
          ErrorDescription('which occupies the same area at a higher elevation.'),
        ];
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
      }
    ));
    return <PictureLayer>[
      _highlightConflictingLayer(predecessor),
      _highlightConflictingLayer(child),
    ];
  }

  /// Checks that no [PhysicalModelLayer] would paint after another overlapping
  /// [PhysicalModelLayer] that has a higher elevation.
  ///
  /// Returns a list of [PictureLayer] objects it added to the tree to highlight
  /// bad nodes. These layers should be removed from the tree after building the
  /// [Scene].
  List<PictureLayer> _debugCheckElevations() {
    final List<PhysicalModelLayer> physicalModelLayers = depthFirstIterateChildren().whereType<PhysicalModelLayer>().toList();
    final List<PictureLayer> addedLayers = <PictureLayer>[];

    for (int i = 0; i < physicalModelLayers.length; i++) {
      final PhysicalModelLayer physicalModelLayer = physicalModelLayers[i];
      assert(
        physicalModelLayer.lastChild?.debugCreator != physicalModelLayer,
        'debugCheckElevations has either already visited this layer or failed '
        'to remove the added picture from it.',
      );
      double accumulatedElevation = physicalModelLayer.elevation;
      Layer ancestor = physicalModelLayer.parent;
      while (ancestor != null) {
        if (ancestor is PhysicalModelLayer) {
          accumulatedElevation += ancestor.elevation;
        }
        ancestor = ancestor.parent;
      }
      for (int j = 0; j <= i; j++) {
        final PhysicalModelLayer predecessor = physicalModelLayers[j];
        double predecessorAccumulatedElevation = predecessor.elevation;
        ancestor = predecessor.parent;
        while (ancestor != null) {
          if (ancestor == predecessor) {
            continue;
          }
          if (ancestor is PhysicalModelLayer) {
            predecessorAccumulatedElevation += ancestor.elevation;
          }
          ancestor = ancestor.parent;
        }
        if (predecessorAccumulatedElevation <= accumulatedElevation) {
          continue;
        }
        final Path intersection = Path.combine(
          PathOperation.intersect,
          predecessor._debugTransformedClipPath,
          physicalModelLayer._debugTransformedClipPath,
        );
        if (intersection != null && intersection.computeMetrics().any((ui.PathMetric metric) => metric.length > 0)) {
          addedLayers.addAll(_processConflictingPhysicalLayers(predecessor, physicalModelLayer));
        }
      }
    }
    return addedLayers;
  }

595 596 597 598 599 600 601 602 603 604 605
  @override
  void updateSubtreeNeedsAddToScene() {
    super.updateSubtreeNeedsAddToScene();
    Layer child = firstChild;
    while (child != null) {
      child.updateSubtreeNeedsAddToScene();
      _subtreeNeedsAddToScene = _subtreeNeedsAddToScene || child._subtreeNeedsAddToScene;
      child = child.nextSibling;
    }
  }

606 607
  @override
  S find<S>(Offset regionOffset) {
608 609 610 611 612 613 614
    Layer current = lastChild;
    while (current != null) {
      final Object value = current.find<S>(regionOffset);
      if (value != null) {
        return value;
      }
      current = current.previousSibling;
615
    }
616
    return null;
617 618 619
  }

  @override
620 621
  Iterable<S> findAll<S>(Offset regionOffset) {
    Iterable<S> result = Iterable<S>.empty();
622
    if (firstChild == null)
623
      return result;
624 625
    Layer child = lastChild;
    while (true) {
626
      result = result.followedBy(child.findAll<S>(regionOffset));
627 628 629
      if (child == firstChild)
        break;
      child = child.previousSibling;
630
    }
631
    return result;
632 633
  }

634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
  @override
  void attach(Object owner) {
    super.attach(owner);
    Layer child = firstChild;
    while (child != null) {
      child.attach(owner);
      child = child.nextSibling;
    }
  }

  @override
  void detach() {
    super.detach();
    Layer child = firstChild;
    while (child != null) {
      child.detach();
      child = child.nextSibling;
    }
  }

  /// Adds the given layer to the end of this layer's child list.
655
  void append(Layer child) {
656
    assert(child != this);
657 658 659 660 661 662
    assert(child != firstChild);
    assert(child != lastChild);
    assert(child.parent == null);
    assert(!child.attached);
    assert(child.nextSibling == null);
    assert(child.previousSibling == null);
663 664 665 666 667 668
    assert(() {
      Layer node = this;
      while (node.parent != null)
        node = node.parent;
      assert(node != child); // indicates we are about to create a cycle
      return true;
669
    }());
670 671 672 673
    adoptChild(child);
    child._previousSibling = lastChild;
    if (lastChild != null)
      lastChild._nextSibling = child;
674
    _lastChild = child;
675
    _firstChild ??= child;
676
    assert(child.attached == attached);
677 678
  }

679 680 681 682 683 684
  // Implementation of [Layer.remove].
  void _removeChild(Layer child) {
    assert(child.parent == this);
    assert(child.attached == attached);
    assert(_debugUltimatePreviousSiblingOf(child, equals: firstChild));
    assert(_debugUltimateNextSiblingOf(child, equals: lastChild));
685 686 687 688
    if (child._previousSibling == null) {
      assert(_firstChild == child);
      _firstChild = child._nextSibling;
    } else {
689
      child._previousSibling._nextSibling = child.nextSibling;
690 691
    }
    if (child._nextSibling == null) {
692 693
      assert(lastChild == child);
      _lastChild = child.previousSibling;
694
    } else {
695
      child.nextSibling._previousSibling = child.previousSibling;
696
    }
697 698 699 700 701
    assert((firstChild == null) == (lastChild == null));
    assert(firstChild == null || firstChild.attached == attached);
    assert(lastChild == null || lastChild.attached == attached);
    assert(firstChild == null || _debugUltimateNextSiblingOf(firstChild, equals: lastChild));
    assert(lastChild == null || _debugUltimatePreviousSiblingOf(lastChild, equals: firstChild));
702 703
    child._previousSibling = null;
    child._nextSibling = null;
704 705
    dropChild(child);
    assert(!child.attached);
706
  }
Hixie's avatar
Hixie committed
707

708
  /// Removes all of this layer's children from its child list.
709
  void removeAllChildren() {
710
    Layer child = firstChild;
711
    while (child != null) {
712
      final Layer next = child.nextSibling;
713 714
      child._previousSibling = null;
      child._nextSibling = null;
715 716
      assert(child.attached == attached);
      dropChild(child);
717 718 719 720 721 722
      child = next;
    }
    _firstChild = null;
    _lastChild = null;
  }

723
  @override
724
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
725
    addChildrenToScene(builder, layerOffset);
726
    return null; // ContainerLayer does not have a corresponding engine layer
Hixie's avatar
Hixie committed
727 728
  }

729 730 731 732
  /// Uploads all of this layer's children to the engine.
  ///
  /// This method is typically used by [addToScene] to insert the children into
  /// the scene. Subclasses of [ContainerLayer] typically override [addToScene]
733
  /// to apply effects to the scene using the [SceneBuilder] API, then insert
734 735
  /// their children using [addChildrenToScene], then reverse the aforementioned
  /// effects before returning from [addToScene].
736
  void addChildrenToScene(ui.SceneBuilder builder, [ Offset childOffset = Offset.zero ]) {
737
    Layer child = firstChild;
Hixie's avatar
Hixie committed
738
    while (child != null) {
739 740 741 742 743
      if (childOffset == Offset.zero) {
        child._addToSceneWithRetainedRendering(builder);
      } else {
        child.addToScene(builder, childOffset);
      }
Hixie's avatar
Hixie committed
744 745 746
      child = child.nextSibling;
    }
  }
747

748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
  /// Applies the transform that would be applied when compositing the given
  /// child to the given matrix.
  ///
  /// Specifically, this should apply the transform that is applied to child's
  /// _origin_. When using [applyTransform] with a chain of layers, results will
  /// be unreliable unless the deepest layer in the chain collapses the
  /// `layerOffset` in [addToScene] to zero, meaning that it passes
  /// [Offset.zero] to its children, and bakes any incoming `layerOffset` into
  /// the [SceneBuilder] as (for instance) a transform (which is then also
  /// included in the transformation applied by [applyTransform]).
  ///
  /// For example, if [addToScene] applies the `layerOffset` and then
  /// passes [Offset.zero] to the children, then it should be included in the
  /// transform applied here, whereas if [addToScene] just passes the
  /// `layerOffset` to the child, then it should not be included in the
  /// transform applied here.
  ///
  /// This method is only valid immediately after [addToScene] has been called,
  /// before any of the properties have been changed.
  ///
  /// The default implementation does nothing, since [ContainerLayer], by
769
  /// default, composites its children at the origin of the [ContainerLayer]
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
  /// itself.
  ///
  /// The `child` argument should generally not be null, since in principle a
  /// layer could transform each child independently. However, certain layers
  /// may explicitly allow null as a value, for example if they know that they
  /// transform all their children identically.
  ///
  /// The `transform` argument must not be null.
  ///
  /// Used by [FollowerLayer] to transform its child to a [LeaderLayer]'s
  /// position.
  void applyTransform(Layer child, Matrix4 transform) {
    assert(child != null);
    assert(transform != null);
  }

786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
  /// Returns the descendants of this layer in depth first order.
  @visibleForTesting
  List<Layer> depthFirstIterateChildren() {
    if (firstChild == null)
      return <Layer>[];
    final List<Layer> children = <Layer>[];
    Layer child = firstChild;
    while(child != null) {
      children.add(child);
      if (child is ContainerLayer) {
        children.addAll(child.depthFirstIterateChildren());
      }
      child = child.nextSibling;
    }
    return children;
  }

803
  @override
804 805
  List<DiagnosticsNode> debugDescribeChildren() {
    final List<DiagnosticsNode> children = <DiagnosticsNode>[];
806
    if (firstChild == null)
807
      return children;
808 809
    Layer child = firstChild;
    int count = 1;
810 811 812 813
    while (true) {
      children.add(child.toDiagnosticsNode(name: 'child $count'));
      if (child == lastChild)
        break;
814 815 816
      count += 1;
      child = child.nextSibling;
    }
817
    return children;
818
  }
Hixie's avatar
Hixie committed
819 820
}

821 822 823 824 825 826 827 828 829
/// A layer that is displayed at an offset from its parent layer.
///
/// Offset layers are key to efficient repainting because they are created by
/// repaint boundaries in the [RenderObject] tree (see
/// [RenderObject.isRepaintBoundary]). When a render object that is a repaint
/// boundary is asked to paint at given offset in a [PaintingContext], the
/// render object first checks whether it needs to repaint itself. If not, it
/// reuses its existing [OffsetLayer] (and its entire subtree) by mutating its
/// [offset] property, cutting off the paint walk.
830
class OffsetLayer extends ContainerLayer {
831 832
  /// Creates an offset layer.
  ///
833 834
  /// By default, [offset] is zero. It must be non-null before the compositing
  /// phase of the pipeline.
835
  OffsetLayer({ Offset offset = Offset.zero }) : _offset = offset;
836 837

  /// Offset from parent in the parent's coordinate system.
838 839 840
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
841 842 843
  ///
  /// The [offset] property must be non-null before the compositing phase of the
  /// pipeline.
844 845 846 847 848 849 850 851
  Offset get offset => _offset;
  Offset _offset;
  set offset(Offset value) {
    if (value != _offset) {
      markNeedsAddToScene();
    }
    _offset = value;
  }
852

853 854 855 856 857
  @override
  S find<S>(Offset regionOffset) {
    return super.find<S>(regionOffset - offset);
  }

858 859 860 861 862
  @override
  Iterable<S> findAll<S>(Offset regionOffset) {
    return super.findAll<S>(regionOffset - offset);
  }

863
  @override
864 865 866 867 868 869
  void applyTransform(Layer child, Matrix4 transform) {
    assert(child != null);
    assert(transform != null);
    transform.multiply(Matrix4.translationValues(offset.dx, offset.dy, 0.0));
  }

870 871 872
  /// Consider this layer as the root and build a scene (a tree of layers)
  /// in the engine.
  ui.Scene buildScene(ui.SceneBuilder builder) {
873 874 875 876 877 878 879
    List<PictureLayer> temporaryLayers;
    assert(() {
      if (debugCheckElevationsEnabled) {
        temporaryLayers = _debugCheckElevations();
      }
      return true;
    }());
880 881
    updateSubtreeNeedsAddToScene();
    addToScene(builder);
882 883 884 885 886 887 888 889 890 891 892 893 894 895
    final ui.Scene scene = builder.build();
    assert(() {
      // We should remove any layers that got added to highlight the incorrect
      // PhysicalModelLayers. If we don't, we'll end up adding duplicate layers
      // or potentially leaving a physical model that is now correct highlighted
      // in red.
      if (temporaryLayers != null) {
        for (PictureLayer temporaryLayer in temporaryLayers) {
          temporaryLayer.remove();
        }
      }
      return true;
    }());
    return scene;
896 897
  }

898
  @override
899
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
900 901 902 903 904
    // Skia has a fast path for concatenating scale/translation only matrices.
    // Hence pushing a translation-only transform layer should be fast. For
    // retained rendering, we don't want to push the offset down to each leaf
    // node. Otherwise, changing an offset layer on the very high level could
    // cascade the change to too many leaves.
905
    final ui.EngineLayer engineLayer = builder.pushOffset(layerOffset.dx + offset.dx, layerOffset.dy + offset.dy);
906 907
    addChildrenToScene(builder);
    builder.pop();
908
    return engineLayer;
909 910
  }

911
  @override
912 913
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
914
    properties.add(DiagnosticsProperty<Offset>('offset', offset));
915
  }
916 917 918

  /// Capture an image of the current state of this layer and its children.
  ///
919 920 921
  /// The returned [ui.Image] has uncompressed raw RGBA bytes, will be offset
  /// by the top-left corner of [bounds], and have dimensions equal to the size
  /// of [bounds] multiplied by [pixelRatio].
922 923 924 925 926 927 928 929 930 931 932
  ///
  /// The [pixelRatio] describes the scale between the logical pixels and the
  /// size of the output image. It is independent of the
  /// [window.devicePixelRatio] for the device, so specifying 1.0 (the default)
  /// will give you a 1:1 mapping between logical pixels and the output pixels
  /// in the image.
  ///
  /// See also:
  ///
  ///  * [RenderRepaintBoundary.toImage] for a similar API at the render object level.
  ///  * [dart:ui.Scene.toImage] for more information about the image returned.
933
  Future<ui.Image> toImage(Rect bounds, { double pixelRatio = 1.0 }) async {
934
    assert(bounds != null);
935
    assert(pixelRatio != null);
936 937
    final ui.SceneBuilder builder = ui.SceneBuilder();
    final Matrix4 transform = Matrix4.translationValues(
938 939 940 941
      (-bounds.left  - offset.dx) * pixelRatio,
      (-bounds.top - offset.dy) * pixelRatio,
      0.0,
    );
942
    transform.scale(pixelRatio, pixelRatio);
943
    builder.pushTransform(transform.storage);
944
    final ui.Scene scene = buildScene(builder);
945 946 947 948
    try {
      // Size is rounded up to the next pixel to make sure we don't clip off
      // anything.
      return await scene.toImage(
949 950
        (pixelRatio * bounds.width).ceil(),
        (pixelRatio * bounds.height).ceil(),
951 952 953 954 955
      );
    } finally {
      scene.dispose();
    }
  }
956 957
}

958
/// A composite layer that clips its children using a rectangle.
959 960 961 962
///
/// When debugging, setting [debugDisableClipLayers] to true will cause this
/// layer to be skipped (directly replaced by its children). This can be helpful
/// to track down the cause of performance problems.
Hixie's avatar
Hixie committed
963
class ClipRectLayer extends ContainerLayer {
964 965 966 967
  /// Creates a layer with a rectangular clip.
  ///
  /// The [clipRect] property must be non-null before the compositing phase of
  /// the pipeline.
968 969 970 971 972 973 974
  ClipRectLayer({
    @required Rect clipRect,
    Clip clipBehavior = Clip.hardEdge,
  }) : _clipRect = clipRect,
       _clipBehavior = clipBehavior,
       assert(clipBehavior != null),
       assert(clipBehavior != Clip.none);
Hixie's avatar
Hixie committed
975

976 977 978 979
  /// The rectangle to clip in the parent's coordinate system.
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
980 981 982 983 984 985 986 987
  Rect get clipRect => _clipRect;
  Rect _clipRect;
  set clipRect(Rect value) {
    if (value != _clipRect) {
      _clipRect = value;
      markNeedsAddToScene();
    }
  }
Hixie's avatar
Hixie committed
988

989
  /// {@template flutter.clipper.clipBehavior}
990
  /// Controls how to clip (defaults to [Clip.hardEdge]).
991 992 993
  ///
  /// [Clip.none] is not allowed here.
  /// {@endtemplate}
994 995 996 997 998
  Clip get clipBehavior => _clipBehavior;
  Clip _clipBehavior;
  set clipBehavior(Clip value) {
    assert(value != null);
    assert(value != Clip.none);
999 1000 1001 1002
    if (value != _clipBehavior) {
      _clipBehavior = value;
      markNeedsAddToScene();
    }
1003 1004
  }

1005 1006 1007 1008 1009 1010 1011
  @override
  S find<S>(Offset regionOffset) {
    if (!clipRect.contains(regionOffset))
      return null;
    return super.find<S>(regionOffset);
  }

1012
  @override
1013
  Iterable<S> findAll<S>(Offset regionOffset) {
1014
    if (!clipRect.contains(regionOffset))
1015 1016
      return Iterable<S>.empty();
    return super.findAll<S>(regionOffset);
1017 1018
  }

1019
  @override
1020
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
1021 1022 1023 1024 1025
    bool enabled = true;
    assert(() {
      enabled = !debugDisableClipLayers;
      return true;
    }());
1026 1027 1028 1029
    if (enabled) {
      final Rect shiftedClipRect = layerOffset == Offset.zero ? clipRect : clipRect.shift(layerOffset);
      builder.pushClipRect(shiftedClipRect, clipBehavior: clipBehavior);
    }
1030
    addChildrenToScene(builder, layerOffset);
1031
    if (enabled)
1032
      builder.pop();
1033
    return null; // this does not return an engine layer yet.
Hixie's avatar
Hixie committed
1034 1035
  }

1036
  @override
1037 1038
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1039
    properties.add(DiagnosticsProperty<Rect>('clipRect', clipRect));
1040
  }
1041
}
Hixie's avatar
Hixie committed
1042

1043
/// A composite layer that clips its children using a rounded rectangle.
1044 1045 1046 1047
///
/// When debugging, setting [debugDisableClipLayers] to true will cause this
/// layer to be skipped (directly replaced by its children). This can be helpful
/// to track down the cause of performance problems.
Hixie's avatar
Hixie committed
1048
class ClipRRectLayer extends ContainerLayer {
1049 1050 1051 1052
  /// Creates a layer with a rounded-rectangular clip.
  ///
  /// The [clipRRect] property must be non-null before the compositing phase of
  /// the pipeline.
1053 1054 1055 1056 1057 1058 1059
  ClipRRectLayer({
    @required RRect clipRRect,
    Clip clipBehavior = Clip.antiAlias,
  }) : _clipRRect = clipRRect,
       _clipBehavior = clipBehavior,
       assert(clipBehavior != null),
       assert(clipBehavior != Clip.none);
1060

1061 1062 1063 1064
  /// The rounded-rect to clip in the parent's coordinate system.
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1065 1066 1067 1068 1069 1070 1071 1072
  RRect get clipRRect => _clipRRect;
  RRect _clipRRect;
  set clipRRect(RRect value) {
    if (value != _clipRRect) {
      _clipRRect = value;
      markNeedsAddToScene();
    }
  }
Hixie's avatar
Hixie committed
1073

1074 1075 1076 1077 1078 1079
  /// {@macro flutter.clipper.clipBehavior}
  Clip get clipBehavior => _clipBehavior;
  Clip _clipBehavior;
  set clipBehavior(Clip value) {
    assert(value != null);
    assert(value != Clip.none);
1080 1081 1082 1083
    if (value != _clipBehavior) {
      _clipBehavior = value;
      markNeedsAddToScene();
    }
1084 1085
  }

1086 1087 1088 1089 1090 1091 1092
  @override
  S find<S>(Offset regionOffset) {
    if (!clipRRect.contains(regionOffset))
      return null;
    return super.find<S>(regionOffset);
  }

1093
  @override
1094
  Iterable<S> findAll<S>(Offset regionOffset) {
1095
    if (!clipRRect.contains(regionOffset))
1096 1097
      return Iterable<S>.empty();
    return super.findAll<S>(regionOffset);
1098 1099
  }

1100
  @override
1101
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
1102 1103 1104 1105 1106
    bool enabled = true;
    assert(() {
      enabled = !debugDisableClipLayers;
      return true;
    }());
1107 1108 1109 1110
    if (enabled) {
      final RRect shiftedClipRRect = layerOffset == Offset.zero ? clipRRect : clipRRect.shift(layerOffset);
      builder.pushClipRRect(shiftedClipRRect, clipBehavior: clipBehavior);
    }
1111
    addChildrenToScene(builder, layerOffset);
1112
    if (enabled)
1113
      builder.pop();
1114
    return null; // this does not return an engine layer yet.
Hixie's avatar
Hixie committed
1115
  }
1116

1117
  @override
1118 1119
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1120
    properties.add(DiagnosticsProperty<RRect>('clipRRect', clipRRect));
1121
  }
Hixie's avatar
Hixie committed
1122 1123
}

1124
/// A composite layer that clips its children using a path.
1125 1126 1127 1128
///
/// When debugging, setting [debugDisableClipLayers] to true will cause this
/// layer to be skipped (directly replaced by its children). This can be helpful
/// to track down the cause of performance problems.
Hixie's avatar
Hixie committed
1129
class ClipPathLayer extends ContainerLayer {
1130 1131 1132 1133
  /// Creates a layer with a path-based clip.
  ///
  /// The [clipPath] property must be non-null before the compositing phase of
  /// the pipeline.
1134 1135 1136 1137 1138 1139 1140
  ClipPathLayer({
    @required Path clipPath,
    Clip clipBehavior = Clip.antiAlias,
  }) : _clipPath = clipPath,
       _clipBehavior = clipBehavior,
       assert(clipBehavior != null),
       assert(clipBehavior != Clip.none);
1141

1142 1143 1144 1145
  /// The path to clip in the parent's coordinate system.
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1146 1147 1148 1149 1150 1151 1152 1153
  Path get clipPath => _clipPath;
  Path _clipPath;
  set clipPath(Path value) {
    if (value != _clipPath) {
      _clipPath = value;
      markNeedsAddToScene();
    }
  }
Hixie's avatar
Hixie committed
1154

1155 1156 1157 1158 1159 1160
  /// {@macro flutter.clipper.clipBehavior}
  Clip get clipBehavior => _clipBehavior;
  Clip _clipBehavior;
  set clipBehavior(Clip value) {
    assert(value != null);
    assert(value != Clip.none);
1161 1162 1163 1164
    if (value != _clipBehavior) {
      _clipBehavior = value;
      markNeedsAddToScene();
    }
1165 1166
  }

1167 1168 1169 1170 1171 1172 1173
  @override
  S find<S>(Offset regionOffset) {
    if (!clipPath.contains(regionOffset))
      return null;
    return super.find<S>(regionOffset);
  }

1174
  @override
1175
  Iterable<S> findAll<S>(Offset regionOffset) {
1176
    if (!clipPath.contains(regionOffset))
1177 1178
      return Iterable<S>.empty();
    return super.findAll<S>(regionOffset);
1179 1180
  }

1181
  @override
1182
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
1183 1184 1185 1186 1187
    bool enabled = true;
    assert(() {
      enabled = !debugDisableClipLayers;
      return true;
    }());
1188 1189 1190 1191
    if (enabled) {
      final Path shiftedPath = layerOffset == Offset.zero ? clipPath : clipPath.shift(layerOffset);
      builder.pushClipPath(shiftedPath, clipBehavior: clipBehavior);
    }
1192
    addChildrenToScene(builder, layerOffset);
1193
    if (enabled)
1194
      builder.pop();
1195
    return null; // this does not return an engine layer yet.
Hixie's avatar
Hixie committed
1196
  }
1197 1198
}

1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237
/// A composite layer that applies a [ColorFilter] to its children.
class ColorFilterLayer extends ContainerLayer {
  /// Creates a layer that applies a [ColorFilter] to its children.
  ///
  /// The [ColorFilter] property must be non-null before the compositing phase
  /// of the pipeline.
  ColorFilterLayer({
    @required ColorFilter colorFilter,
  }) : _colorFilter = colorFilter,
       assert(colorFilter != null);

  /// The color filter to apply to children.
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
  ColorFilter get colorFilter => _colorFilter;
  ColorFilter _colorFilter;
  set colorFilter(ColorFilter value) {
    if (value != _colorFilter) {
      _colorFilter = value;
      markNeedsAddToScene();
    }
  }

  @override
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
    builder.pushColorFilter(colorFilter);
    addChildrenToScene(builder, layerOffset);
    builder.pop();
    return null; // this does not return an engine layer yet.
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<ColorFilter>('colorFilter', colorFilter));
  }
}

1238 1239 1240 1241 1242
/// A composited layer that applies a given transformation matrix to its
/// children.
///
/// This class inherits from [OffsetLayer] to make it one of the layers that
/// can be used at the root of a [RenderObject] hierarchy.
1243
class TransformLayer extends OffsetLayer {
1244 1245
  /// Creates a transform layer.
  ///
1246 1247
  /// The [transform] and [offset] properties must be non-null before the
  /// compositing phase of the pipeline.
1248
  TransformLayer({ Matrix4 transform, Offset offset = Offset.zero })
1249 1250
    : assert(transform.storage.every((double value) => value.isFinite)),
      _transform = transform,
1251
      super(offset: offset);
1252

1253 1254 1255 1256
  /// The matrix to apply.
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1257 1258 1259 1260 1261
  ///
  /// This transform is applied before [offset], if both are set.
  ///
  /// The [transform] property must be non-null before the compositing phase of
  /// the pipeline.
1262 1263 1264 1265 1266 1267
  Matrix4 get transform => _transform;
  Matrix4 _transform;
  set transform(Matrix4 value) {
    if (value == _transform)
      return;
    _transform = value;
1268
    _inverseDirty = true;
1269
  }
1270

1271
  Matrix4 _lastEffectiveTransform;
1272
  Matrix4 _invertedTransform;
1273
  bool _inverseDirty = true;
1274

1275
  @override
1276
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
1277 1278 1279
    _lastEffectiveTransform = transform;
    final Offset totalOffset = offset + layerOffset;
    if (totalOffset != Offset.zero) {
1280
      _lastEffectiveTransform = Matrix4.translationValues(totalOffset.dx, totalOffset.dy, 0.0)
1281
        ..multiply(_lastEffectiveTransform);
1282
    }
1283
    builder.pushTransform(_lastEffectiveTransform.storage);
1284
    addChildrenToScene(builder);
1285
    builder.pop();
1286
    return null; // this does not return an engine layer yet.
1287
  }
1288

1289
  Offset _transformOffset(Offset regionOffset) {
1290
    if (_inverseDirty) {
1291 1292 1293
      _invertedTransform = Matrix4.tryInvert(
        PointerEvent.removePerspectiveTransform(transform)
      );
1294 1295 1296
      _inverseDirty = false;
    }
    if (_invertedTransform == null)
1297
      return null;
1298
    final Vector4 vector = Vector4(regionOffset.dx, regionOffset.dy, 0.0, 1.0);
1299
    final Vector4 result = _invertedTransform.transform(vector);
1300 1301 1302 1303 1304 1305 1306 1307 1308 1309
    return Offset(result[0], result[1]);
  }

  @override
  S find<S>(Offset regionOffset) {
    final Offset transformedOffset = _transformOffset(regionOffset);
    return transformedOffset == null ? null : super.find<S>(transformedOffset);
  }

  @override
1310
  Iterable<S> findAll<S>(Offset regionOffset) {
1311 1312
    final Offset transformedOffset = _transformOffset(regionOffset);
    if (transformedOffset == null) {
1313
      return Iterable<S>.empty();
1314
    }
1315
    return super.findAll<S>(transformedOffset);
1316 1317
  }

1318 1319 1320 1321
  @override
  void applyTransform(Layer child, Matrix4 transform) {
    assert(child != null);
    assert(transform != null);
1322 1323 1324 1325 1326 1327
    assert(_lastEffectiveTransform != null || this.transform != null);
    if (_lastEffectiveTransform == null) {
      transform.multiply(this.transform);
    } else {
      transform.multiply(_lastEffectiveTransform);
    }
1328 1329
  }

1330
  @override
1331 1332
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1333
    properties.add(TransformProperty('transform', transform));
1334
  }
1335 1336
}

1337
/// A composited layer that makes its children partially transparent.
1338 1339 1340 1341
///
/// When debugging, setting [debugDisableOpacityLayers] to true will cause this
/// layer to be skipped (directly replaced by its children). This can be helpful
/// to track down the cause of performance problems.
1342 1343 1344
///
/// Try to avoid an [OpacityLayer] with no children. Remove that layer if
/// possible to save some tree walks.
1345
class OpacityLayer extends ContainerLayer {
1346 1347 1348 1349
  /// Creates an opacity layer.
  ///
  /// The [alpha] property must be non-null before the compositing phase of
  /// the pipeline.
1350 1351 1352 1353 1354
  OpacityLayer({
    @required int alpha,
    Offset offset = Offset.zero,
  }) : _alpha = alpha,
       _offset = offset;
1355

1356
  /// The amount to multiply into the alpha channel.
1357 1358 1359
  ///
  /// The opacity is expressed as an integer from 0 to 255, where 0 is fully
  /// transparent and 255 is fully opaque.
1360 1361 1362
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
  int get alpha => _alpha;
  int _alpha;
  set alpha(int value) {
    if (value != _alpha) {
      _alpha = value;
      markNeedsAddToScene();
    }
  }

  /// Offset from parent in the parent's coordinate system.
  Offset get offset => _offset;
  Offset _offset;
  set offset(Offset value) {
    if (value != _offset) {
      _offset = value;
      markNeedsAddToScene();
    }
  }
1381 1382 1383 1384 1385 1386 1387

  @override
  void applyTransform(Layer child, Matrix4 transform) {
    assert(child != null);
    assert(transform != null);
    transform.translate(offset.dx, offset.dy);
  }
1388

1389
  @override
1390
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
1391
    bool enabled = firstChild != null;  // don't add this layer if there's no child
1392
    assert(() {
1393
      enabled = enabled && !debugDisableOpacityLayers;
1394 1395 1396
      return true;
    }());
    if (enabled)
1397 1398
      builder.pushOpacity(alpha, offset: offset + layerOffset);
    addChildrenToScene(builder);
1399
    if (enabled)
1400
      builder.pop();
1401
    return null; // this does not return an engine layer yet.
1402
  }
1403

1404
  @override
1405 1406
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1407
    properties.add(IntProperty('alpha', alpha));
1408
    properties.add(DiagnosticsProperty<Offset>('offset', offset));
1409
  }
1410
}
1411

1412
/// A composited layer that applies a shader to its children.
1413
class ShaderMaskLayer extends ContainerLayer {
1414 1415
  /// Creates a shader mask layer.
  ///
1416
  /// The [shader], [maskRect], and [blendMode] properties must be non-null
1417
  /// before the compositing phase of the pipeline.
1418 1419 1420 1421 1422 1423 1424
  ShaderMaskLayer({
    @required Shader shader,
    @required Rect maskRect,
    @required BlendMode blendMode,
  }) : _shader = shader,
       _maskRect = maskRect,
       _blendMode = blendMode;
1425 1426

  /// The shader to apply to the children.
1427 1428 1429
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1430 1431 1432 1433 1434 1435 1436 1437
  Shader get shader => _shader;
  Shader _shader;
  set shader(Shader value) {
    if (value != _shader) {
      _shader = value;
      markNeedsAddToScene();
    }
  }
1438 1439

  /// The size of the shader.
1440 1441 1442
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1443 1444 1445 1446 1447 1448 1449 1450
  Rect get maskRect => _maskRect;
  Rect _maskRect;
  set maskRect(Rect value) {
    if (value != _maskRect) {
      _maskRect = value;
      markNeedsAddToScene();
    }
  }
1451

1452
  /// The blend mode to apply when blending the shader with the children.
1453 1454 1455
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1456 1457 1458 1459 1460 1461 1462 1463
  BlendMode get blendMode => _blendMode;
  BlendMode _blendMode;
  set blendMode(BlendMode value) {
    if (value != _blendMode) {
      _blendMode = value;
      markNeedsAddToScene();
    }
  }
1464

1465
  @override
1466
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
1467 1468
    final Rect shiftedMaskRect = layerOffset == Offset.zero ? maskRect : maskRect.shift(layerOffset);
    builder.pushShaderMask(shader, shiftedMaskRect, blendMode);
1469
    addChildrenToScene(builder, layerOffset);
1470
    builder.pop();
1471
    return null; // this does not return an engine layer yet.
1472 1473
  }

1474
  @override
1475 1476
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1477 1478 1479
    properties.add(DiagnosticsProperty<Shader>('shader', shader));
    properties.add(DiagnosticsProperty<Rect>('maskRect', maskRect));
    properties.add(DiagnosticsProperty<BlendMode>('blendMode', blendMode));
1480 1481
  }
}
1482 1483 1484

/// A composited layer that applies a filter to the existing contents of the scene.
class BackdropFilterLayer extends ContainerLayer {
1485 1486 1487 1488
  /// Creates a backdrop filter layer.
  ///
  /// The [filter] property must be non-null before the compositing phase of the
  /// pipeline.
1489
  BackdropFilterLayer({ @required ui.ImageFilter filter }) : _filter = filter;
1490 1491

  /// The filter to apply to the existing contents of the scene.
1492 1493 1494
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1495 1496 1497 1498 1499 1500 1501 1502
  ui.ImageFilter get filter => _filter;
  ui.ImageFilter _filter;
  set filter(ui.ImageFilter value) {
    if (value != _filter) {
      _filter = value;
      markNeedsAddToScene();
    }
  }
1503 1504

  @override
1505
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
1506 1507 1508
    builder.pushBackdropFilter(filter);
    addChildrenToScene(builder, layerOffset);
    builder.pop();
1509
    return null; // this does not return an engine layer yet.
1510 1511
  }
}
1512

1513 1514 1515
/// A composited layer that uses a physical model to producing lighting effects.
///
/// For example, the layer casts a shadow according to its geometry and the
1516
/// relative position of lights and other physically modeled objects in the
1517
/// scene.
1518 1519 1520 1521
///
/// When debugging, setting [debugDisablePhysicalShapeLayers] to true will cause this
/// layer to be skipped (directly replaced by its children). This can be helpful
/// to track down the cause of performance problems.
1522
class PhysicalModelLayer extends ContainerLayer {
1523 1524 1525
  /// Creates a composited layer that uses a physical model to producing
  /// lighting effects.
  ///
1526
  /// The [clipPath], [elevation], and [color] arguments must not be null.
1527
  PhysicalModelLayer({
1528 1529 1530 1531 1532
    @required Path clipPath,
    Clip clipBehavior = Clip.none,
    @required double elevation,
    @required Color color,
    @required Color shadowColor,
1533
  }) : assert(clipPath != null),
1534
       assert(clipBehavior != null),
1535
       assert(elevation != null),
1536
       assert(color != null),
1537 1538 1539 1540 1541 1542
       assert(shadowColor != null),
       _clipPath = clipPath,
       _clipBehavior = clipBehavior,
       _elevation = elevation,
       _color = color,
       _shadowColor = shadowColor;
1543

1544
  /// The path to clip in the parent's coordinate system.
1545 1546 1547
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1548 1549 1550 1551 1552 1553 1554 1555
  Path get clipPath => _clipPath;
  Path _clipPath;
  set clipPath(Path value) {
    if (value != _clipPath) {
      _clipPath = value;
      markNeedsAddToScene();
    }
  }
1556

1557 1558 1559 1560 1561 1562 1563 1564 1565 1566
  Path get _debugTransformedClipPath {
    ContainerLayer ancestor = parent;
    final Matrix4 matrix = Matrix4.identity();
    while (ancestor != null && ancestor.parent != null) {
      ancestor.applyTransform(this, matrix);
      ancestor = ancestor.parent;
    }
    return clipPath.transform(matrix.storage);
  }

1567
  /// {@macro flutter.widgets.Clip}
1568 1569 1570
  Clip get clipBehavior => _clipBehavior;
  Clip _clipBehavior;
  set clipBehavior(Clip value) {
1571
    assert(value != null);
1572 1573 1574 1575 1576
    if (value != _clipBehavior) {
      _clipBehavior = value;
      markNeedsAddToScene();
    }
  }
1577

1578
  /// The z-coordinate at which to place this physical object.
1579 1580 1581
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1582 1583 1584 1585 1586 1587
  ///
  /// In tests, the [debugDisableShadows] flag is set to true by default.
  /// Several widgets and render objects force all elevations to zero when this
  /// flag is set. For this reason, this property will often be set to zero in
  /// tests even if the layer should be raised. To verify the actual value,
  /// consider setting [debugDisableShadows] to false in your test.
1588 1589 1590 1591 1592 1593 1594 1595
  double get elevation => _elevation;
  double _elevation;
  set elevation(double value) {
    if (value != _elevation) {
      _elevation = value;
      markNeedsAddToScene();
    }
  }
1596 1597

  /// The background color.
1598 1599 1600
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1601 1602 1603 1604 1605 1606 1607 1608
  Color get color => _color;
  Color _color;
  set color(Color value) {
    if (value != _color) {
      _color = value;
      markNeedsAddToScene();
    }
  }
1609

1610
  /// The shadow color.
1611 1612 1613 1614 1615 1616 1617 1618
  Color get shadowColor => _shadowColor;
  Color _shadowColor;
  set shadowColor(Color value) {
    if (value != _shadowColor) {
      _shadowColor = value;
      markNeedsAddToScene();
    }
  }
1619

1620 1621
  @override
  S find<S>(Offset regionOffset) {
1622
    if (!clipPath.contains(regionOffset))
1623
      return null;
1624
    return super.find<S>(regionOffset);
1625 1626 1627
  }

  @override
1628
  Iterable<S> findAll<S>(Offset regionOffset) {
1629
    if (!clipPath.contains(regionOffset))
1630 1631
      return Iterable<S>.empty();
    return super.findAll<S>(regionOffset);
1632 1633
  }

1634
  @override
1635
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
1636
    ui.EngineLayer engineLayer;
1637 1638 1639 1640 1641 1642
    bool enabled = true;
    assert(() {
      enabled = !debugDisablePhysicalShapeLayers;
      return true;
    }());
    if (enabled) {
1643
      engineLayer = builder.pushPhysicalShape(
1644
        path: layerOffset == Offset.zero ? clipPath : clipPath.shift(layerOffset),
1645 1646
        elevation: elevation,
        color: color,
1647
        shadowColor: shadowColor,
1648
        clipBehavior: clipBehavior,
1649
      );
1650
    }
1651
    addChildrenToScene(builder, layerOffset);
1652
    if (enabled)
1653
      builder.pop();
1654
    return engineLayer;
1655 1656 1657
  }

  @override
1658 1659
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1660
    properties.add(DoubleProperty('elevation', elevation));
1661
    properties.add(ColorProperty('color', color));
1662 1663
  }
}
1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682

/// An object that a [LeaderLayer] can register with.
///
/// An instance of this class should be provided as the [LeaderLayer.link] and
/// the [FollowerLayer.link] properties to cause the [FollowerLayer] to follow
/// the [LeaderLayer].
///
/// See also:
///
///  * [CompositedTransformTarget], the widget that creates a [LeaderLayer].
///  * [CompositedTransformFollower], the widget that creates a [FollowerLayer].
///  * [RenderLeaderLayer] and [RenderFollowerLayer], the corresponding
///    render objects.
class LayerLink {
  /// The currently-registered [LeaderLayer], if any.
  LeaderLayer get leader => _leader;
  LeaderLayer _leader;

  @override
1683
  String toString() => '${describeIdentity(this)}(${ _leader != null ? "<linked>" : "<dangling>" })';
1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699
}

/// A composited layer that can be followed by a [FollowerLayer].
///
/// This layer collapses the accumulated offset into a transform and passes
/// [Offset.zero] to its child layers in the [addToScene]/[addChildrenToScene]
/// methods, so that [applyTransform] will work reliably.
class LeaderLayer extends ContainerLayer {
  /// Creates a leader layer.
  ///
  /// The [link] property must not be null, and must not have been provided to
  /// any other [LeaderLayer] layers that are [attached] to the layer tree at
  /// the same time.
  ///
  /// The [offset] property must be non-null before the compositing phase of the
  /// pipeline.
1700
  LeaderLayer({ @required this.link, this.offset = Offset.zero }) : assert(link != null);
1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716

  /// The object with which this layer should register.
  ///
  /// The link will be established when this layer is [attach]ed, and will be
  /// cleared when this layer is [detach]ed.
  final LayerLink link;

  /// Offset from parent in the parent's coordinate system.
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
  ///
  /// The [offset] property must be non-null before the compositing phase of the
  /// pipeline.
  Offset offset;

1717 1718 1719 1720
  /// {@macro flutter.leaderFollower.alwaysNeedsAddToScene}
  @override
  bool get alwaysNeedsAddToScene => true;

1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743
  @override
  void attach(Object owner) {
    super.attach(owner);
    assert(link.leader == null);
    _lastOffset = null;
    link._leader = this;
  }

  @override
  void detach() {
    assert(link.leader == this);
    link._leader = null;
    _lastOffset = null;
    super.detach();
  }

  /// The offset the last time this layer was composited.
  ///
  /// This is reset to null when the layer is attached or detached, to help
  /// catch cases where the follower layer ends up before the leader layer, but
  /// not every case can be detected.
  Offset _lastOffset;

1744
  @override
1745 1746 1747 1748
  S find<S>(Offset regionOffset) => super.find<S>(regionOffset - offset);

  @override
  Iterable<S> findAll<S>(Offset regionOffset) => super.findAll<S>(regionOffset - offset);
1749

1750
  @override
1751
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
1752 1753 1754
    assert(offset != null);
    _lastOffset = offset + layerOffset;
    if (_lastOffset != Offset.zero)
1755
      builder.pushTransform(Matrix4.translationValues(_lastOffset.dx, _lastOffset.dy, 0.0).storage);
1756
    addChildrenToScene(builder);
1757 1758
    if (_lastOffset != Offset.zero)
      builder.pop();
1759
    return null; // this does not have an engine layer.
1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776
  }

  /// Applies the transform that would be applied when compositing the given
  /// child to the given matrix.
  ///
  /// See [ContainerLayer.applyTransform] for details.
  ///
  /// The `child` argument may be null, as the same transform is applied to all
  /// children.
  @override
  void applyTransform(Layer child, Matrix4 transform) {
    assert(_lastOffset != null);
    if (_lastOffset != Offset.zero)
      transform.translate(_lastOffset.dx, _lastOffset.dy);
  }

  @override
1777 1778
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1779 1780
    properties.add(DiagnosticsProperty<Offset>('offset', offset));
    properties.add(DiagnosticsProperty<LayerLink>('link', link));
1781 1782 1783 1784 1785 1786 1787 1788
  }
}

/// A composited layer that applies a transformation matrix to its children such
/// that they are positioned to match a [LeaderLayer].
///
/// If any of the ancestors of this layer have a degenerate matrix (e.g. scaling
/// by zero), then the [FollowerLayer] will not be able to transform its child
1789
/// to the coordinate space of the [LeaderLayer].
1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802
///
/// A [linkedOffset] property can be provided to further offset the child layer
/// from the leader layer, for example if the child is to follow the linked
/// layer at a distance rather than directly overlapping it.
class FollowerLayer extends ContainerLayer {
  /// Creates a follower layer.
  ///
  /// The [link] property must not be null.
  ///
  /// The [unlinkedOffset], [linkedOffset], and [showWhenUnlinked] properties
  /// must be non-null before the compositing phase of the pipeline.
  FollowerLayer({
    @required this.link,
1803 1804 1805
    this.showWhenUnlinked = true,
    this.unlinkedOffset = Offset.zero,
    this.linkedOffset = Offset.zero,
1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858
  }) : assert(link != null);

  /// The link to the [LeaderLayer].
  ///
  /// The same object should be provided to a [LeaderLayer] that is earlier in
  /// the layer tree. When this layer is composited, it will apply a transform
  /// that moves its children to match the position of the [LeaderLayer].
  final LayerLink link;

  /// Whether to show the layer's contents when the [link] does not point to a
  /// [LeaderLayer].
  ///
  /// When the layer is linked, children layers are positioned such that they
  /// have the same global position as the linked [LeaderLayer].
  ///
  /// When the layer is not linked, then: if [showWhenUnlinked] is true,
  /// children are positioned as if the [FollowerLayer] was a [ContainerLayer];
  /// if it is false, then children are hidden.
  ///
  /// The [showWhenUnlinked] property must be non-null before the compositing
  /// phase of the pipeline.
  bool showWhenUnlinked;

  /// Offset from parent in the parent's coordinate system, used when the layer
  /// is not linked to a [LeaderLayer].
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
  ///
  /// The [unlinkedOffset] property must be non-null before the compositing
  /// phase of the pipeline.
  ///
  /// See also:
  ///
  ///  * [linkedOffset], for when the layers are linked.
  Offset unlinkedOffset;

  /// Offset from the origin of the leader layer to the origin of the child
  /// layers, used when the layer is linked to a [LeaderLayer].
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
  ///
  /// The [linkedOffset] property must be non-null before the compositing phase
  /// of the pipeline.
  ///
  /// See also:
  ///
  ///  * [unlinkedOffset], for when the layer is not linked.
  Offset linkedOffset;

  Offset _lastOffset;
  Matrix4 _lastTransform;
1859
  Matrix4 _invertedTransform;
1860
  bool _inverseDirty = true;
1861

1862
  Offset _transformOffset<S>(Offset regionOffset) {
1863 1864 1865
    if (_inverseDirty) {
      _invertedTransform = Matrix4.tryInvert(getLastTransform());
      _inverseDirty = false;
1866
    }
1867 1868
    if (_invertedTransform == null)
      return null;
1869
    final Vector4 vector = Vector4(regionOffset.dx, regionOffset.dy, 0.0, 1.0);
1870
    final Vector4 result = _invertedTransform.transform(vector);
1871 1872 1873 1874 1875
    return Offset(result[0] - linkedOffset.dx, result[1] - linkedOffset.dy);
  }

  @override
  S find<S>(Offset regionOffset) {
1876 1877
    if (link.leader == null) {
      return showWhenUnlinked ? super.find<S>(regionOffset - unlinkedOffset) : null;
1878
    }
1879 1880
    final Offset transformedOffset = _transformOffset<S>(regionOffset);
    return transformedOffset == null ? null : super.find<S>(transformedOffset);
1881 1882 1883 1884 1885 1886 1887
  }

  @override
  Iterable<S> findAll<S>(Offset regionOffset) {
    if (link.leader == null) {
      return showWhenUnlinked ? super.findAll<S>(regionOffset - unlinkedOffset) : <S>[];
    }
1888 1889 1890 1891 1892
    final Offset transformedOffset = _transformOffset<S>(regionOffset);
    if (transformedOffset == null) {
      return <S>[];
    }
    return super.findAll<S>(transformedOffset);
1893
  }
1894 1895 1896 1897

  /// The transform that was used during the last composition phase.
  ///
  /// If the [link] was not linked to a [LeaderLayer], or if this layer has
1898
  /// a degenerate matrix applied, then this will be null.
1899 1900 1901 1902 1903
  ///
  /// This method returns a new [Matrix4] instance each time it is invoked.
  Matrix4 getLastTransform() {
    if (_lastTransform == null)
      return null;
1904
    final Matrix4 result = Matrix4.translationValues(-_lastOffset.dx, -_lastOffset.dy, 0.0);
1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916
    result.multiply(_lastTransform);
    return result;
  }

  /// Call [applyTransform] for each layer in the provided list.
  ///
  /// The list is in reverse order (deepest first). The first layer will be
  /// treated as the child of the second, and so forth. The first layer in the
  /// list won't have [applyTransform] called on it. The first layer may be
  /// null.
  Matrix4 _collectTransformForLayerChain(List<ContainerLayer> layers) {
    // Initialize our result matrix.
1917
    final Matrix4 result = Matrix4.identity();
1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935
    // Apply each layer to the matrix in turn, starting from the last layer,
    // and providing the previous layer as the child.
    for (int index = layers.length - 1; index > 0; index -= 1)
      layers[index].applyTransform(layers[index - 1], result);
    return result;
  }

  /// Populate [_lastTransform] given the current state of the tree.
  void _establishTransform() {
    assert(link != null);
    _lastTransform = null;
    // Check to see if we are linked.
    if (link.leader == null)
      return;
    // If we're linked, check the link is valid.
    assert(link.leader.owner == owner, 'Linked LeaderLayer anchor is not in the same layer tree as the FollowerLayer.');
    assert(link.leader._lastOffset != null, 'LeaderLayer anchor must come before FollowerLayer in paint order, but the reverse was true.');
    // Collect all our ancestors into a Set so we can recognize them.
1936
    final Set<Layer> ancestors = HashSet<Layer>();
1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968
    Layer ancestor = parent;
    while (ancestor != null) {
      ancestors.add(ancestor);
      ancestor = ancestor.parent;
    }
    // Collect all the layers from a hypothetical child (null) of the target
    // layer up to the common ancestor layer.
    ContainerLayer layer = link.leader;
    final List<ContainerLayer> forwardLayers = <ContainerLayer>[null, layer];
    do {
      layer = layer.parent;
      forwardLayers.add(layer);
    } while (!ancestors.contains(layer));
    ancestor = layer;
    // Collect all the layers from this layer up to the common ancestor layer.
    layer = this;
    final List<ContainerLayer> inverseLayers = <ContainerLayer>[layer];
    do {
      layer = layer.parent;
      inverseLayers.add(layer);
    } while (layer != ancestor);
    // Establish the forward and backward matrices given these lists of layers.
    final Matrix4 forwardTransform = _collectTransformForLayerChain(forwardLayers);
    final Matrix4 inverseTransform = _collectTransformForLayerChain(inverseLayers);
    if (inverseTransform.invert() == 0.0) {
      // We are in a degenerate transform, so there's not much we can do.
      return;
    }
    // Combine the matrices and store the result.
    inverseTransform.multiply(forwardTransform);
    inverseTransform.translate(linkedOffset.dx, linkedOffset.dy);
    _lastTransform = inverseTransform;
1969
    _inverseDirty = true;
1970 1971
  }

1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984
  /// {@template flutter.leaderFollower.alwaysNeedsAddToScene}
  /// This disables retained rendering for Leader/FollowerLayer.
  ///
  /// A FollowerLayer copies changes from a LeaderLayer that could be anywhere
  /// in the Layer tree, and that LeaderLayer could change without notifying the
  /// FollowerLayer. Therefore we have to always call a FollowerLayer's
  /// [addToScene]. In order to call FollowerLayer's [addToScene], LeaderLayer's
  /// [addToScene] must be called first so LeaderLayer must also be considered
  /// as [alwaysNeedsAddToScene].
  /// {@endtemplate}
  @override
  bool get alwaysNeedsAddToScene => true;

1985
  @override
1986
  ui.EngineLayer addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
1987 1988 1989 1990 1991
    assert(link != null);
    assert(showWhenUnlinked != null);
    if (link.leader == null && !showWhenUnlinked) {
      _lastTransform = null;
      _lastOffset = null;
1992
      _inverseDirty = true;
1993
      return null; // this does not have an engine layer.
1994 1995 1996 1997
    }
    _establishTransform();
    if (_lastTransform != null) {
      builder.pushTransform(_lastTransform.storage);
1998
      addChildrenToScene(builder);
1999 2000 2001 2002
      builder.pop();
      _lastOffset = unlinkedOffset + layerOffset;
    } else {
      _lastOffset = null;
2003 2004 2005 2006
      final Matrix4 matrix = Matrix4.translationValues(unlinkedOffset.dx, unlinkedOffset.dy, .0);
      builder.pushTransform(matrix.storage);
      addChildrenToScene(builder);
      builder.pop();
2007
    }
2008
    _inverseDirty = true;
2009
    return null; // this does not have an engine layer.
2010 2011 2012 2013 2014 2015
  }

  @override
  void applyTransform(Layer child, Matrix4 transform) {
    assert(child != null);
    assert(transform != null);
2016
    if (_lastTransform != null) {
2017
      transform.multiply(_lastTransform);
2018 2019 2020
    } else {
      transform.multiply(Matrix4.translationValues(unlinkedOffset.dx, unlinkedOffset.dy, .0));
    }
2021 2022 2023
  }

  @override
2024 2025
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
2026 2027
    properties.add(DiagnosticsProperty<LayerLink>('link', link));
    properties.add(TransformProperty('transform', getLastTransform(), defaultValue: null));
2028 2029
  }
}
2030 2031 2032 2033 2034 2035 2036

/// A composited layer which annotates its children with a value.
///
/// These values can be retrieved using [Layer.find] with a given [Offset]. If
/// a [Size] is provided to this layer, then find will check if the provided
/// offset is within the bounds of the layer.
class AnnotatedRegionLayer<T> extends ContainerLayer {
2037 2038
  /// Creates a new layer annotated with [value] that clips to rectangle defined
  /// by the [size] and [offset] if provided.
2039
  ///
2040 2041 2042 2043
  /// The [value] provided cannot be null.
  AnnotatedRegionLayer(this.value, {this.size, Offset offset})
      : offset = offset ?? Offset.zero,
        assert(value != null);
2044 2045 2046 2047 2048 2049 2050 2051

  /// The value returned by [find] if the offset is contained within this layer.
  final T value;

  /// The [size] is optionally used to clip the hit-testing of [find].
  ///
  /// If not provided, all offsets are considered to be contained within this
  /// layer, unless an ancestor layer applies a clip.
2052 2053 2054
  ///
  /// If [offset] is set, then the offset is applied to the size region before
  /// hit testing in [find].
2055 2056
  final Size size;

2057 2058 2059 2060 2061 2062 2063 2064
  /// The [offset] is optionally used to translate the clip region for the
  /// hit-testing of [find] by [offset].
  ///
  /// If not provided, offset defaults to [Offset.zero].
  ///
  /// Ignored if [size] is not set.
  final Offset offset;

2065 2066
  @override
  S find<S>(Offset regionOffset) {
2067 2068 2069 2070
    final S result = super.find<S>(regionOffset);
    if (result != null)
      return result;
    if (size != null && !(offset & size).contains(regionOffset))
2071
      return null;
2072 2073 2074 2075
    if (T == S) {
      final Object untypedResult = value;
      final S typedResult = untypedResult;
      return typedResult;
2076
    }
2077
    return null;
2078 2079 2080
  }

  @override
2081 2082
  Iterable<S> findAll<S>(Offset regionOffset) {
    final Iterable<S> childResults = super.findAll<S>(regionOffset);
2083
    if (size != null && !(offset & size).contains(regionOffset)) {
2084
      return childResults;
2085
    }
2086 2087 2088
    if (T == S) {
      final Object untypedResult = value;
      final S typedResult = untypedResult;
2089
      return childResults.followedBy(<S>[typedResult]);
2090
    }
2091
    return childResults;
2092 2093 2094 2095 2096
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
2097 2098
    properties.add(DiagnosticsProperty<T>('value', value));
    properties.add(DiagnosticsProperty<Size>('size', size, defaultValue: null));
2099
    properties.add(DiagnosticsProperty<Offset>('offset', offset, defaultValue: null));
2100 2101
  }
}