使用 dart ffi 和 libpng 读取 png

Reading png using dart ffi and libpng

我正在尝试使用 libpng1.6 从文件加载 png,但执行失败并出现分段错误。

我加载库和 libpng 函数 png_image_begin_read_from_file,如图所示:

  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();

这是重现问题的完整代码段。

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

看起来 Dart 结构的顺序错误 - 检查 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;

所以,Dart 结构应该是:

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;
}

另请注意 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.)

确保将 opaque 指针设置为 nullPtr - 虽然这可能是不必要的,因为您正在使用 calloc 创建它 - 这将为您将其归零。 (还要记住,您可能需要对 read_from_file 方法分配的空闲内容进行另一个 C 调用,以及 freecalloc 分配的任何内容。)