From 7cf0c3139ba11bea58bf6e9a37135165fa60809f Mon Sep 17 00:00:00 2001 From: ColdPaleLight <31977171+ColdPaleLight@users.noreply.github.com> Date: Sat, 28 Aug 2021 01:11:01 +0800 Subject: [PATCH] Support raw straight RGBA format in Image.toByteData() (#28293) --- lib/ui/painting.dart | 7 +- lib/ui/painting/image_encoding.cc | 18 +++-- lib/web_ui/lib/src/ui/painting.dart | 1 + testing/dart/encoding_test.dart | 83 ++++++++++++++++++++++++ testing/resources/transparent_image.png | Bin 0 -> 133 bytes 5 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 testing/resources/transparent_image.png diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 01dbea2b37b5b..7ca9ec31065f4 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -1577,9 +1577,14 @@ class Paint { enum ImageByteFormat { /// Raw RGBA format. /// - /// Unencoded bytes, in RGBA row-primary form, 8 bits per channel. + /// Unencoded bytes, in RGBA row-primary form with premultiplied alpha, 8 bits per channel. rawRgba, + /// Raw straight RGBA format. + /// + /// Unencoded bytes, in RGBA row-primary form with straight alpha, 8 bits per channel. + rawStraightRgba, + /// Raw unmodified format. /// /// Unencoded bytes, in the image's existing format. For example, a grayscale diff --git a/lib/ui/painting/image_encoding.cc b/lib/ui/painting/image_encoding.cc index 90dbbb6f34bc0..8f6e2526e1e57 100644 --- a/lib/ui/painting/image_encoding.cc +++ b/lib/ui/painting/image_encoding.cc @@ -31,6 +31,7 @@ namespace { // This must be kept in sync with the enum in painting.dart enum ImageByteFormat { kRawRGBA, + kRawStraightRGBA, kRawUnmodified, kPNG, }; @@ -151,7 +152,8 @@ void ConvertImageToRaster(sk_sp image, } sk_sp CopyImageByteData(sk_sp raster_image, - SkColorType color_type) { + SkColorType color_type, + SkAlphaType alpha_type) { FML_DCHECK(raster_image); SkPixmap pixmap; @@ -162,14 +164,14 @@ sk_sp CopyImageByteData(sk_sp raster_image, } // The color types already match. No need to swizzle. Return early. - if (pixmap.colorType() == color_type) { + if (pixmap.colorType() == color_type && pixmap.alphaType() == alpha_type) { return SkData::MakeWithCopy(pixmap.addr(), pixmap.computeByteSize()); } // Perform swizzle if the type doesnt match the specification. auto surface = SkSurface::MakeRaster( SkImageInfo::Make(raster_image->width(), raster_image->height(), - color_type, kPremul_SkAlphaType, nullptr)); + color_type, alpha_type, nullptr)); if (!surface) { FML_LOG(ERROR) << "Could not set up the surface for swizzle."; @@ -205,10 +207,16 @@ sk_sp EncodeImage(sk_sp raster_image, ImageByteFormat format) { return png_image; } break; case kRawRGBA: { - return CopyImageByteData(raster_image, kRGBA_8888_SkColorType); + return CopyImageByteData(raster_image, kRGBA_8888_SkColorType, + kPremul_SkAlphaType); + } break; + case kRawStraightRGBA: { + return CopyImageByteData(raster_image, kRGBA_8888_SkColorType, + kUnpremul_SkAlphaType); } break; case kRawUnmodified: { - return CopyImageByteData(raster_image, raster_image->colorType()); + return CopyImageByteData(raster_image, raster_image->colorType(), + raster_image->alphaType()); } break; } diff --git a/lib/web_ui/lib/src/ui/painting.dart b/lib/web_ui/lib/src/ui/painting.dart index bb8f5e05269e8..33fb98737879c 100644 --- a/lib/web_ui/lib/src/ui/painting.dart +++ b/lib/web_ui/lib/src/ui/painting.dart @@ -425,6 +425,7 @@ class ImageFilter { enum ImageByteFormat { rawRgba, + rawStraightRgba, rawUnmodified, png, } diff --git a/testing/dart/encoding_test.dart b/testing/dart/encoding_test.dart index 52559f297a7d5..79176c52f6b43 100644 --- a/testing/dart/encoding_test.dart +++ b/testing/dart/encoding_test.dart @@ -31,6 +31,22 @@ void main() { expect(bytes, GrayscaleImage.bytesAsRgba); }); + test('Image.toByteData RGBA format works with transparent image', () async { + final Image image = await TransparentImage.load(); + final ByteData data = (await image.toByteData())!; + final Uint8List bytes = data.buffer.asUint8List(); + expect(bytes, hasLength(64)); + expect(bytes, TransparentImage.bytesAsPremultipliedRgba); + }); + + test('Image.toByteData Straight RGBA format works with transparent image', () async { + final Image image = await TransparentImage.load(); + final ByteData data = (await image.toByteData(format: ImageByteFormat.rawStraightRgba))!; + final Uint8List bytes = data.buffer.asUint8List(); + expect(bytes, hasLength(64)); + expect(bytes, TransparentImage.bytesAsStraightRgba); + }); + test('Image.toByteData Unmodified format works with simple image', () async { final Image image = await Square4x4Image.image; final ByteData data = (await image.toByteData(format: ImageByteFormat.rawUnmodified))!; @@ -124,6 +140,73 @@ class GrayscaleImage { static List get bytesUnmodified => [255, 127, 127, 0]; } +class TransparentImage { + TransparentImage._(); + + static Future load() async { + final Uint8List bytes = await readFile('transparent_image.png'); + final Completer completer = Completer(); + decodeImageFromList(bytes, (Image image) => completer.complete(image)); + return completer.future; + } + + static List get bytesAsPremultipliedRgba { + return [ + //First raw, solid colors + 255, 0, 0, 255, // red + 0, 255, 0, 255, // green + 0, 0, 255, 255, // blue + 136, 136, 136, 255, // grey + + //Second raw, 50% transparent + 127, 0, 0, 127, // red + 0, 127, 0, 127, // green + 0, 0, 127, 127, // blue + 67, 67, 67, 127, // grey + + //Third raw, 25% transparent + 63, 0, 0, 63, // red + 0, 63, 0, 63, // green + 0, 0, 63, 63, // blue + 33, 33, 33, 63, // grey + + //Fourth raw, transparent + 0, 0, 0, 0, // red + 0, 0, 0, 0, // green + 0, 0, 0, 0, // blue + 0, 0, 0, 0, // grey + ]; + } + + static List get bytesAsStraightRgba { + return [ + //First raw, solid colors + 255, 0, 0, 255, // red + 0, 255, 0, 255, // green + 0, 0, 255, 255, // blue + 136, 136, 136, 255, // grey + + //Second raw, 50% transparent + 255, 0, 0, 127, // red + 0, 255, 0, 127, // green + 0, 0, 255, 127, // blue + 135, 135, 135, 127, // grey + + //Third raw, 25% transparent + 255, 0, 0, 63, // red + 0, 255, 0, 63, // green + 0, 0, 255, 63, // blue + 134, 134, 134, 63, // grey + + //Fourth raw, transparent + 0, 0, 0, 0, // red + 0, 0, 0, 0, // green + 0, 0, 0, 0, // blue + 0, 0, 0, 0, // grey + ]; + } +} + Future readFile(String fileName) async { final File file = File(path.join('flutter', 'testing', 'resources', fileName)); return file.readAsBytes(); diff --git a/testing/resources/transparent_image.png b/testing/resources/transparent_image.png new file mode 100644 index 0000000000000000000000000000000000000000..353b869886162423a85cd37885a8129d89003b1b GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1|(O0oL2{=7>k44ofvPP)Tsw@I14-?iy0WW zg+Z8+Vb&Z8prDqgi(^Q|oa8^}4}6#}=wZOn+1KYMa5R8lI3xc(1NV)?EVpjml9Z6V az`(F1hQD#A)&ig=1_n=8KbLh*2~7a?a3mZ6 literal 0 HcmV?d00001