From 42c75e0c06d6a3c19d5d6aab4530433284211a73 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 15 Dec 2021 04:15:23 -0800 Subject: [PATCH 1/4] Impl --- lib/ui/painting.dart | 10 ++++++---- lib/web_ui/test/html/image_test.dart | 30 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 4fb8203945cc6..8976f5e2dc017 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -2129,8 +2129,9 @@ Future _decodeImageFromListAsync(Uint8List list, /// Convert an array of pixel values into an [Image] object. /// -/// The `pixels` parameter is the pixel data in the encoding described by -/// `format`. +/// The `pixels` parameter is the pixel data. They are packed in bytes in the +/// order described by `format`, then grouped in rows, from left to right, +/// then top to bottom. /// /// The `rowBytes` parameter is the number of bytes consumed by each row of /// pixels in the data buffer. If unspecified, it defaults to `width` multiplied @@ -5637,8 +5638,9 @@ class ImageDescriptor extends NativeFieldWrapperClass1 { /// Creates an image descriptor from raw image pixels. /// - /// The `pixels` parameter is the pixel data in the encoding described by - /// `format`. + /// The `pixels` parameter is the pixel data. They are packed in bytes in the + /// order described by `pixelFormat`, then grouped in rows, from left to right, + /// then top to bottom. /// /// The `rowBytes` parameter is the number of bytes consumed by each row of /// pixels in the data buffer. If unspecified, it defaults to `width` multiplied diff --git a/lib/web_ui/test/html/image_test.dart b/lib/web_ui/test/html/image_test.dart index 6b6ec48bd312c..c63f158621c12 100644 --- a/lib/web_ui/test/html/image_test.dart +++ b/lib/web_ui/test/html/image_test.dart @@ -72,6 +72,16 @@ Future _encodeToHtmlThenDecode( return (await (await descriptor.instantiateCodec()).getNextFrame()).image; } +Future imageRawUsesCorrectBehavior(PixelFormat format) async { + final ImageDescriptor descriptor = ImageDescriptor.raw( + await ImmutableBuffer.fromUint8List(Uint8List.fromList([0xED, 0, 0, 0])), + width: 1, height: 1, pixelFormat: format); + final Image image = (await (await descriptor.instantiateCodec()).getNextFrame()).image; + final Uint8List resultPixels = Uint8List.sublistView( + (await image.toByteData(format: ImageByteFormat.rawStraightRgba))!); + return resultPixels[0] == 0xED; +} + Future testMain() async { test('Correctly encodes an opaque image', () async { // A 2x2 testing image without transparency. @@ -145,4 +155,24 @@ Future testMain() async { ); expect(actualPixels, listEqual(benchmarkPixels, tolerance: 1)); }); + + test('Correctly encodes an opaque image in bgra8888', () async { + // A 1x1 testing image without transparency. + final Image sourceImage = await _encodeToHtmlThenDecode( + _pixelsToBytes([0xFE0000FF]), 1, 1, pixelFormat: PixelFormat.rgba8888, + ); + final Uint8List actualPixels = Uint8List.sublistView( + (await sourceImage.toByteData(format: ImageByteFormat.rawStraightRgba))!); + // The `benchmarkPixels` is the same as `sourceImage` except that the R and + // G channels are swapped and the fully transparent last pixel is turned 0. + final Uint8List benchmarkPixels = _pixelsToBytes( + [0x0201FFFF, 0x05FE04FF, 0xFD0807FF, 0x00000000], + ); + expect(actualPixels, listEqual(benchmarkPixels)); + }); + + test('The check detector is correct', () async { + expect(await imageRawUsesCorrectBehavior(PixelFormat.rgba8888), true); + expect(await imageRawUsesCorrectBehavior(PixelFormat.bgra8888), false); + }); } From 9e9bed165433409fb68f3a7efbb3b7ecb6a1cbaf Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 15 Dec 2021 05:16:36 -0800 Subject: [PATCH 2/4] Doc --- lib/web_ui/test/html/image_test.dart | 39 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/lib/web_ui/test/html/image_test.dart b/lib/web_ui/test/html/image_test.dart index c63f158621c12..1401ca9052c35 100644 --- a/lib/web_ui/test/html/image_test.dart +++ b/lib/web_ui/test/html/image_test.dart @@ -72,7 +72,23 @@ Future _encodeToHtmlThenDecode( return (await (await descriptor.instantiateCodec()).getNextFrame()).image; } -Future imageRawUsesCorrectBehavior(PixelFormat format) async { +// This utility function detects how the current Web engine decodes pixel data. +// +// The HTML renderer uses the BMP format to display pixel data, but it used to +// uses a wrong implementation. The bug has been fixed, but the fix breaks apps +// that had to provide incorrect data to work around this issue. This function +// is used in the migration guide to assist libraries that would like to run on +// both pre- and post-patch engines by testing the current behavior on a single +// pixel, making use the fact that the patch fixes the pixel order. +// +// The `format` argument is used for testing. In the actual code it should be +// replaced by `PixelFormat.rgba8888`. +// +// See also: +// +// * Patch: https://github.com/flutter/engine/pull/29448 +// * Migration guide: (TODO) +Future rawImageUsesCorrectBehavior(PixelFormat format) async { final ImageDescriptor descriptor = ImageDescriptor.raw( await ImmutableBuffer.fromUint8List(Uint8List.fromList([0xED, 0, 0, 0])), width: 1, height: 1, pixelFormat: format); @@ -156,23 +172,8 @@ Future testMain() async { expect(actualPixels, listEqual(benchmarkPixels, tolerance: 1)); }); - test('Correctly encodes an opaque image in bgra8888', () async { - // A 1x1 testing image without transparency. - final Image sourceImage = await _encodeToHtmlThenDecode( - _pixelsToBytes([0xFE0000FF]), 1, 1, pixelFormat: PixelFormat.rgba8888, - ); - final Uint8List actualPixels = Uint8List.sublistView( - (await sourceImage.toByteData(format: ImageByteFormat.rawStraightRgba))!); - // The `benchmarkPixels` is the same as `sourceImage` except that the R and - // G channels are swapped and the fully transparent last pixel is turned 0. - final Uint8List benchmarkPixels = _pixelsToBytes( - [0x0201FFFF, 0x05FE04FF, 0xFD0807FF, 0x00000000], - ); - expect(actualPixels, listEqual(benchmarkPixels)); - }); - - test('The check detector is correct', () async { - expect(await imageRawUsesCorrectBehavior(PixelFormat.rgba8888), true); - expect(await imageRawUsesCorrectBehavior(PixelFormat.bgra8888), false); + test('The behavior detector works correctly', () async { + expect(await rawImageUsesCorrectBehavior(PixelFormat.rgba8888), true); + expect(await rawImageUsesCorrectBehavior(PixelFormat.bgra8888), false); }); } From 7ec9d8b48bc9da7a386a4e61bb8f092c3e74b44b Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 20 May 2022 15:44:21 -0700 Subject: [PATCH 3/4] Doc --- lib/web_ui/test/html/image_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web_ui/test/html/image_test.dart b/lib/web_ui/test/html/image_test.dart index 1401ca9052c35..b29a9abfa877c 100644 --- a/lib/web_ui/test/html/image_test.dart +++ b/lib/web_ui/test/html/image_test.dart @@ -75,7 +75,7 @@ Future _encodeToHtmlThenDecode( // This utility function detects how the current Web engine decodes pixel data. // // The HTML renderer uses the BMP format to display pixel data, but it used to -// uses a wrong implementation. The bug has been fixed, but the fix breaks apps +// use a wrong implementation. The bug has been fixed, but the fix breaks apps // that had to provide incorrect data to work around this issue. This function // is used in the migration guide to assist libraries that would like to run on // both pre- and post-patch engines by testing the current behavior on a single @@ -87,7 +87,7 @@ Future _encodeToHtmlThenDecode( // See also: // // * Patch: https://github.com/flutter/engine/pull/29448 -// * Migration guide: (TODO) +// * Migration guide: https://docs.flutter.dev/release/breaking-changes/raw-images-on-web-uses-correct-origin-and-colors Future rawImageUsesCorrectBehavior(PixelFormat format) async { final ImageDescriptor descriptor = ImageDescriptor.raw( await ImmutableBuffer.fromUint8List(Uint8List.fromList([0xED, 0, 0, 0])), From a7b33aa90368d40dac7b6304850301d47a7a29d9 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 23 May 2022 17:34:17 -0700 Subject: [PATCH 4/4] Fix tests --- lib/web_ui/test/html/image_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/test/html/image_test.dart b/lib/web_ui/test/html/image_test.dart index b29a9abfa877c..318ed62d3a6d7 100644 --- a/lib/web_ui/test/html/image_test.dart +++ b/lib/web_ui/test/html/image_test.dart @@ -90,7 +90,7 @@ Future _encodeToHtmlThenDecode( // * Migration guide: https://docs.flutter.dev/release/breaking-changes/raw-images-on-web-uses-correct-origin-and-colors Future rawImageUsesCorrectBehavior(PixelFormat format) async { final ImageDescriptor descriptor = ImageDescriptor.raw( - await ImmutableBuffer.fromUint8List(Uint8List.fromList([0xED, 0, 0, 0])), + await ImmutableBuffer.fromUint8List(Uint8List.fromList([0xED, 0, 0, 0xFF])), width: 1, height: 1, pixelFormat: format); final Image image = (await (await descriptor.instantiateCodec()).getNextFrame()).image; final Uint8List resultPixels = Uint8List.sublistView(