-
Notifications
You must be signed in to change notification settings - Fork 30.1k
Expand file tree
/
Copy pathlayer.dart
More file actions
3029 lines (2786 loc) · 102 KB
/
layer.dart
File metadata and controls
3029 lines (2786 loc) · 102 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
49
50
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
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
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
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
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
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
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
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
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// @docImport 'package:flutter/services.dart';
/// @docImport 'package:flutter/widgets.dart';
///
/// @docImport 'binding.dart';
/// @docImport 'object.dart';
/// @docImport 'performance_overlay.dart';
/// @docImport 'proxy_box.dart';
/// @docImport 'view.dart';
library;
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/scheduler.dart';
import 'package:vector_math/vector_math_64.dart' show Vector4;
import 'debug.dart';
/// Information collected for an annotation that is found in the layer tree.
///
/// See also:
///
/// * [Layer.findAnnotations], which create and use objects of this class.
@immutable
class AnnotationEntry<T> {
/// Create an entry of found annotation by providing the object and related
/// information.
const AnnotationEntry({required this.annotation, required this.localPosition});
/// The annotation object that is found.
final T annotation;
/// The target location described by the local coordinate space of the
/// annotation object.
final Offset localPosition;
@override
String toString() {
return '${objectRuntimeType(this, 'AnnotationEntry')}(annotation: $annotation, localPosition: $localPosition)';
}
}
/// Information collected about a list of annotations that are found in the
/// layer tree.
///
/// See also:
///
/// * [AnnotationEntry], which are members of this class.
/// * [Layer.findAllAnnotations], and [Layer.findAnnotations], which create and
/// use an object of this class.
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 {
return _entries.map((AnnotationEntry<T> entry) => entry.annotation);
}
}
/// A composited layer.
///
/// During painting, the render tree generates a tree of composited layers that
/// are uploaded into the engine and displayed by the compositor. This class is
/// the base class for all composited layers.
///
/// 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.
///
/// To composite the tree, create a [ui.SceneBuilder] object using
/// [RendererBinding.createSceneBuilder], pass it to the root [Layer] object's
/// [addToScene] method, and then call [ui.SceneBuilder.build] to obtain a [ui.Scene].
/// A [ui.Scene] can then be painted using [ui.FlutterView.render].
///
/// ## Memory
///
/// Layers retain resources between frames to speed up rendering. A layer will
/// retain these resources until all [LayerHandle]s referring to the layer have
/// nulled out their references.
///
/// Layers must not be used after disposal. If a RenderObject needs to maintain
/// a layer for later usage, it must create a handle to that layer. This is
/// handled automatically for the [RenderObject.layer] property, but additional
/// layers must use their own [LayerHandle].
///
/// {@tool snippet}
///
/// This [RenderObject] is a repaint boundary that pushes an additional
/// [ClipRectLayer].
///
/// ```dart
/// class ClippingRenderObject extends RenderBox {
/// final LayerHandle<ClipRectLayer> _clipRectLayer = LayerHandle<ClipRectLayer>();
///
/// @override
/// bool get isRepaintBoundary => true; // The [layer] property will be used.
///
/// @override
/// void paint(PaintingContext context, Offset offset) {
/// _clipRectLayer.layer = context.pushClipRect(
/// needsCompositing,
/// offset,
/// Offset.zero & size,
/// super.paint,
/// oldLayer: _clipRectLayer.layer,
/// );
/// }
///
/// @override
/// void dispose() {
/// _clipRectLayer.layer = null;
/// super.dispose();
/// }
/// }
/// ```
/// {@end-tool}
/// See also:
///
/// * [RenderView.compositeFrame], which implements this recomposition protocol
/// for painting [RenderObject] trees on the display.
abstract class Layer with DiagnosticableTreeMixin {
/// Creates an instance of Layer.
Layer() {
assert(debugMaybeDispatchCreated('rendering', 'Layer', this));
}
final Map<int, VoidCallback> _callbacks = <int, VoidCallback>{};
static int _nextCallbackId = 0;
/// Whether the subtree rooted at this layer has any composition callback
/// observers.
///
/// This only evaluates to true if the subtree rooted at this node has
/// observers. For example, it may evaluate to true on a parent node but false
/// on a child if the parent has observers but the child does not.
///
/// See also:
///
/// * [Layer.addCompositionCallback].
bool get subtreeHasCompositionCallbacks => _compositionCallbackCount > 0;
int _compositionCallbackCount = 0;
void _updateSubtreeCompositionObserverCount(int delta) {
assert(delta != 0);
_compositionCallbackCount += delta;
assert(_compositionCallbackCount >= 0);
parent?._updateSubtreeCompositionObserverCount(delta);
}
void _fireCompositionCallbacks({required bool includeChildren}) {
if (_callbacks.isEmpty) {
return;
}
for (final callback in List<VoidCallback>.of(_callbacks.values)) {
callback();
}
}
bool _debugMutationsLocked = false;
/// Whether or not this layer, or any child layers, can be rasterized with
/// [ui.Scene.toImage] or [ui.Scene.toImageSync].
///
/// If `false`, calling the above methods may yield an image which is
/// incomplete.
///
/// This value may change throughout the lifetime of the object, as the
/// child layers themselves are added or removed.
bool supportsRasterization() {
return true;
}
/// Describes the clip that would be applied to contents of this layer,
/// if any.
Rect? describeClipBounds() => null;
/// Adds a callback for when the layer tree that this layer is part of gets
/// composited, or when it is detached and will not be rendered again.
///
/// This callback will fire even if an ancestor layer is added with retained
/// rendering, meaning that it will fire even if this layer gets added to the
/// scene via some call to [ui.SceneBuilder.addRetained] on one of its
/// ancestor layers.
///
/// The callback receives a reference to this layer. The recipient must not
/// mutate the layer during the scope of the callback, but may traverse the
/// tree to find information about the current transform or clip. The layer
/// may not be [attached] anymore in this state, but even if it is detached it
/// may still have an also detached parent it can visit.
///
/// If new callbacks are added or removed within the [callback], the new
/// callbacks will fire (or stop firing) on the _next_ compositing event.
///
/// {@template flutter.rendering.Layer.compositionCallbacks}
/// Composition callbacks are useful in place of pushing a layer that would
/// otherwise try to observe the layer tree without actually affecting
/// compositing. For example, a composition callback may be used to observe
/// the total transform and clip of the current container layer to determine
/// whether a render object drawn into it is visible or not.
///
/// Calling the returned callback will remove [callback] from the composition
/// callbacks.
/// {@endtemplate}
VoidCallback addCompositionCallback(CompositionCallback callback) {
_updateSubtreeCompositionObserverCount(1);
final int callbackId = _nextCallbackId += 1;
_callbacks[callbackId] = () {
assert(() {
_debugMutationsLocked = true;
return true;
}());
callback(this);
assert(() {
_debugMutationsLocked = false;
return true;
}());
};
return () {
assert(debugDisposed || _callbacks.containsKey(callbackId));
_callbacks.remove(callbackId);
_updateSubtreeCompositionObserverCount(-1);
};
}
/// If asserts are enabled, returns whether [dispose] has
/// been called since the last time any retained resources were created.
///
/// Throws an exception if asserts are disabled.
bool get debugDisposed {
late bool disposed;
assert(() {
disposed = _debugDisposed;
return true;
}());
return disposed;
}
bool _debugDisposed = false;
/// Set when this layer is appended to a [ContainerLayer], and
/// unset when it is removed.
///
/// This cannot be set from [attach] or [detach] which is called when an
/// entire subtree is attached to or detached from an owner. Layers may be
/// appended to or removed from a [ContainerLayer] regardless of whether they
/// are attached or detached, and detaching a layer from an owner does not
/// imply that it has been removed from its parent.
final LayerHandle<Layer> _parentHandle = LayerHandle<Layer>();
/// Incremented by [LayerHandle].
int _refCount = 0;
/// Called by [LayerHandle].
void _unref() {
assert(!_debugMutationsLocked);
assert(_refCount > 0);
_refCount -= 1;
if (_refCount == 0) {
dispose();
}
}
/// Returns the number of objects holding a [LayerHandle] to this layer.
///
/// This method throws if asserts are disabled.
int get debugHandleCount {
late int count;
assert(() {
count = _refCount;
return true;
}());
return count;
}
/// Clears any retained resources that this layer holds.
///
/// This method must dispose resources such as [ui.EngineLayer] and [ui.Picture]
/// objects. The layer is still usable after this call, but any graphics
/// related resources it holds will need to be recreated.
///
/// This method _only_ disposes resources for this layer. For example, if it
/// is a [ContainerLayer], it does not dispose resources of any children.
/// However, [ContainerLayer]s do remove any children they have when
/// this method is called, and if this layer was the last holder of a removed
/// child handle, the child may recursively clean up its resources.
///
/// This method automatically gets called when all outstanding [LayerHandle]s
/// are disposed. [LayerHandle] objects are typically held by the [parent]
/// layer of this layer and any [RenderObject]s that participated in creating
/// it.
///
/// After calling this method, the object is unusable.
@mustCallSuper
@protected
@visibleForTesting
void dispose() {
assert(!_debugMutationsLocked);
assert(
!_debugDisposed,
'Layers must only be disposed once. This is typically handled by '
'LayerHandle and createHandle. Subclasses should not directly call '
'dispose, except to call super.dispose() in an overridden dispose '
'method. Tests must only call dispose once.',
);
assert(() {
assert(
_refCount == 0,
'Do not directly call dispose on a $runtimeType. Instead, '
'use createHandle and LayerHandle.dispose.',
);
_debugDisposed = true;
return true;
}());
assert(debugMaybeDispatchDisposed(this));
_engineLayer?.dispose();
_engineLayer = null;
}
/// 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.
ContainerLayer? get parent => _parent;
ContainerLayer? _parent;
// Whether this layer has any changes since its last call to [addToScene].
//
// 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.
bool _needsAddToScene = true;
/// Mark that this layer has changed and [addToScene] needs to be called.
@protected
@visibleForTesting
void markNeedsAddToScene() {
assert(!_debugMutationsLocked);
assert(
!alwaysNeedsAddToScene,
'$runtimeType with alwaysNeedsAddToScene set called markNeedsAddToScene.\n'
"The layer's alwaysNeedsAddToScene is set to true, and therefore it should not call markNeedsAddToScene.",
);
assert(!_debugDisposed);
// Already marked. Short-circuit.
if (_needsAddToScene) {
return;
}
_needsAddToScene = true;
}
/// Mark that this layer is in sync with engine.
///
/// This is for debugging and testing purposes only. In release builds
/// this method has no effect.
@visibleForTesting
void debugMarkClean() {
assert(!_debugMutationsLocked);
assert(() {
_needsAddToScene = false;
return true;
}());
}
/// Subclasses may override this to true to disable retained rendering.
@protected
bool get alwaysNeedsAddToScene => false;
/// Whether this or any descendant layer in the subtree needs [addToScene].
///
/// This is for debug and test purpose only. It only becomes valid after
/// calling [updateSubtreeNeedsAddToScene].
@visibleForTesting
bool? get debugSubtreeNeedsAddToScene {
bool? result;
assert(() {
result = _needsAddToScene;
return true;
}());
return result;
}
/// 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
@visibleForTesting
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
@visibleForTesting
set engineLayer(ui.EngineLayer? value) {
assert(!_debugMutationsLocked);
assert(!_debugDisposed);
_engineLayer?.dispose();
_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();
}
}
}
ui.EngineLayer? _engineLayer;
/// Traverses the layer subtree starting from this layer and determines whether it needs [addToScene].
///
/// 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.
@protected
@visibleForTesting
void updateSubtreeNeedsAddToScene() {
assert(!_debugMutationsLocked);
_needsAddToScene = _needsAddToScene || alwaysNeedsAddToScene;
}
/// The owner for this layer (null if unattached).
///
/// The entire layer tree that this layer belongs to will have the same owner.
///
/// Typically the owner is a [RenderView].
Object? get owner => _owner;
Object? _owner;
/// Whether the layer tree containing this layer is attached to an owner.
///
/// This becomes true during the call to [attach].
///
/// This becomes false during the call to [detach].
bool get attached => _owner != null;
/// Mark this layer as attached to the given owner.
///
/// Typically called only from the [parent]'s [attach] method, and by the
/// [owner] to mark the root of a tree as attached.
///
/// Subclasses with children should override this method to
/// [attach] all their children to the same [owner]
/// after calling the inherited method, as in `super.attach(owner)`.
@mustCallSuper
void attach(covariant Object owner) {
assert(_owner == null);
_owner = owner;
}
/// Mark this layer as detached from its owner.
///
/// Typically called only from the [parent]'s [detach], and by the [owner] to
/// mark the root of a tree as detached.
///
/// Subclasses with children should override this method to
/// [detach] all their children after calling the inherited method,
/// as in `super.detach()`.
@mustCallSuper
void detach() {
assert(_owner != null);
_owner = null;
assert(parent == null || attached == parent!.attached);
}
/// The depth of this layer in the layer tree.
///
/// The depth of nodes in a tree monotonically increases as you traverse down
/// the tree. There's no guarantee regarding depth between siblings.
///
/// The depth is used to ensure that nodes are processed in depth order.
int get depth => _depth;
int _depth = 0;
/// Adjust the [depth] of this node's children, if any.
///
/// Override this method in subclasses with child nodes to call
/// [ContainerLayer.redepthChild] for each child. Do not call this method
/// directly.
@protected
void redepthChildren() {
// ContainerLayer provides an implementation since its the only one that
// can actually have children.
}
/// This layer's next sibling in the parent layer's child list.
Layer? get nextSibling => _nextSibling;
Layer? _nextSibling;
/// This layer's previous sibling in the parent layer's child list.
Layer? get previousSibling => _previousSibling;
Layer? _previousSibling;
/// Removes this layer from its parent layer's child list.
///
/// This has no effect if the layer's parent is already null.
@mustCallSuper
void remove() {
assert(!_debugMutationsLocked);
parent?._removeChild(this);
}
/// 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
/// [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 always returns `false`, which means neither
/// the layer nor its children has annotations, and the annotation search
/// is not absorbed either (see below for explanation).
///
/// ## About layer annotations
///
/// {@template flutter.rendering.Layer.findAnnotations.aboutAnnotations}
/// An annotation is an optional object of any type that can be carried with a
/// layer. An annotation can be found at a location as long as the owner layer
/// contains the location and is walked to.
///
/// 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.
///
/// The common way for a value to be found here is by pushing an
/// [AnnotatedRegionLayer] into the layer tree, or by adding the desired
/// annotation by overriding [findAnnotations].
/// {@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 extends Object>(
AnnotationResult<S> result,
Offset localPosition, {
required bool onlyFirst,
}) {
return false;
}
/// 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 calls [findAnnotations] with `onlyFirst:
/// 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.
///
/// ## About layer annotations
///
/// {@macro flutter.rendering.Layer.findAnnotations.aboutAnnotations}
///
/// See also:
///
/// * [findAllAnnotations], which is similar but returns all annotations found
/// at the given position.
/// * [AnnotatedRegionLayer], for placing values in the layer tree.
S? find<S extends Object>(Offset localPosition) {
final result = AnnotationResult<S>();
findAnnotations<S>(result, localPosition, onlyFirst: true);
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 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.
/// * [AnnotatedRegionLayer], for placing values in the layer tree.
AnnotationResult<S> findAllAnnotations<S extends Object>(Offset localPosition) {
final result = AnnotationResult<S>();
findAnnotations<S>(result, localPosition, onlyFirst: false);
return result;
}
/// Override this method to upload this layer to the engine.
@protected
void addToScene(ui.SceneBuilder builder);
void _addToSceneWithRetainedRendering(ui.SceneBuilder builder) {
assert(!_debugMutationsLocked);
// There can't be a loop by adding a retained layer subtree whose
// _needsAddToScene is false.
//
// Proof by contradiction:
//
// If we introduce a loop, this retained layer must be appended to one of
// its descendant layers, say A. That means the child structure of A has
// changed so A's _needsAddToScene is true. This contradicts
// _needsAddToScene being false.
if (!_needsAddToScene && _engineLayer != null) {
builder.addRetained(_engineLayer!);
return;
}
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;
}
/// 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.
Object? debugCreator;
@override
String toStringShort() => '${super.toStringShort()}${owner == null ? " DETACHED" : ""}';
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(
DiagnosticsProperty<Object>(
'owner',
owner,
level: parent != null ? DiagnosticLevel.hidden : DiagnosticLevel.info,
defaultValue: null,
),
);
properties.add(
DiagnosticsProperty<Object?>(
'creator',
debugCreator,
defaultValue: null,
level: DiagnosticLevel.debug,
),
);
if (_engineLayer != null) {
properties.add(DiagnosticsProperty<String>('engine layer', describeIdentity(_engineLayer)));
}
properties.add(DiagnosticsProperty<int>('handles', debugHandleCount));
}
}
/// A handle to prevent a [Layer]'s platform graphics resources from being
/// disposed.
///
/// [Layer] objects retain native resources such as [ui.EngineLayer]s and [ui.Picture]
/// objects. These objects may in turn retain large chunks of texture memory,
/// either directly or indirectly.
///
/// The layer's native resources must be retained as long as there is some
/// object that can add it to a scene. Typically, this is either its
/// [Layer.parent] or an undisposed [RenderObject] that will append it to a
/// [ContainerLayer]. Layers automatically hold a handle to their children, and
/// RenderObjects automatically hold a handle to their [RenderObject.layer] as
/// well as any [PictureLayer]s that they paint into using the
/// [PaintingContext.canvas]. A layer automatically releases its resources once
/// at least one handle has been acquired and all handles have been disposed.
/// [RenderObject]s that create additional layer objects must manually manage
/// the handles for that layer similarly to the implementation of
/// [RenderObject.layer].
///
/// A handle is automatically managed for [RenderObject.layer].
///
/// If a [RenderObject] creates layers in addition to its [RenderObject.layer]
/// and it intends to reuse those layers separately from [RenderObject.layer],
/// it must create a handle to that layer and dispose of it when the layer is
/// no longer needed. For example, if it re-creates or nulls out an existing
/// layer in [RenderObject.paint], it should dispose of the handle to the
/// old layer. It should also dispose of any layer handles it holds in
/// [RenderObject.dispose].
///
/// To dispose of a layer handle, set its [layer] property to null.
class LayerHandle<T extends Layer> {
/// Create a new layer handle, optionally referencing a [Layer].
LayerHandle([this._layer]) {
if (_layer != null) {
_layer!._refCount += 1;
}
}
T? _layer;
/// The [Layer] whose resources this object keeps alive.
///
/// Setting a new value or null will dispose the previously held layer if
/// there are no other open handles to that layer.
T? get layer => _layer;
set layer(T? layer) {
assert(
layer?.debugDisposed != true,
'Attempted to create a handle to an already disposed layer: $layer.',
);
if (identical(layer, _layer)) {
return;
}
_layer?._unref();
_layer = layer;
if (_layer != null) {
_layer!._refCount += 1;
}
}
@override
String toString() => 'LayerHandle(${_layer != null ? _layer.toString() : 'DISPOSED'})';
}
/// A composited layer containing a [ui.Picture].
///
/// Picture layers are always leaves in the layer tree. They are also
/// responsible for disposing of the [ui.Picture] object they hold. This is
/// typically done when their parent and all [RenderObject]s that participated
/// in painting the picture have been disposed.
class PictureLayer extends Layer {
/// Creates a leaf layer for the layer tree.
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
/// [debugDumpLayerTree] (which can be triggered by pressing "L" when using
/// "flutter run" at the console), which can help debug why certain drawing
/// commands are being culled.
final Rect canvasBounds;
/// The picture recorded for this layer.
///
/// The picture's coordinate system matches this layer's coordinate system.
///
/// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]).
ui.Picture? get picture => _picture;
ui.Picture? _picture;
set picture(ui.Picture? picture) {
assert(!_debugDisposed);
markNeedsAddToScene();
_picture?.dispose();
_picture = picture;
}
/// 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.
///
/// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]).
bool get isComplexHint => _isComplexHint;
bool _isComplexHint = false;
set isComplexHint(bool value) {
if (value != _isComplexHint) {
_isComplexHint = value;
markNeedsAddToScene();
}
}
/// 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.
///
/// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]).
bool get willChangeHint => _willChangeHint;
bool _willChangeHint = false;
set willChangeHint(bool value) {
if (value != _willChangeHint) {
_willChangeHint = value;
markNeedsAddToScene();
}
}
@override
void dispose() {
picture = null; // Will dispose _picture.
super.dispose();
}
@override
void addToScene(ui.SceneBuilder builder) {
assert(picture != null);
builder.addPicture(
Offset.zero,
picture!,
isComplexHint: isComplexHint,
willChangeHint: willChangeHint,
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<Rect>('paint bounds', canvasBounds));
properties.add(DiagnosticsProperty<String>('picture', describeIdentity(_picture)));
properties.add(
DiagnosticsProperty<String>(
'raster cache hints',
'isComplex = $isComplexHint, willChange = $willChangeHint',
),
);
}
@override
bool findAnnotations<S extends Object>(
AnnotationResult<S> result,
Offset localPosition, {
required bool onlyFirst,
}) {
return false;
}
}
/// 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:
///
/// * [TextureRegistry](/javadoc/io/flutter/view/TextureRegistry.html)
/// for how to create and manage backend textures on Android.
/// * [TextureRegistry Protocol](/ios-embedder/protocol_flutter_texture_registry-p.html)
/// for how to create and manage backend textures on iOS.
class TextureLayer extends Layer {
/// Creates a texture layer bounded by [rect] and with backend texture
/// identified by [textureId], if [freeze] is true new texture frames will not be
/// populated to the texture, and use [filterQuality] to set layer's [FilterQuality].
TextureLayer({
required this.rect,
required this.textureId,
this.freeze = false,
this.filterQuality = ui.FilterQuality.low,
});
/// Bounding rectangle of this layer.
final Rect rect;
/// The identity of the backend texture.
final int textureId;
/// When true the texture will not be updated with new frames.
///
/// This is used for resizing 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 work around 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.
final bool freeze;
/// {@macro flutter.widgets.Texture.filterQuality}
final ui.FilterQuality filterQuality;
@override
void addToScene(ui.SceneBuilder builder) {
builder.addTexture(
textureId,
offset: rect.topLeft,
width: rect.width,
height: rect.height,
freeze: freeze,
filterQuality: filterQuality,
);
}
@override
bool findAnnotations<S extends Object>(
AnnotationResult<S> result,
Offset localPosition, {
required bool onlyFirst,
}) {
return false;
}