Skip to content

Commit

Permalink
cacheWidth cacheHeight support for canvaskit on web (#117423)
Browse files Browse the repository at this point in the history
* cacheWidth cacheHeight support for web canvaskit

* comments

* clarifying comment for loadTestImageProvider class

Co-authored-by: alanwutang11 <alpwu@google.com>
  • Loading branch information
alanwutang11 and alanwutang11 committed Dec 21, 2022
1 parent 2a50236 commit 1970bc9
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 13 deletions.
5 changes: 5 additions & 0 deletions dev/bots/test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,11 @@ Future<void> _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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Object> {
LoadTestImageProvider(this.provider);

final ImageProvider provider;

ImageStreamCompleter testLoad(Object key, DecoderBufferCallback decode) {
return provider.loadBuffer(key, decode);
}

@override
Future<Object> 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<ui.Codec> 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<ResizeImage>());

final LoadTestImageProvider testProvider = LoadTestImageProvider(image.image);
final ImageStreamCompleter streamCompleter = testProvider.testLoad(await resizeImage.obtainKey(ImageConfiguration.empty), decode);

final Completer<void> completer = Completer<void>();
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);
});
}
Original file line number Diff line number Diff line change
@@ -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<void> main() async => test.integrationDriver();
17 changes: 9 additions & 8 deletions packages/flutter/lib/src/painting/_network_image_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<ui.Codec> _loadAsync(
Expand All @@ -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<DomXMLHttpRequest> completer =
Completer<DomXMLHttpRequest>();
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;
Expand Down
7 changes: 4 additions & 3 deletions packages/flutter/lib/src/painting/image_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -901,9 +901,10 @@ class ResizeImage extends ImageProvider<ResizeImageKey> {
/// 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:
///
Expand Down
5 changes: 3 additions & 2 deletions packages/flutter/lib/src/widgets/image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 1970bc9

Please sign in to comment.