* Reland dispose images when done (#67100)
Changes since last time:
- Test for CanvasKit image rendering
(https://github.com/flutter/flutter/pull/67176)
- Fix CanvasKit dispose impl
(https://github.com/flutter/engine/pull/21555)
- Update internal google3 customer with a problematic ImageStream
Listener impl (cl/335091311, cl/335459002)
This reverts commit 473358d93d.
120 lines
3.9 KiB
Dart
120 lines
3.9 KiB
Dart
// 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:typed_data';
|
|
import 'dart:ui' as ui;
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/painting.dart';
|
|
|
|
import 'test_async_utils.dart';
|
|
|
|
final Map<int, ui.Image> _cache = <int, ui.Image>{};
|
|
|
|
/// Creates an arbitrarily sized image for testing.
|
|
///
|
|
/// If the [cache] parameter is set to true, the image will be cached for the
|
|
/// rest of this suite. This is normally desirable, assuming a test suite uses
|
|
/// images with the same dimensions in most tests, as it will save on memory
|
|
/// usage and CPU time over the course of the suite. However, it should be
|
|
/// avoided for images that are used only once in a test suite, especially if
|
|
/// the image is large, as it will require holding on to the memory for that
|
|
/// image for the duration of the suite.
|
|
///
|
|
/// This method requires real async work, and will not work properly in the
|
|
/// [FakeAsync] zones set up by [testWidgets]. Typically, it should be invoked
|
|
/// as a setup step before [testWidgets] are run, such as [setUp] or [setUpAll].
|
|
/// If needed, it can be invoked using [WidgetTester.runAsync].
|
|
Future<ui.Image> createTestImage({
|
|
int width = 1,
|
|
int height = 1,
|
|
bool cache = true,
|
|
}) => TestAsyncUtils.guard(() async {
|
|
assert(width != null && width > 0);
|
|
assert(height != null && height > 0);
|
|
assert(cache != null);
|
|
|
|
final int cacheKey = hashValues(width, height);
|
|
if (cache && _cache.containsKey(cacheKey)) {
|
|
return _cache[cacheKey]!.clone();
|
|
}
|
|
|
|
final ui.Image image = await _createImage(width, height);
|
|
if (cache) {
|
|
_cache[cacheKey] = image.clone();
|
|
}
|
|
return image;
|
|
});
|
|
|
|
Future<ui.Image> _createImage(int width, int height) async {
|
|
if (kIsWeb) {
|
|
return await _webCreateTestImage(
|
|
width: width,
|
|
height: height,
|
|
);
|
|
}
|
|
|
|
final Completer<ui.Image> completer = Completer<ui.Image>();
|
|
ui.decodeImageFromPixels(
|
|
Uint8List.fromList(List<int>.filled(width * height * 4, 0, growable: false)),
|
|
width,
|
|
height,
|
|
ui.PixelFormat.rgba8888,
|
|
(ui.Image image) {
|
|
completer.complete(image);
|
|
},
|
|
);
|
|
return await completer.future;
|
|
}
|
|
|
|
/// Web doesn't support [decodeImageFromPixels]. Instead, generate a 1bpp BMP
|
|
/// and just use [instantiateImageCodec].
|
|
// TODO(dnfield): Remove this when https://github.com/flutter/flutter/issues/49244
|
|
// is resolved.
|
|
Future<ui.Image> _webCreateTestImage({
|
|
required int width,
|
|
required int height,
|
|
}) async {
|
|
// See https://en.wikipedia.org/wiki/BMP_file_format for format examples.
|
|
final int bufferSize = 0x36 + (width * height);
|
|
final ByteData bmpData = ByteData(bufferSize);
|
|
// 'BM' header
|
|
bmpData.setUint8(0x00, 0x42);
|
|
bmpData.setUint8(0x01, 0x4D);
|
|
// Size of data
|
|
bmpData.setUint32(0x02, bufferSize, Endian.little);
|
|
// Offset where pixel array begins
|
|
bmpData.setUint32(0x0A, 0x36, Endian.little);
|
|
// Bytes in DIB header
|
|
bmpData.setUint32(0x0E, 0x28, Endian.little);
|
|
// width
|
|
bmpData.setUint32(0x12, width, Endian.little);
|
|
// height
|
|
bmpData.setUint32(0x16, height, Endian.little);
|
|
// Color panes
|
|
bmpData.setUint16(0x1A, 0x01, Endian.little);
|
|
// bpp
|
|
bmpData.setUint16(0x1C, 0x01, Endian.little);
|
|
// no compression
|
|
bmpData.setUint32(0x1E, 0x00, Endian.little);
|
|
// raw bitmap data size
|
|
bmpData.setUint32(0x22, width * height, Endian.little);
|
|
// print DPI width
|
|
bmpData.setUint32(0x26, width, Endian.little);
|
|
// print DPI height
|
|
bmpData.setUint32(0x2A, height, Endian.little);
|
|
// colors in the palette
|
|
bmpData.setUint32(0x2E, 0x00, Endian.little);
|
|
// important colors
|
|
bmpData.setUint32(0x32, 0x00, Endian.little);
|
|
// rest of data is zeroed as black pixels.
|
|
|
|
final ui.Codec codec = await ui.instantiateImageCodec(
|
|
bmpData.buffer.asUint8List(),
|
|
);
|
|
final ui.FrameInfo frameInfo = await codec.getNextFrame();
|
|
return frameInfo.image;
|
|
}
|