From 1970bc919bc3b0bc9e09d252e91dcd9890652c62 Mon Sep 17 00:00:00 2001 From: alanwutang11 <50225138+alanwutang11@users.noreply.github.com> Date: Wed, 21 Dec 2022 13:06:06 -0800 Subject: [PATCH] cacheWidth cacheHeight support for canvaskit on web (#117423) * cacheWidth cacheHeight support for web canvaskit * comments * clarifying comment for loadTestImageProvider class Co-authored-by: alanwutang11 --- dev/bots/test.dart | 5 ++ .../cache_width_cache_height_integration.dart | 80 +++++++++++++++++++ ...e_width_cache_height_integration_test.dart | 7 ++ .../lib/src/painting/_network_image_web.dart | 17 ++-- .../lib/src/painting/image_provider.dart | 7 +- packages/flutter/lib/src/widgets/image.dart | 5 +- 6 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 dev/integration_tests/web_e2e_tests/test_driver/cache_width_cache_height_integration.dart create mode 100644 dev/integration_tests/web_e2e_tests/test_driver/cache_width_cache_height_integration_test.dart diff --git a/dev/bots/test.dart b/dev/bots/test.dart index 52e2e31e6b5d..68d1b583c436 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -1182,6 +1182,11 @@ Future _runWebLongRunningTests() async { () => _runWebE2eTest('capabilities_integration_canvaskit', buildMode: 'profile', renderer: 'canvaskit'), () => _runWebE2eTest('capabilities_integration_html', buildMode: 'release', renderer: 'html'), + // This test doesn't do anything interesting w.r.t. rendering, so we don't run the full build mode x renderer matrix. + // CacheWidth and CacheHeight are only currently supported in CanvasKit mode, so we don't run the test in HTML mode. + () => _runWebE2eTest('cache_width_cache_height_integration', buildMode: 'debug', renderer: 'auto'), + () => _runWebE2eTest('cache_width_cache_height_integration', buildMode: 'profile', renderer: 'canvaskit'), + () => _runWebTreeshakeTest(), () => _runFlutterDriverWebTest( diff --git a/dev/integration_tests/web_e2e_tests/test_driver/cache_width_cache_height_integration.dart b/dev/integration_tests/web_e2e_tests/test_driver/cache_width_cache_height_integration.dart new file mode 100644 index 000000000000..97df31bfa5e8 --- /dev/null +++ b/dev/integration_tests/web_e2e_tests/test_driver/cache_width_cache_height_integration.dart @@ -0,0 +1,80 @@ +// 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:async'; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +// This class allows loadBuffer, a protected method, to be called with a custom +// DecoderBufferCallback function. +class LoadTestImageProvider extends ImageProvider { + LoadTestImageProvider(this.provider); + + final ImageProvider provider; + + ImageStreamCompleter testLoad(Object key, DecoderBufferCallback decode) { + return provider.loadBuffer(key, decode); + } + + @override + Future obtainKey(ImageConfiguration configuration) { + throw UnimplementedError(); + } + + @override + ImageStreamCompleter loadBuffer(Object key, DecoderBufferCallback decode) { + throw UnimplementedError(); + } +} + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Image.network uses cacheWidth and cacheHeight', (WidgetTester tester) async { + const int expectedCacheHeight = 9; + const int expectedCacheWidth = 11; + await tester.pumpAndSettle(); + + final Image image = Image.network( + 'assets/packages/flutter_gallery_assets/assets/icons/material/material.png', + cacheHeight: 9, + cacheWidth: 11, + ); + + bool called = false; + + Future decode(ui.ImmutableBuffer buffer, {int? cacheWidth, int? cacheHeight, bool allowUpscaling = false}) { + expect(cacheHeight, expectedCacheHeight); + expect(cacheWidth, expectedCacheWidth); + expect(allowUpscaling, false); + called = true; + return PaintingBinding.instance.instantiateImageCodecFromBuffer(buffer, cacheWidth: cacheWidth, cacheHeight: cacheHeight, allowUpscaling: allowUpscaling); + } + + final ImageProvider resizeImage = image.image; + expect(image.image, isA()); + + final LoadTestImageProvider testProvider = LoadTestImageProvider(image.image); + final ImageStreamCompleter streamCompleter = testProvider.testLoad(await resizeImage.obtainKey(ImageConfiguration.empty), decode); + + final Completer completer = Completer(); + int? imageInfoCachedWidth; + int? imageInfoCachedHeight; + streamCompleter.addListener(ImageStreamListener((ImageInfo imageInfo, bool syncCall) { + imageInfoCachedWidth = imageInfo.image.width; + imageInfoCachedHeight = imageInfo.image.height; + completer.complete(); + })); + await completer.future; + + expect(imageInfoCachedHeight, isNotNull); + expect(imageInfoCachedHeight, expectedCacheHeight); + expect(imageInfoCachedWidth, isNotNull); + expect(imageInfoCachedWidth, expectedCacheWidth); + expect(called, true); + }); +} diff --git a/dev/integration_tests/web_e2e_tests/test_driver/cache_width_cache_height_integration_test.dart b/dev/integration_tests/web_e2e_tests/test_driver/cache_width_cache_height_integration_test.dart new file mode 100644 index 000000000000..b2d2a1770b2f --- /dev/null +++ b/dev/integration_tests/web_e2e_tests/test_driver/cache_width_cache_height_integration_test.dart @@ -0,0 +1,7 @@ +// 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 'package:integration_test/integration_test_driver.dart' as test; + +Future main() async => test.integrationDriver(); diff --git a/packages/flutter/lib/src/painting/_network_image_web.dart b/packages/flutter/lib/src/painting/_network_image_web.dart index 4d2282e1b22a..ee7db7676239 100644 --- a/packages/flutter/lib/src/painting/_network_image_web.dart +++ b/packages/flutter/lib/src/painting/_network_image_web.dart @@ -103,10 +103,7 @@ class NetworkImage return collector; } - // TODO(garyq): We should eventually support custom decoding of network images on Web as - // well, see https://github.com/flutter/flutter/issues/42789. - // - // Web does not support decoding network images to a specified size. The decode parameter + // Html renderer does not support decoding network images to a specified size. The decode parameter // here is ignored and the web-only `ui.webOnlyInstantiateImageCodecFromUrl` will be used // directly in place of the typical `instantiateImageCodec` method. Future _loadAsync( @@ -119,18 +116,22 @@ class NetworkImage final Uri resolved = Uri.base.resolve(key.url); + final bool containsNetworkImageHeaders = key.headers?.isNotEmpty ?? false; + // We use a different method when headers are set because the // `ui.webOnlyInstantiateImageCodecFromUrl` method is not capable of handling headers. - if (key.headers?.isNotEmpty ?? false) { + if (isCanvasKit || containsNetworkImageHeaders) { final Completer completer = Completer(); final DomXMLHttpRequest request = httpRequestFactory(); request.open('GET', key.url, true); request.responseType = 'arraybuffer'; - key.headers!.forEach((String header, String value) { - request.setRequestHeader(header, value); - }); + if (containsNetworkImageHeaders) { + key.headers!.forEach((String header, String value) { + request.setRequestHeader(header, value); + }); + } request.addEventListener('load', allowInterop((DomEvent e) { final int? status = request.status; diff --git a/packages/flutter/lib/src/painting/image_provider.dart b/packages/flutter/lib/src/painting/image_provider.dart index 0ec2217816f3..f346eae94fee 100644 --- a/packages/flutter/lib/src/painting/image_provider.dart +++ b/packages/flutter/lib/src/painting/image_provider.dart @@ -901,9 +901,10 @@ class ResizeImage extends ImageProvider { /// The image will be cached regardless of cache headers from the server. /// /// When a network image is used on the Web platform, the `cacheWidth` and -/// `cacheHeight` parameters of the [DecoderCallback] are ignored as the Web -/// engine delegates image decoding of network images to the Web, which does -/// not support custom decode sizes. +/// `cacheHeight` parameters of the [DecoderCallback] are only supported when the +/// application is running with the CanvasKit renderer. When the application is using +/// the HTML renderer, the web engine delegates image decoding of network images to the Web, +/// which does not support custom decode sizes. /// /// See also: /// diff --git a/packages/flutter/lib/src/widgets/image.dart b/packages/flutter/lib/src/widgets/image.dart index 4336f0ab3f61..50c521388318 100644 --- a/packages/flutter/lib/src/widgets/image.dart +++ b/packages/flutter/lib/src/widgets/image.dart @@ -288,8 +288,9 @@ typedef ImageErrorWidgetBuilder = Widget Function( /// memory usage of [ImageCache]. /// /// In the case where a network image is used on the Web platform, the -/// `cacheWidth` and `cacheHeight` parameters are ignored as the Web engine -/// delegates image decoding of network images to the Web, which does not support +/// `cacheWidth` and `cacheHeight` parameters are only supported when the application is +/// running with the CanvasKit renderer. When the application is using the HTML renderer, +/// the web engine delegates image decoding of network images to the Web, which does not support /// custom decode sizes. /// /// See also: