diff --git a/mobile/lib/src/rust/api/image_processing.dart b/mobile/lib/src/rust/api/image_processing.dart index 6dbbb6d93c..3aad985e74 100644 --- a/mobile/lib/src/rust/api/image_processing.dart +++ b/mobile/lib/src/rust/api/image_processing.dart @@ -8,6 +8,8 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; import 'package:photos/src/rust/frb_generated.dart'; +// These functions are ignored because they are not marked as `pub`: `process_image_ml` + Future< ( Uint8List, @@ -24,6 +26,24 @@ Future< RustLib.instance.api .crateApiImageProcessingProcessImageMlFromPath(imagePath: imagePath); +Future< + ( + Uint8List, + BigInt, + BigInt, + Uint8List, + BigInt, + BigInt, + Uint8List, + BigInt, + BigInt + )> processImageMlFromData( + {required List rgbaData, + required int width, + required int height}) => + RustLib.instance.api.crateApiImageProcessingProcessImageMlFromData( + rgbaData: rgbaData, width: width, height: height); + Future<(Uint8List, String, BigInt, BigInt)> processYoloFace( {required String imagePath}) => RustLib.instance.api diff --git a/mobile/lib/src/rust/frb_generated.dart b/mobile/lib/src/rust/frb_generated.dart index 103bbd5bc7..618210654e 100644 --- a/mobile/lib/src/rust/frb_generated.dart +++ b/mobile/lib/src/rust/frb_generated.dart @@ -72,7 +72,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.5.1'; @override - int get rustContentHash => -1741400115; + int get rustContentHash => -804230794; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -86,6 +86,23 @@ abstract class RustLibApi extends BaseApi { Future<(Uint8List, String, BigInt, BigInt)> crateApiImageProcessingProcessClip({required String imagePath}); + Future< + ( + Uint8List, + BigInt, + BigInt, + Uint8List, + BigInt, + BigInt, + Uint8List, + BigInt, + BigInt + )> + crateApiImageProcessingProcessImageMlFromData( + {required List rgbaData, + required int width, + required int height}); + Future< ( Uint8List, @@ -143,6 +160,49 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["imagePath"], ); + @override + Future< + ( + Uint8List, + BigInt, + BigInt, + Uint8List, + BigInt, + BigInt, + Uint8List, + BigInt, + BigInt + )> + crateApiImageProcessingProcessImageMlFromData( + {required List rgbaData, + required int width, + required int height}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + final arg0 = cst_encode_list_prim_u_8_loose(rgbaData); + final arg1 = cst_encode_u_32(width); + final arg2 = cst_encode_u_32(height); + return wire + .wire__crate__api__image_processing__process_image_ml_from_data( + port_, arg0, arg1, arg2); + }, + codec: DcoCodec( + decodeSuccessData: + dco_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize, + decodeErrorData: null, + ), + constMeta: kCrateApiImageProcessingProcessImageMlFromDataConstMeta, + argValues: [rgbaData, width, height], + apiImpl: this, + )); + } + + TaskConstMeta get kCrateApiImageProcessingProcessImageMlFromDataConstMeta => + const TaskConstMeta( + debugName: "process_image_ml_from_data", + argNames: ["rgbaData", "width", "height"], + ); + @override Future< ( @@ -257,6 +317,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as String; } + @protected + List dco_decode_list_prim_u_8_loose(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw as List; + } + @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -310,6 +376,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } + @protected + int dco_decode_u_32(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw as int; + } + @protected int dco_decode_u_8(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -335,6 +407,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return utf8.decoder.convert(inner); } + @protected + List sse_decode_list_prim_u_8_loose(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + final len_ = sse_decode_i_32(deserializer); + return deserializer.buffer.getUint8List(len_); + } + @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -390,6 +469,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } + @protected + int sse_decode_u_32(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return deserializer.buffer.getUint32(); + } + @protected int sse_decode_u_8(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -419,6 +504,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getUint8() != 0; } + @protected + int cst_encode_u_32(int raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + return raw; + } + @protected int cst_encode_u_8(int raw) { // Codec=Cst (C-struct based), see doc to use other codecs @@ -437,6 +528,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_strict(utf8.encoder.convert(self), serializer); } + @protected + void sse_encode_list_prim_u_8_loose( + List self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_i_32(self.length, serializer); + serializer.buffer + .putUint8List(self is Uint8List ? self : Uint8List.fromList(self)); + } + @protected void sse_encode_list_prim_u_8_strict( Uint8List self, SseSerializer serializer) { @@ -482,6 +582,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_usize(self.$9, serializer); } + @protected + void sse_encode_u_32(int self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + serializer.buffer.putUint32(self); + } + @protected void sse_encode_u_8(int self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs diff --git a/mobile/lib/src/rust/frb_generated.io.dart b/mobile/lib/src/rust/frb_generated.io.dart index 07b33596d1..f83cf0ba41 100644 --- a/mobile/lib/src/rust/frb_generated.io.dart +++ b/mobile/lib/src/rust/frb_generated.io.dart @@ -24,6 +24,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String dco_decode_String(dynamic raw); + @protected + List dco_decode_list_prim_u_8_loose(dynamic raw); + @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); @@ -45,6 +48,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { ) dco_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize( dynamic raw); + @protected + int dco_decode_u_32(dynamic raw); + @protected int dco_decode_u_8(dynamic raw); @@ -57,6 +63,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String sse_decode_String(SseDeserializer deserializer); + @protected + List sse_decode_list_prim_u_8_loose(SseDeserializer deserializer); + @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); @@ -79,6 +88,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { ) sse_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize( SseDeserializer deserializer); + @protected + int sse_decode_u_32(SseDeserializer deserializer); + @protected int sse_decode_u_8(SseDeserializer deserializer); @@ -100,6 +112,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { return cst_encode_list_prim_u_8_strict(utf8.encoder.convert(raw)); } + @protected + ffi.Pointer cst_encode_list_prim_u_8_loose( + List raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + final ans = wire.cst_new_list_prim_u_8_loose(raw.length); + ans.ref.ptr.asTypedList(raw.length).setAll(0, raw); + return ans; + } + @protected ffi.Pointer cst_encode_list_prim_u_8_strict( Uint8List raw) { @@ -151,6 +172,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.field8 = cst_encode_usize(apiObj.$9); } + @protected + int cst_encode_u_32(int raw); + @protected int cst_encode_u_8(int raw); @@ -160,6 +184,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_String(String self, SseSerializer serializer); + @protected + void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer); + @protected void sse_encode_list_prim_u_8_strict( Uint8List self, SseSerializer serializer); @@ -184,6 +211,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { ) self, SseSerializer serializer); + @protected + void sse_encode_u_32(int self, SseSerializer serializer); + @protected void sse_encode_u_8(int self, SseSerializer serializer); @@ -260,6 +290,35 @@ class RustLibWire implements BaseWire { _wire__crate__api__image_processing__process_clipPtr.asFunction< void Function(int, ffi.Pointer)>(); + void wire__crate__api__image_processing__process_image_ml_from_data( + int port_, + ffi.Pointer rgba_data, + int width, + int height, + ) { + return _wire__crate__api__image_processing__process_image_ml_from_data( + port_, + rgba_data, + width, + height, + ); + } + + late final _wire__crate__api__image_processing__process_image_ml_from_dataPtr = + _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int64, + ffi.Pointer, + ffi.Uint32, + ffi.Uint32)>>( + 'frbgen_photos_wire__crate__api__image_processing__process_image_ml_from_data'); + late final _wire__crate__api__image_processing__process_image_ml_from_data = + _wire__crate__api__image_processing__process_image_ml_from_dataPtr + .asFunction< + void Function( + int, ffi.Pointer, int, int)>(); + void wire__crate__api__image_processing__process_image_ml_from_path( int port_, ffi.Pointer image_path, @@ -332,6 +391,21 @@ class RustLibWire implements BaseWire { late final _wire__crate__api__simple__init_app = _wire__crate__api__simple__init_appPtr.asFunction(); + ffi.Pointer cst_new_list_prim_u_8_loose( + int len, + ) { + return _cst_new_list_prim_u_8_loose( + len, + ); + } + + late final _cst_new_list_prim_u_8_loosePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Int32)>>('frbgen_photos_cst_new_list_prim_u_8_loose'); + late final _cst_new_list_prim_u_8_loose = _cst_new_list_prim_u_8_loosePtr + .asFunction Function(int)>(); + ffi.Pointer cst_new_list_prim_u_8_strict( int len, ) { @@ -374,6 +448,13 @@ final class wire_cst_list_prim_u_8_strict extends ffi.Struct { external int len; } +final class wire_cst_list_prim_u_8_loose extends ffi.Struct { + external ffi.Pointer ptr; + + @ffi.Int32() + external int len; +} + final class wire_cst_record_list_prim_u_8_strict_string_usize_usize extends ffi.Struct { external ffi.Pointer field0; diff --git a/mobile/lib/utils/image_ml_util.dart b/mobile/lib/utils/image_ml_util.dart index f276d34344..72be807a51 100644 --- a/mobile/lib/utils/image_ml_util.dart +++ b/mobile/lib/utils/image_ml_util.dart @@ -32,7 +32,7 @@ final List> gaussianKernel = const maxKernelSize = gaussianKernelSize; const maxKernelRadius = maxKernelSize ~/ 2; -const List supportedImageFormats = [ +const List supportedRustImageFormats = [ 'bmp', 'dds', 'farbfeld', @@ -48,8 +48,6 @@ const List supportedImageFormats = [ 'tga', 'tiff', 'webp', - 'heic', - 'heif', ]; Future<(Image, Uint8List)> decodeImageFromPath(String imagePath) async { @@ -86,35 +84,9 @@ Future<(Image, Uint8List)> decodeImageFromPath(String imagePath) async { } } -Future safePathFromImagepath(String imagePath) async { +bool canRustDecodeImage(String imagePath) { final format = imagePath.split('.').last; - if (supportedImageFormats.contains(format)) { - return imagePath; - } - try { - final newPath = imagePath.replaceAll(format, 'jpeg'); - final time = DateTime.now(); - final File? convertedFile = await FlutterImageCompress.compressAndGetFile( - imagePath, - newPath, - format: CompressFormat.jpeg, - ); - _logger.info( - 'Conversion successful, heic converted in ${DateTime.now().difference(time).inMilliseconds} ms', - ); - if (convertedFile == null) { - throw Exception('Error converting image to jpeg'); - } - return newPath; - } catch (e) { - _logger.severe( - 'Error decoding image of format $format on ${Platform.isAndroid ? "Android" : "iOS"}', - e, - ); - throw Exception( - 'InvalidImageFormatException: Error decoding image of format $format', - ); - } + return supportedRustImageFormats.contains(format); } /// Decodes [Uint8List] image data to an ui.[Image] object. diff --git a/mobile/lib/utils/ml_util.dart b/mobile/lib/utils/ml_util.dart index 5a51d426ae..e720ed6d80 100644 --- a/mobile/lib/utils/ml_util.dart +++ b/mobile/lib/utils/ml_util.dart @@ -1,5 +1,6 @@ import "dart:io" show File; import "dart:math" as math show sqrt, min, max; +import "dart:typed_data" show Uint8List; import "package:flutter/services.dart" show PlatformException; import "package:logging/logging.dart"; @@ -399,10 +400,28 @@ Future analyzeImageStatic(Map args) async { final startTime = DateTime.now(); // Decode the image once to use for both face detection and alignment - final safePath = await safePathFromImagepath(imagePath); - final format = safePath.split('.').last; - // final (image, rawRgbaBytes) = await decodeImageFromPath(imagePath); - + final bool decodeInRust = canRustDecodeImage(imagePath); + late ( + Uint8List, + BigInt, + BigInt, + Uint8List, + BigInt, + BigInt, + Uint8List, + BigInt, + BigInt + ) rustResults; + if (decodeInRust) { + rustResults = await processImageMlFromPath(imagePath: imagePath); + } else { + final (image, rawRgbaBytes) = await decodeImageFromPath(imagePath); + rustResults = await processImageMlFromData( + rgbaData: rawRgbaBytes, + width: image.width, + height: image.height, + ); + } final ( rawRgbaBytes, imageHeight, @@ -413,17 +432,17 @@ Future analyzeImageStatic(Map args) async { clipBytes, clipHeight, clipWidth - ) = await processImageMlFromPath(imagePath: safePath); + ) = rustResults; + final decodedImageSize = + Dimensions(height: imageHeight.toInt(), width: imageWidth.toInt()); final decodeTime = DateTime.now(); final decodeMs = decodeTime.difference(startTime).inMilliseconds; _logger.info( - 'ML processing in rust took ${DateTime.now().difference(decodeTime).inMilliseconds} ms for format $format', + 'ML total image processing (in rust) took ${DateTime.now().difference(decodeTime).inMilliseconds} ms', ); - final decodedImageSize = - Dimensions(height: imageHeight.toInt(), width: imageWidth.toInt()); + final result = MLResult.fromEnteFileID(enteFileID); result.decodedImageSize = decodedImageSize; - String faceMsString = "", clipMsString = ""; final pipelines = await Future.wait([ runFaces diff --git a/mobile/rust/Cargo.lock b/mobile/rust/Cargo.lock index cad6aa20c9..9e5453e2c2 100644 --- a/mobile/rust/Cargo.lock +++ b/mobile/rust/Cargo.lock @@ -146,29 +146,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "bindgen" -version = "0.69.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" -dependencies = [ - "bitflags 2.6.0", - "cexpr", - "clang-sys", - "itertools", - "lazy_static", - "lazycell", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", - "which", -] - [[package]] name = "bit_field" version = "0.10.2" @@ -181,12 +158,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - [[package]] name = "bitstream-io" version = "2.5.3" @@ -249,15 +220,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-expr" version = "0.15.8" @@ -274,17 +236,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "color_quant" version = "1.1.0" @@ -397,17 +348,6 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" -[[package]] -name = "enumn" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "env_logger" version = "0.10.2" @@ -424,16 +364,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "exr" version = "1.72.0" @@ -520,12 +450,6 @@ dependencies = [ "syn", ] -[[package]] -name = "four-cc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431a4c31778fde52b4400de34975f219eeca55cc829a9de157cd743a5b230ecb" - [[package]] name = "futures" version = "0.3.31" @@ -652,12 +576,6 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - [[package]] name = "half" version = "2.4.1" @@ -692,15 +610,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "image" version = "0.25.4" @@ -800,12 +709,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "lebe" version = "0.5.2" @@ -829,47 +732,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "libheif-rs" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c32c0a0c970782707070f11c8612bced800b916af4ddaf6229161dc3ceb907" -dependencies = [ - "enumn", - "four-cc", - "libc", - "libheif-sys", -] - -[[package]] -name = "libheif-sys" -version = "2.1.1+1.17.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60b29be1ef3ab2aba61344f09a18c3edf552bf4f9fbec9bd68b9ea6f98e71f8" -dependencies = [ - "bindgen", - "libc", - "pkg-config", - "vcpkg", - "walkdir", -] - -[[package]] -name = "libloading" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" -dependencies = [ - "cfg-if", - "windows-targets", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - [[package]] name = "lock_api" version = "0.4.12" @@ -1083,7 +945,7 @@ version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" dependencies = [ - "bitflags 1.3.2", + "bitflags", "crc32fast", "fdeflate", "flate2", @@ -1105,16 +967,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "prettyplease" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" -dependencies = [ - "proc-macro2", - "syn", -] - [[package]] name = "proc-macro2" version = "1.0.89" @@ -1321,7 +1173,6 @@ dependencies = [ "bytemuck", "flutter_rust_bridge", "image", - "libheif-rs", "resize", "rgb", ] @@ -1332,34 +1183,6 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustix" -version = "0.38.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" -dependencies = [ - "bitflags 2.6.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "scopeguard" version = "1.2.0" @@ -1577,12 +1400,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version-compare" version = "0.2.0" @@ -1595,16 +1412,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1694,45 +1501,6 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-targets" version = "0.52.6" diff --git a/mobile/rust/Cargo.toml b/mobile/rust/Cargo.toml index 3aed2d253a..8524a864e5 100644 --- a/mobile/rust/Cargo.toml +++ b/mobile/rust/Cargo.toml @@ -11,5 +11,4 @@ flutter_rust_bridge = "=2.5.1" image = "0.25.4" resize = "0.8.7" rgb = "0.8.50" -bytemuck = "1.16.0" -libheif-rs = "1.0.2" +bytemuck = "1.16.0" \ No newline at end of file diff --git a/mobile/rust/src/api/image_processing.rs b/mobile/rust/src/api/image_processing.rs index 375b2c8d2d..cd95b92daa 100644 --- a/mobile/rust/src/api/image_processing.rs +++ b/mobile/rust/src/api/image_processing.rs @@ -1,5 +1,4 @@ use image::ImageBuffer; -use libheif_rs::{ColorSpace, HeifContext, LibHeif, RgbChroma}; use resize::{px::RGB, Pixel::RGB8, Type::Lanczos3, Type::Mitchell}; use rgb::FromSlice; @@ -16,36 +15,55 @@ pub fn process_image_ml_from_path( usize, usize, ) { - // Check the image format by checking the file extension in the image_path string (~0ms) - let format = image_path.split('.').last().unwrap().to_lowercase(); + // Load the image from the path (~200ms) + let img: image::DynamicImage = image::open(image_path).expect("Failed to open image"); - let img = if format == "heic" || format == "heif" { - let lib_heif = LibHeif::new(); - let ctx = HeifContext::read_from_file(image_path).expect("Failed to read HEIF file"); - let handle = ctx - .primary_image_handle() - .expect("Failed to get primary image handle"); - let decoded = lib_heif - .decode(&handle, ColorSpace::Rgb(RgbChroma::Rgb), None) - .expect("Failed to decode image"); - let plane = decoded - .planes() - .interleaved - .expect("Failed to get interleaved plane"); - let rgb_data = plane.data.to_vec(); - let img = image::DynamicImage::from( - ImageBuffer::, _>::from_raw(decoded.width(), decoded.height(), rgb_data) - .expect("Failed to create image buffer"), - ); - img - } else { - let img = image::open(image_path).expect("Failed to open image"); - img - }; + // Process the image + let results = process_image_ml(img); - // Load the image (~200ms) - // let img = image::open(image_path).expect("Failed to open image"); + results +} +pub fn process_image_ml_from_data( + rgba_data: Vec, + width: u32, + height: u32, +) -> ( + Vec, + usize, + usize, + Vec, + usize, + usize, + Vec, + usize, + usize, +) { + // Load the image from the data + let img = image::DynamicImage::from( + ImageBuffer::, _>::from_raw(width, height, rgba_data) + .expect("Failed to create image buffer"), + ); + + // Process the image + let results = process_image_ml(img); + + results +} + +fn process_image_ml( + img: image::DynamicImage, +) -> ( + Vec, + usize, + usize, + Vec, + usize, + usize, + Vec, + usize, + usize, +) { // Get dimensions for resized images (0ms) let (width, height) = (img.width() as usize, img.height() as usize); let scale_face = f32::min(640.0 / width as f32, 640.0 / height as f32); diff --git a/mobile/rust/src/frb_generated.rs b/mobile/rust/src/frb_generated.rs index f02586ab34..7a05bea526 100644 --- a/mobile/rust/src/frb_generated.rs +++ b/mobile/rust/src/frb_generated.rs @@ -37,7 +37,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueNom, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.5.1"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1741400115; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -804230794; // Section: executor @@ -68,6 +68,37 @@ fn wire__crate__api__image_processing__process_clip_impl( }, ) } +fn wire__crate__api__image_processing__process_image_ml_from_data_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + rgba_data: impl CstDecode>, + width: impl CstDecode, + height: impl CstDecode, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "process_image_ml_from_data", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let api_rgba_data = rgba_data.cst_decode(); + let api_width = width.cst_decode(); + let api_height = height.cst_decode(); + move |context| { + transform_result_dco::<_, _, ()>((move || { + let output_ok = Result::<_, ()>::Ok( + crate::api::image_processing::process_image_ml_from_data( + api_rgba_data, + api_width, + api_height, + ), + )?; + Ok(output_ok) + })()) + } + }, + ) +} fn wire__crate__api__image_processing__process_image_ml_from_path_impl( port_: flutter_rust_bridge::for_generated::MessagePort, image_path: impl CstDecode, @@ -154,6 +185,12 @@ fn wire__crate__api__simple__init_app_impl(port_: flutter_rust_bridge::for_gener // Section: dart2rust +impl CstDecode for u32 { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> u32 { + self + } +} impl CstDecode for u8 { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> u8 { @@ -228,6 +265,13 @@ impl SseDecode } } +impl SseDecode for u32 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_u32::().unwrap() + } +} + impl SseDecode for u8 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -342,6 +386,13 @@ impl SseEncode } } +impl SseEncode for u32 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_u32::(self).unwrap(); + } +} + impl SseEncode for u8 { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -405,6 +456,15 @@ mod io { String::from_utf8(vec).unwrap() } } + impl CstDecode> for *mut wire_cst_list_prim_u_8_loose { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> Vec { + unsafe { + let wrap = flutter_rust_bridge::for_generated::box_from_leak_ptr(self); + flutter_rust_bridge::for_generated::vec_from_leak_ptr(wrap.ptr, wrap.len) + } + } + } impl CstDecode> for *mut wire_cst_list_prim_u_8_strict { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> Vec { @@ -475,6 +535,18 @@ field8: Default::default(), } wire__crate__api__image_processing__process_clip_impl(port_, image_path) } + #[no_mangle] + pub extern "C" fn frbgen_photos_wire__crate__api__image_processing__process_image_ml_from_data( + port_: i64, + rgba_data: *mut wire_cst_list_prim_u_8_loose, + width: u32, + height: u32, + ) { + wire__crate__api__image_processing__process_image_ml_from_data_impl( + port_, rgba_data, width, height, + ) + } + #[no_mangle] pub extern "C" fn frbgen_photos_wire__crate__api__image_processing__process_image_ml_from_path( port_: i64, @@ -503,6 +575,17 @@ field8: Default::default(), } wire__crate__api__simple__init_app_impl(port_) } + #[no_mangle] + pub extern "C" fn frbgen_photos_cst_new_list_prim_u_8_loose( + len: i32, + ) -> *mut wire_cst_list_prim_u_8_loose { + let ans = wire_cst_list_prim_u_8_loose { + ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr(Default::default(), len), + len, + }; + flutter_rust_bridge::for_generated::new_leak_box_ptr(ans) + } + #[no_mangle] pub extern "C" fn frbgen_photos_cst_new_list_prim_u_8_strict( len: i32, @@ -514,6 +597,12 @@ field8: Default::default(), } flutter_rust_bridge::for_generated::new_leak_box_ptr(ans) } + #[repr(C)] + #[derive(Clone, Copy)] + pub struct wire_cst_list_prim_u_8_loose { + ptr: *mut u8, + len: i32, + } #[repr(C)] #[derive(Clone, Copy)] pub struct wire_cst_list_prim_u_8_strict {