From 21c24a59d990049b7736b9d23dd333fb14037557 Mon Sep 17 00:00:00 2001 From: IgoKom <38077701+IgoKom@users.noreply.github.com> Date: Mon, 8 May 2023 21:30:01 +0300 Subject: [PATCH] Add NV21 support Update gradle version --- .../zuzu/yuvtransform/YuvtransformPlugin.java | 118 +++++++----- .../gradle/wrapper/gradle-wrapper.properties | 3 +- example/lib/yuv_transform_screen.dart | 4 +- example/pubspec.lock | 181 +++++++++++------- lib/yuvtransform.dart | 20 +- pubspec.yaml | 2 +- 6 files changed, 198 insertions(+), 130 deletions(-) diff --git a/android/src/main/java/dev/zuzu/yuvtransform/YuvtransformPlugin.java b/android/src/main/java/dev/zuzu/yuvtransform/YuvtransformPlugin.java index 5c5f49e..226d1e7 100644 --- a/android/src/main/java/dev/zuzu/yuvtransform/YuvtransformPlugin.java +++ b/android/src/main/java/dev/zuzu/yuvtransform/YuvtransformPlugin.java @@ -1,67 +1,83 @@ package dev.zuzu.yuvtransform; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; + +import androidx.annotation.NonNull; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.List; -import androidx.annotation.NonNull; - import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Matrix; - -/** YuvtransformPlugin */ +/** + * YuvtransformPlugin + */ public class YuvtransformPlugin implements FlutterPlugin, MethodCallHandler { - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - private MethodChannel channel; - - @Override - public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "yuvtransform"); - channel.setMethodCallHandler(this); - } - - @Override - public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { - if (call.method.equals("yuvtransform")) { - List bytesList = call.argument("platforms"); - int[] strides = call.argument("strides"); - int width = call.argument("width"); - int height = call.argument("height"); - int quality = call.argument("quality"); - - try { - byte[] data = YuvConverter.NV21toJPEG(YuvConverter.YUVtoNV21(bytesList, strides, width, height), width, height, 100); - Bitmap bitmapRaw = BitmapFactory.decodeByteArray(data, 0, data.length); - - Matrix matrix = new Matrix(); - matrix.postRotate(90); - Bitmap finalbitmap = Bitmap.createBitmap(bitmapRaw, 0, 0, bitmapRaw.getWidth(), bitmapRaw.getHeight(), matrix, true); - ByteArrayOutputStream outputStreamCompressed = new ByteArrayOutputStream(); - finalbitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStreamCompressed); - - result.success(outputStreamCompressed.toByteArray()); - outputStreamCompressed.close(); - data = null; - } catch (IOException e) { - e.printStackTrace(); - } - } else { - result.notImplemented(); + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private MethodChannel channel; + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { + channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "yuvtransform"); + channel.setMethodCallHandler(this); } - } - @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - channel.setMethodCallHandler(null); - } + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { + int width = call.argument("width"); + int height = call.argument("height"); + int quality = call.argument("quality"); + + if (call.method.equals("nv21_to_jpeg")) { + byte[] bytes = call.argument("bytes"); + + + byte[] data = YuvConverter.NV21toJPEG(bytes, width, height, quality); + convertToJPEG(result, quality, data); + + } else if (call.method.equals("yuvtransform")) { + List bytesList = call.argument("platforms"); + int[] strides = call.argument("strides"); + + byte[] data = YuvConverter.NV21toJPEG(YuvConverter.YUVtoNV21(bytesList, strides, width, height), width, height, quality); + convertToJPEG(result, quality, data); + + } else { + result.notImplemented(); + } + } + + private static void convertToJPEG(Result result, int quality, byte[] data) { + try { + Bitmap bitmapRaw = BitmapFactory.decodeByteArray(data, 0, data.length); + + Matrix matrix = new Matrix(); + matrix.postRotate(90); + Bitmap finalbitmap = Bitmap.createBitmap(bitmapRaw, 0, 0, bitmapRaw.getWidth(), bitmapRaw.getHeight(), matrix, true); + ByteArrayOutputStream outputStreamCompressed = new ByteArrayOutputStream(); + finalbitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStreamCompressed); + + result.success(outputStreamCompressed.toByteArray()); + outputStreamCompressed.close(); + data = null; + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + channel.setMethodCallHandler(null); + } } diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index bc6a58a..ffed3a2 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/example/lib/yuv_transform_screen.dart b/example/lib/yuv_transform_screen.dart index e5b96ea..6ee8c18 100644 --- a/example/lib/yuv_transform_screen.dart +++ b/example/lib/yuv_transform_screen.dart @@ -23,7 +23,7 @@ class _YuvTransformScreenState extends State super.initState(); // Registers the page to observer for life cycle managing. _imageResultProcessorService = ImageResultProcessorService(); - WidgetsBinding.instance?.addObserver(this); + WidgetsBinding.instance.addObserver(this); _subscription.add(_imageResultProcessorService.queue.listen((event) { _isProcessing = false; })); @@ -32,7 +32,7 @@ class _YuvTransformScreenState extends State @override void dispose() { - WidgetsBinding.instance?.removeObserver(this); + WidgetsBinding.instance.removeObserver(this); // Dispose all streams! _subscription.forEach((element) { element.cancel(); diff --git a/example/pubspec.lock b/example/pubspec.lock index e400b25..76d0cc0 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,86 +5,106 @@ packages: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.dev" source: hosted - version: "2.8.2" + version: "2.10.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" camera: dependency: transitive description: name: camera - url: "https://pub.dartlang.org" + sha256: "309b823e61f15ff6b5b2e4c0ff2e1512ea661cad5355f71fc581e510ae5b26bb" + url: "https://pub.dev" + source: hosted + version: "0.10.5" + camera_android: + dependency: transitive + description: + name: camera_android + sha256: e0f9b7eea2d1f4d4f5460f178522f0d02c095d2ae00b01a77419ce61c4184bfe + url: "https://pub.dev" + source: hosted + version: "0.10.7" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + sha256: "7ac8b950672716722af235eed7a7c37896853669800b7da706bb0a9fd41d3737" + url: "https://pub.dev" source: hosted - version: "0.9.4+21" + version: "0.9.13+1" camera_platform_interface: dependency: transitive description: name: camera_platform_interface - url: "https://pub.dartlang.org" + sha256: "525017018d116c5db8c4c43ec2d9b1663216b369c9f75149158280168a7ce472" + url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.5.0" camera_web: dependency: transitive description: name: camera_web - url: "https://pub.dartlang.org" + sha256: d77965f32479ee6d8f48205dcf10f845d7210595c6c00faa51eab265d1cae993 + url: "https://pub.dev" source: hosted - version: "0.2.1+3" + version: "0.3.1+3" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.2.1" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.17.0" cross_file: dependency: transitive description: name: cross_file - url: "https://pub.dartlang.org" + sha256: "93ccb9d2a91854ce7233583809774e2d3fbac6eef03c255eb6cd4db8b1242f0f" + url: "https://pub.dev" source: hosted version: "0.3.3" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.dartlang.org" + sha256: "1989d917fbe8e6b39806207df5a3fdd3d816cbd090fac2ce26fb45e9a71476e5" + url: "https://pub.dev" source: hosted version: "1.0.4" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -94,14 +114,16 @@ packages: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.dartlang.org" + sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493 + url: "https://pub.dev" source: hosted version: "1.0.4" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - url: "https://pub.dartlang.org" + sha256: "5c574d21b98ec92adab05ead10afd2b13ff5856c7ca79696edb338a9dd8ed387" + url: "https://pub.dev" source: hosted version: "2.0.5" flutter_test: @@ -118,98 +140,112 @@ packages: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" source: hosted - version: "0.6.3" + version: "0.6.5" lints: dependency: transitive description: name: lints - url: "https://pub.dartlang.org" + sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c + url: "https://pub.dev" source: hosted version: "1.0.1" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.dev" source: hosted - version: "0.12.11" + version: "0.12.13" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.2.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" source: hosted - version: "1.7.0" + version: "1.8.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.8.2" permission_handler: dependency: "direct main" description: name: permission_handler - url: "https://pub.dartlang.org" + sha256: "5749ebeb7ec0c3865ea17e3eb337174b87747be816dab582c551e1aff6f6bbf3" + url: "https://pub.dev" source: hosted version: "9.2.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - url: "https://pub.dartlang.org" + sha256: a512e0fa8abcb0659d938ec2df93a70eb1df1fdea5fdc6d79a866bfd858a28fc + url: "https://pub.dev" source: hosted version: "9.0.2+1" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - url: "https://pub.dartlang.org" + sha256: "6367799be76d1fe70ffe2df7f025abfe28818b450f550621778995badbebf519" + url: "https://pub.dev" source: hosted version: "9.0.4" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - url: "https://pub.dartlang.org" + sha256: ca16451bfdc6d26693d10b37b2d81370bdf3f0318422f3eecfd6004f5bd7d21f + url: "https://pub.dev" source: hosted version: "3.7.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - url: "https://pub.dartlang.org" + sha256: "40ad5ab4d3c65d75c7f3a069065c77503aae19a1cf01ba246d43489e14f1b90c" + url: "https://pub.dev" source: hosted version: "0.1.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.dartlang.org" + sha256: "075f927ebbab4262ace8d0b283929ac5410c0ac4e7fc123c76429564facfb757" + url: "https://pub.dev" source: hosted version: "2.1.2" quiver: dependency: transitive description: name: quiver - url: "https://pub.dartlang.org" + sha256: "93982981971e812c94d4a6fa3a57b89f9ec12b38b6380cd3c1370c3b01e4580e" + url: "https://pub.dev" source: hosted version: "3.1.0" rxdart: dependency: "direct main" description: name: rxdart - url: "https://pub.dartlang.org" + sha256: bc2d2b17b87fab32e2dca53ca3066d3147de6f96c74d76cfe1a379a24239c46d + url: "https://pub.dev" source: hosted version: "0.27.3" sky_engine: @@ -221,65 +257,66 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" source: hosted - version: "1.8.1" + version: "1.9.1" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" stream_transform: dependency: transitive description: name: stream_transform - url: "https://pub.dartlang.org" + sha256: ed464977cb26a1f41537e177e190c67223dbd9f4f683489b6ab2e5d211ec564e + url: "https://pub.dev" source: hosted version: "2.0.0" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.dev" source: hosted - version: "0.4.8" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" + version: "0.4.16" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.4" yuvtransform: dependency: "direct main" description: @@ -288,5 +325,5 @@ packages: source: path version: "0.0.2" sdks: - dart: ">=2.16.1 <3.0.0" - flutter: ">=2.8.0" + dart: ">=2.18.0 <3.0.0" + flutter: ">=3.3.0" diff --git a/lib/yuvtransform.dart b/lib/yuvtransform.dart index c0fa436..c291111 100644 --- a/lib/yuvtransform.dart +++ b/lib/yuvtransform.dart @@ -7,8 +7,10 @@ import 'package:flutter/services.dart'; class Yuvtransform { static const MethodChannel _channel = MethodChannel('yuvtransform'); - static Future yuvTransform(CameraImage image, - {int? quality = 60}) async { + static Future yuvTransform( + CameraImage image, { + int? quality = 60, + }) async { List strides = Int32List(image.planes.length * 2); int index = 0; // We need to transform the image to Uint8List so that the native code could @@ -30,4 +32,18 @@ class Yuvtransform { return imageJpeg; } + + static Future nv21ToJPEG( + CameraImage image, { + int? quality = 100, + }) async { + Uint8List imageJpeg = await _channel.invokeMethod('nv21_to_jpeg', { + 'bytes': image.planes[0].bytes, + 'height': image.height, + 'width': image.width, + 'quality': quality + }); + + return imageJpeg; + } } diff --git a/pubspec.yaml b/pubspec.yaml index 21f0bd5..63285fb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,7 +10,7 @@ environment: dependencies: flutter: sdk: flutter - camera: ^0.9.4 + camera: ^0.10.5 dev_dependencies: flutter_test: