blob: 8f563066edd0ef26cc1bc64536c25be50f78d53d [file] [log] [blame]
// 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:typed_data' show Uint8List;
import 'dart:ui' as ui show instantiateImageCodec, Codec;
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.
static PaintingBinding? get instance => _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;
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].
///
/// 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> 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,
);
}
@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;