From 9e797d146196a1c3d2cdae389f3b40b3c04d86fc Mon Sep 17 00:00:00 2001
From: Harry Terkelsen <1961493+harryterkelsen@users.noreply.github.com>
Date: Fri, 19 Jul 2024 10:41:05 -0700
Subject: [PATCH] [canvaskit] Decode images using
tag decoding
(flutter/engine#53201)
Prefer to decode images using the browser API rather than with CanvasKit to avoid jank when decoding.
Part of deprecating the HTML renderer: https://github.com/flutter/flutter/issues/145954
[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
---
.../src/engine/canvaskit/canvaskit_api.dart | 6 +-
.../lib/src/engine/canvaskit/image.dart | 470 ++++++-
.../engine/canvaskit/image_web_codecs.dart | 90 +-
.../lib/src/engine/canvaskit/picture.dart | 9 +-
.../lib/src/engine/canvaskit/renderer.dart | 5 +-
.../lib/web_ui/lib/src/engine/dom.dart | 2 +-
.../lib/web_ui/lib/src/engine/html/image.dart | 4 +
.../src/engine/html_image_element_codec.dart | 134 +-
.../web_ui/lib/src/engine/image_decoder.dart | 37 +-
.../test/canvaskit/embedded_views_test.dart | 1 +
.../test/canvaskit/image_golden_test.dart | 1118 ++++-------------
11 files changed, 823 insertions(+), 1053 deletions(-)
diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
index 7a847220972..99fd3671206 100644
--- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
+++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
@@ -1184,10 +1184,10 @@ extension SkImageExtension on SkImage {
matrix?.toJS);
@JS('readPixels')
- external JSUint8Array _readPixels(
+ external JSUint8Array? _readPixels(
JSNumber srcX, JSNumber srcY, SkImageInfo imageInfo);
- Uint8List readPixels(double srcX, double srcY, SkImageInfo imageInfo) =>
- _readPixels(srcX.toJS, srcY.toJS, imageInfo).toDart;
+ Uint8List? readPixels(double srcX, double srcY, SkImageInfo imageInfo) =>
+ _readPixels(srcX.toJS, srcY.toJS, imageInfo)?.toDart;
@JS('encodeToBytes')
external JSUint8Array? _encodeToBytes();
diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/image.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/image.dart
index 23c585f7f8a..c55dff72645 100644
--- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/image.dart
+++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/image.dart
@@ -4,6 +4,7 @@
import 'dart:async';
import 'dart:js_interop';
+import 'dart:math' as math;
import 'dart:typed_data';
import 'package:ui/src/engine.dart';
@@ -11,17 +12,185 @@ import 'package:ui/ui.dart' as ui;
import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
/// Instantiates a [ui.Codec] backed by an `SkAnimatedImage` from Skia.
-FutureOr skiaInstantiateImageCodec(Uint8List list,
- [int? targetWidth, int? targetHeight]) {
- // If we have either a target width or target height, use canvaskit to decode.
- if (browserSupportsImageDecoder && (targetWidth == null && targetHeight == null)) {
- return CkBrowserImageDecoder.create(
+Future skiaInstantiateImageCodec(Uint8List list,
+ [int? targetWidth, int? targetHeight, bool allowUpscaling = true]) async {
+ ui.Codec codec;
+ // ImageDecoder does not detect image type automatically. It requires us to
+ // tell it what the image type is.
+ final String contentType = tryDetectContentType(list, 'encoded image bytes');
+
+ if (browserSupportsImageDecoder) {
+ codec = await CkBrowserImageDecoder.create(
data: list,
+ contentType: contentType,
debugSource: 'encoded image bytes',
);
} else {
- return CkAnimatedImage.decodeFromBytes(list, 'encoded image bytes', targetWidth: targetWidth, targetHeight: targetHeight);
+ // TODO(harryterkelsen): If the image is animated, then use Skia to decode.
+ // This is currently too conservative, assuming all GIF and WEBP images are
+ // animated. We should detect if they are actually animated by reading the
+ // image headers, https://github.com/flutter/flutter/issues/151911.
+ if (contentType == 'image/gif' || contentType == 'image/webp') {
+ codec = CkAnimatedImage.decodeFromBytes(list, 'encoded image bytes',
+ targetWidth: targetWidth, targetHeight: targetHeight);
+ } else {
+ final DomBlob blob = createDomBlob([list.buffer]);
+ codec = await decodeBlobToCkImage(blob);
+ }
}
+ return CkResizingCodec(
+ codec,
+ targetWidth: targetWidth,
+ targetHeight: targetHeight,
+ allowUpscaling: allowUpscaling,
+ );
+}
+
+/// A resizing codec which uses an HTML