binding.dart 6.08 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

6 7
import 'dart:typed_data' show Uint8List;
import 'dart:ui' as ui show instantiateImageCodec, Codec;
8 9 10 11
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart' show ServicesBinding;

import 'image_cache.dart';
12
import 'shader_warm_up.dart';
13 14 15 16 17 18

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

  /// The current [PaintingBinding], if one has been created.
29 30
  static PaintingBinding? get instance => _instance;
  static PaintingBinding? _instance;
31

32 33 34 35 36 37
  /// [ShaderWarmUp] to be executed during [initInstances].
  ///
  /// If the application has scenes that require the compilation of complex
  /// shaders that are not covered by [DefaultShaderWarmUp], it may cause jank
  /// in the middle of an animation or interaction. In that case, set
  /// [shaderWarmUp] to a custom [ShaderWarmUp] before calling [initInstances]
38
  /// (usually before [runApp] for normal Flutter apps, and before
39
  /// [enableFlutterDriverExtension] for Flutter driver tests). Paint the scene
40 41 42 43 44 45
  /// in the custom [ShaderWarmUp] so Flutter can pre-compile and cache the
  /// shaders during startup. 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 main thread
  /// so there should be no "Application Not Responding" warning.
  ///
46 47 48
  /// 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.
49 50 51 52
  ///
  /// See also:
  ///
  ///  * [ShaderWarmUp], the interface of how this warm up works.
53
  static ShaderWarmUp? shaderWarmUp = const DefaultShaderWarmUp();
54

55 56 57 58 59 60 61
  /// 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.
62 63
  ImageCache? get imageCache => _imageCache;
  ImageCache? _imageCache;
64 65 66 67 68

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

71
  /// Calls through to [dart:ui] from [ImageCache].
72
  ///
73 74
  /// The `cacheWidth` and `cacheHeight` parameters, when specified, indicate
  /// the size to decode the image to.
75
  ///
76 77 78 79 80 81 82 83 84 85 86 87 88
  /// 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.
89
  Future<ui.Codec> instantiateImageCodec(Uint8List bytes, {
90 91
    int? cacheWidth,
    int? cacheHeight,
92
    bool allowUpscaling = false,
93 94 95
  }) {
    assert(cacheWidth == null || cacheWidth > 0);
    assert(cacheHeight == null || cacheHeight > 0);
96
    assert(allowUpscaling != null);
97 98 99 100
    return ui.instantiateImageCodec(
      bytes,
      targetWidth: cacheWidth,
      targetHeight: cacheHeight,
101
      allowUpscaling: allowUpscaling,
102
    );
103 104
  }

105 106 107
  @override
  void evict(String asset) {
    super.evict(asset);
108 109
    imageCache!.clear();
    imageCache!.clearLiveImages();
110
  }
111

112 113 114
  @override
  void handleMemoryPressure() {
    super.handleMemoryPressure();
115
    imageCache?.clear();
116 117
  }

118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
  /// 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);
133 134
    final Map<String, dynamic> message = systemMessage as Map<String, dynamic>;
    final String type = message['type'] as String;
135 136 137 138 139 140 141 142 143 144 145 146 147
    switch (type) {
      case 'fontsChange':
        _systemFonts.notifyListeners();
        break;
    }
    return;
  }
}

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

  void notifyListeners () {
148
    for (final VoidCallback callback in _systemFontsCallbacks) {
149 150 151 152 153 154 155 156 157 158 159 160
      callback();
    }
  }

  @override
  void addListener(VoidCallback listener) {
    _systemFontsCallbacks.add(listener);
  }
  @override
  void removeListener(VoidCallback listener) {
    _systemFontsCallbacks.remove(listener);
  }
161 162 163 164 165 166 167 168 169
}

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