How to convert Uint8List to C equivalent datatype using dart:ffi

1,546

You have to allocate, and then copy the data into, a Pointer<something>. Your exported C function can then have a something* argument. You will also need to pass the length of the data. As the goal is to construct a cv::Mat you might want to pass the geometry instead, and infer the data length from that.

So, if your exported C function has a signature:

void process(int32_t width, int32_t height, uint8_t *bytes);

your two Dart typedefs would be:

typedef process_func = Void Function(Int32 width, Int32 height, Pointer<Uint8> bytes);
typedef ProcessFunc = void Function(int width, int height, Pointer<Uint8> bytes);

use those in the lookup

_process = nativeLib
    .lookup<NativeFunction<process_func>>('process')
    .asFunction();

To allocate the buffer, first import 'package:ffi/ffi.dart'; and use allocate

var outBuf = allocate<Uint8>(count: outSize);

then use a for loop to copy the data. Alternatively use asTypedData on outBuf to get a Uint8List and use setAll.

Remember to free the buffer after using it.

(As your pixels are rgba8888, they fit in unsigned 32 bit ints. You might want to pass Uint32s across in your pointer, and get them using data.buffer.asUint32List().)

You want to return a list of bool. bool isn't supported you you have to pass a list of, say, Int8 where you've set the values to 1 and 0 respectively. (If your list is bounded and reasonably sized, you could use a bitmap. For example, if you knew it was always 32 bits long, pack those bits into a Uint32 as 32 ones and zeroes.)

There are a couple of strategies for returning a list, where list in ffi is going to mean Pointer<something> of course.

If the list is bounded, you can allocate it on the Dart side, pass the pointer with the function and have the C function fill in the values, maybe returning an integer of the number it filled in.

If the list is unbounded you must allow C to malloc it, as only the C function knows how much space it needs. You now need to return 2 values: the pointer and the length. Since you can only return one value you need to do something like pass in a Pointer<Int32> and have the C function assign the length to that.

Share:
1,546
Aawaz Gyawali
Author by

Aawaz Gyawali

Android developer with knowledge of MySQL and php.

Updated on December 21, 2022

Comments

  • Aawaz Gyawali
    Aawaz Gyawali over 1 year

    I am trying to send an image to my Custom C++ code which I am planning to run using dart:ffi. I have successfully run the hello world problem where I send two integers and get the some of them as another intiger.

    Now I want to send a whole image to it and process it on my C++ code. I am able to get Uint8List from the CameraImage using the below code

    Future<ui.Image> convertCameraImageToUiImage(CameraImage image) async {
    imglib.Image libImage = imglib.Image.fromBytes(
      image.width,
      image.height,
      image.planes[0].bytes,
      format: imglib.Format.bgra,
    );
    final c = Completer<ui.Image>();
    ui.decodeImageFromPixels(
      libImage.getBytes(),
      image.width,
      image.height,
      ui.PixelFormat.rgba8888,
      c.complete,
    );
    return c.future;
    

    }

    Then I converted it to Uint8List

    ByteData data = await image.toByteData();
    Uint8List bytes = data.buffer.asUint8List();
    

    My problem is, how should I define lookup function to send a Uint8List to the C++ code and get List<bool>, my function over C++ expects cv::mat (I can tweak it to receive array of integers) as a parameter and sends a Vector of boolean.

    This is what I have tried

    final List<bool> Function(Uint8List image) _imageToCode = nativeAddLib
    .lookup<NativeFunction<List<bool> Function(List<Uint8List>)>>("imageToCode")
    .asFunction();
    

    The problem is, the bool and Uint8List are not a type supported by C++ so the function doesn't recognize them. How should I send it over the channel.