binding.dart 8.64 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:ui' as ui show instantiateImageCodec, instantiateImageCodecFromBuffer, Codec, ImmutableBuffer;
6 7 8 9
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart' show ServicesBinding;

import 'image_cache.dart';
10
import 'shader_warm_up.dart';
11 12 13 14 15 16

/// Binding for the painting library.
///
/// Hooks into the cache eviction logic to clear the image cache.
///
/// Requires the [ServicesBinding] to be mixed in earlier.
17
mixin PaintingBinding on BindingBase, ServicesBinding {
18 19 20 21 22
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _imageCache = createImageCache();
23
    shaderWarmUp?.execute();
24 25 26
  }

  /// The current [PaintingBinding], if one has been created.
27 28 29 30 31
  ///
  /// 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);
32
  static PaintingBinding? _instance;
33

34 35
  /// [ShaderWarmUp] instance to be executed during [initInstances].
  ///
36 37
  /// 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.
38 39
  ///
  /// If the application has scenes that require the compilation of complex
40 41 42 43 44 45 46
  /// 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.
47
  ///
48 49 50
  /// 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.
51
  ///
52 53 54 55 56 57 58
  /// 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.
  ///
59 60
  /// See also:
  ///
61 62
  ///  * [ShaderWarmUp], the interface for implementing custom warm-up scenes.
  ///  * <https://flutter.dev/docs/perf/rendering/shader>
63
  static ShaderWarmUp? shaderWarmUp;
64

65 66 67 68 69 70 71
  /// 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.
72 73
  ImageCache get imageCache => _imageCache;
  late ImageCache _imageCache;
74 75 76 77 78

  /// Creates the [ImageCache] singleton (accessible via [imageCache]).
  ///
  /// This method can be overridden to provide a custom image cache.
  @protected
79
  ImageCache createImageCache() => ImageCache();
80

81
  /// Calls through to [dart:ui.instantiateImageCodec] from [ImageCache].
82
  ///
83 84 85
  /// This method is deprecated. use [instantiateImageCodecFromBuffer] with an
  /// [ImmutableBuffer] instance instead of this method.
  ///
86 87
  /// The `cacheWidth` and `cacheHeight` parameters, when specified, indicate
  /// the size to decode the image to.
88
  ///
89 90 91 92 93 94 95 96 97 98 99 100 101
  /// 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.
102 103 104 105
  @Deprecated(
    'Use instantiateImageCodecFromBuffer with an ImmutableBuffer instance instead. '
    'This feature was deprecated after v2.13.0-1.0.pre.',
  )
106 107
  Future<ui.Codec> instantiateImageCodec(
    Uint8List bytes, {
108 109
    int? cacheWidth,
    int? cacheHeight,
110
    bool allowUpscaling = false,
111 112 113
  }) {
    assert(cacheWidth == null || cacheWidth > 0);
    assert(cacheHeight == null || cacheHeight > 0);
114
    assert(allowUpscaling != null);
115 116 117 118
    return ui.instantiateImageCodec(
      bytes,
      targetWidth: cacheWidth,
      targetHeight: cacheHeight,
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
      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,
157
      allowUpscaling: allowUpscaling,
158
    );
159 160
  }

161 162 163
  @override
  void evict(String asset) {
    super.evict(asset);
164 165
    imageCache.clear();
    imageCache.clearLiveImages();
166
  }
167

168 169 170
  @override
  void handleMemoryPressure() {
    super.handleMemoryPressure();
171
    imageCache.clear();
172 173
  }

174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
  /// 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);
189 190
    final Map<String, dynamic> message = systemMessage as Map<String, dynamic>;
    final String type = message['type'] as String;
191 192 193 194 195 196 197 198 199 200 201 202 203
    switch (type) {
      case 'fontsChange':
        _systemFonts.notifyListeners();
        break;
    }
    return;
  }
}

class _SystemFontsNotifier extends Listenable {
  final Set<VoidCallback> _systemFontsCallbacks = <VoidCallback>{};

  void notifyListeners () {
204
    for (final VoidCallback callback in _systemFontsCallbacks) {
205 206 207 208 209 210 211 212 213 214 215 216
      callback();
    }
  }

  @override
  void addListener(VoidCallback listener) {
    _systemFontsCallbacks.add(listener);
  }
  @override
  void removeListener(VoidCallback listener) {
    _systemFontsCallbacks.remove(listener);
  }
217 218 219 220 221 222 223 224 225
}

/// 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.
226
ImageCache get imageCache => PaintingBinding.instance.imageCache;