javajava-native-interfaceswigopenexr

OpenExr, Swig and Java


I try to create a solution that allows me to read OpenExr images in Java and use the pixel data for textures in JOGL. Because there is no free OpenExr library in Java (didn't find anything that is working) my Idea was to write a small c++ program utilizing the ILM OpenExr library and wrap it with Swig so I can use JNI to load and use the dll in Java.

I built the OpenExr library and set up Swig in Visual Studio 2005. It already creates a dll that allows me to get the dimensions of the image so I guess that the tool chain is running properly.

The h file:

#include <ImfRgbaFile.h>
#include <ImathBox.h>
#include <ImathVec.h>
#include <ImfRgba.h>
#include <ImfArray.h>
#include <iostream>
#include "String.h"
#include "RgbaStruct.h"

using namespace std;

class OpenExrReader {

    private:
        int width;
        int height;
        Imf::Array2D<Imf::Rgba> newPixels;
        Rgba rgba;

    public:

        //constructor
        OpenExrReader::OpenExrReader();
        //destructor
        OpenExrReader::~OpenExrReader();
        //methods
        int OpenExrReader::readExrFile(const char *filePath);
        int OpenExrReader::getHeight();
        int OpenExrReader::getWidth();
        Rgba OpenExrReader::getScruct(int i, int j);
};

The cpp file:

#include "OpenExrReader.h"

OpenExrReader::OpenExrReader() {

    width = 0;
    height = 0;
}

OpenExrReader::~OpenExrReader() {

}

int OpenExrReader::readExrFile(const char *filePath) {

    const char* fileName = filePath;

    try {

        Imf::RgbaInputFile file(fileName);
        Imath::Box2i dw = file.dataWindow();
        Imath::V2i dim(dw.max.x - dw.min.x + 1, dw.max.y - dw.min.y + 1);

        width = dw.max.x - dw.min.x + 1;
        height = dw.max.y - dw.min.y + 1;

        newPixels.resizeErase(height, width);

        int dx = dw.min.x;
        int dy = dw.min.y;

        file.setFrameBuffer(&newPixels[0][0] - dw.min.x - dw.min.y * width, 1, width);
        file.readPixels(dw.min.y, dw.max.y);
    }
    catch (Iex::BaseExc &e) {

        std::cerr << e.what() << std::endl;
    }

    return 0;
}

int OpenExrReader::getHeight() {

    return height;
}

int OpenExrReader::getWidth() {

    return width;
}

Rgba OpenExrReader::getScruct(int i, int j) {

    struct Rgba rgba = {newPixels[i][j].r,
                        newPixels[i][j].g,
                        newPixels[i][j].b,
                        newPixels[i][j].a};

    return rgba;
}

The Rgba sturct:

#include <half.h>

#ifndef RGBASTRUCT_H
#define RGBASTRUCT_H

struct Rgba{

    half r;
    half g;
    half b;
    half a;
};

#endif

The Swig interface file:

/* File : OpenExrReader.i */
%module OpenExrReaderDll

%{
/* Put header files here or function declarations like below */
#include "OpenExrReader.h"
#include "RgbaStruct.h"
%}
%include "OpenExrReader.h"
%include "RgbaStruct.h"

The Java test program:

public class OpenExrReaderMain {

static {

    System.loadLibrary("OpenExrReader");
}

/**
 * @param args
 */
public static void main(String[] args) {

    OpenExrReader reader = new OpenExrReader();
    reader.readExrFile("C:\\path\\someExrFile.exr");

    int height  = reader.getHeight();
    int width = reader.getWidth();

    for(int i = 0; i < height; i++) {

        for(int j = 0; j < width; j++) {

            Rgba rgba = reader.getScruct(i, j);

            SWIGTYPE_p_half r = rgba.getR();
            SWIGTYPE_p_half g = rgba.getG();
            SWIGTYPE_p_half b = rgba.getB();
            SWIGTYPE_p_half a = rgba.getA();

            System.out.print("r: " + r + " || ");
            System.out.print("g: " + g + " || ");
            System.out.print("b: " + b + " || ");
            System.out.print("a: " + a);
            System.out.println();
        }
    }
  }
}

Like I said this is working but instead of the pixel-data I get this for every pixel:

r: SWIGTYPE_p_half@6b8741 || g: SWIGTYPE_p_half@17cfa2a || b: SWIGTYPE_p_half@c0a52 || a: SWIGTYPE_p_half@79c3e2

The initial idea was that I have to copy the pixel data at some point into a Java buffer to use it in JOGL. So I wrote the getScruct(int i, int j) method to get the rgba-data of one pixel and to keep the JNI interface as simple as possible.

The problem is now that Swig doesn't know how to convert the ILM half data-type into a Java data-type. At first I tried to store float values in the Rgba-struct because Swig knows how to convert those. Accordingly to the OpenExr doc converting half to float should be no problem but every time I try something like this:

half a = newPixels[i][j].r;
float b = a;

I get an error message from VS that says:

OpenExrReader.obj : error LNK2001: unresolved external symbol "private: static union half::uif const * const half::_toFloat" (?_toFloat@half@@0QBTuif@1@B)

The other soltuton that I came up with was to use a Swig typemap and tell the wrapper to convert ILM half into Java float but I'm not sure if this is even possible.

Because I know very little about c++ and VS and it's the first time I work with Swig and JNI I have absolutly no idea how to solve this.

So, does anyone know how to solve this so I can get the pixel data into a java data-type?


Solution

  • I found a solution that works for me. After I added the preprocessor macros:

    OPENEXR_DLL and PLATFORM_WINDOWS

    in Visual Studio I was able to cast the half values into float and swig converts them into the correct java data-type.