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
// 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.
import 'dart:ui' as ui show Codec, ImmutableBuffer, instantiateImageCodec, instantiateImageCodecFromBuffer;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart' show ServicesBinding;
import 'image_cache.dart';
import 'shader_warm_up.dart';
/// Binding for the painting library.
///
/// Hooks into the cache eviction logic to clear the image cache.
///
/// Requires the [ServicesBinding] to be mixed in earlier.
mixin PaintingBinding on BindingBase, ServicesBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
_imageCache = createImageCache();
shaderWarmUp?.execute();
}
/// The current [PaintingBinding], if one has been created.
///
/// Provides access to the features exposed by this mixin. The binding must
/// be initialized before using this getter; this is typically done by calling
/// [runApp] or [WidgetsFlutterBinding.ensureInitialized].
static PaintingBinding get instance => BindingBase.checkInstance(_instance);
static PaintingBinding? _instance;
/// [ShaderWarmUp] instance to be executed during [initInstances].
///
/// Defaults to `null`, meaning no shader warm-up is done. Some platforms may
/// not support shader warm-up before at least one frame has been displayed.
///
/// If the application has scenes that require the compilation of complex
/// shaders, it may cause jank in the middle of an animation or interaction.
/// In that case, setting [shaderWarmUp] to a custom [ShaderWarmUp] before
/// creating the binding (usually before [runApp] for normal Flutter apps, and
/// before [enableFlutterDriverExtension] for Flutter driver tests) may help
/// if that object paints the difficult scene in its
/// [ShaderWarmUp.warmUpOnCanvas] method, as this allows Flutter to
/// pre-compile and cache the required shaders during startup.
///
/// Currently the warm-up happens synchronously on the raster thread which
/// means the rendering of the first frame on the raster thread will be
/// postponed until the warm-up is finished.
///
/// The warm up is only costly (100ms-200ms, depending on the shaders to
/// compile) during the first run after the installation or a data wipe. The
/// warm up does not block the platform thread so there should be no
/// "Application Not Responding" warning.
///
/// If this is null, no shader warm-up is executed.
///
/// See also:
///
/// * [ShaderWarmUp], the interface for implementing custom warm-up scenes.
/// * <https://flutter.dev/docs/perf/rendering/shader>
static ShaderWarmUp? shaderWarmUp;
/// The singleton that implements the Flutter framework's image cache.
///
/// The cache is used internally by [ImageProvider] and should generally not
/// be accessed directly.
///
/// The image cache is created during startup by the [createImageCache]
/// method.
ImageCache get imageCache => _imageCache;
late ImageCache _imageCache;
/// Creates the [ImageCache] singleton (accessible via [imageCache]).
///
/// This method can be overridden to provide a custom image cache.
@protected
ImageCache createImageCache() => ImageCache();
/// Calls through to [dart:ui.instantiateImageCodec] from [ImageCache].
///
/// This method is deprecated. use [instantiateImageCodecFromBuffer] with an
/// [ImmutableBuffer] instance instead of this method.
///
/// The `cacheWidth` and `cacheHeight` parameters, when specified, indicate
/// the size to decode the image to.
///
/// Both `cacheWidth` and `cacheHeight` must be positive values greater than
/// or equal to 1, or null. It is valid to specify only one of `cacheWidth`
/// and `cacheHeight` with the other remaining null, in which case the omitted
/// dimension will be scaled to maintain the aspect ratio of the original
/// dimensions. When both are null or omitted, the image will be decoded at
/// its native resolution.
///
/// The `allowUpscaling` parameter determines whether the `cacheWidth` or
/// `cacheHeight` parameters are clamped to the intrinsic width and height of
/// the original image. By default, the dimensions are clamped to avoid
/// unnecessary memory usage for images. Callers that wish to display an image
/// above its native resolution should prefer scaling the canvas the image is
/// drawn into.
@Deprecated(
'Use instantiateImageCodecFromBuffer with an ImmutableBuffer instance instead. '
'This feature was deprecated after v2.13.0-1.0.pre.',
)
Future<ui.Codec> instantiateImageCodec(
Uint8List bytes, {
int? cacheWidth,
int? cacheHeight,
bool allowUpscaling = false,
}) {
assert(cacheWidth == null || cacheWidth > 0);
assert(cacheHeight == null || cacheHeight > 0);
assert(allowUpscaling != null);
return ui.instantiateImageCodec(
bytes,
targetWidth: cacheWidth,
targetHeight: cacheHeight,
allowUpscaling: allowUpscaling,
);
}
/// Calls through to [dart:ui.instantiateImageCodecFromBuffer] from [ImageCache].
///
/// The [buffer] parameter should be an [ui.ImmutableBuffer] instance which can
/// be acquired from [ui.ImmutableBuffer.fromUint8List] or [ui.ImmutableBuffer.fromAsset].
///
/// The [cacheWidth] and [cacheHeight] parameters, when specified, indicate
/// the size to decode the image to.
///
/// Both [cacheWidth] and [cacheHeight] must be positive values greater than
/// or equal to 1, or null. It is valid to specify only one of `cacheWidth`
/// and [cacheHeight] with the other remaining null, in which case the omitted
/// dimension will be scaled to maintain the aspect ratio of the original
/// dimensions. When both are null or omitted, the image will be decoded at
/// its native resolution.
///
/// The [allowUpscaling] parameter determines whether the `cacheWidth` or
/// [cacheHeight] parameters are clamped to the intrinsic width and height of
/// the original image. By default, the dimensions are clamped to avoid
/// unnecessary memory usage for images. Callers that wish to display an image
/// above its native resolution should prefer scaling the canvas the image is
/// drawn into.
Future<ui.Codec> instantiateImageCodecFromBuffer(
ui.ImmutableBuffer buffer, {
int? cacheWidth,
int? cacheHeight,
bool allowUpscaling = false,
}) {
assert(cacheWidth == null || cacheWidth > 0);
assert(cacheHeight == null || cacheHeight > 0);
assert(allowUpscaling != null);
return ui.instantiateImageCodecFromBuffer(
buffer,
targetWidth: cacheWidth,
targetHeight: cacheHeight,
allowUpscaling: allowUpscaling,
);
}
@override
void evict(String asset) {
super.evict(asset);
imageCache.clear();
imageCache.clearLiveImages();
}
@override
void handleMemoryPressure() {
super.handleMemoryPressure();
imageCache.clear();
}
/// Listenable that notifies when the available fonts on the system have
/// changed.
///
/// System fonts can change when the system installs or removes new font. To
/// correctly reflect the change, it is important to relayout text related
/// widgets when this happens.
///
/// Objects that show text and/or measure text (e.g. via [TextPainter] or
/// [Paragraph]) should listen to this and redraw/remeasure.
Listenable get systemFonts => _systemFonts;
final _SystemFontsNotifier _systemFonts = _SystemFontsNotifier();
@override
Future<void> handleSystemMessage(Object systemMessage) async {
await super.handleSystemMessage(systemMessage);
final Map<String, dynamic> message = systemMessage as Map<String, dynamic>;
final String type = message['type'] as String;
switch (type) {
case 'fontsChange':
_systemFonts.notifyListeners();
break;
}
return;
}
}
class _SystemFontsNotifier extends Listenable {
final Set<VoidCallback> _systemFontsCallbacks = <VoidCallback>{};
void notifyListeners () {
for (final VoidCallback callback in _systemFontsCallbacks) {
callback();
}
}
@override
void addListener(VoidCallback listener) {
_systemFontsCallbacks.add(listener);
}
@override
void removeListener(VoidCallback listener) {
_systemFontsCallbacks.remove(listener);
}
}
/// The singleton that implements the Flutter framework's image cache.
///
/// The cache is used internally by [ImageProvider] and should generally not be
/// accessed directly.
///
/// The image cache is created during startup by the [PaintingBinding]'s
/// [PaintingBinding.createImageCache] method.
ImageCache get imageCache => PaintingBinding.instance.imageCache;