binding.dart 8.04 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;
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 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
  /// 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.
102 103 104 105
  @Deprecated(
    'Use instantiateImageCodecWithSize instead. '
    'This feature was deprecated after v3.7.0-1.4.pre.',
  )
106 107 108 109 110 111 112 113 114 115 116 117
  Future<ui.Codec> instantiateImageCodecFromBuffer(
    ui.ImmutableBuffer buffer, {
    int? cacheWidth,
    int? cacheHeight,
    bool allowUpscaling = false,
  }) {
    assert(cacheWidth == null || cacheWidth > 0);
    assert(cacheHeight == null || cacheHeight > 0);
    return ui.instantiateImageCodecFromBuffer(
      buffer,
      targetWidth: cacheWidth,
      targetHeight: cacheHeight,
118
      allowUpscaling: allowUpscaling,
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
  /// Calls through to [dart:ui.instantiateImageCodecWithSize] from [ImageCache].
  ///
  /// The [buffer] parameter should be an [ui.ImmutableBuffer] instance which can
  /// be acquired from [ui.ImmutableBuffer.fromUint8List] or
  /// [ui.ImmutableBuffer.fromAsset].
  ///
  /// The [getTargetSize] parameter, when specified, will be invoked and passed
  /// the image's intrinsic size to determine the size to decode the image to.
  /// The width and the height of the size it returns must be positive values
  /// greater than or equal to 1, or null. It is valid to return a [TargetImageSize]
  /// that specifies only one of `width` and `height` 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 (as will be the case if
  /// the [getTargetSize] parameter is omitted).
  Future<ui.Codec> instantiateImageCodecWithSize(
    ui.ImmutableBuffer buffer, {
    ui.TargetImageSizeCallback? getTargetSize,
  }) {
    return ui.instantiateImageCodecWithSize(buffer, getTargetSize: getTargetSize);
  }

144 145 146
  @override
  void evict(String asset) {
    super.evict(asset);
147 148
    imageCache.clear();
    imageCache.clearLiveImages();
149
  }
150

151 152 153
  @override
  void handleMemoryPressure() {
    super.handleMemoryPressure();
154
    imageCache.clear();
155 156
  }

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
  /// 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);
172 173
    final Map<String, dynamic> message = systemMessage as Map<String, dynamic>;
    final String type = message['type'] as String;
174 175 176 177 178 179 180 181 182 183 184 185
    switch (type) {
      case 'fontsChange':
        _systemFonts.notifyListeners();
    }
    return;
  }
}

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

  void notifyListeners () {
186
    for (final VoidCallback callback in _systemFontsCallbacks) {
187 188 189 190 191 192 193 194 195 196 197 198
      callback();
    }
  }

  @override
  void addListener(VoidCallback listener) {
    _systemFontsCallbacks.add(listener);
  }
  @override
  void removeListener(VoidCallback listener) {
    _systemFontsCallbacks.remove(listener);
  }
199 200 201 202 203 204 205 206 207
}

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