layer.dart 91.1 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// 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
import 'dart:ui' as ui;
8

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

14 15
import 'debug.dart';

16 17 18 19
/// Information collected for an annotation that is found in the layer tree.
///
/// See also:
///
20
///  * [Layer.findAnnotations], which create and use objects of this class.
21 22
@immutable
class AnnotationEntry<T> {
23
  /// Create an entry of found annotation by providing the object and related
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
  /// information.
  const AnnotationEntry({
    @required this.annotation,
    @required this.localPosition,
  }) : assert(localPosition != null);

  /// The annotation object that is found.
  final T annotation;

  /// The target location described by the local coordinate space of the layer
  /// that contains the annotation.
  final Offset localPosition;

  @override
  String toString() {
    return '$runtimeType(annotation: $annotation, localPostion: $localPosition)';
  }
}

/// Information collected about a list of annotations that are found in the
/// layer tree.
///
/// See also:
///
///  * [AnnotationEntry], which are members of this class.
49 50
///  * [Layer.findAllAnnotations], and [Layer.findAnnotations], which create and
///    use an object of this class.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
class AnnotationResult<T> {
  final List<AnnotationEntry<T>> _entries = <AnnotationEntry<T>>[];

  /// Add a new entry to the end of the result.
  ///
  /// Usually, entries should be added in order from most specific to least
  /// specific, typically during an upward walk of the tree.
  void add(AnnotationEntry<T> entry) => _entries.add(entry);

  /// An unmodifiable list of [AnnotationEntry] objects recorded.
  ///
  /// The first entry is the most specific, typically the one at the leaf of
  /// tree.
  Iterable<AnnotationEntry<T>> get entries => _entries;

  /// An unmodifiable list of annotations recorded.
  ///
  /// The first entry is the most specific, typically the one at the leaf of
  /// tree.
  ///
  /// It is similar to [entries] but does not contain other information.
  Iterable<T> get annotations sync* {
    for (AnnotationEntry<T> entry in _entries)
      yield entry.annotation;
  }
}

78
/// A composited layer.
79 80 81 82
///
/// 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.
83 84 85 86 87
///
/// 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.
///
88
/// To composite the tree, create a [SceneBuilder] object, pass it to the
89
/// root [Layer] object's [addToScene] method, and then call
90 91
/// [SceneBuilder.build] to obtain a [Scene]. A [Scene] can then be painted
/// using [Window.render].
92 93 94 95
///
/// See also:
///
///  * [RenderView.compositeFrame], which implements this recomposition protocol
96
///    for painting [RenderObject] trees on the display.
97
abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
98 99 100 101 102 103 104
  /// 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
105
  ContainerLayer get parent => super.parent as ContainerLayer;
106

107 108
  // Whether this layer has any changes since its last call to [addToScene].
  //
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
  // Initialized to true as a new layer has never called [addToScene], and is
  // set to false after calling [addToScene]. The value can become true again
  // if [markNeedsAddToScene] is called, or when [updateSubtreeNeedsAddToScene]
  // is called on this layer or on an ancestor layer.
  //
  // The values of [_needsAddToScene] in a tree of layers are said to be
  // _consistent_ if every layer in the tree satisfies the following:
  //
  // - If [alwaysNeedsAddToScene] is true, then [_needsAddToScene] is also true.
  // - If [_needsAddToScene] is true and [parent] is not null, then
  //   `parent._needsAddToScene` is true.
  //
  // Typically, this value is set during the paint phase and during compositing.
  // During the paint phase render objects create new layers and call
  // [markNeedsAddToScene] on existing layers, causing this value to become
  // true. After the paint phase the tree may be in an inconsistent state.
  // During compositing [ContainerLayer.buildScene] first calls
  // [updateSubtreeNeedsAddToScene] to bring this tree to a consistent state,
  // then it calls [addToScene], and finally sets this field to false.
128 129 130 131
  bool _needsAddToScene = true;

  /// Mark that this layer has changed and [addToScene] needs to be called.
  @protected
132
  @visibleForTesting
133
  void markNeedsAddToScene() {
134 135 136 137 138 139 140 141 142 143 144
    assert(
      !alwaysNeedsAddToScene,
      '$runtimeType with alwaysNeedsAddToScene set called markNeedsAddToScene.\n'
      'The layer\'s alwaysNeedsAddToScene is set to true, and therefore it should not call markNeedsAddToScene.',
    );

    // Already marked. Short-circuit.
    if (_needsAddToScene) {
      return;
    }

145 146 147 148 149
    _needsAddToScene = true;
  }

  /// Mark that this layer is in sync with engine.
  ///
150 151
  /// This is for debugging and testing purposes only. In release builds
  /// this method has no effect.
152 153
  @visibleForTesting
  void debugMarkClean() {
154
    assert(() {
155 156 157 158 159 160 161 162 163
      _needsAddToScene = false;
      return true;
    }());
  }

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

164
  /// Whether this or any descendant layer in the subtree needs [addToScene].
165 166 167 168 169 170
  ///
  /// This is for debug and test purpose only. It only becomes valid after
  /// calling [updateSubtreeNeedsAddToScene].
  @visibleForTesting
  bool get debugSubtreeNeedsAddToScene {
    bool result;
171
    assert(() {
172
      result = _needsAddToScene;
173 174 175 176 177
      return true;
    }());
    return result;
  }

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
  /// Stores the engine layer created for this layer in order to reuse engine
  /// resources across frames for better app performance.
  ///
  /// This value may be passed to [ui.SceneBuilder.addRetained] to communicate
  /// to the engine that nothing in this layer or any of its descendants
  /// changed. The native engine could, for example, reuse the texture rendered
  /// in a previous frame. The web engine could, for example, reuse the HTML
  /// DOM nodes created for a previous frame.
  ///
  /// This value may be passed as `oldLayer` argument to a "push" method to
  /// communicate to the engine that a layer is updating a previously rendered
  /// layer. The web engine could, for example, update the properties of
  /// previously rendered HTML DOM nodes rather than creating new nodes.
  @protected
  ui.EngineLayer get engineLayer => _engineLayer;

  /// Sets the engine layer used to render this layer.
  ///
  /// Typically this field is set to the value returned by [addToScene], which
  /// in turn returns the engine layer produced by one of [ui.SceneBuilder]'s
  /// "push" methods, such as [ui.SceneBuilder.pushOpacity].
  @protected
  set engineLayer(ui.EngineLayer value) {
    _engineLayer = value;
    if (!alwaysNeedsAddToScene) {
      // The parent must construct a new engine layer to add this layer to, and
      // so we mark it as needing [addToScene].
      //
      // This is designed to handle two situations:
      //
      // 1. When rendering the complete layer tree as normal. In this case we
      // call child `addToScene` methods first, then we call `set engineLayer`
      // for the parent. The children will call `markNeedsAddToScene` on the
      // parent to signal that they produced new engine layers and therefore
      // the parent needs to update. In this case, the parent is already adding
      // itself to the scene via [addToScene], and so after it's done, its
      // `set engineLayer` is called and it clears the `_needsAddToScene` flag.
      //
      // 2. When rendering an interior layer (e.g. `OffsetLayer.toImage`). In
      // this case we call `addToScene` for one of the children but not the
      // parent, i.e. we produce new engine layers for children but not for the
      // parent. Here the children will mark the parent as needing
      // `addToScene`, but the parent does not clear the flag until some future
      // frame decides to render it, at which point the parent knows that it
      // cannot retain its engine layer and will call `addToScene` again.
      if (parent != null && !parent.alwaysNeedsAddToScene) {
        parent.markNeedsAddToScene();
      }
    }
  }
228 229
  ui.EngineLayer _engineLayer;

230
  /// Traverses the layer subtree starting from this layer and determines whether it needs [addToScene].
231
  ///
232 233 234 235 236 237 238
  /// A layer needs [addToScene] if any of the following is true:
  ///
  /// - [alwaysNeedsAddToScene] is true.
  /// - [markNeedsAddToScene] has been called.
  /// - Any of its descendants need [addToScene].
  ///
  /// [ContainerLayer] overrides this method to recursively call it on its children.
239
  @protected
240
  @visibleForTesting
241
  void updateSubtreeNeedsAddToScene() {
242
    _needsAddToScene = _needsAddToScene || alwaysNeedsAddToScene;
243 244
  }

245
  /// This layer's next sibling in the parent layer's child list.
246
  Layer get nextSibling => _nextSibling;
247
  Layer _nextSibling;
248

249
  /// This layer's previous sibling in the parent layer's child list.
250
  Layer get previousSibling => _previousSibling;
251
  Layer _previousSibling;
252

253 254
  @override
  void dropChild(AbstractNode child) {
255 256 257
    if (!alwaysNeedsAddToScene) {
      markNeedsAddToScene();
    }
258 259 260 261 262
    super.dropChild(child);
  }

  @override
  void adoptChild(AbstractNode child) {
263 264 265
    if (!alwaysNeedsAddToScene) {
      markNeedsAddToScene();
    }
266 267 268
    super.adoptChild(child);
  }

269
  /// Removes this layer from its parent layer's child list.
270 271
  ///
  /// This has no effect if the layer's parent is already null.
272
  @mustCallSuper
273 274
  void remove() {
    parent?._removeChild(this);
275
  }
276

277 278 279 280
  /// Search this layer and its subtree for annotations of type `S` at the
  /// location described by `localPosition`.
  ///
  /// This method is called by the default implementation of [find] and
281 282 283 284 285 286 287
  /// [findAllAnnotations]. Override this method to customize how the layer
  /// should search for annotations, or if the layer has its own annotations to
  /// add.
  ///
  /// The default implementation simply returns `false`, which means neither
  /// the layer nor its children has annotations, and the annotation search
  /// is not absorbed either (see below for explanation).
288 289 290 291
  ///
  /// ## About layer annotations
  ///
  /// {@template flutter.rendering.layer.findAnnotations.aboutAnnotations}
292
  /// An annotation is an optional object of any type that can be carried with a
293 294
  /// layer. An annotation can be found at a location as long as the owner layer
  /// contains the location and is walked to.
295
  ///
296 297 298
  /// The annotations are searched by first visiting each child recursively,
  /// then this layer, resulting in an order from visually front to back.
  /// Annotations must meet the given restrictions, such as type and position.
299
  ///
300 301
  /// The common way for a value to be found here is by pushing an
  /// [AnnotatedRegionLayer] into the layer tree, or by adding the desired
302
  /// annotation by overriding [findAnnotations].
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
  /// {@endtemplate}
  ///
  /// ## Parameters and return value
  ///
  /// The [result] parameter is where the method outputs the resulting
  /// annotations. New annotations found during the walk are added to the tail.
  ///
  /// The [onlyFirst] parameter indicates that, if true, the search will stop
  /// when it finds the first qualified annotation; otherwise, it will walk the
  /// entire subtree.
  ///
  /// The return value indicates the opacity of this layer and its subtree at
  /// this position. If it returns true, then this layer's parent should skip
  /// the children behind this layer. In other words, it is opaque to this type
  /// of annotation and has absorbed the search so that its siblings behind it
  /// are not aware of the search. If the return value is false, then the parent
  /// might continue with other siblings.
  ///
  /// The return value does not affect whether the parent adds its own
  /// annotations; in other words, if a layer is supposed to add an annotation,
  /// it will always add it even if its children are opaque to this type of
  /// annotation. However, the opacity that the parents return might be affected
  /// by their children, hence making all of its ancestors opaque to this type
  /// of annotation.
  @protected
  bool findAnnotations<S>(
    AnnotationResult<S> result,
    Offset localPosition, {
    @required bool onlyFirst,
332 333 334
  }) {
    return false;
  }
335 336 337 338 339 340 341

  /// Search this layer and its subtree for the first annotation of type `S`
  /// under the point described by `localPosition`.
  ///
  /// Returns null if no matching annotations are found.
  ///
  /// By default this method simply calls [findAnnotations] with `onlyFirst:
342 343 344 345
  /// true` and returns the annotation of the first result. Prefer overriding
  /// [findAnnotations] instead of this method, because during an annotation
  /// search, only [findAnnotations] is recursively called, while custom
  /// behavior in this method is ignored.
346 347 348 349
  ///
  /// ## About layer annotations
  ///
  /// {@macro flutter.rendering.layer.findAnnotations.aboutAnnotations}
350 351 352
  ///
  /// See also:
  ///
353 354
  ///  * [findAllAnnotations], which is similar but returns all annotations found
  ///    at the given position.
355
  ///  * [AnnotatedRegionLayer], for placing values in the layer tree.
356
  S find<S>(Offset localPosition) {
357 358
    final AnnotationResult<S> result = AnnotationResult<S>();
    findAnnotations<S>(result, localPosition, onlyFirst: true);
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
    return result.entries.isEmpty ? null : result.entries.first.annotation;
  }

  /// Search this layer and its subtree for all annotations of type `S` under
  /// the point described by `localPosition`.
  ///
  /// Returns a result with empty entries if no matching annotations are found.
  ///
  /// By default this method simply calls [findAnnotations] with `onlyFirst:
  /// false` and returns the annotations of its result. Prefer overriding
  /// [findAnnotations] instead of this method, because during an annotation
  /// search, only [findAnnotations] is recursively called, while custom
  /// behavior in this method is ignored.
  ///
  /// ## About layer annotations
  ///
  /// {@macro flutter.rendering.layer.findAnnotations.aboutAnnotations}
  ///
  /// See also:
  ///
  ///  * [find], which is similar but returns the first annotation found at the
  ///    given position.
  ///  * [findAllAnnotations], which is similar but returns an
  ///    [AnnotationResult], which contains more information, such as the local
  ///    position of the event related to each annotation, and is equally fast,
  ///    hence is preferred over [findAll].
  ///  * [AnnotatedRegionLayer], for placing values in the layer tree.
386 387 388 389
  @Deprecated(
    'Use findAllAnnotations(...).annotations instead. '
    'This feature was deprecated after v1.10.14.'
  )
390 391 392
  Iterable<S> findAll<S>(Offset localPosition) {
    final AnnotationResult<S> result = findAllAnnotations(localPosition);
    return result.entries.map((AnnotationEntry<S> entry) => entry.annotation);
393
  }
394

395 396
  /// Search this layer and its subtree for all annotations of type `S` under
  /// the point described by `localPosition`.
397
  ///
398
  /// Returns a result with empty entries if no matching annotations are found.
399
  ///
400
  /// By default this method simply calls [findAnnotations] with `onlyFirst:
401 402 403 404
  /// false` and returns the annotations of its result. Prefer overriding
  /// [findAnnotations] instead of this method, because during an annotation
  /// search, only [findAnnotations] is recursively called, while custom
  /// behavior in this method is ignored.
405 406 407 408
  ///
  /// ## About layer annotations
  ///
  /// {@macro flutter.rendering.layer.findAnnotations.aboutAnnotations}
409 410 411
  ///
  /// See also:
  ///
412 413
  ///  * [find], which is similar but returns the first annotation found at the
  ///    given position.
414
  ///  * [AnnotatedRegionLayer], for placing values in the layer tree.
415
  AnnotationResult<S> findAllAnnotations<S>(Offset localPosition) {
416 417 418 419
    final AnnotationResult<S> result = AnnotationResult<S>();
    findAnnotations<S>(result, localPosition, onlyFirst: false);
    return result;
  }
420

421
  /// Override this method to upload this layer to the engine.
422 423 424 425
  ///
  /// Return the engine layer for retained rendering. When there's no
  /// corresponding engine layer, null is returned.
  @protected
426
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]);
427 428 429

  void _addToSceneWithRetainedRendering(ui.SceneBuilder builder) {
    // There can't be a loop by adding a retained layer subtree whose
430
    // _needsAddToScene is false.
431 432 433 434
    //
    // Proof by contradiction:
    //
    // If we introduce a loop, this retained layer must be appended to one of
xster's avatar
xster committed
435
    // its descendant layers, say A. That means the child structure of A has
436
    // changed so A's _needsAddToScene is true. This contradicts
437 438
    // _needsAddToScene being false.
    if (!_needsAddToScene && _engineLayer != null) {
439 440 441
      builder.addRetained(_engineLayer);
      return;
    }
442 443 444 445
    addToScene(builder);
    // Clearing the flag _after_ calling `addToScene`, not _before_. This is
    // because `addToScene` calls children's `addToScene` methods, which may
    // mark this layer as dirty.
446 447
    _needsAddToScene = false;
  }
448

449 450 451 452
  /// 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.
453
  dynamic debugCreator;
454

455
  @override
456
  String toStringShort() => '${super.toStringShort()}${ owner == null ? " DETACHED" : ""}';
457

458
  @override
459 460
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
461 462
    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));
463
  }
464 465
}

466 467 468
/// A composited layer containing a [Picture].
///
/// Picture layers are always leaves in the layer tree.
469
class PictureLayer extends Layer {
470
  /// Creates a leaf layer for the layer tree.
471 472 473 474 475
  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
476
  /// [debugDumpLayerTree] (which can be triggered by pressing "L" when using
477 478 479 480
  /// "flutter run" at the console), which can help debug why certain drawing
  /// commands are being culled.
  final Rect canvasBounds;

481
  /// The picture recorded for this layer.
482
  ///
483
  /// The picture's coordinate system matches this layer's coordinate system.
484 485 486
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
487 488 489
  ui.Picture get picture => _picture;
  ui.Picture _picture;
  set picture(ui.Picture picture) {
490
    markNeedsAddToScene();
491 492
    _picture = picture;
  }
493

494 495 496 497 498
  /// 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.
499 500 501
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
502 503 504 505 506 507 508 509
  bool get isComplexHint => _isComplexHint;
  bool _isComplexHint = false;
  set isComplexHint(bool value) {
    if (value != _isComplexHint) {
      _isComplexHint = value;
      markNeedsAddToScene();
    }
  }
510 511 512 513 514 515 516

  /// 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.
517 518 519
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
520 521 522 523 524 525 526 527
  bool get willChangeHint => _willChangeHint;
  bool _willChangeHint = false;
  set willChangeHint(bool value) {
    if (value != _willChangeHint) {
      _willChangeHint = value;
      markNeedsAddToScene();
    }
  }
528

529
  @override
530
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
531
    builder.addPicture(layerOffset, picture, isComplexHint: isComplexHint, willChangeHint: willChangeHint);
532
  }
533 534

  @override
535 536
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
537
    properties.add(DiagnosticsProperty<Rect>('paint bounds', canvasBounds));
538
  }
539 540

  @override
541 542 543 544
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
    return false;
  }
545 546
}

547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
/// 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:
///
569
///  * <https://api.flutter.dev/javadoc/io/flutter/view/TextureRegistry.html>
570
///    for how to create and manage backend textures on Android.
571
///  * <https://api.flutter.dev/objcdoc/Protocols/FlutterTextureRegistry.html>
572
///    for how to create and manage backend textures on iOS.
573 574
class TextureLayer extends Layer {
  /// Creates a texture layer bounded by [rect] and with backend texture
575 576
  /// identified by [textureId], if [freeze] is true new texture frames will not be
  /// populated to the texture.
577 578 579
  TextureLayer({
    @required this.rect,
    @required this.textureId,
580
    this.freeze = false,
581 582
  }) : assert(rect != null),
       assert(textureId != null);
583 584 585 586 587 588 589

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

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

590 591
  /// When true the texture that will not be updated with new frames.
  ///
592 593 594 595 596
  /// 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
  /// un-freezes it when it is certain that a frame with the new size is ready.
597 598
  final bool freeze;

599
  @override
600
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
601
    final Rect shiftedRect = layerOffset == Offset.zero ? rect : rect.shift(layerOffset);
602 603 604 605 606
    builder.addTexture(
      textureId,
      offset: shiftedRect.topLeft,
      width: shiftedRect.width,
      height: shiftedRect.height,
607
      freeze: freeze,
608 609
    );
  }
610 611

  @override
612 613 614 615
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
    return false;
  }
616 617
}

618 619 620 621 622 623 624 625 626
/// 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,
627
    this.hoverAnnotation,
628 629
  }) : assert(rect != null),
       assert(viewId != null);
630 631 632 633 634 635 636 637 638

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

639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
  /// [MouseTrackerAnnotation] that handles mouse events for this layer.
  ///
  /// If [hoverAnnotation] is non-null, [PlatformViewLayer] will annotate the
  /// region of this platform view such that annotation callbacks will receive
  /// mouse events, including mouse enter, exit, and hover, but not including
  /// mouse down, move, and up. The layer will be treated as opaque during an
  /// annotation search, which will prevent layers behind it from receiving
  /// these events.
  ///
  /// By default, [hoverAnnotation] is null, and [PlatformViewLayer] will not
  /// receive mouse events, and will therefore appear translucent during the
  /// annotation search.
  ///
  /// See also:
  ///
  ///  * [MouseRegion], which explains more about the mouse events and opacity
  ///    during annotation search.
  final MouseTrackerAnnotation hoverAnnotation;

658
  @override
659
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
660
    final Rect shiftedRect = layerOffset == Offset.zero ? rect : rect.shift(layerOffset);
661 662 663 664 665 666 667 668 669
    builder.addPlatformView(
      viewId,
      offset: shiftedRect.topLeft,
      width: shiftedRect.width,
      height: shiftedRect.height,
    );
  }

  @override
670 671
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
672 673 674
    if (hoverAnnotation == null || !rect.contains(localPosition)) {
      return false;
    }
675
    if (S == MouseTrackerAnnotation) {
676
      final Object untypedValue = hoverAnnotation;
677
      final S typedValue = untypedValue as S;
678 679 680 681 682 683
      result.add(AnnotationEntry<S>(
        annotation: typedValue,
        localPosition: localPosition,
      ));
      return true;
    }
684 685
    return false;
  }
686 687
}

688
/// A layer that indicates to the compositor that it should display
689
/// certain performance statistics within it.
690 691
///
/// Performance overlay layers are always leaves in the layer tree.
692
class PerformanceOverlayLayer extends Layer {
693
  /// Creates a layer that displays a performance overlay.
694
  PerformanceOverlayLayer({
695
    @required Rect overlayRect,
696 697 698
    @required this.optionsMask,
    @required this.rasterizerThreshold,
    @required this.checkerboardRasterCacheImages,
699
    @required this.checkerboardOffscreenLayers,
700
  }) : _overlayRect = overlayRect;
701

702
  /// The rectangle in this layer's coordinate system that the overlay should occupy.
703 704 705
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
706 707 708 709 710 711 712 713
  Rect get overlayRect => _overlayRect;
  Rect _overlayRect;
  set overlayRect(Rect value) {
    if (value != _overlayRect) {
      _overlayRect = value;
      markNeedsAddToScene();
    }
  }
714

715 716
  /// The mask is created by shifting 1 by the index of the specific
  /// [PerformanceOverlayOption] to enable.
717 718
  final int optionsMask;

719 720 721
  /// 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.
722
  final int rasterizerThreshold;
723

724 725 726 727 728 729 730 731 732 733 734 735 736
  /// 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;

737 738 739 740 741 742 743 744 745 746
  /// 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;

747
  @override
748
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
749
    assert(optionsMask != null);
750 751
    final Rect shiftedOverlayRect = layerOffset == Offset.zero ? overlayRect : overlayRect.shift(layerOffset);
    builder.addPerformanceOverlay(optionsMask, shiftedOverlayRect);
752
    builder.setRasterizerTracingThreshold(rasterizerThreshold);
753
    builder.setCheckerboardRasterCacheImages(checkerboardRasterCacheImages);
754
    builder.setCheckerboardOffscreenLayers(checkerboardOffscreenLayers);
755
  }
756 757

  @override
758 759 760 761
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
    return false;
  }
762 763
}

764 765 766 767 768
/// 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.
769
class ContainerLayer extends Layer {
770
  /// The first composited layer in this layer's child list.
771
  Layer get firstChild => _firstChild;
772
  Layer _firstChild;
773

774
  /// The last composited layer in this layer's child list.
775
  Layer get lastChild => _lastChild;
776
  Layer _lastChild;
777

778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
  /// Returns whether this layer has at least one child layer.
  bool get hasChildren => _firstChild != null;

  /// Consider this layer as the root and build a scene (a tree of layers)
  /// in the engine.
  // The reason this method is in the `ContainerLayer` class rather than
  // `PipelineOwner` or other singleton level is because this method can be used
  // both to render the whole layer tree (e.g. a normal application frame) and
  // to render a subtree (e.g. `OffsetLayer.toImage`).
  ui.Scene buildScene(ui.SceneBuilder builder) {
    List<PictureLayer> temporaryLayers;
    assert(() {
      if (debugCheckElevationsEnabled) {
        temporaryLayers = _debugCheckElevations();
      }
      return true;
    }());
    updateSubtreeNeedsAddToScene();
    addToScene(builder);
    // Clearing the flag _after_ calling `addToScene`, not _before_. This is
    // because `addToScene` calls children's `addToScene` methods, which may
    // mark this layer as dirty.
    _needsAddToScene = false;
    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 continuing to render stale outlines.
      if (temporaryLayers != null) {
        for (PictureLayer temporaryLayer in temporaryLayers) {
          temporaryLayer.remove();
        }
      }
      return true;
    }());
    return scene;
  }

816
  bool _debugUltimatePreviousSiblingOf(Layer child, { Layer equals }) {
817 818 819 820 821
    assert(child.attached == attached);
    while (child.previousSibling != null) {
      assert(child.previousSibling != child);
      child = child.previousSibling;
      assert(child.attached == attached);
822 823 824 825 826
    }
    return child == equals;
  }

  bool _debugUltimateNextSiblingOf(Layer child, { Layer equals }) {
827
    assert(child.attached == attached);
828 829 830
    while (child._nextSibling != null) {
      assert(child._nextSibling != child);
      child = child._nextSibling;
831
      assert(child.attached == attached);
832 833 834 835
    }
    return child == equals;
  }

836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
  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'
858
                              'See https://api.flutter.dev/flutter/rendering/debugCheckElevationsEnabled.html '
859 860
                              'for more details.'),
      library: 'rendering library',
861
      context: ErrorDescription('during compositing'),
862 863 864 865 866 867
      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.'),
        ];
868
      },
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
    ));
    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;
  }

930 931 932 933 934 935
  @override
  void updateSubtreeNeedsAddToScene() {
    super.updateSubtreeNeedsAddToScene();
    Layer child = firstChild;
    while (child != null) {
      child.updateSubtreeNeedsAddToScene();
936
      _needsAddToScene = _needsAddToScene || child._needsAddToScene;
937 938 939 940
      child = child.nextSibling;
    }
  }

941
  @override
942 943 944 945 946 947 948 949
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
    for (Layer child = lastChild; child != null; child = child.previousSibling) {
      final bool isAbsorbed = child.findAnnotations<S>(result, localPosition, onlyFirst: onlyFirst);
      if (isAbsorbed)
        return true;
      if (onlyFirst && result.entries.isNotEmpty)
        return isAbsorbed;
950
    }
951
    return false;
952 953
  }

954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
  @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.
975
  void append(Layer child) {
976
    assert(child != this);
977 978 979 980 981 982
    assert(child != firstChild);
    assert(child != lastChild);
    assert(child.parent == null);
    assert(!child.attached);
    assert(child.nextSibling == null);
    assert(child.previousSibling == null);
983 984 985 986 987 988
    assert(() {
      Layer node = this;
      while (node.parent != null)
        node = node.parent;
      assert(node != child); // indicates we are about to create a cycle
      return true;
989
    }());
990 991 992 993
    adoptChild(child);
    child._previousSibling = lastChild;
    if (lastChild != null)
      lastChild._nextSibling = child;
994
    _lastChild = child;
995
    _firstChild ??= child;
996
    assert(child.attached == attached);
997 998
  }

999 1000 1001 1002 1003 1004
  // 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));
1005 1006 1007 1008
    if (child._previousSibling == null) {
      assert(_firstChild == child);
      _firstChild = child._nextSibling;
    } else {
1009
      child._previousSibling._nextSibling = child.nextSibling;
1010 1011
    }
    if (child._nextSibling == null) {
1012 1013
      assert(lastChild == child);
      _lastChild = child.previousSibling;
1014
    } else {
1015
      child.nextSibling._previousSibling = child.previousSibling;
1016
    }
1017 1018 1019 1020 1021
    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));
1022 1023
    child._previousSibling = null;
    child._nextSibling = null;
1024 1025
    dropChild(child);
    assert(!child.attached);
1026
  }
Hixie's avatar
Hixie committed
1027

1028
  /// Removes all of this layer's children from its child list.
1029
  void removeAllChildren() {
1030
    Layer child = firstChild;
1031
    while (child != null) {
1032
      final Layer next = child.nextSibling;
1033 1034
      child._previousSibling = null;
      child._nextSibling = null;
1035 1036
      assert(child.attached == attached);
      dropChild(child);
1037 1038 1039 1040 1041 1042
      child = next;
    }
    _firstChild = null;
    _lastChild = null;
  }

1043
  @override
1044
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
1045
    addChildrenToScene(builder, layerOffset);
Hixie's avatar
Hixie committed
1046 1047
  }

1048 1049 1050 1051
  /// 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]
1052
  /// to apply effects to the scene using the [SceneBuilder] API, then insert
1053 1054
  /// their children using [addChildrenToScene], then reverse the aforementioned
  /// effects before returning from [addToScene].
1055
  void addChildrenToScene(ui.SceneBuilder builder, [ Offset childOffset = Offset.zero ]) {
1056
    Layer child = firstChild;
Hixie's avatar
Hixie committed
1057
    while (child != null) {
1058 1059 1060 1061 1062
      if (childOffset == Offset.zero) {
        child._addToSceneWithRetainedRendering(builder);
      } else {
        child.addToScene(builder, childOffset);
      }
Hixie's avatar
Hixie committed
1063 1064 1065
      child = child.nextSibling;
    }
  }
1066

1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
  /// 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
1088
  /// default, composites its children at the origin of the [ContainerLayer]
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104
  /// 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);
  }

1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
  /// 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;
  }

1122
  @override
1123 1124
  List<DiagnosticsNode> debugDescribeChildren() {
    final List<DiagnosticsNode> children = <DiagnosticsNode>[];
1125
    if (firstChild == null)
1126
      return children;
1127 1128
    Layer child = firstChild;
    int count = 1;
1129 1130 1131 1132
    while (true) {
      children.add(child.toDiagnosticsNode(name: 'child $count'));
      if (child == lastChild)
        break;
1133 1134 1135
      count += 1;
      child = child.nextSibling;
    }
1136
    return children;
1137
  }
Hixie's avatar
Hixie committed
1138 1139
}

1140 1141 1142 1143 1144 1145 1146 1147 1148
/// 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.
1149
class OffsetLayer extends ContainerLayer {
1150 1151
  /// Creates an offset layer.
  ///
1152 1153
  /// By default, [offset] is zero. It must be non-null before the compositing
  /// phase of the pipeline.
1154
  OffsetLayer({ Offset offset = Offset.zero }) : _offset = offset;
1155 1156

  /// Offset from parent in the parent's coordinate system.
1157 1158 1159
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1160 1161 1162
  ///
  /// The [offset] property must be non-null before the compositing phase of the
  /// pipeline.
1163 1164 1165 1166 1167 1168 1169 1170
  Offset get offset => _offset;
  Offset _offset;
  set offset(Offset value) {
    if (value != _offset) {
      markNeedsAddToScene();
    }
    _offset = value;
  }
1171

1172
  @override
1173 1174 1175
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
    return super.findAnnotations<S>(result, localPosition - offset, onlyFirst: onlyFirst);
1176 1177
  }

1178
  @override
1179 1180 1181 1182 1183 1184 1185
  void applyTransform(Layer child, Matrix4 transform) {
    assert(child != null);
    assert(transform != null);
    transform.multiply(Matrix4.translationValues(offset.dx, offset.dy, 0.0));
  }

  @override
1186
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
1187 1188 1189 1190 1191
    // 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.
1192 1193 1194 1195 1196
    engineLayer = builder.pushOffset(
      layerOffset.dx + offset.dx,
      layerOffset.dy + offset.dy,
      oldLayer: _engineLayer as ui.OffsetEngineLayer,
    );
1197 1198
    addChildrenToScene(builder);
    builder.pop();
1199 1200
  }

1201
  @override
1202 1203
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1204
    properties.add(DiagnosticsProperty<Offset>('offset', offset));
1205
  }
1206 1207 1208

  /// Capture an image of the current state of this layer and its children.
  ///
1209 1210 1211
  /// 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].
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222
  ///
  /// 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.
1223
  Future<ui.Image> toImage(Rect bounds, { double pixelRatio = 1.0 }) async {
1224
    assert(bounds != null);
1225
    assert(pixelRatio != null);
1226 1227
    final ui.SceneBuilder builder = ui.SceneBuilder();
    final Matrix4 transform = Matrix4.translationValues(
1228 1229 1230 1231
      (-bounds.left  - offset.dx) * pixelRatio,
      (-bounds.top - offset.dy) * pixelRatio,
      0.0,
    );
1232
    transform.scale(pixelRatio, pixelRatio);
1233
    builder.pushTransform(transform.storage);
1234
    final ui.Scene scene = buildScene(builder);
1235

1236 1237 1238 1239
    try {
      // Size is rounded up to the next pixel to make sure we don't clip off
      // anything.
      return await scene.toImage(
1240 1241
        (pixelRatio * bounds.width).ceil(),
        (pixelRatio * bounds.height).ceil(),
1242 1243 1244 1245 1246
      );
    } finally {
      scene.dispose();
    }
  }
1247 1248
}

1249
/// A composite layer that clips its children using a rectangle.
1250 1251 1252 1253
///
/// 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
1254
class ClipRectLayer extends ContainerLayer {
1255 1256
  /// Creates a layer with a rectangular clip.
  ///
1257 1258 1259 1260
  /// The [clipRect] argument must not be null before the compositing phase of
  /// the pipeline.
  ///
  /// The [clipBehavior] argument must not be null, and must not be [Clip.none].
1261
  ClipRectLayer({
1262
    Rect clipRect,
1263 1264 1265 1266 1267
    Clip clipBehavior = Clip.hardEdge,
  }) : _clipRect = clipRect,
       _clipBehavior = clipBehavior,
       assert(clipBehavior != null),
       assert(clipBehavior != Clip.none);
Hixie's avatar
Hixie committed
1268

1269 1270 1271 1272
  /// 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]).
1273 1274 1275 1276 1277 1278 1279 1280
  Rect get clipRect => _clipRect;
  Rect _clipRect;
  set clipRect(Rect value) {
    if (value != _clipRect) {
      _clipRect = value;
      markNeedsAddToScene();
    }
  }
Hixie's avatar
Hixie committed
1281

1282
  /// {@template flutter.clipper.clipBehavior}
1283
  /// Controls how to clip.
1284
  ///
1285
  /// Must not be set to null or [Clip.none].
1286
  /// {@endtemplate}
1287 1288
  ///
  /// Defaults to [Clip.hardEdge].
1289 1290 1291 1292 1293
  Clip get clipBehavior => _clipBehavior;
  Clip _clipBehavior;
  set clipBehavior(Clip value) {
    assert(value != null);
    assert(value != Clip.none);
1294 1295 1296 1297
    if (value != _clipBehavior) {
      _clipBehavior = value;
      markNeedsAddToScene();
    }
1298 1299
  }

1300
  @override
1301 1302 1303 1304 1305
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
    if (!clipRect.contains(localPosition))
      return false;
    return super.findAnnotations<S>(result, localPosition, onlyFirst: onlyFirst);
1306 1307
  }

1308
  @override
1309 1310 1311
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
    assert(clipRect != null);
    assert(clipBehavior != null);
1312 1313 1314 1315 1316
    bool enabled = true;
    assert(() {
      enabled = !debugDisableClipLayers;
      return true;
    }());
1317 1318
    if (enabled) {
      final Rect shiftedClipRect = layerOffset == Offset.zero ? clipRect : clipRect.shift(layerOffset);
1319 1320 1321 1322 1323
      engineLayer = builder.pushClipRect(
        shiftedClipRect,
        clipBehavior: clipBehavior,
        oldLayer: _engineLayer as ui.ClipRectEngineLayer,
      );
1324 1325
    } else {
      engineLayer = null;
1326
    }
1327
    addChildrenToScene(builder, layerOffset);
1328
    if (enabled)
1329
      builder.pop();
Hixie's avatar
Hixie committed
1330 1331
  }

1332
  @override
1333 1334
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1335
    properties.add(DiagnosticsProperty<Rect>('clipRect', clipRect));
1336
    properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior));
1337
  }
1338
}
Hixie's avatar
Hixie committed
1339

1340
/// A composite layer that clips its children using a rounded rectangle.
1341 1342 1343 1344
///
/// 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
1345
class ClipRRectLayer extends ContainerLayer {
1346 1347
  /// Creates a layer with a rounded-rectangular clip.
  ///
1348 1349
  /// The [clipRRect] and [clipBehavior] properties must be non-null before the
  /// compositing phase of the pipeline.
1350
  ClipRRectLayer({
1351
    RRect clipRRect,
1352 1353 1354 1355 1356
    Clip clipBehavior = Clip.antiAlias,
  }) : _clipRRect = clipRRect,
       _clipBehavior = clipBehavior,
       assert(clipBehavior != null),
       assert(clipBehavior != Clip.none);
1357

1358 1359 1360 1361
  /// 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]).
1362 1363 1364 1365 1366 1367 1368 1369
  RRect get clipRRect => _clipRRect;
  RRect _clipRRect;
  set clipRRect(RRect value) {
    if (value != _clipRRect) {
      _clipRRect = value;
      markNeedsAddToScene();
    }
  }
Hixie's avatar
Hixie committed
1370

1371
  /// {@macro flutter.clipper.clipBehavior}
1372 1373
  ///
  /// Defaults to [Clip.antiAlias].
1374 1375 1376 1377 1378
  Clip get clipBehavior => _clipBehavior;
  Clip _clipBehavior;
  set clipBehavior(Clip value) {
    assert(value != null);
    assert(value != Clip.none);
1379 1380 1381 1382
    if (value != _clipBehavior) {
      _clipBehavior = value;
      markNeedsAddToScene();
    }
1383 1384
  }

1385
  @override
1386 1387 1388 1389 1390
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
    if (!clipRRect.contains(localPosition))
      return false;
    return super.findAnnotations<S>(result, localPosition, onlyFirst: onlyFirst);
1391 1392
  }

1393
  @override
1394 1395 1396
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
    assert(clipRRect != null);
    assert(clipBehavior != null);
1397 1398 1399 1400 1401
    bool enabled = true;
    assert(() {
      enabled = !debugDisableClipLayers;
      return true;
    }());
1402 1403
    if (enabled) {
      final RRect shiftedClipRRect = layerOffset == Offset.zero ? clipRRect : clipRRect.shift(layerOffset);
1404 1405 1406 1407 1408
      engineLayer = builder.pushClipRRect(
        shiftedClipRRect,
        clipBehavior: clipBehavior,
        oldLayer: _engineLayer as ui.ClipRRectEngineLayer,
      );
1409 1410
    } else {
      engineLayer = null;
1411
    }
1412
    addChildrenToScene(builder, layerOffset);
1413
    if (enabled)
1414
      builder.pop();
Hixie's avatar
Hixie committed
1415
  }
1416

1417
  @override
1418 1419
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1420
    properties.add(DiagnosticsProperty<RRect>('clipRRect', clipRRect));
1421
    properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior));
1422
  }
Hixie's avatar
Hixie committed
1423 1424
}

1425
/// A composite layer that clips its children using a path.
1426 1427 1428 1429
///
/// 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
1430
class ClipPathLayer extends ContainerLayer {
1431 1432
  /// Creates a layer with a path-based clip.
  ///
1433 1434
  /// The [clipPath] and [clipBehavior] properties must be non-null before the
  /// compositing phase of the pipeline.
1435
  ClipPathLayer({
1436
    Path clipPath,
1437 1438 1439 1440 1441
    Clip clipBehavior = Clip.antiAlias,
  }) : _clipPath = clipPath,
       _clipBehavior = clipBehavior,
       assert(clipBehavior != null),
       assert(clipBehavior != Clip.none);
1442

1443 1444 1445 1446
  /// 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]).
1447 1448 1449 1450 1451 1452 1453 1454
  Path get clipPath => _clipPath;
  Path _clipPath;
  set clipPath(Path value) {
    if (value != _clipPath) {
      _clipPath = value;
      markNeedsAddToScene();
    }
  }
Hixie's avatar
Hixie committed
1455

1456
  /// {@macro flutter.clipper.clipBehavior}
1457 1458
  ///
  /// Defaults to [Clip.antiAlias].
1459 1460 1461 1462 1463
  Clip get clipBehavior => _clipBehavior;
  Clip _clipBehavior;
  set clipBehavior(Clip value) {
    assert(value != null);
    assert(value != Clip.none);
1464 1465 1466 1467
    if (value != _clipBehavior) {
      _clipBehavior = value;
      markNeedsAddToScene();
    }
1468 1469
  }

1470
  @override
1471 1472 1473 1474 1475
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
    if (!clipPath.contains(localPosition))
      return false;
    return super.findAnnotations<S>(result, localPosition, onlyFirst: onlyFirst);
1476 1477
  }

1478
  @override
1479 1480 1481
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
    assert(clipPath != null);
    assert(clipBehavior != null);
1482 1483 1484 1485 1486
    bool enabled = true;
    assert(() {
      enabled = !debugDisableClipLayers;
      return true;
    }());
1487 1488
    if (enabled) {
      final Path shiftedPath = layerOffset == Offset.zero ? clipPath : clipPath.shift(layerOffset);
1489 1490 1491 1492 1493
      engineLayer = builder.pushClipPath(
        shiftedPath,
        clipBehavior: clipBehavior,
        oldLayer: _engineLayer as ui.ClipPathEngineLayer,
      );
1494 1495
    } else {
      engineLayer = null;
1496
    }
1497
    addChildrenToScene(builder, layerOffset);
1498
    if (enabled)
1499
      builder.pop();
Hixie's avatar
Hixie committed
1500
  }
1501 1502 1503 1504 1505 1506

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior));
  }
1507 1508
}

1509 1510 1511 1512
/// A composite layer that applies a [ColorFilter] to its children.
class ColorFilterLayer extends ContainerLayer {
  /// Creates a layer that applies a [ColorFilter] to its children.
  ///
1513
  /// The [colorFilter] property must be non-null before the compositing phase
1514 1515
  /// of the pipeline.
  ColorFilterLayer({
1516 1517
    ColorFilter colorFilter,
  }) : _colorFilter = colorFilter;
1518 1519 1520 1521 1522 1523 1524 1525

  /// 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) {
1526
    assert(value != null);
1527 1528 1529 1530 1531 1532 1533
    if (value != _colorFilter) {
      _colorFilter = value;
      markNeedsAddToScene();
    }
  }

  @override
1534 1535
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
    assert(colorFilter != null);
1536 1537 1538 1539
    engineLayer = builder.pushColorFilter(
      colorFilter,
      oldLayer: _engineLayer as ui.ColorFilterEngineLayer,
    );
1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550
    addChildrenToScene(builder, layerOffset);
    builder.pop();
  }

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

1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592
/// A composite layer that applies an [ImageFilter] to its children.
class ImageFilterLayer extends ContainerLayer {
  /// Creates a layer that applies an [ImageFilter] to its children.
  ///
  /// The [imageFilter] property must be non-null before the compositing phase
  /// of the pipeline.
  ImageFilterLayer({
    ui.ImageFilter imageFilter,
  }) : _imageFilter = imageFilter;

  /// The image filter to apply to children.
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
  ui.ImageFilter get imageFilter => _imageFilter;
  ui.ImageFilter _imageFilter;
  set imageFilter(ui.ImageFilter value) {
    assert(value != null);
    if (value != _imageFilter) {
      _imageFilter = value;
      markNeedsAddToScene();
    }
  }

  @override
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
    assert(imageFilter != null);
    engineLayer = builder.pushImageFilter(
      imageFilter,
      oldLayer: _engineLayer as ui.ImageFilterEngineLayer,
    );
    addChildrenToScene(builder, layerOffset);
    builder.pop();
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<ui.ImageFilter>('imageFilter', imageFilter));
  }
}

1593 1594 1595 1596 1597
/// 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.
1598
class TransformLayer extends OffsetLayer {
1599 1600
  /// Creates a transform layer.
  ///
1601 1602
  /// The [transform] and [offset] properties must be non-null before the
  /// compositing phase of the pipeline.
1603
  TransformLayer({ Matrix4 transform, Offset offset = Offset.zero })
1604
    : _transform = transform,
1605
      super(offset: offset);
1606

1607 1608 1609 1610
  /// The matrix to apply.
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1611 1612 1613 1614 1615
  ///
  /// This transform is applied before [offset], if both are set.
  ///
  /// The [transform] property must be non-null before the compositing phase of
  /// the pipeline.
1616 1617 1618
  Matrix4 get transform => _transform;
  Matrix4 _transform;
  set transform(Matrix4 value) {
1619 1620
    assert(value != null);
    assert(value.storage.every((double component) => component.isFinite));
1621 1622 1623
    if (value == _transform)
      return;
    _transform = value;
1624
    _inverseDirty = true;
1625
    markNeedsAddToScene();
1626
  }
1627

1628
  Matrix4 _lastEffectiveTransform;
1629
  Matrix4 _invertedTransform;
1630
  bool _inverseDirty = true;
1631

1632
  @override
1633 1634
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
    assert(transform != null);
1635 1636 1637
    _lastEffectiveTransform = transform;
    final Offset totalOffset = offset + layerOffset;
    if (totalOffset != Offset.zero) {
1638
      _lastEffectiveTransform = Matrix4.translationValues(totalOffset.dx, totalOffset.dy, 0.0)
1639
        ..multiply(_lastEffectiveTransform);
1640
    }
1641 1642 1643 1644
    engineLayer = builder.pushTransform(
      _lastEffectiveTransform.storage,
      oldLayer: _engineLayer as ui.TransformEngineLayer,
    );
1645
    addChildrenToScene(builder);
1646
    builder.pop();
1647
  }
1648

1649
  Offset _transformOffset(Offset localPosition) {
1650
    if (_inverseDirty) {
1651 1652 1653
      _invertedTransform = Matrix4.tryInvert(
        PointerEvent.removePerspectiveTransform(transform)
      );
1654 1655 1656
      _inverseDirty = false;
    }
    if (_invertedTransform == null)
1657
      return null;
1658
    final Vector4 vector = Vector4(localPosition.dx, localPosition.dy, 0.0, 1.0);
1659
    final Vector4 result = _invertedTransform.transform(vector);
1660 1661 1662 1663
    return Offset(result[0], result[1]);
  }

  @override
1664 1665 1666 1667 1668 1669
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
    final Offset transformedOffset = _transformOffset(localPosition);
    if (transformedOffset == null)
      return false;
    return super.findAnnotations<S>(result, transformedOffset, onlyFirst: onlyFirst);
1670 1671
  }

1672 1673 1674 1675
  @override
  void applyTransform(Layer child, Matrix4 transform) {
    assert(child != null);
    assert(transform != null);
1676 1677 1678 1679 1680 1681
    assert(_lastEffectiveTransform != null || this.transform != null);
    if (_lastEffectiveTransform == null) {
      transform.multiply(this.transform);
    } else {
      transform.multiply(_lastEffectiveTransform);
    }
1682 1683
  }

1684
  @override
1685 1686
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1687
    properties.add(TransformProperty('transform', transform));
1688
  }
1689 1690
}

1691
/// A composited layer that makes its children partially transparent.
1692 1693 1694 1695
///
/// 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.
1696 1697 1698
///
/// Try to avoid an [OpacityLayer] with no children. Remove that layer if
/// possible to save some tree walks.
1699
class OpacityLayer extends ContainerLayer {
1700 1701 1702 1703
  /// Creates an opacity layer.
  ///
  /// The [alpha] property must be non-null before the compositing phase of
  /// the pipeline.
1704
  OpacityLayer({
1705
    int alpha,
1706 1707 1708
    Offset offset = Offset.zero,
  }) : _alpha = alpha,
       _offset = offset;
1709

1710
  /// The amount to multiply into the alpha channel.
1711 1712 1713
  ///
  /// The opacity is expressed as an integer from 0 to 255, where 0 is fully
  /// transparent and 255 is fully opaque.
1714 1715 1716
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1717 1718 1719
  int get alpha => _alpha;
  int _alpha;
  set alpha(int value) {
1720
    assert(value != null);
1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735
    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();
    }
  }
1736 1737 1738 1739 1740 1741 1742

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

1744
  @override
1745 1746
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
    assert(alpha != null);
1747
    bool enabled = firstChild != null;  // don't add this layer if there's no child
1748
    assert(() {
1749
      enabled = enabled && !debugDisableOpacityLayers;
1750 1751
      return true;
    }());
1752

1753
    if (enabled)
1754 1755 1756 1757 1758
      engineLayer = builder.pushOpacity(
        alpha,
        offset: offset + layerOffset,
        oldLayer: _engineLayer as ui.OpacityEngineLayer,
      );
1759 1760
    else
      engineLayer = null;
1761
    addChildrenToScene(builder);
1762
    if (enabled)
1763
      builder.pop();
1764
  }
1765

1766
  @override
1767 1768
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1769
    properties.add(IntProperty('alpha', alpha));
1770
    properties.add(DiagnosticsProperty<Offset>('offset', offset));
1771
  }
1772
}
1773

1774
/// A composited layer that applies a shader to its children.
1775 1776 1777 1778 1779
///
/// The shader is only applied inside the given [maskRect]. The shader itself
/// uses the top left of the [maskRect] as its origin.
///
/// The [maskRect] does not affect the positions of any child layers.
1780
class ShaderMaskLayer extends ContainerLayer {
1781 1782
  /// Creates a shader mask layer.
  ///
1783
  /// The [shader], [maskRect], and [blendMode] properties must be non-null
1784
  /// before the compositing phase of the pipeline.
1785
  ShaderMaskLayer({
1786 1787 1788
    Shader shader,
    Rect maskRect,
    BlendMode blendMode,
1789 1790 1791
  }) : _shader = shader,
       _maskRect = maskRect,
       _blendMode = blendMode;
1792 1793

  /// The shader to apply to the children.
1794
  ///
1795 1796 1797 1798
  /// The origin of the shader (e.g. of the coordinate system used by the `from`
  /// and `to` arguments to [ui.Gradient.linear]) is at the top left of the
  /// [maskRect].
  ///
1799 1800
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1801 1802 1803 1804
  ///
  /// See also:
  ///
  ///  * [ui.Gradient] and [ui.ImageShader], two shader types that can be used.
1805 1806 1807 1808 1809 1810 1811 1812
  Shader get shader => _shader;
  Shader _shader;
  set shader(Shader value) {
    if (value != _shader) {
      _shader = value;
      markNeedsAddToScene();
    }
  }
1813

1814 1815 1816 1817
  /// The position and size of the shader.
  ///
  /// The [shader] is only rendered inside this rectangle, using the top left of
  /// the rectangle as its origin.
1818 1819 1820
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1821 1822 1823 1824 1825 1826 1827 1828
  Rect get maskRect => _maskRect;
  Rect _maskRect;
  set maskRect(Rect value) {
    if (value != _maskRect) {
      _maskRect = value;
      markNeedsAddToScene();
    }
  }
1829

1830
  /// The blend mode to apply when blending the shader with the children.
1831 1832 1833
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1834 1835 1836 1837 1838 1839 1840 1841
  BlendMode get blendMode => _blendMode;
  BlendMode _blendMode;
  set blendMode(BlendMode value) {
    if (value != _blendMode) {
      _blendMode = value;
      markNeedsAddToScene();
    }
  }
1842

1843
  @override
1844 1845 1846 1847
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
    assert(shader != null);
    assert(maskRect != null);
    assert(blendMode != null);
1848
    assert(layerOffset != null);
1849
    final Rect shiftedMaskRect = layerOffset == Offset.zero ? maskRect : maskRect.shift(layerOffset);
1850 1851 1852 1853 1854 1855
    engineLayer = builder.pushShaderMask(
      shader,
      shiftedMaskRect,
      blendMode,
      oldLayer: _engineLayer as ui.ShaderMaskEngineLayer,
    );
1856
    addChildrenToScene(builder, layerOffset);
1857 1858 1859
    builder.pop();
  }

1860
  @override
1861 1862
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1863 1864 1865
    properties.add(DiagnosticsProperty<Shader>('shader', shader));
    properties.add(DiagnosticsProperty<Rect>('maskRect', maskRect));
    properties.add(DiagnosticsProperty<BlendMode>('blendMode', blendMode));
1866 1867
  }
}
1868 1869 1870

/// A composited layer that applies a filter to the existing contents of the scene.
class BackdropFilterLayer extends ContainerLayer {
1871 1872 1873 1874
  /// Creates a backdrop filter layer.
  ///
  /// The [filter] property must be non-null before the compositing phase of the
  /// pipeline.
1875
  BackdropFilterLayer({ ui.ImageFilter filter }) : _filter = filter;
1876 1877

  /// The filter to apply to the existing contents of the scene.
1878 1879 1880
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1881 1882 1883 1884 1885 1886 1887 1888
  ui.ImageFilter get filter => _filter;
  ui.ImageFilter _filter;
  set filter(ui.ImageFilter value) {
    if (value != _filter) {
      _filter = value;
      markNeedsAddToScene();
    }
  }
1889 1890

  @override
1891 1892
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
    assert(filter != null);
1893 1894 1895 1896
    engineLayer = builder.pushBackdropFilter(
      filter,
      oldLayer: _engineLayer as ui.BackdropFilterEngineLayer,
    );
1897 1898 1899 1900
    addChildrenToScene(builder, layerOffset);
    builder.pop();
  }
}
1901

1902 1903 1904
/// 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
1905
/// relative position of lights and other physically modeled objects in the
1906
/// scene.
1907 1908 1909 1910
///
/// 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.
1911
class PhysicalModelLayer extends ContainerLayer {
1912 1913 1914
  /// Creates a composited layer that uses a physical model to producing
  /// lighting effects.
  ///
1915 1916
  /// The [clipPath], [clipBehavior], [elevation], [color], and [shadowColor]
  /// arguments must be non-null before the compositing phase of the pipeline.
1917
  PhysicalModelLayer({
1918
    Path clipPath,
1919
    Clip clipBehavior = Clip.none,
1920 1921 1922 1923
    double elevation,
    Color color,
    Color shadowColor,
  }) : _clipPath = clipPath,
1924 1925 1926 1927
       _clipBehavior = clipBehavior,
       _elevation = elevation,
       _color = color,
       _shadowColor = shadowColor;
1928

1929
  /// The path to clip in the parent's coordinate system.
1930 1931 1932
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1933 1934 1935 1936 1937 1938 1939 1940
  Path get clipPath => _clipPath;
  Path _clipPath;
  set clipPath(Path value) {
    if (value != _clipPath) {
      _clipPath = value;
      markNeedsAddToScene();
    }
  }
1941

1942 1943 1944 1945 1946 1947 1948 1949 1950 1951
  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);
  }

1952
  /// {@macro flutter.widgets.Clip}
1953 1954 1955
  Clip get clipBehavior => _clipBehavior;
  Clip _clipBehavior;
  set clipBehavior(Clip value) {
1956
    assert(value != null);
1957 1958 1959 1960 1961
    if (value != _clipBehavior) {
      _clipBehavior = value;
      markNeedsAddToScene();
    }
  }
1962

1963
  /// The z-coordinate at which to place this physical object.
1964 1965 1966
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1967 1968 1969 1970 1971 1972
  ///
  /// 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.
1973 1974 1975 1976 1977 1978 1979 1980
  double get elevation => _elevation;
  double _elevation;
  set elevation(double value) {
    if (value != _elevation) {
      _elevation = value;
      markNeedsAddToScene();
    }
  }
1981 1982

  /// The background color.
1983 1984 1985
  ///
  /// The scene must be explicitly recomposited after this property is changed
  /// (as described at [Layer]).
1986 1987 1988 1989 1990 1991 1992 1993
  Color get color => _color;
  Color _color;
  set color(Color value) {
    if (value != _color) {
      _color = value;
      markNeedsAddToScene();
    }
  }
1994

1995
  /// The shadow color.
1996 1997 1998 1999 2000 2001 2002 2003
  Color get shadowColor => _shadowColor;
  Color _shadowColor;
  set shadowColor(Color value) {
    if (value != _shadowColor) {
      _shadowColor = value;
      markNeedsAddToScene();
    }
  }
2004

2005
  @override
2006 2007 2008 2009 2010
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
    if (!clipPath.contains(localPosition))
      return false;
    return super.findAnnotations<S>(result, localPosition, onlyFirst: onlyFirst);
2011 2012
  }

2013
  @override
2014 2015 2016 2017 2018 2019 2020
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
    assert(clipPath != null);
    assert(clipBehavior != null);
    assert(elevation != null);
    assert(color != null);
    assert(shadowColor != null);

2021 2022 2023 2024 2025 2026
    bool enabled = true;
    assert(() {
      enabled = !debugDisablePhysicalShapeLayers;
      return true;
    }());
    if (enabled) {
2027
      engineLayer = builder.pushPhysicalShape(
2028
        path: layerOffset == Offset.zero ? clipPath : clipPath.shift(layerOffset),
2029 2030
        elevation: elevation,
        color: color,
2031
        shadowColor: shadowColor,
2032
        clipBehavior: clipBehavior,
2033
        oldLayer: _engineLayer as ui.PhysicalShapeEngineLayer,
2034
      );
2035 2036
    } else {
      engineLayer = null;
2037
    }
2038
    addChildrenToScene(builder, layerOffset);
2039
    if (enabled)
2040
      builder.pop();
2041 2042 2043
  }

  @override
2044 2045
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
2046
    properties.add(DoubleProperty('elevation', elevation));
2047
    properties.add(ColorProperty('color', color));
2048 2049
  }
}
2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068

/// 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
2069
  String toString() => '${describeIdentity(this)}(${ _leader != null ? "<linked>" : "<dangling>" })';
2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085
}

/// 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.
2086
  LeaderLayer({ @required LayerLink link, this.offset = Offset.zero }) : assert(link != null), _link = link;
2087 2088 2089 2090 2091

  /// 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.
2092 2093 2094 2095 2096 2097
  LayerLink get link => _link;
  set link(LayerLink value) {
    assert(value != null);
    _link = value;
  }
  LayerLink _link;
2098 2099 2100 2101 2102 2103 2104 2105 2106 2107

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

2108 2109 2110 2111
  /// {@macro flutter.leaderFollower.alwaysNeedsAddToScene}
  @override
  bool get alwaysNeedsAddToScene => true;

2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134
  @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;

2135
  @override
2136 2137 2138 2139
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
    return super.findAnnotations<S>(result, localPosition - offset, onlyFirst: onlyFirst);
  }
2140

2141
  @override
2142
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
2143 2144 2145
    assert(offset != null);
    _lastOffset = offset + layerOffset;
    if (_lastOffset != Offset.zero)
2146 2147 2148 2149
      engineLayer = builder.pushTransform(
        Matrix4.translationValues(_lastOffset.dx, _lastOffset.dy, 0.0).storage,
        oldLayer: _engineLayer as ui.TransformEngineLayer,
      );
2150
    addChildrenToScene(builder);
2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169
    if (_lastOffset != Offset.zero)
      builder.pop();
  }

  /// 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
2170 2171
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
2172 2173
    properties.add(DiagnosticsProperty<Offset>('offset', offset));
    properties.add(DiagnosticsProperty<LayerLink>('link', link));
2174 2175 2176 2177 2178 2179 2180 2181
  }
}

/// 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
2182
/// to the coordinate space of the [LeaderLayer].
2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194
///
/// 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({
2195
    @required LayerLink link,
2196 2197 2198
    this.showWhenUnlinked = true,
    this.unlinkedOffset = Offset.zero,
    this.linkedOffset = Offset.zero,
2199
  }) : assert(link != null), _link = link;
2200 2201 2202 2203 2204 2205

  /// 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].
2206 2207 2208 2209 2210 2211
  LayerLink get link => _link;
  set link(LayerLink value) {
    assert(value != null);
    _link = value;
  }
  LayerLink _link;
2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256

  /// 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;
2257
  Matrix4 _invertedTransform;
2258
  bool _inverseDirty = true;
2259

2260
  Offset _transformOffset<S>(Offset localPosition) {
2261 2262 2263
    if (_inverseDirty) {
      _invertedTransform = Matrix4.tryInvert(getLastTransform());
      _inverseDirty = false;
2264
    }
2265 2266
    if (_invertedTransform == null)
      return null;
2267
    final Vector4 vector = Vector4(localPosition.dx, localPosition.dy, 0.0, 1.0);
2268
    final Vector4 result = _invertedTransform.transform(vector);
2269 2270 2271 2272
    return Offset(result[0] - linkedOffset.dx, result[1] - linkedOffset.dy);
  }

  @override
2273 2274
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
2275
    if (link.leader == null) {
2276 2277 2278 2279
      if (showWhenUnlinked) {
        return super.findAnnotations(result, localPosition - unlinkedOffset, onlyFirst: onlyFirst);
      }
      return false;
2280
    }
2281
    final Offset transformedOffset = _transformOffset<S>(localPosition);
2282
    if (transformedOffset == null) {
2283
      return false;
2284
    }
2285
    return super.findAnnotations<S>(result, transformedOffset, onlyFirst: onlyFirst);
2286
  }
2287 2288 2289 2290

  /// The transform that was used during the last composition phase.
  ///
  /// If the [link] was not linked to a [LeaderLayer], or if this layer has
2291
  /// a degenerate matrix applied, then this will be null.
2292 2293 2294 2295 2296
  ///
  /// This method returns a new [Matrix4] instance each time it is invoked.
  Matrix4 getLastTransform() {
    if (_lastTransform == null)
      return null;
2297
    final Matrix4 result = Matrix4.translationValues(-_lastOffset.dx, -_lastOffset.dy, 0.0);
2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309
    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.
2310
    final Matrix4 result = Matrix4.identity();
2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328
    // 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.
2329
    final Set<Layer> ancestors = HashSet<Layer>();
2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361
    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;
2362
    _inverseDirty = true;
2363 2364
  }

2365
  /// {@template flutter.leaderFollower.alwaysNeedsAddToScene}
2366
  /// This disables retained rendering.
2367
  ///
2368 2369 2370 2371 2372
  /// A [FollowerLayer] copies changes from a [LeaderLayer] that could be anywhere
  /// in the Layer tree, and that leader layer could change without notifying the
  /// follower layer. Therefore we have to always call a follower layer's
  /// [addToScene]. In order to call follower layer's [addToScene], leader layer's
  /// [addToScene] must be called first so leader layer must also be considered
2373 2374 2375 2376 2377
  /// as [alwaysNeedsAddToScene].
  /// {@endtemplate}
  @override
  bool get alwaysNeedsAddToScene => true;

2378
  @override
2379
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
2380 2381 2382 2383 2384
    assert(link != null);
    assert(showWhenUnlinked != null);
    if (link.leader == null && !showWhenUnlinked) {
      _lastTransform = null;
      _lastOffset = null;
2385
      _inverseDirty = true;
2386 2387
      engineLayer = null;
      return;
2388 2389 2390
    }
    _establishTransform();
    if (_lastTransform != null) {
2391 2392 2393 2394
      engineLayer = builder.pushTransform(
        _lastTransform.storage,
        oldLayer: _engineLayer as ui.TransformEngineLayer,
      );
2395
      addChildrenToScene(builder);
2396 2397 2398 2399
      builder.pop();
      _lastOffset = unlinkedOffset + layerOffset;
    } else {
      _lastOffset = null;
2400
      final Matrix4 matrix = Matrix4.translationValues(unlinkedOffset.dx, unlinkedOffset.dy, .0);
2401 2402 2403 2404
      engineLayer = builder.pushTransform(
        matrix.storage,
        oldLayer: _engineLayer as ui.TransformEngineLayer,
      );
2405 2406
      addChildrenToScene(builder);
      builder.pop();
2407
    }
2408
    _inverseDirty = true;
2409 2410 2411 2412 2413 2414
  }

  @override
  void applyTransform(Layer child, Matrix4 transform) {
    assert(child != null);
    assert(transform != null);
2415
    if (_lastTransform != null) {
2416
      transform.multiply(_lastTransform);
2417 2418 2419
    } else {
      transform.multiply(Matrix4.translationValues(unlinkedOffset.dx, unlinkedOffset.dy, .0));
    }
2420 2421 2422
  }

  @override
2423 2424
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
2425 2426
    properties.add(DiagnosticsProperty<LayerLink>('link', link));
    properties.add(TransformProperty('transform', getLastTransform(), defaultValue: null));
2427 2428
  }
}
2429

2430 2431 2432 2433
/// A composited layer which annotates its children with a value. Pushing this
/// layer to the tree is the common way of adding an annotation.
///
/// An annotation is an optional object of any type that, when attached with a
2434 2435 2436 2437
/// layer, can be retrieved using [Layer.find] or [Layer.findAllAnnotations]
/// with a position. The search process is done recursively, controlled by a
/// concept of being opaque to a type of annotation, explained in the document
/// of [Layer.findAnnotations].
2438 2439 2440 2441 2442 2443 2444 2445 2446 2447
///
/// When an annotation search arrives, this layer defers the same search to each
/// of this layer's children, respecting their opacity. Then it adds this
/// layer's [annotation] if all of the following restrictions are met:
///
/// {@template flutter.rendering.annotatedRegionLayer.restrictions}
/// * The target type must be identical to the annotated type `T`.
/// * If [size] is provided, the target position must be contained within the
///   rectangle formed by [size] and [offset].
/// {@endtemplate}
2448
///
2449 2450
/// This layer is opaque to a type of annotation if any child is also opaque, or
/// if [opaque] is true and the layer's annotation is added.
2451
class AnnotatedRegionLayer<T> extends ContainerLayer {
2452
  /// Creates a new layer that annotates its children with [value].
2453
  ///
2454
  /// The [value] provided cannot be null.
2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465
  AnnotatedRegionLayer(
    this.value, {
    this.size,
    Offset offset,
    this.opaque = false,
  }) : assert(value != null),
       assert(opaque != null),
       offset = offset ?? Offset.zero;

  /// The annotated object, which is added to the result if all restrictions are
  /// met.
2466 2467
  final T value;

2468 2469
  /// The size of an optional clipping rectangle, used to control whether a
  /// position is contained by the annotation.
2470
  ///
2471 2472 2473 2474
  /// If [size] is provided, then the annotation is only added if the target
  /// position is contained by the rectangle formed by [size] and [offset].
  /// Otherwise no such restriction is applied, and clipping can only be done by
  /// the ancestor layers.
2475 2476
  final Size size;

2477
  /// The offset of the optional clipping rectangle that is indicated by [size].
2478
  ///
2479 2480
  /// The [offset] defaults to [Offset.zero] if not provided, and is ignored if
  /// [size] is not set.
2481
  ///
2482
  /// The [offset] only offsets the clipping rectangle, and does not affect
2483
  /// how the painting or annotation search is propagated to its children.
2484 2485
  final Offset offset;

2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510
  /// Whether the annotation of this layer should be opaque during an annotation
  /// search of type `T`, preventing siblings visually behind it from being
  /// searched.
  ///
  /// If [opaque] is true, and this layer does add its annotation [value],
  /// then the layer will always be opaque during the search.
  ///
  /// If [opaque] is false, or if this layer does not add its annotation,
  /// then the opacity of this layer will be the one returned by the children,
  /// meaning that it will be opaque if any child is opaque.
  ///
  /// The [opaque] defaults to false.
  ///
  /// The [opaque] is effectively useless during [Layer.find] (more
  /// specifically, [Layer.findAnnotations] with `onlyFirst: true`), since the
  /// search process then skips the remaining tree after finding the first
  /// annotation.
  ///
  /// See also:
  ///
  ///  * [Layer.findAnnotations], which explains the concept of being opaque
  ///    to a type of annotation as the return value.
  ///  * [HitTestBehavior], which controls similar logic when hit-testing in the
  ///    render tree.
  final bool opaque;
2511

2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531
  /// Searches the subtree for annotations of type `S` at the location
  /// `localPosition`, then adds the annotation [value] if applicable.
  ///
  /// This method always searches its children, and if any child returns `true`,
  /// the remaining children are skipped. Regardless of what the children
  /// return, this method then adds this layer's annotation if all of the
  /// following restrictions are met:
  ///
  /// {@macro flutter.rendering.annotatedRegionLayer.restrictions}
  ///
  /// This search process respects `onlyFirst`, meaning that when `onlyFirst` is
  /// true, the search will stop when it finds the first annotation from the
  /// children, and the layer's own annotation is checked only when none is
  /// given by the children.
  ///
  /// The return value is true if any child returns `true`, or if [opaque] is
  /// true and the layer's annotation is added.
  ///
  /// For explanation of layer annotations, parameters and return value, refer
  /// to [Layer.findAnnotations].
2532
  @override
2533 2534 2535 2536 2537 2538 2539
  @protected
  bool findAnnotations<S>(AnnotationResult<S> result, Offset localPosition, { @required bool onlyFirst }) {
    bool isAbsorbed = super.findAnnotations(result, localPosition, onlyFirst: onlyFirst);
    if (result.entries.isNotEmpty && onlyFirst)
      return isAbsorbed;
    if (size != null && !(offset & size).contains(localPosition)) {
      return isAbsorbed;
2540
    }
2541
    if (T == S) {
2542 2543
      isAbsorbed = isAbsorbed || opaque;
      final Object untypedValue = value;
2544
      final S typedValue = untypedValue as S;
2545 2546 2547 2548
      result.add(AnnotationEntry<S>(
        annotation: typedValue,
        localPosition: localPosition,
      ));
2549
    }
2550
    return isAbsorbed;
2551 2552 2553 2554 2555
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
2556 2557
    properties.add(DiagnosticsProperty<T>('value', value));
    properties.add(DiagnosticsProperty<Size>('size', size, defaultValue: null));
2558
    properties.add(DiagnosticsProperty<Offset>('offset', offset, defaultValue: null));
2559
    properties.add(DiagnosticsProperty<bool>('opaque', opaque, defaultValue: false));
2560 2561
  }
}