diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index b8aa56c7186..6ee345816fe 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -1381,10 +1381,13 @@ enum PixelFormat { } class _ImageInfo { - _ImageInfo(this.width, this.height, this.format); + _ImageInfo(this.width, this.height, this.format, this.rowBytes) { + rowBytes ??= width * 4; + } int width; int height; int format; + int rowBytes; } /// Opaque handle to raw decoded image data (pixels). @@ -1529,14 +1532,19 @@ Future _decodeImageFromListAsync(Uint8List list, /// Convert an array of pixel values into an [Image] object. /// /// [pixels] is the pixel data in the encoding described by [format]. +/// +/// [rowBytes] is the number of bytes consumed by each row of pixels in the +/// data buffer. If unspecified, it defaults to [width] multipled by the +/// number of bytes per pixel in the provided [format]. void decodeImageFromPixels( Uint8List pixels, int width, int height, PixelFormat format, - ImageDecoderCallback callback + ImageDecoderCallback callback, + {int rowBytes} ) { - final _ImageInfo imageInfo = new _ImageInfo(width, height, format.index); + final _ImageInfo imageInfo = new _ImageInfo(width, height, format.index, rowBytes); final Future codecFuture = _futurize( (_Callback callback) => _instantiateImageCodec(pixels, callback, imageInfo) ); diff --git a/lib/ui/painting/codec.cc b/lib/ui/painting/codec.cc index ddee0dedc01..4b52881f540 100644 --- a/lib/ui/painting/codec.cc +++ b/lib/ui/painting/codec.cc @@ -38,6 +38,11 @@ enum PixelFormat { kBGRA8888, }; +struct ImageInfo { + SkImageInfo sk_info; + size_t row_bytes; +}; + static void InvokeCodecCallback(fxl::RefPtr codec, std::unique_ptr callback, size_t trace_id) { @@ -113,7 +118,7 @@ fxl::RefPtr InitCodec(fml::WeakPtr context, fxl::RefPtr InitCodecUncompressed( fml::WeakPtr context, sk_sp buffer, - SkImageInfo image_info, + ImageInfo image_info, fxl::RefPtr unref_queue, size_t trace_id) { TRACE_FLOW_STEP("flutter", kInitCodecTraceTag, trace_id); @@ -126,12 +131,12 @@ fxl::RefPtr InitCodecUncompressed( sk_sp skImage; if (context) { - SkPixmap pixmap(image_info, buffer->data(), image_info.minRowBytes()); + SkPixmap pixmap(image_info.sk_info, buffer->data(), image_info.row_bytes); skImage = SkImage::MakeCrossContextFromPixmap(context.get(), pixmap, false, nullptr, true); } else { - skImage = SkImage::MakeRasterData(image_info, std::move(buffer), - image_info.minRowBytes()); + skImage = SkImage::MakeRasterData(image_info.sk_info, std::move(buffer), + image_info.row_bytes); } auto image = CanvasImage::Create(); @@ -146,7 +151,7 @@ void InitCodecAndInvokeCodecCallback( fxl::RefPtr unref_queue, std::unique_ptr callback, sk_sp buffer, - std::unique_ptr image_info, + std::unique_ptr image_info, size_t trace_id) { fxl::RefPtr codec; if (image_info) { @@ -165,7 +170,7 @@ void InitCodecAndInvokeCodecCallback( bool ConvertImageInfo(Dart_Handle image_info_handle, Dart_NativeArguments args, - SkImageInfo* image_info) { + ImageInfo* image_info) { Dart_Handle width_handle = Dart_GetField(image_info_handle, ToDart("width")); if (!Dart_IsInteger(width_handle)) { Dart_SetReturnValue(args, ToDart("ImageInfo.width must be an integer")); @@ -183,6 +188,12 @@ bool ConvertImageInfo(Dart_Handle image_info_handle, Dart_SetReturnValue(args, ToDart("ImageInfo.format must be an integer")); return false; } + Dart_Handle row_bytes_handle = + Dart_GetField(image_info_handle, ToDart("rowBytes")); + if (!Dart_IsInteger(row_bytes_handle)) { + Dart_SetReturnValue(args, ToDart("ImageInfo.rowBytes must be an integer")); + return false; + } PixelFormat pixel_format = static_cast( tonic::DartConverter::FromDart(format_handle)); @@ -200,10 +211,26 @@ bool ConvertImageInfo(Dart_Handle image_info_handle, return false; } - *image_info = - SkImageInfo::Make(tonic::DartConverter::FromDart(width_handle), - tonic::DartConverter::FromDart(height_handle), - color_type, kPremul_SkAlphaType); + int width = tonic::DartConverter::FromDart(width_handle); + if (width <= 0) { + Dart_SetReturnValue(args, ToDart("width must be greater than zero")); + return false; + } + int height = tonic::DartConverter::FromDart(height_handle); + if (height <= 0) { + Dart_SetReturnValue(args, ToDart("height must be greater than zero")); + return false; + } + image_info->sk_info = + SkImageInfo::Make(width, height, color_type, kPremul_SkAlphaType); + image_info->row_bytes = + tonic::DartConverter::FromDart(row_bytes_handle); + + if (image_info->row_bytes < image_info->sk_info.minRowBytes()) { + Dart_SetReturnValue( + args, ToDart("rowBytes does not match the width of the image")); + return false; + } return true; } @@ -221,9 +248,9 @@ void InstantiateImageCodec(Dart_NativeArguments args) { } Dart_Handle image_info_handle = Dart_GetNativeArgument(args, 2); - std::unique_ptr image_info; + std::unique_ptr image_info; if (!Dart_IsNull(image_info_handle)) { - image_info = std::make_unique(); + image_info = std::make_unique(); if (!ConvertImageInfo(image_info_handle, args, image_info.get())) { TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id); return; @@ -240,7 +267,7 @@ void InstantiateImageCodec(Dart_NativeArguments args) { } if (image_info) { - int expected_size = image_info->minRowBytes() * image_info->height(); + int expected_size = image_info->row_bytes * image_info->sk_info.height(); if (list.num_elements() < expected_size) { TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id); list.Release();