java - Struggling with JNA wrappers for LibRaw image data pointers - Stack Overflow

admin2025-04-21  4

Background: I'm not a professional developer - I used C a few decades ago and Java a few years ago. I'm rusty.

I'm trying to write JNA wrappers for LibRaw and I'm struggling to get the right JNA 'spells' for accessing the raw image data (other data, e.g., image width/height are no problem).

The relevant bit of the libraw_types.h file are:

typedef struct
{
  ushort (*image)[4];
  libraw_image_sizes_t sizes;

  /*
  Other members
  */

  libraw_rawdata_t rawdata;
  void *parent_class;
} libraw_data_t;

typedef struct
{
 void *raw_alloc;
  ushort *raw_image;
  ushort (*color4_image)[4];
  ushort (*color3_image)[3];

  /*
  Other members
  */

} libraw_rawdata_t;

In C this is typically used as follows:

LibRaw RawProcessor;

ret = RawProcessor.open_file(av[i])
ret = RawProcessor.unpack()

val = RawProcessor.imgdata.rawdata.raw_image[0];

From this I assumed that the DLL manages the memory allocation for the image data and populates the structures with pointers to arrays of ushorts.

My equivalent JNA wrapper looks like this (just a snip since the nested structures are extensive to the tune of >1000 lines!):

public interface LibRaw extends Library {

  public class LibRaw_data extends Structure {
    public Pointer image; 
    
    /*
    Other members
    */

    public LibRaw_rawdata rawdata; 
  }

  public class LibRaw_rawdata extends Structure {
    public Pointer raw_alloc; 
    
    public Pointer raw_image; 
    //public PointerByReference raw_image = new PointerByReference(); 
    
    /*
    Other members
    */
    
  }
  
  LibRaw INSTANCE = (LibRaw) Native.loadLibrary(dllPath, LibRaw.class);
   
  LibRaw_data libraw_init(int flags);
  int libraw_open_file( LibRaw_data lr, String fname );
  int libraw_unpack( LibRaw_data lr );
  void libraw_close( LibRaw_data lr );
  int libraw_raw2image();
  int libraw_dcraw_process();
  void libraw_close( LibRaw_data lr );
  
  // ... other functions ...
  
}

... and my main code has:

LibRaw.LibRaw_data librawdata = new LibRaw.LibRaw_data();

librawdata = LibRaw.INSTANCE.libraw_init(0);

retCode = LibRaw.INSTANCE.libraw_open_file( librawdata, fname );
retCode = LibRaw.INSTANCE.libraw_unpack( librawdata );

if (librawdata.rawdata.raw_image != null) ...

This all compiles OK and runs with no error, but after the unpack call, I expect raw_image to be something other than null and that I can then do something line raw_image.getByteArray() to get access to the image data.

But despite everything else appearing OK (other values are valid and reasonable, no errors), raw_image and the other image data pointer are all null.

I've seen some examples which seem relevant which suggest using PointerByReference, but that has the same end result, no errors and null pointers.

Background: I'm not a professional developer - I used C a few decades ago and Java a few years ago. I'm rusty.

I'm trying to write JNA wrappers for LibRaw and I'm struggling to get the right JNA 'spells' for accessing the raw image data (other data, e.g., image width/height are no problem).

The relevant bit of the libraw_types.h file are:

typedef struct
{
  ushort (*image)[4];
  libraw_image_sizes_t sizes;

  /*
  Other members
  */

  libraw_rawdata_t rawdata;
  void *parent_class;
} libraw_data_t;

typedef struct
{
 void *raw_alloc;
  ushort *raw_image;
  ushort (*color4_image)[4];
  ushort (*color3_image)[3];

  /*
  Other members
  */

} libraw_rawdata_t;

In C this is typically used as follows:

LibRaw RawProcessor;

ret = RawProcessor.open_file(av[i])
ret = RawProcessor.unpack()

val = RawProcessor.imgdata.rawdata.raw_image[0];

From this I assumed that the DLL manages the memory allocation for the image data and populates the structures with pointers to arrays of ushorts.

My equivalent JNA wrapper looks like this (just a snip since the nested structures are extensive to the tune of >1000 lines!):

public interface LibRaw extends Library {

  public class LibRaw_data extends Structure {
    public Pointer image; 
    
    /*
    Other members
    */

    public LibRaw_rawdata rawdata; 
  }

  public class LibRaw_rawdata extends Structure {
    public Pointer raw_alloc; 
    
    public Pointer raw_image; 
    //public PointerByReference raw_image = new PointerByReference(); 
    
    /*
    Other members
    */
    
  }
  
  LibRaw INSTANCE = (LibRaw) Native.loadLibrary(dllPath, LibRaw.class);
   
  LibRaw_data libraw_init(int flags);
  int libraw_open_file( LibRaw_data lr, String fname );
  int libraw_unpack( LibRaw_data lr );
  void libraw_close( LibRaw_data lr );
  int libraw_raw2image();
  int libraw_dcraw_process();
  void libraw_close( LibRaw_data lr );
  
  // ... other functions ...
  
}

... and my main code has:

LibRaw.LibRaw_data librawdata = new LibRaw.LibRaw_data();

librawdata = LibRaw.INSTANCE.libraw_init(0);

retCode = LibRaw.INSTANCE.libraw_open_file( librawdata, fname );
retCode = LibRaw.INSTANCE.libraw_unpack( librawdata );

if (librawdata.rawdata.raw_image != null) ...

This all compiles OK and runs with no error, but after the unpack call, I expect raw_image to be something other than null and that I can then do something line raw_image.getByteArray() to get access to the image data.

But despite everything else appearing OK (other values are valid and reasonable, no errors), raw_image and the other image data pointer are all null.

I've seen some examples which seem relevant which suggest using PointerByReference, but that has the same end result, no errors and null pointers.

Share Improve this question edited Feb 8 at 10:28 Mark Rotteveel 110k229 gold badges156 silver badges225 bronze badges asked Jan 22 at 19:18 Marek PMarek P 211 silver badge3 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

The crux of your problem is here.

LibRaw.LibRaw_data librawdata = new LibRaw.LibRaw_data();
librawdata = LibRaw.INSTANCE.libraw_init(0);

Of note, in the first line, you're allocating (in JNA) a LibRaw_data() structure but then in the second one you're immediately throwing it away and overwriting it with the return value from the libraw_init() call.

And this is the problem. JNA structures (usually) auto populate when they are passed as arguments to native calls, but they don't do that for return values. Reading the documentation you can see the signature of that init call:

libraw_data_t *libraw_init(unsigned int flags);

The function returns the pointer to the instance of libraw_data_t structure. The resultant pointer should be passed as the first argument to all C API functions

A JNA structure can be accessed two ways, ByValue and ByReference. There are tagging interfaces when you want to do something other than the default, but in short, by default:

  • Structures as standalone or in-line in another structure are ByValue
  • Structures as a method/function argument are ByReference

When you call LibRaw.INSTANCE.libraw_init(0) you get a Pointer as a return value. However, you're assigning it to a ByValue structure, which doesn't know what to do with it.

There are two ways to address this.

The "low level" way, to understand what's happening, is to add a pointer constructor to your LibRaw_data structure, something like:

@FieldOrder{ ... fields ... }
public class LibRaw_rawdata extends Structure {
    // fields go here

    public LibRaw_rawdata() {
        super();
    }

    public LibRaw_rawdata(Pointer p) {
        super(p);
    }
}

Then change your code for those two lines:

Pointer p = LibRaw.INSTANCE.libraw_init(0);
LibRaw.LibRaw_data librawdata = new LibRaw.LibRaw_data(p);

You'll also need to change the libraw_init() mapping:

Pointer libraw_init(int flags);

You can then access librawdata "by value" in your code and pass it to the other API functions where it will automatically be treated "by reference" as a pointer.

Alternately, and more succint, you can keep your mappings closer by using the ByReference tagging interface on your structure:

@FieldOrder{ ... fields ... }
public class LibRaw_rawdata extends Structure {
    
    public static class ByReference extends LibRaw_rawdata implements Structure.ByReference {}
    
    // fields go here
}

And then map libraw_init():

LibRaw_rawdata.ByReference libraw_init(int flags);

This lets JNA know what to do with the pointer when it's assigned, and also gains the benefit of type safety. It's essentially the same as the pointer constructor approach under the hood.

转载请注明原文地址:http://anycun.com/QandA/1745233305a90528.html