Reading png using dart ffi and libpng

327 Views Asked by At

I am trying to use libpng1.6 to load a png from file, but the execution fails with a segmentation fault.

I load the library and libpng function called png_image_begin_read_from_file as shown:

  final dylib = ffi.DynamicLibrary.open(
      "/opt/homebrew/Cellar/libpng/1.6.37/lib/libpng.dylib");
  final dart_function_t png_image_begin_read_from_file = dylib
      .lookup<ffi.NativeFunction<native_function_t>>(
          'png_image_begin_read_from_file')
      .asFunction();

Here is the full snippet to reproduce the problem.

import 'dart:ffi' as ffi;
import 'package:ffi/ffi.dart';

class png_controlp extends ffi.Opaque {}

// Fields taken from "png.h"
class PngImage extends ffi.Struct {
  @ffi.Uint32()
  external int version,
      width,
      height,
      flags,
      format,
      colormap_entries,
      warning_or_error;

  @ffi.Array(64)
  external ffi.Array<ffi.Char> message;
  external ffi.Pointer<png_controlp> opaque;
}

// native type
typedef native_function_t = ffi.Int32 Function(
    ffi.Pointer<PngImage> image, ffi.Pointer<Utf8> file_name);

// dart type
typedef dart_function_t = int Function(
    ffi.Pointer<PngImage> image, ffi.Pointer<Utf8> file_name);

void main() {
  final dylib = ffi.DynamicLibrary.open(
      "/opt/homebrew/Cellar/libpng/1.6.37/lib/libpng.dylib");
  final dart_function_t png_image_begin_read_from_file = dylib
      .lookup<ffi.NativeFunction<native_function_t>>(
          'png_image_begin_read_from_file')
      .asFunction();

  final path = "my_image.png";
  ffi.Pointer<PngImage> p_pngImage = calloc<PngImage>();
  p_pngImage.ref.version = 1;
  final res = png_image_begin_read_from_file(p_pngImage, path.toNativeUtf8());
}
===== CRASH =====
si_signo=Segmentation fault: 11(11), si_code=2, si_addr=0x11
version=2.17.0-266.7.beta (beta) (Mon Apr 25 14:54:53 2022 +0000) on "macos_arm64"
pid=42862, thread=20995, isolate_group=main(0x123810200), isolate=main(0x122034200)
isolate_instructions=10263d8c0, vm_instructions=10263d8c0
  pc 0x00000001051b6604 fp 0x000000016e1be830 png_image_free+0x24
  pc 0x00000001051b66c8 fp 0x000000016e1be850 png_image_error+0x38
1

There are 1 best solutions below

0
On BEST ANSWER

It looks like the Dart struct is in the wrong order - check the C header:

 2672 typedef struct
 2673 {
 2674    png_controlp opaque;    /* Initialize to NULL, free with png_image_free */
 2675    png_uint_32  version;   /* Set to PNG_IMAGE_VERSION */
 2676    png_uint_32  width;     /* Image width in pixels (columns) */
 2677    png_uint_32  height;    /* Image height in pixels (rows) */
 2678    png_uint_32  format;    /* Image format as defined below */
 2679    png_uint_32  flags;     /* A bit mask containing informational flags */
 2680    png_uint_32  colormap_entries;
 2681                            /* Number of entries in the color-map */
 ....
 2704 
 2705    png_uint_32  warning_or_error;
 2706 
 2707    char         message[64];
 2708 } png_image, *png_imagep;

So, the Dart struct should be:

class PngImage extends ffi.Struct {
  external ffi.Pointer<png_controlp> opaque;

  @ffi.Uint32()
  external int version,
      width,
      height,
      flags,
      format,
      colormap_entries,
      warning_or_error;

  @ffi.Array(64)
  external ffi.Array<ffi.Char> message;
}

Also note the comment in the header:

 2976  * The png_image passed to the read APIs must have been initialized by setting
 2977  * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.)

Be sure to set the opaque pointer to nullPtr - though this is probably unnecessary as you are using calloc to create it - which will zero it for you. (Also remember that you may need to make another C call to free stuff allocated by the read_from_file method, and also free anything that you calloc.)