Unverified Commit 4c7fae93 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Revert "migrate part of painting to nullsafety (#62696)" (#62868)

This reverts commit 4518a72f.
parent 0e558042
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:ui' as ui show lerpDouble; import 'dart:ui' as ui show lerpDouble;
...@@ -87,12 +88,12 @@ abstract class AlignmentGeometry { ...@@ -87,12 +88,12 @@ abstract class AlignmentGeometry {
/// into a concrete [Alignment] using [resolve]. /// into a concrete [Alignment] using [resolve].
/// ///
/// {@macro dart.ui.shadow.lerp} /// {@macro dart.ui.shadow.lerp}
static AlignmentGeometry? lerp(AlignmentGeometry? a, AlignmentGeometry? b, double t) { static AlignmentGeometry lerp(AlignmentGeometry a, AlignmentGeometry b, double t) {
assert(t != null); assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
return b! * t; return b * t;
if (b == null) if (b == null)
return a * (1.0 - t); return a * (1.0 - t);
if (a is Alignment && b is Alignment) if (a is Alignment && b is Alignment)
...@@ -100,9 +101,9 @@ abstract class AlignmentGeometry { ...@@ -100,9 +101,9 @@ abstract class AlignmentGeometry {
if (a is AlignmentDirectional && b is AlignmentDirectional) if (a is AlignmentDirectional && b is AlignmentDirectional)
return AlignmentDirectional.lerp(a, b, t); return AlignmentDirectional.lerp(a, b, t);
return _MixedAlignment( return _MixedAlignment(
ui.lerpDouble(a._x, b._x, t)!, ui.lerpDouble(a._x, b._x, t),
ui.lerpDouble(a._start, b._start, t)!, ui.lerpDouble(a._start, b._start, t),
ui.lerpDouble(a._y, b._y, t)!, ui.lerpDouble(a._y, b._y, t),
); );
} }
...@@ -115,7 +116,7 @@ abstract class AlignmentGeometry { ...@@ -115,7 +116,7 @@ abstract class AlignmentGeometry {
/// * [Alignment], for which this is a no-op (returns itself). /// * [Alignment], for which this is a no-op (returns itself).
/// * [AlignmentDirectional], which flips the horizontal direction /// * [AlignmentDirectional], which flips the horizontal direction
/// based on the `direction` argument. /// based on the `direction` argument.
Alignment resolve(TextDirection? direction); Alignment resolve(TextDirection direction);
@override @override
String toString() { String toString() {
...@@ -332,19 +333,19 @@ class Alignment extends AlignmentGeometry { ...@@ -332,19 +333,19 @@ class Alignment extends AlignmentGeometry {
/// If either is null, this function interpolates from [Alignment.center]. /// If either is null, this function interpolates from [Alignment.center].
/// ///
/// {@macro dart.ui.shadow.lerp} /// {@macro dart.ui.shadow.lerp}
static Alignment? lerp(Alignment? a, Alignment? b, double t) { static Alignment lerp(Alignment a, Alignment b, double t) {
assert(t != null); assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
return Alignment(ui.lerpDouble(0.0, b!.x, t)!, ui.lerpDouble(0.0, b.y, t)!); return Alignment(ui.lerpDouble(0.0, b.x, t), ui.lerpDouble(0.0, b.y, t));
if (b == null) if (b == null)
return Alignment(ui.lerpDouble(a.x, 0.0, t)!, ui.lerpDouble(a.y, 0.0, t)!); return Alignment(ui.lerpDouble(a.x, 0.0, t), ui.lerpDouble(a.y, 0.0, t));
return Alignment(ui.lerpDouble(a.x, b.x, t)!, ui.lerpDouble(a.y, b.y, t)!); return Alignment(ui.lerpDouble(a.x, b.x, t), ui.lerpDouble(a.y, b.y, t));
} }
@override @override
Alignment resolve(TextDirection? direction) => this; Alignment resolve(TextDirection direction) => this;
static String _stringify(double x, double y) { static String _stringify(double x, double y) {
if (x == -1.0 && y == -1.0) if (x == -1.0 && y == -1.0)
...@@ -513,26 +514,27 @@ class AlignmentDirectional extends AlignmentGeometry { ...@@ -513,26 +514,27 @@ class AlignmentDirectional extends AlignmentGeometry {
/// If either is null, this function interpolates from [AlignmentDirectional.center]. /// If either is null, this function interpolates from [AlignmentDirectional.center].
/// ///
/// {@macro dart.ui.shadow.lerp} /// {@macro dart.ui.shadow.lerp}
static AlignmentDirectional? lerp(AlignmentDirectional? a, AlignmentDirectional? b, double t) { static AlignmentDirectional lerp(AlignmentDirectional a, AlignmentDirectional b, double t) {
assert(t != null); assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
return AlignmentDirectional(ui.lerpDouble(0.0, b!.start, t)!, ui.lerpDouble(0.0, b.y, t)!); return AlignmentDirectional(ui.lerpDouble(0.0, b.start, t), ui.lerpDouble(0.0, b.y, t));
if (b == null) if (b == null)
return AlignmentDirectional(ui.lerpDouble(a.start, 0.0, t)!, ui.lerpDouble(a.y, 0.0, t)!); return AlignmentDirectional(ui.lerpDouble(a.start, 0.0, t), ui.lerpDouble(a.y, 0.0, t));
return AlignmentDirectional(ui.lerpDouble(a.start, b.start, t)!, ui.lerpDouble(a.y, b.y, t)!); return AlignmentDirectional(ui.lerpDouble(a.start, b.start, t), ui.lerpDouble(a.y, b.y, t));
} }
@override @override
Alignment resolve(TextDirection? direction) { Alignment resolve(TextDirection direction) {
assert(direction != null, 'Cannot resolve $runtimeType without a TextDirection.'); assert(direction != null, 'Cannot resolve $runtimeType without a TextDirection.');
switch (direction!) { switch (direction) {
case TextDirection.rtl: case TextDirection.rtl:
return Alignment(-start, y); return Alignment(-start, y);
case TextDirection.ltr: case TextDirection.ltr:
return Alignment(start, y); return Alignment(start, y);
} }
return null;
} }
static String _stringify(double start, double y) { static String _stringify(double start, double y) {
...@@ -620,14 +622,15 @@ class _MixedAlignment extends AlignmentGeometry { ...@@ -620,14 +622,15 @@ class _MixedAlignment extends AlignmentGeometry {
} }
@override @override
Alignment resolve(TextDirection? direction) { Alignment resolve(TextDirection direction) {
assert(direction != null, 'Cannot resolve $runtimeType without a TextDirection.'); assert(direction != null, 'Cannot resolve $runtimeType without a TextDirection.');
switch (direction!) { switch (direction) {
case TextDirection.rtl: case TextDirection.rtl:
return Alignment(_x - _start, _y); return Alignment(_x - _start, _y);
case TextDirection.ltr: case TextDirection.ltr:
return Alignment(_x + _start, _y); return Alignment(_x + _start, _y);
} }
return null;
} }
} }
...@@ -649,7 +652,7 @@ class _MixedAlignment extends AlignmentGeometry { ...@@ -649,7 +652,7 @@ class _MixedAlignment extends AlignmentGeometry {
class TextAlignVertical { class TextAlignVertical {
/// Creates a TextAlignVertical from any y value between -1.0 and 1.0. /// Creates a TextAlignVertical from any y value between -1.0 and 1.0.
const TextAlignVertical({ const TextAlignVertical({
required this.y, @required this.y,
}) : assert(y != null), }) : assert(y != null),
assert(y >= -1.0 && y <= 1.0); assert(y >= -1.0 && y <= 1.0);
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:ui' show TextDirection; import 'dart:ui' show TextDirection;
...@@ -142,6 +143,7 @@ Axis flipAxis(Axis direction) { ...@@ -142,6 +143,7 @@ Axis flipAxis(Axis direction) {
case Axis.vertical: case Axis.vertical:
return Axis.horizontal; return Axis.horizontal;
} }
return null;
} }
/// A direction in which boxes flow vertically. /// A direction in which boxes flow vertically.
...@@ -212,6 +214,7 @@ Axis axisDirectionToAxis(AxisDirection axisDirection) { ...@@ -212,6 +214,7 @@ Axis axisDirectionToAxis(AxisDirection axisDirection) {
case AxisDirection.right: case AxisDirection.right:
return Axis.horizontal; return Axis.horizontal;
} }
return null;
} }
/// Returns the [AxisDirection] in which reading occurs in the given [TextDirection]. /// Returns the [AxisDirection] in which reading occurs in the given [TextDirection].
...@@ -226,6 +229,7 @@ AxisDirection textDirectionToAxisDirection(TextDirection textDirection) { ...@@ -226,6 +229,7 @@ AxisDirection textDirectionToAxisDirection(TextDirection textDirection) {
case TextDirection.ltr: case TextDirection.ltr:
return AxisDirection.right; return AxisDirection.right;
} }
return null;
} }
/// Returns the opposite of the given [AxisDirection]. /// Returns the opposite of the given [AxisDirection].
...@@ -249,6 +253,7 @@ AxisDirection flipAxisDirection(AxisDirection axisDirection) { ...@@ -249,6 +253,7 @@ AxisDirection flipAxisDirection(AxisDirection axisDirection) {
case AxisDirection.left: case AxisDirection.left:
return AxisDirection.right; return AxisDirection.right;
} }
return null;
} }
/// Returns whether traveling along the given axis direction visits coordinates /// Returns whether traveling along the given axis direction visits coordinates
...@@ -266,4 +271,5 @@ bool axisDirectionIsReversed(AxisDirection axisDirection) { ...@@ -266,4 +271,5 @@ bool axisDirectionIsReversed(AxisDirection axisDirection) {
case AxisDirection.right: case AxisDirection.right:
return false; return false;
} }
return null;
} }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:typed_data' show Uint8List; import 'dart:typed_data' show Uint8List;
import 'dart:ui' as ui show instantiateImageCodec, Codec; import 'dart:ui' as ui show instantiateImageCodec, Codec;
...@@ -22,12 +23,14 @@ mixin PaintingBinding on BindingBase, ServicesBinding { ...@@ -22,12 +23,14 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
super.initInstances(); super.initInstances();
_instance = this; _instance = this;
_imageCache = createImageCache(); _imageCache = createImageCache();
shaderWarmUp?.execute(); if (shaderWarmUp != null) {
shaderWarmUp.execute();
}
} }
/// The current [PaintingBinding], if one has been created. /// The current [PaintingBinding], if one has been created.
static PaintingBinding? get instance => _instance; static PaintingBinding get instance => _instance;
static PaintingBinding? _instance; static PaintingBinding _instance;
/// [ShaderWarmUp] to be executed during [initInstances]. /// [ShaderWarmUp] to be executed during [initInstances].
/// ///
...@@ -50,7 +53,7 @@ mixin PaintingBinding on BindingBase, ServicesBinding { ...@@ -50,7 +53,7 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
/// See also: /// See also:
/// ///
/// * [ShaderWarmUp], the interface of how this warm up works. /// * [ShaderWarmUp], the interface of how this warm up works.
static ShaderWarmUp? shaderWarmUp = const DefaultShaderWarmUp(); static ShaderWarmUp shaderWarmUp = const DefaultShaderWarmUp();
/// The singleton that implements the Flutter framework's image cache. /// The singleton that implements the Flutter framework's image cache.
/// ///
...@@ -59,8 +62,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding { ...@@ -59,8 +62,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
/// ///
/// The image cache is created during startup by the [createImageCache] /// The image cache is created during startup by the [createImageCache]
/// method. /// method.
ImageCache? get imageCache => _imageCache; ImageCache get imageCache => _imageCache;
ImageCache? _imageCache; ImageCache _imageCache;
/// Creates the [ImageCache] singleton (accessible via [imageCache]). /// Creates the [ImageCache] singleton (accessible via [imageCache]).
/// ///
...@@ -87,8 +90,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding { ...@@ -87,8 +90,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
/// above its native resolution should prefer scaling the canvas the image is /// above its native resolution should prefer scaling the canvas the image is
/// drawn into. /// drawn into.
Future<ui.Codec> instantiateImageCodec(Uint8List bytes, { Future<ui.Codec> instantiateImageCodec(Uint8List bytes, {
int? cacheWidth, int cacheWidth,
int? cacheHeight, int cacheHeight,
bool allowUpscaling = false, bool allowUpscaling = false,
}) { }) {
assert(cacheWidth == null || cacheWidth > 0); assert(cacheWidth == null || cacheWidth > 0);
...@@ -105,8 +108,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding { ...@@ -105,8 +108,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
@override @override
void evict(String asset) { void evict(String asset) {
super.evict(asset); super.evict(asset);
imageCache!.clear(); imageCache.clear();
imageCache!.clearLiveImages(); imageCache.clearLiveImages();
} }
@override @override
...@@ -167,4 +170,4 @@ class _SystemFontsNotifier extends Listenable { ...@@ -167,4 +170,4 @@ class _SystemFontsNotifier extends Listenable {
/// ///
/// The image cache is created during startup by the [PaintingBinding]'s /// The image cache is created during startup by the [PaintingBinding]'s
/// [PaintingBinding.createImageCache] method. /// [PaintingBinding.createImageCache] method.
ImageCache? get imageCache => PaintingBinding.instance!.imageCache; ImageCache get imageCache => PaintingBinding.instance.imageCache;
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:ui' show Color, lerpDouble, hashValues; import 'dart:ui' show Color, lerpDouble, hashValues;
...@@ -9,7 +10,7 @@ import 'dart:ui' show Color, lerpDouble, hashValues; ...@@ -9,7 +10,7 @@ import 'dart:ui' show Color, lerpDouble, hashValues;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
double _getHue(double red, double green, double blue, double max, double delta) { double _getHue(double red, double green, double blue, double max, double delta) {
late double hue; double hue;
if (max == 0.0) { if (max == 0.0) {
hue = 0.0; hue = 0.0;
} else if (max == red) { } else if (max == red) {
...@@ -198,19 +199,19 @@ class HSVColor { ...@@ -198,19 +199,19 @@ class HSVColor {
/// {@macro dart.ui.shadow.lerp} /// {@macro dart.ui.shadow.lerp}
/// ///
/// Values outside of the valid range for each channel will be clamped. /// Values outside of the valid range for each channel will be clamped.
static HSVColor? lerp(HSVColor? a, HSVColor? b, double t) { static HSVColor lerp(HSVColor a, HSVColor b, double t) {
assert(t != null); assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
return b!._scaleAlpha(t); return b._scaleAlpha(t);
if (b == null) if (b == null)
return a._scaleAlpha(1.0 - t); return a._scaleAlpha(1.0 - t);
return HSVColor.fromAHSV( return HSVColor.fromAHSV(
lerpDouble(a.alpha, b.alpha, t)!.clamp(0.0, 1.0) as double, lerpDouble(a.alpha, b.alpha, t).clamp(0.0, 1.0) as double,
lerpDouble(a.hue, b.hue, t)! % 360.0, lerpDouble(a.hue, b.hue, t) % 360.0,
lerpDouble(a.saturation, b.saturation, t)!.clamp(0.0, 1.0) as double, lerpDouble(a.saturation, b.saturation, t).clamp(0.0, 1.0) as double,
lerpDouble(a.value, b.value, t)!.clamp(0.0, 1.0) as double, lerpDouble(a.value, b.value, t).clamp(0.0, 1.0) as double,
); );
} }
...@@ -382,19 +383,19 @@ class HSLColor { ...@@ -382,19 +383,19 @@ class HSLColor {
/// ///
/// Values for `t` are usually obtained from an [Animation<double>], such as /// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController]. /// an [AnimationController].
static HSLColor? lerp(HSLColor? a, HSLColor? b, double t) { static HSLColor lerp(HSLColor a, HSLColor b, double t) {
assert(t != null); assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
return b!._scaleAlpha(t); return b._scaleAlpha(t);
if (b == null) if (b == null)
return a._scaleAlpha(1.0 - t); return a._scaleAlpha(1.0 - t);
return HSLColor.fromAHSL( return HSLColor.fromAHSL(
lerpDouble(a.alpha, b.alpha, t)!.clamp(0.0, 1.0) as double, lerpDouble(a.alpha, b.alpha, t).clamp(0.0, 1.0) as double,
lerpDouble(a.hue, b.hue, t)! % 360.0, lerpDouble(a.hue, b.hue, t) % 360.0,
lerpDouble(a.saturation, b.saturation, t)!.clamp(0.0, 1.0) as double, lerpDouble(a.saturation, b.saturation, t).clamp(0.0, 1.0) as double,
lerpDouble(a.lightness, b.lightness, t)!.clamp(0.0, 1.0) as double, lerpDouble(a.lightness, b.lightness, t).clamp(0.0, 1.0) as double,
); );
} }
...@@ -440,7 +441,7 @@ class ColorSwatch<T> extends Color { ...@@ -440,7 +441,7 @@ class ColorSwatch<T> extends Color {
final Map<T, Color> _swatch; final Map<T, Color> _swatch;
/// Returns an element of the swatch table. /// Returns an element of the swatch table.
Color? operator [](T index) => _swatch[index]; Color operator [](T index) => _swatch[index];
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
...@@ -467,9 +468,9 @@ class ColorProperty extends DiagnosticsProperty<Color> { ...@@ -467,9 +468,9 @@ class ColorProperty extends DiagnosticsProperty<Color> {
/// The [showName], [style], and [level] arguments must not be null. /// The [showName], [style], and [level] arguments must not be null.
ColorProperty( ColorProperty(
String name, String name,
Color? value, { Color value, {
bool showName = true, bool showName = true,
Object? defaultValue = kNoDefaultValue, Object defaultValue = kNoDefaultValue,
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.singleLine, DiagnosticsTreeStyle style = DiagnosticsTreeStyle.singleLine,
DiagnosticLevel level = DiagnosticLevel.info, DiagnosticLevel level = DiagnosticLevel.info,
}) : assert(showName != null), }) : assert(showName != null),
...@@ -483,14 +484,14 @@ class ColorProperty extends DiagnosticsProperty<Color> { ...@@ -483,14 +484,14 @@ class ColorProperty extends DiagnosticsProperty<Color> {
); );
@override @override
Map<String, Object?> toJsonMap(DiagnosticsSerializationDelegate delegate) { Map<String, Object> toJsonMap(DiagnosticsSerializationDelegate delegate) {
final Map<String, Object?> json = super.toJsonMap(delegate); final Map<String, Object> json = super.toJsonMap(delegate);
if (value != null) { if (value != null) {
json['valueProperties'] = <String, Object>{ json['valueProperties'] = <String, Object>{
'red': value!.red, 'red': value.red,
'green': value!.green, 'green': value.green,
'blue': value!.blue, 'blue': value.blue,
'alpha': value!.alpha, 'alpha': value.alpha,
}; };
} }
return json; return json;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:io'; import 'dart:io';
import 'dart:ui' show Size, hashValues; import 'dart:ui' show Size, hashValues;
...@@ -29,7 +30,7 @@ typedef HttpClientProvider = HttpClient Function(); ...@@ -29,7 +30,7 @@ typedef HttpClientProvider = HttpClient Function();
/// a mock client that hasn't been affected by other tests. /// a mock client that hasn't been affected by other tests.
/// ///
/// This value is ignored in non-debug builds. /// This value is ignored in non-debug builds.
HttpClientProvider? debugNetworkImageHttpClientProvider; HttpClientProvider debugNetworkImageHttpClientProvider;
typedef PaintImageCallback = void Function(ImageSizeInfo); typedef PaintImageCallback = void Function(ImageSizeInfo);
...@@ -43,20 +44,20 @@ class ImageSizeInfo { ...@@ -43,20 +44,20 @@ class ImageSizeInfo {
/// This class is used by the framework when it paints an image to a canvas /// This class is used by the framework when it paints an image to a canvas
/// to report to `dart:developer`'s [postEvent], as well as to the /// to report to `dart:developer`'s [postEvent], as well as to the
/// [debugOnPaintImage] callback if it is set. /// [debugOnPaintImage] callback if it is set.
const ImageSizeInfo({this.source, this.displaySize, required this.imageSize}); const ImageSizeInfo({this.source, this.displaySize, this.imageSize});
/// A unique identifier for this image, for example its asset path or network /// A unique identifier for this image, for example its asset path or network
/// URL. /// URL.
final String? source; final String source;
/// The size of the area the image will be rendered in. /// The size of the area the image will be rendered in.
final Size? displaySize; final Size displaySize;
/// The size the image has been decoded to. /// The size the image has been decoded to.
final Size imageSize; final Size imageSize;
/// The number of bytes needed to render the image without scaling it. /// The number of bytes needed to render the image without scaling it.
int get displaySizeInBytes => _sizeToBytes(displaySize!); int get displaySizeInBytes => _sizeToBytes(displaySize);
/// The number of bytes used by the image in memory. /// The number of bytes used by the image in memory.
int get decodedSizeInBytes => _sizeToBytes(imageSize); int get decodedSizeInBytes => _sizeToBytes(imageSize);
...@@ -68,15 +69,14 @@ class ImageSizeInfo { ...@@ -68,15 +69,14 @@ class ImageSizeInfo {
} }
/// Returns a JSON encodable representation of this object. /// Returns a JSON encodable representation of this object.
Map<String, Object?> toJson() { Map<String, Object> toJson() {
return <String, Object?>{ return <String, Object>{
'source': source, 'source': source,
if (displaySize != null) 'displaySize': <String, double>{
'displaySize': <String, Object?>{ 'width': displaySize.width,
'width': displaySize!.width, 'height': displaySize.height,
'height': displaySize!.height,
}, },
'imageSize': <String, Object?>{ 'imageSize': <String, double>{
'width': imageSize.width, 'width': imageSize.width,
'height': imageSize.height, 'height': imageSize.height,
}, },
...@@ -125,7 +125,7 @@ class ImageSizeInfo { ...@@ -125,7 +125,7 @@ class ImageSizeInfo {
/// a higher resolution while animating, but it would be problematic to have /// a higher resolution while animating, but it would be problematic to have
/// a grid or list of such thumbnails all be at the full resolution at the same /// a grid or list of such thumbnails all be at the full resolution at the same
/// time. /// time.
PaintImageCallback? debugOnPaintImage; PaintImageCallback debugOnPaintImage;
/// If true, the framework will color invert and horizontally flip images that /// If true, the framework will color invert and horizontally flip images that
/// have been decoded to a size taking at least [debugImageOverheadAllowance] /// have been decoded to a size taking at least [debugImageOverheadAllowance]
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:developer'; import 'dart:developer';
import 'dart:ui' show hashValues; import 'dart:ui' show hashValues;
...@@ -102,7 +103,7 @@ class ImageCache { ...@@ -102,7 +103,7 @@ class ImageCache {
assert(value >= 0); assert(value >= 0);
if (value == maximumSize) if (value == maximumSize)
return; return;
TimelineTask? timelineTask; TimelineTask timelineTask;
if (!kReleaseMode) { if (!kReleaseMode) {
timelineTask = TimelineTask()..start( timelineTask = TimelineTask()..start(
'ImageCache.setMaximumSize', 'ImageCache.setMaximumSize',
...@@ -116,7 +117,7 @@ class ImageCache { ...@@ -116,7 +117,7 @@ class ImageCache {
_checkCacheSize(timelineTask); _checkCacheSize(timelineTask);
} }
if (!kReleaseMode) { if (!kReleaseMode) {
timelineTask!.finish(); timelineTask.finish();
} }
} }
...@@ -141,7 +142,7 @@ class ImageCache { ...@@ -141,7 +142,7 @@ class ImageCache {
assert(value >= 0); assert(value >= 0);
if (value == _maximumSizeBytes) if (value == _maximumSizeBytes)
return; return;
TimelineTask? timelineTask; TimelineTask timelineTask;
if (!kReleaseMode) { if (!kReleaseMode) {
timelineTask = TimelineTask()..start( timelineTask = TimelineTask()..start(
'ImageCache.setMaximumSizeBytes', 'ImageCache.setMaximumSizeBytes',
...@@ -155,7 +156,7 @@ class ImageCache { ...@@ -155,7 +156,7 @@ class ImageCache {
_checkCacheSize(timelineTask); _checkCacheSize(timelineTask);
} }
if (!kReleaseMode) { if (!kReleaseMode) {
timelineTask!.finish(); timelineTask.finish();
} }
} }
...@@ -238,10 +239,10 @@ class ImageCache { ...@@ -238,10 +239,10 @@ class ImageCache {
// will never complete, e.g. it was loaded in a FakeAsync zone. // will never complete, e.g. it was loaded in a FakeAsync zone.
// In such a case, we need to make sure subsequent calls to // In such a case, we need to make sure subsequent calls to
// putIfAbsent don't return this image that may never complete. // putIfAbsent don't return this image that may never complete.
final _LiveImage? image = _liveImages.remove(key); final _LiveImage image = _liveImages.remove(key);
image?.removeListener(); image?.removeListener();
} }
final _PendingImage? pendingImage = _pendingImages.remove(key); final _PendingImage pendingImage = _pendingImages.remove(key);
if (pendingImage != null) { if (pendingImage != null) {
if (!kReleaseMode) { if (!kReleaseMode) {
Timeline.instantSync('ImageCache.evict', arguments: <String, dynamic>{ Timeline.instantSync('ImageCache.evict', arguments: <String, dynamic>{
...@@ -251,7 +252,7 @@ class ImageCache { ...@@ -251,7 +252,7 @@ class ImageCache {
pendingImage.removeListener(); pendingImage.removeListener();
return true; return true;
} }
final _CachedImage? image = _cache.remove(key); final _CachedImage image = _cache.remove(key);
if (image != null) { if (image != null) {
if (!kReleaseMode) { if (!kReleaseMode) {
Timeline.instantSync('ImageCache.evict', arguments: <String, dynamic>{ Timeline.instantSync('ImageCache.evict', arguments: <String, dynamic>{
...@@ -259,7 +260,7 @@ class ImageCache { ...@@ -259,7 +260,7 @@ class ImageCache {
'sizeInBytes': image.sizeBytes, 'sizeInBytes': image.sizeBytes,
}); });
} }
_currentSizeBytes -= image.sizeBytes!; _currentSizeBytes -= image.sizeBytes;
return true; return true;
} }
if (!kReleaseMode) { if (!kReleaseMode) {
...@@ -275,13 +276,13 @@ class ImageCache { ...@@ -275,13 +276,13 @@ class ImageCache {
/// ///
/// Resizes the cache as appropriate to maintain the constraints of /// Resizes the cache as appropriate to maintain the constraints of
/// [maximumSize] and [maximumSizeBytes]. /// [maximumSize] and [maximumSizeBytes].
void _touch(Object key, _CachedImage image, TimelineTask? timelineTask) { void _touch(Object key, _CachedImage image, TimelineTask timelineTask) {
// TODO(dnfield): Some customers test in release mode with asserts enabled. // TODO(dnfield): Some customers test in release mode with asserts enabled.
// This is bound to cause problems, b/150295238 is tracking that. For now, // This is bound to cause problems, b/150295238 is tracking that. For now,
// avoid this being a point of failure. // avoid this being a point of failure.
assert(kReleaseMode || timelineTask != null); assert(kReleaseMode || timelineTask != null);
if (image.sizeBytes != null && image.sizeBytes! <= maximumSizeBytes) { if (image.sizeBytes != null && image.sizeBytes <= maximumSizeBytes) {
_currentSizeBytes += image.sizeBytes!; _currentSizeBytes += image.sizeBytes;
_cache[key] = image; _cache[key] = image;
_checkCacheSize(timelineTask); _checkCacheSize(timelineTask);
} }
...@@ -309,11 +310,11 @@ class ImageCache { ...@@ -309,11 +310,11 @@ class ImageCache {
/// `onError` is also provided. When an exception is caught resolving an image, /// `onError` is also provided. When an exception is caught resolving an image,
/// no completers are cached and `null` is returned instead of a new /// no completers are cached and `null` is returned instead of a new
/// completer. /// completer.
ImageStreamCompleter? putIfAbsent(Object key, ImageStreamCompleter loader(), { ImageErrorListener? onError }) { ImageStreamCompleter putIfAbsent(Object key, ImageStreamCompleter loader(), { ImageErrorListener onError }) {
assert(key != null); assert(key != null);
assert(loader != null); assert(loader != null);
TimelineTask? timelineTask; TimelineTask timelineTask;
TimelineTask? listenerTask; TimelineTask listenerTask;
if (!kReleaseMode) { if (!kReleaseMode) {
timelineTask = TimelineTask()..start( timelineTask = TimelineTask()..start(
'ImageCache.putIfAbsent', 'ImageCache.putIfAbsent',
...@@ -322,11 +323,11 @@ class ImageCache { ...@@ -322,11 +323,11 @@ class ImageCache {
}, },
); );
} }
ImageStreamCompleter? result = _pendingImages[key]?.completer; ImageStreamCompleter result = _pendingImages[key]?.completer;
// Nothing needs to be done because the image hasn't loaded yet. // Nothing needs to be done because the image hasn't loaded yet.
if (result != null) { if (result != null) {
if (!kReleaseMode) { if (!kReleaseMode) {
timelineTask!.finish(arguments: <String, dynamic>{'result': 'pending'}); timelineTask.finish(arguments: <String, dynamic>{'result': 'pending'});
} }
return result; return result;
} }
...@@ -334,10 +335,10 @@ class ImageCache { ...@@ -334,10 +335,10 @@ class ImageCache {
// recently used position below. // recently used position below.
// Don't use _touch here, which would trigger a check on cache size that is // Don't use _touch here, which would trigger a check on cache size that is
// not needed since this is just moving an existing cache entry to the head. // not needed since this is just moving an existing cache entry to the head.
final _CachedImage? image = _cache.remove(key); final _CachedImage image = _cache.remove(key);
if (image != null) { if (image != null) {
if (!kReleaseMode) { if (!kReleaseMode) {
timelineTask!.finish(arguments: <String, dynamic>{'result': 'keepAlive'}); timelineTask.finish(arguments: <String, dynamic>{'result': 'keepAlive'});
} }
// The image might have been keptAlive but had no listeners (so not live). // The image might have been keptAlive but had no listeners (so not live).
// Make sure the cache starts tracking it as live again. // Make sure the cache starts tracking it as live again.
...@@ -346,11 +347,11 @@ class ImageCache { ...@@ -346,11 +347,11 @@ class ImageCache {
return image.completer; return image.completer;
} }
final _CachedImage? liveImage = _liveImages[key]; final _CachedImage liveImage = _liveImages[key];
if (liveImage != null) { if (liveImage != null) {
_touch(key, liveImage, timelineTask); _touch(key, liveImage, timelineTask);
if (!kReleaseMode) { if (!kReleaseMode) {
timelineTask!.finish(arguments: <String, dynamic>{'result': 'keepAlive'}); timelineTask.finish(arguments: <String, dynamic>{'result': 'keepAlive'});
} }
return liveImage.completer; return liveImage.completer;
} }
...@@ -360,7 +361,7 @@ class ImageCache { ...@@ -360,7 +361,7 @@ class ImageCache {
_trackLiveImage(key, _LiveImage(result, null, () => _liveImages.remove(key))); _trackLiveImage(key, _LiveImage(result, null, () => _liveImages.remove(key)));
} catch (error, stackTrace) { } catch (error, stackTrace) {
if (!kReleaseMode) { if (!kReleaseMode) {
timelineTask!.finish(arguments: <String, dynamic>{ timelineTask.finish(arguments: <String, dynamic>{
'result': 'error', 'result': 'error',
'error': error.toString(), 'error': error.toString(),
'stackTrace': stackTrace.toString(), 'stackTrace': stackTrace.toString(),
...@@ -386,12 +387,12 @@ class ImageCache { ...@@ -386,12 +387,12 @@ class ImageCache {
// will have to listen to the image at least once so we don't leak it in // will have to listen to the image at least once so we don't leak it in
// the live image tracking. // the live image tracking.
// If the cache is disabled, this variable will be set. // If the cache is disabled, this variable will be set.
_PendingImage? untrackedPendingImage; _PendingImage untrackedPendingImage;
void listener(ImageInfo? info, bool syncCall) { void listener(ImageInfo info, bool syncCall) {
// Images that fail to load don't contribute to cache size. // Images that fail to load don't contribute to cache size.
final int imageSize = info == null || info.image == null ? 0 : info.image.height * info.image.width * 4; final int imageSize = info?.image == null ? 0 : info.image.height * info.image.width * 4;
final _CachedImage image = _CachedImage(result!, imageSize); final _CachedImage image = _CachedImage(result, imageSize);
_trackLiveImage( _trackLiveImage(
key, key,
...@@ -402,7 +403,7 @@ class ImageCache { ...@@ -402,7 +403,7 @@ class ImageCache {
), ),
); );
final _PendingImage? pendingImage = untrackedPendingImage ?? _pendingImages.remove(key); final _PendingImage pendingImage = untrackedPendingImage ?? _pendingImages.remove(key);
if (pendingImage != null) { if (pendingImage != null) {
pendingImage.removeListener(); pendingImage.removeListener();
} }
...@@ -412,11 +413,11 @@ class ImageCache { ...@@ -412,11 +413,11 @@ class ImageCache {
} }
if (!kReleaseMode && !listenedOnce) { if (!kReleaseMode && !listenedOnce) {
listenerTask!.finish(arguments: <String, dynamic>{ listenerTask.finish(arguments: <String, dynamic>{
'syncCall': syncCall, 'syncCall': syncCall,
'sizeInBytes': imageSize, 'sizeInBytes': imageSize,
}); });
timelineTask!.finish(arguments: <String, dynamic>{ timelineTask.finish(arguments: <String, dynamic>{
'currentSizeBytes': currentSizeBytes, 'currentSizeBytes': currentSizeBytes,
'currentSize': currentSize, 'currentSize': currentSize,
}); });
...@@ -480,9 +481,9 @@ class ImageCache { ...@@ -480,9 +481,9 @@ class ImageCache {
// Remove images from the cache until both the length and bytes are below // Remove images from the cache until both the length and bytes are below
// maximum, or the cache is empty. // maximum, or the cache is empty.
void _checkCacheSize(TimelineTask? timelineTask) { void _checkCacheSize(TimelineTask timelineTask) {
final Map<String, dynamic> finishArgs = <String, dynamic>{}; final Map<String, dynamic> finishArgs = <String, dynamic>{};
TimelineTask? checkCacheTask; TimelineTask checkCacheTask;
if (!kReleaseMode) { if (!kReleaseMode) {
checkCacheTask = TimelineTask(parent: timelineTask)..start('checkCacheSize'); checkCacheTask = TimelineTask(parent: timelineTask)..start('checkCacheSize');
finishArgs['evictedKeys'] = <String>[]; finishArgs['evictedKeys'] = <String>[];
...@@ -491,8 +492,8 @@ class ImageCache { ...@@ -491,8 +492,8 @@ class ImageCache {
} }
while (_currentSizeBytes > _maximumSizeBytes || _cache.length > _maximumSize) { while (_currentSizeBytes > _maximumSizeBytes || _cache.length > _maximumSize) {
final Object key = _cache.keys.first; final Object key = _cache.keys.first;
final _CachedImage image = _cache[key]!; final _CachedImage image = _cache[key];
_currentSizeBytes -= image.sizeBytes!; _currentSizeBytes -= image.sizeBytes;
_cache.remove(key); _cache.remove(key);
if (!kReleaseMode) { if (!kReleaseMode) {
finishArgs['evictedKeys'].add(key.toString()); finishArgs['evictedKeys'].add(key.toString());
...@@ -501,7 +502,7 @@ class ImageCache { ...@@ -501,7 +502,7 @@ class ImageCache {
if (!kReleaseMode) { if (!kReleaseMode) {
finishArgs['endSize'] = currentSize; finishArgs['endSize'] = currentSize;
finishArgs['endSizeBytes'] = currentSizeBytes; finishArgs['endSizeBytes'] = currentSizeBytes;
checkCacheTask!.finish(arguments: finishArgs); checkCacheTask.finish(arguments: finishArgs);
} }
assert(_currentSizeBytes >= 0); assert(_currentSizeBytes >= 0);
assert(_cache.length <= maximumSize); assert(_cache.length <= maximumSize);
...@@ -584,11 +585,11 @@ class _CachedImage { ...@@ -584,11 +585,11 @@ class _CachedImage {
_CachedImage(this.completer, this.sizeBytes); _CachedImage(this.completer, this.sizeBytes);
final ImageStreamCompleter completer; final ImageStreamCompleter completer;
int? sizeBytes; int sizeBytes;
} }
class _LiveImage extends _CachedImage { class _LiveImage extends _CachedImage {
_LiveImage(ImageStreamCompleter completer, int? sizeBytes, this.handleRemove) _LiveImage(ImageStreamCompleter completer, int sizeBytes, this.handleRemove)
: super(completer, sizeBytes); : super(completer, sizeBytes);
final VoidCallback handleRemove; final VoidCallback handleRemove;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui show Image, Codec, FrameInfo; import 'dart:ui' as ui show Image, Codec, FrameInfo;
...@@ -21,7 +22,7 @@ class ImageInfo { ...@@ -21,7 +22,7 @@ class ImageInfo {
/// Both the image and the scale must not be null. /// Both the image and the scale must not be null.
/// ///
/// The tag may be used to identify the source of this image. /// The tag may be used to identify the source of this image.
const ImageInfo({ required this.image, this.scale = 1.0, this.debugLabel }) const ImageInfo({ @required this.image, this.scale = 1.0, this.debugLabel })
: assert(image != null), : assert(image != null),
assert(scale != null); assert(scale != null);
...@@ -44,7 +45,7 @@ class ImageInfo { ...@@ -44,7 +45,7 @@ class ImageInfo {
final double scale; final double scale;
/// A string used for debugging purpopses to identify the source of this image. /// A string used for debugging purpopses to identify the source of this image.
final String? debugLabel; final String debugLabel;
@override @override
String toString() => '${debugLabel != null ? '$debugLabel ' : ''}$image @ ${debugFormatDouble(scale)}x'; String toString() => '${debugLabel != null ? '$debugLabel ' : ''}$image @ ${debugFormatDouble(scale)}x';
...@@ -111,13 +112,13 @@ class ImageStreamListener { ...@@ -111,13 +112,13 @@ class ImageStreamListener {
/// This callback may also continue to fire after the [onImage] callback has /// This callback may also continue to fire after the [onImage] callback has
/// fired (e.g. for multi-frame images that continue to load after the first /// fired (e.g. for multi-frame images that continue to load after the first
/// frame is available). /// frame is available).
final ImageChunkListener? onChunk; final ImageChunkListener onChunk;
/// Callback for getting notified when an error occurs while loading an image. /// Callback for getting notified when an error occurs while loading an image.
/// ///
/// If an error occurs during loading, [onError] will be called instead of /// If an error occurs during loading, [onError] will be called instead of
/// [onImage]. /// [onImage].
final ImageErrorListener? onError; final ImageErrorListener onError;
@override @override
int get hashCode => hashValues(onImage, onChunk, onError); int get hashCode => hashValues(onImage, onChunk, onError);
...@@ -154,7 +155,7 @@ typedef ImageChunkListener = void Function(ImageChunkEvent event); ...@@ -154,7 +155,7 @@ typedef ImageChunkListener = void Function(ImageChunkEvent event);
/// ///
/// Used in [ImageStreamListener], as well as by [ImageCache.putIfAbsent] and /// Used in [ImageStreamListener], as well as by [ImageCache.putIfAbsent] and
/// [precacheImage], to report errors. /// [precacheImage], to report errors.
typedef ImageErrorListener = void Function(dynamic exception, StackTrace? stackTrace); typedef ImageErrorListener = void Function(dynamic exception, StackTrace stackTrace);
/// An immutable notification of image bytes that have been incrementally loaded. /// An immutable notification of image bytes that have been incrementally loaded.
/// ///
...@@ -169,8 +170,8 @@ typedef ImageErrorListener = void Function(dynamic exception, StackTrace? stackT ...@@ -169,8 +170,8 @@ typedef ImageErrorListener = void Function(dynamic exception, StackTrace? stackT
class ImageChunkEvent with Diagnosticable { class ImageChunkEvent with Diagnosticable {
/// Creates a new chunk event. /// Creates a new chunk event.
const ImageChunkEvent({ const ImageChunkEvent({
required this.cumulativeBytesLoaded, @required this.cumulativeBytesLoaded,
required this.expectedTotalBytes, @required this.expectedTotalBytes,
}) : assert(cumulativeBytesLoaded >= 0), }) : assert(cumulativeBytesLoaded >= 0),
assert(expectedTotalBytes == null || expectedTotalBytes >= 0); assert(expectedTotalBytes == null || expectedTotalBytes >= 0);
...@@ -188,7 +189,7 @@ class ImageChunkEvent with Diagnosticable { ...@@ -188,7 +189,7 @@ class ImageChunkEvent with Diagnosticable {
/// When this value is null, the chunk event may still be useful as an /// When this value is null, the chunk event may still be useful as an
/// indication that data is loading (and how much), but it cannot represent a /// indication that data is loading (and how much), but it cannot represent a
/// loading completion percentage. /// loading completion percentage.
final int? expectedTotalBytes; final int expectedTotalBytes;
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
...@@ -228,10 +229,10 @@ class ImageStream with Diagnosticable { ...@@ -228,10 +229,10 @@ class ImageStream with Diagnosticable {
/// The completer that has been assigned to this image stream. /// The completer that has been assigned to this image stream.
/// ///
/// Generally there is no need to deal with the completer directly. /// Generally there is no need to deal with the completer directly.
ImageStreamCompleter? get completer => _completer; ImageStreamCompleter get completer => _completer;
ImageStreamCompleter? _completer; ImageStreamCompleter _completer;
List<ImageStreamListener>? _listeners; List<ImageStreamListener> _listeners;
/// Assigns a particular [ImageStreamCompleter] to this [ImageStream]. /// Assigns a particular [ImageStreamCompleter] to this [ImageStream].
/// ///
...@@ -245,9 +246,9 @@ class ImageStream with Diagnosticable { ...@@ -245,9 +246,9 @@ class ImageStream with Diagnosticable {
assert(_completer == null); assert(_completer == null);
_completer = value; _completer = value;
if (_listeners != null) { if (_listeners != null) {
final List<ImageStreamListener> initialListeners = _listeners!; final List<ImageStreamListener> initialListeners = _listeners;
_listeners = null; _listeners = null;
initialListeners.forEach(_completer!.addListener); initialListeners.forEach(_completer.addListener);
} }
} }
...@@ -271,9 +272,9 @@ class ImageStream with Diagnosticable { ...@@ -271,9 +272,9 @@ class ImageStream with Diagnosticable {
/// {@endtemplate} /// {@endtemplate}
void addListener(ImageStreamListener listener) { void addListener(ImageStreamListener listener) {
if (_completer != null) if (_completer != null)
return _completer!.addListener(listener); return _completer.addListener(listener);
_listeners ??= <ImageStreamListener>[]; _listeners ??= <ImageStreamListener>[];
_listeners!.add(listener); _listeners.add(listener);
} }
/// Stops listening for events from this stream's [ImageStreamCompleter]. /// Stops listening for events from this stream's [ImageStreamCompleter].
...@@ -282,11 +283,11 @@ class ImageStream with Diagnosticable { ...@@ -282,11 +283,11 @@ class ImageStream with Diagnosticable {
/// instance of the listener. /// instance of the listener.
void removeListener(ImageStreamListener listener) { void removeListener(ImageStreamListener listener) {
if (_completer != null) if (_completer != null)
return _completer!.removeListener(listener); return _completer.removeListener(listener);
assert(_listeners != null); assert(_listeners != null);
for (int i = 0; i < _listeners!.length; i += 1) { for (int i = 0; i < _listeners.length; i += 1) {
if (_listeners![i] == listener) { if (_listeners[i] == listener) {
_listeners!.removeAt(i); _listeners.removeAt(i);
break; break;
} }
} }
...@@ -333,11 +334,11 @@ class ImageStream with Diagnosticable { ...@@ -333,11 +334,11 @@ class ImageStream with Diagnosticable {
/// configure it with the right [ImageStreamCompleter] when possible. /// configure it with the right [ImageStreamCompleter] when possible.
abstract class ImageStreamCompleter with Diagnosticable { abstract class ImageStreamCompleter with Diagnosticable {
final List<ImageStreamListener> _listeners = <ImageStreamListener>[]; final List<ImageStreamListener> _listeners = <ImageStreamListener>[];
ImageInfo? _currentImage; ImageInfo _currentImage;
FlutterErrorDetails? _currentError; FlutterErrorDetails _currentError;
/// A string identifying the source of the underlying image. /// A string identifying the source of the underlying image.
String? debugLabel; String debugLabel;
/// Whether any listeners are currently registered. /// Whether any listeners are currently registered.
/// ///
...@@ -371,7 +372,7 @@ abstract class ImageStreamCompleter with Diagnosticable { ...@@ -371,7 +372,7 @@ abstract class ImageStreamCompleter with Diagnosticable {
_listeners.add(listener); _listeners.add(listener);
if (_currentImage != null) { if (_currentImage != null) {
try { try {
listener.onImage(_currentImage!, true); listener.onImage(_currentImage, true);
} catch (exception, stack) { } catch (exception, stack) {
reportError( reportError(
context: ErrorDescription('by a synchronously-called image listener'), context: ErrorDescription('by a synchronously-called image listener'),
...@@ -382,7 +383,7 @@ abstract class ImageStreamCompleter with Diagnosticable { ...@@ -382,7 +383,7 @@ abstract class ImageStreamCompleter with Diagnosticable {
} }
if (_currentError != null && listener.onError != null) { if (_currentError != null && listener.onError != null) {
try { try {
listener.onError!(_currentError!.exception, _currentError!.stack); listener.onError(_currentError.exception, _currentError.stack);
} catch (exception, stack) { } catch (exception, stack) {
FlutterError.reportError( FlutterError.reportError(
FlutterErrorDetails( FlutterErrorDetails(
...@@ -485,10 +486,10 @@ abstract class ImageStreamCompleter with Diagnosticable { ...@@ -485,10 +486,10 @@ abstract class ImageStreamCompleter with Diagnosticable {
/// See [FlutterErrorDetails] for further details on these values. /// See [FlutterErrorDetails] for further details on these values.
@protected @protected
void reportError({ void reportError({
DiagnosticsNode? context, DiagnosticsNode context,
dynamic exception, dynamic exception,
StackTrace? stack, StackTrace stack,
InformationCollector? informationCollector, InformationCollector informationCollector,
bool silent = false, bool silent = false,
}) { }) {
_currentError = FlutterErrorDetails( _currentError = FlutterErrorDetails(
...@@ -502,12 +503,12 @@ abstract class ImageStreamCompleter with Diagnosticable { ...@@ -502,12 +503,12 @@ abstract class ImageStreamCompleter with Diagnosticable {
// Make a copy to allow for concurrent modification. // Make a copy to allow for concurrent modification.
final List<ImageErrorListener> localErrorListeners = _listeners final List<ImageErrorListener> localErrorListeners = _listeners
.map<ImageErrorListener?>((ImageStreamListener listener) => listener.onError) .map<ImageErrorListener>((ImageStreamListener listener) => listener.onError)
.whereType<ImageErrorListener>() .where((ImageErrorListener errorListener) => errorListener != null)
.toList(); .toList();
if (localErrorListeners.isEmpty) { if (localErrorListeners.isEmpty) {
FlutterError.reportError(_currentError!); FlutterError.reportError(_currentError);
} else { } else {
for (final ImageErrorListener errorListener in localErrorListeners) { for (final ImageErrorListener errorListener in localErrorListeners) {
try { try {
...@@ -534,8 +535,8 @@ abstract class ImageStreamCompleter with Diagnosticable { ...@@ -534,8 +535,8 @@ abstract class ImageStreamCompleter with Diagnosticable {
if (hasListeners) { if (hasListeners) {
// Make a copy to allow for concurrent modification. // Make a copy to allow for concurrent modification.
final List<ImageChunkListener> localListeners = _listeners final List<ImageChunkListener> localListeners = _listeners
.map<ImageChunkListener?>((ImageStreamListener listener) => listener.onChunk) .map<ImageChunkListener>((ImageStreamListener listener) => listener.onChunk)
.whereType<ImageChunkListener>() .where((ImageChunkListener chunkListener) => chunkListener != null)
.toList(); .toList();
for (final ImageChunkListener listener in localListeners) { for (final ImageChunkListener listener in localListeners) {
listener(event); listener(event);
...@@ -552,7 +553,7 @@ abstract class ImageStreamCompleter with Diagnosticable { ...@@ -552,7 +553,7 @@ abstract class ImageStreamCompleter with Diagnosticable {
description.add(ObjectFlagProperty<List<ImageStreamListener>>( description.add(ObjectFlagProperty<List<ImageStreamListener>>(
'listeners', 'listeners',
_listeners, _listeners,
ifPresent: '${_listeners.length} listener${_listeners.length == 1 ? "" : "s" }', ifPresent: '${_listeners?.length} listener${_listeners?.length == 1 ? "" : "s" }',
)); ));
} }
} }
...@@ -574,7 +575,7 @@ class OneFrameImageStreamCompleter extends ImageStreamCompleter { ...@@ -574,7 +575,7 @@ class OneFrameImageStreamCompleter extends ImageStreamCompleter {
/// argument on [FlutterErrorDetails] set to true, meaning that by default the /// argument on [FlutterErrorDetails] set to true, meaning that by default the
/// message is only dumped to the console in debug mode (see [new /// message is only dumped to the console in debug mode (see [new
/// FlutterErrorDetails]). /// FlutterErrorDetails]).
OneFrameImageStreamCompleter(Future<ImageInfo> image, { InformationCollector? informationCollector }) OneFrameImageStreamCompleter(Future<ImageInfo> image, { InformationCollector informationCollector })
: assert(image != null) { : assert(image != null) {
image.then<void>(setImage, onError: (dynamic error, StackTrace stack) { image.then<void>(setImage, onError: (dynamic error, StackTrace stack) {
reportError( reportError(
...@@ -639,11 +640,11 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter { ...@@ -639,11 +640,11 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
/// produced by the stream will be delivered to registered [ImageChunkListener]s /// produced by the stream will be delivered to registered [ImageChunkListener]s
/// (see [addListener]). /// (see [addListener]).
MultiFrameImageStreamCompleter({ MultiFrameImageStreamCompleter({
required Future<ui.Codec> codec, @required Future<ui.Codec> codec,
required double scale, @required double scale,
String? debugLabel, String debugLabel,
Stream<ImageChunkEvent>? chunkEvents, Stream<ImageChunkEvent> chunkEvents,
InformationCollector? informationCollector, InformationCollector informationCollector,
}) : assert(codec != null), }) : assert(codec != null),
_informationCollector = informationCollector, _informationCollector = informationCollector,
_scale = scale { _scale = scale {
...@@ -672,17 +673,17 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter { ...@@ -672,17 +673,17 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
} }
} }
ui.Codec? _codec; ui.Codec _codec;
final double _scale; final double _scale;
final InformationCollector? _informationCollector; final InformationCollector _informationCollector;
ui.FrameInfo? _nextFrame; ui.FrameInfo _nextFrame;
// When the current was first shown. // When the current was first shown.
late Duration _shownTimestamp; Duration _shownTimestamp;
// The requested duration for the current frame; // The requested duration for the current frame;
Duration? _frameDuration; Duration _frameDuration;
// How many frames have been emitted so far. // How many frames have been emitted so far.
int _framesEmitted = 0; int _framesEmitted = 0;
Timer? _timer; Timer _timer;
// Used to guard against registering multiple _handleAppFrame callbacks for the same frame. // Used to guard against registering multiple _handleAppFrame callbacks for the same frame.
bool _frameCallbackScheduled = false; bool _frameCallbackScheduled = false;
...@@ -701,17 +702,17 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter { ...@@ -701,17 +702,17 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
if (!hasListeners) if (!hasListeners)
return; return;
if (_isFirstFrame() || _hasFrameDurationPassed(timestamp)) { if (_isFirstFrame() || _hasFrameDurationPassed(timestamp)) {
_emitFrame(ImageInfo(image: _nextFrame!.image, scale: _scale, debugLabel: debugLabel)); _emitFrame(ImageInfo(image: _nextFrame.image, scale: _scale, debugLabel: debugLabel));
_shownTimestamp = timestamp; _shownTimestamp = timestamp;
_frameDuration = _nextFrame!.duration; _frameDuration = _nextFrame.duration;
_nextFrame = null; _nextFrame = null;
final int completedCycles = _framesEmitted ~/ _codec!.frameCount; final int completedCycles = _framesEmitted ~/ _codec.frameCount;
if (_codec!.repetitionCount == -1 || completedCycles <= _codec!.repetitionCount) { if (_codec.repetitionCount == -1 || completedCycles <= _codec.repetitionCount) {
_decodeNextFrameAndSchedule(); _decodeNextFrameAndSchedule();
} }
return; return;
} }
final Duration delay = _frameDuration! - (timestamp - _shownTimestamp); final Duration delay = _frameDuration - (timestamp - _shownTimestamp);
_timer = Timer(delay * timeDilation, () { _timer = Timer(delay * timeDilation, () {
_scheduleAppFrame(); _scheduleAppFrame();
}); });
...@@ -722,12 +723,13 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter { ...@@ -722,12 +723,13 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
} }
bool _hasFrameDurationPassed(Duration timestamp) { bool _hasFrameDurationPassed(Duration timestamp) {
return timestamp - _shownTimestamp >= _frameDuration!; assert(_shownTimestamp != null);
return timestamp - _shownTimestamp >= _frameDuration;
} }
Future<void> _decodeNextFrameAndSchedule() async { Future<void> _decodeNextFrameAndSchedule() async {
try { try {
_nextFrame = await _codec!.getNextFrame(); _nextFrame = await _codec.getNextFrame();
} catch (exception, stack) { } catch (exception, stack) {
reportError( reportError(
context: ErrorDescription('resolving an image frame'), context: ErrorDescription('resolving an image frame'),
...@@ -738,10 +740,10 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter { ...@@ -738,10 +740,10 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
); );
return; return;
} }
if (_codec!.frameCount == 1) { if (_codec.frameCount == 1) {
// This is not an animated image, just return it and don't schedule more // This is not an animated image, just return it and don't schedule more
// frames. // frames.
_emitFrame(ImageInfo(image: _nextFrame!.image, scale: _scale, debugLabel: debugLabel)); _emitFrame(ImageInfo(image: _nextFrame.image, scale: _scale, debugLabel: debugLabel));
return; return;
} }
_scheduleAppFrame(); _scheduleAppFrame();
...@@ -752,7 +754,7 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter { ...@@ -752,7 +754,7 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
return; return;
} }
_frameCallbackScheduled = true; _frameCallbackScheduled = true;
SchedulerBinding.instance!.scheduleFrameCallback(_handleAppFrame); SchedulerBinding.instance.scheduleFrameCallback(_handleAppFrame);
} }
void _emitFrame(ImageInfo imageInfo) { void _emitFrame(ImageInfo imageInfo) {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:developer'; import 'dart:developer';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment