mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Report error when instantiating CanvasKit network image (#22159)
This commit is contained in:
parent
67d55ed42b
commit
7c8f57c07d
@ -15,24 +15,33 @@ void skiaInstantiateImageCodec(Uint8List list, Callback<ui.Codec> callback,
|
||||
callback(codec);
|
||||
}
|
||||
|
||||
/// Instantiates a [ui.Codec] backed by an `SkAnimatedImage` from Skia after requesting from URI.
|
||||
void skiaInstantiateWebImageCodec(String src, Callback<ui.Codec> callback,
|
||||
WebOnlyImageCodecChunkCallback? chunkCallback) {
|
||||
chunkCallback?.call(0, 100);
|
||||
/// Instantiates a [ui.Codec] backed by an `SkAnimatedImage` from Skia after
|
||||
/// requesting from URI.
|
||||
Future<ui.Codec> skiaInstantiateWebImageCodec(
|
||||
String src, WebOnlyImageCodecChunkCallback? chunkCallback) {
|
||||
Completer<ui.Codec> completer = Completer<ui.Codec>();
|
||||
//TODO: Switch to using MakeImageFromCanvasImageSource when animated images are supported.
|
||||
html.HttpRequest.request(
|
||||
src,
|
||||
responseType: "arraybuffer",
|
||||
).then((html.HttpRequest response) {
|
||||
chunkCallback?.call(100, 100);
|
||||
html.HttpRequest.request(src, responseType: "arraybuffer",
|
||||
onProgress: (html.ProgressEvent event) {
|
||||
if (event.lengthComputable) {
|
||||
chunkCallback?.call(event.loaded!, event.total!);
|
||||
}
|
||||
}).then((html.HttpRequest response) {
|
||||
if (response.status != 200) {
|
||||
completer.completeError(Exception(
|
||||
'Network image request failed with status: ${response.status}'));
|
||||
}
|
||||
final Uint8List list =
|
||||
new Uint8List.view((response.response as ByteBuffer));
|
||||
final SkAnimatedImage skAnimatedImage =
|
||||
canvasKit.MakeAnimatedImageFromEncoded(list);
|
||||
final CkAnimatedImage animatedImage = CkAnimatedImage(skAnimatedImage);
|
||||
final CkAnimatedImageCodec codec = CkAnimatedImageCodec(animatedImage);
|
||||
callback(codec);
|
||||
completer.complete(codec);
|
||||
}, onError: (dynamic error) {
|
||||
completer.completeError(error);
|
||||
});
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
/// A wrapper for `SkAnimatedImage`.
|
||||
@ -43,7 +52,8 @@ class CkAnimatedImage implements ui.Image {
|
||||
// being garbage-collected, or by an explicit call to [delete].
|
||||
late final SkiaObjectBox box;
|
||||
|
||||
CkAnimatedImage(SkAnimatedImage skAnimatedImage) : this._(skAnimatedImage, null);
|
||||
CkAnimatedImage(SkAnimatedImage skAnimatedImage)
|
||||
: this._(skAnimatedImage, null);
|
||||
|
||||
CkAnimatedImage._(this._skAnimatedImage, SkiaObjectBox? boxToClone) {
|
||||
if (boxToClone != null) {
|
||||
@ -66,19 +76,21 @@ class CkAnimatedImage implements ui.Image {
|
||||
if (assertionsEnabled) {
|
||||
return _disposed;
|
||||
}
|
||||
throw StateError('Image.debugDisposed is only available when asserts are enabled.');
|
||||
throw StateError(
|
||||
'Image.debugDisposed is only available when asserts are enabled.');
|
||||
}
|
||||
|
||||
ui.Image clone() => CkAnimatedImage._(_skAnimatedImage, box);
|
||||
|
||||
@override
|
||||
bool isCloneOf(ui.Image other) {
|
||||
return other is CkAnimatedImage
|
||||
&& other._skAnimatedImage.isAliasOf(_skAnimatedImage);
|
||||
return other is CkAnimatedImage &&
|
||||
other._skAnimatedImage.isAliasOf(_skAnimatedImage);
|
||||
}
|
||||
|
||||
@override
|
||||
List<StackTrace>? debugGetOpenHandleStackTraces() => box.debugGetStackTraces();
|
||||
List<StackTrace>? debugGetOpenHandleStackTraces() =>
|
||||
box.debugGetStackTraces();
|
||||
|
||||
int get frameCount => _skAnimatedImage.getFrameCount();
|
||||
|
||||
@ -115,8 +127,9 @@ class CkAnimatedImage implements ui.Image {
|
||||
);
|
||||
bytes = _skAnimatedImage.readPixels(imageInfo, 0, 0);
|
||||
} else {
|
||||
final SkData skData = _skAnimatedImage.encodeToData(); //defaults to PNG 100%
|
||||
// make a copy that we can return
|
||||
// Defaults to PNG 100%.
|
||||
final SkData skData = _skAnimatedImage.encodeToData();
|
||||
// Make a copy that we can return.
|
||||
bytes = Uint8List.fromList(canvasKit.getSkDataBytes(skData));
|
||||
}
|
||||
|
||||
@ -162,7 +175,8 @@ class CkImage implements ui.Image {
|
||||
if (assertionsEnabled) {
|
||||
return _disposed;
|
||||
}
|
||||
throw StateError('Image.debugDisposed is only available when asserts are enabled.');
|
||||
throw StateError(
|
||||
'Image.debugDisposed is only available when asserts are enabled.');
|
||||
}
|
||||
|
||||
@override
|
||||
@ -170,12 +184,12 @@ class CkImage implements ui.Image {
|
||||
|
||||
@override
|
||||
bool isCloneOf(ui.Image other) {
|
||||
return other is CkImage
|
||||
&& other.skImage.isAliasOf(skImage);
|
||||
return other is CkImage && other.skImage.isAliasOf(skImage);
|
||||
}
|
||||
|
||||
@override
|
||||
List<StackTrace>? debugGetOpenHandleStackTraces() => box.debugGetStackTraces();
|
||||
List<StackTrace>? debugGetOpenHandleStackTraces() =>
|
||||
box.debugGetStackTraces();
|
||||
|
||||
@override
|
||||
int get width => skImage.width();
|
||||
|
||||
@ -462,9 +462,14 @@ String? _instantiateImageCodec(Uint8List list, engine.Callback<Codec> callback)
|
||||
}
|
||||
|
||||
Future<Codec> webOnlyInstantiateImageCodecFromUrl(Uri uri,
|
||||
{engine.WebOnlyImageCodecChunkCallback? chunkCallback}) {
|
||||
return _futurize<Codec>((engine.Callback<Codec> callback) =>
|
||||
{engine.WebOnlyImageCodecChunkCallback? chunkCallback}) {
|
||||
if (engine.useCanvasKit) {
|
||||
return engine.skiaInstantiateWebImageCodec(
|
||||
uri.toString(), chunkCallback);
|
||||
} else {
|
||||
return _futurize<Codec>((engine.Callback<Codec> callback) =>
|
||||
_instantiateImageCodecFromUrl(uri, chunkCallback, callback));
|
||||
}
|
||||
}
|
||||
|
||||
String? _instantiateImageCodecFromUrl(
|
||||
@ -472,13 +477,8 @@ String? _instantiateImageCodecFromUrl(
|
||||
engine.WebOnlyImageCodecChunkCallback? chunkCallback,
|
||||
engine.Callback<Codec> callback,
|
||||
) {
|
||||
if (engine.useCanvasKit) {
|
||||
engine.skiaInstantiateWebImageCodec(uri.toString(), callback, chunkCallback);
|
||||
return null;
|
||||
} else {
|
||||
callback(engine.HtmlCodec(uri.toString(), chunkCallback: chunkCallback));
|
||||
return null;
|
||||
}
|
||||
callback(engine.HtmlCodec(uri.toString(), chunkCallback: chunkCallback));
|
||||
return null;
|
||||
}
|
||||
|
||||
void decodeImageFromList(Uint8List list, ImageDecoderCallback callback) {
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.6
|
||||
import 'dart:html' show ProgressEvent;
|
||||
|
||||
import 'package:test/bootstrap/browser.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
@ -22,14 +24,16 @@ void testMain() {
|
||||
});
|
||||
|
||||
test('CkAnimatedImage toString', () {
|
||||
final SkAnimatedImage skAnimatedImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage);
|
||||
final SkAnimatedImage skAnimatedImage =
|
||||
canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage);
|
||||
final CkAnimatedImage image = CkAnimatedImage(skAnimatedImage);
|
||||
expect(image.toString(), '[1×1]');
|
||||
image.dispose();
|
||||
});
|
||||
|
||||
test('CkAnimatedImage can be explicitly disposed of', () {
|
||||
final SkAnimatedImage skAnimatedImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage);
|
||||
final SkAnimatedImage skAnimatedImage =
|
||||
canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage);
|
||||
final CkAnimatedImage image = CkAnimatedImage(skAnimatedImage);
|
||||
expect(image.box.isDeleted, false);
|
||||
expect(image.debugDisposed, false);
|
||||
@ -42,7 +46,8 @@ void testMain() {
|
||||
});
|
||||
|
||||
test('CkAnimatedImage can be cloned and explicitly disposed of', () async {
|
||||
final SkAnimatedImage skAnimatedImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage);
|
||||
final SkAnimatedImage skAnimatedImage =
|
||||
canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage);
|
||||
final CkAnimatedImage image = CkAnimatedImage(skAnimatedImage);
|
||||
final CkAnimatedImage imageClone = image.clone();
|
||||
|
||||
@ -63,14 +68,18 @@ void testMain() {
|
||||
});
|
||||
|
||||
test('CkImage toString', () {
|
||||
final SkImage skImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage).getCurrentFrame();
|
||||
final SkImage skImage =
|
||||
canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)
|
||||
.getCurrentFrame();
|
||||
final CkImage image = CkImage(skImage);
|
||||
expect(image.toString(), '[1×1]');
|
||||
image.dispose();
|
||||
});
|
||||
|
||||
test('CkImage can be explicitly disposed of', () {
|
||||
final SkImage skImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage).getCurrentFrame();
|
||||
final SkImage skImage =
|
||||
canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)
|
||||
.getCurrentFrame();
|
||||
final CkImage image = CkImage(skImage);
|
||||
expect(image.debugDisposed, false);
|
||||
expect(image.box.isDeleted, false);
|
||||
@ -83,7 +92,9 @@ void testMain() {
|
||||
});
|
||||
|
||||
test('CkImage can be explicitly disposed of when cloned', () async {
|
||||
final SkImage skImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage).getCurrentFrame();
|
||||
final SkImage skImage =
|
||||
canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)
|
||||
.getCurrentFrame();
|
||||
final CkImage image = CkImage(skImage);
|
||||
final CkImage imageClone = image.clone();
|
||||
|
||||
@ -102,6 +113,12 @@ void testMain() {
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
expect(skImage.isDeleted(), true);
|
||||
});
|
||||
// TODO: https://github.com/flutter/flutter/issues/60040
|
||||
|
||||
test('skiaInstantiateWebImageCodec throws exception if given invalid URL',
|
||||
() async {
|
||||
expect(skiaInstantiateWebImageCodec('invalid-url', null),
|
||||
throwsA(isA<ProgressEvent>()));
|
||||
});
|
||||
// TODO: https://github.com/flutter/flutter/issues/60040
|
||||
}, skip: isIosSafari);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user