richgel999 / fpng

Super fast C++ .PNG writer/reader

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to provide the `const void* pImage` Array

manticore-projects opened this issue · comments

Apologies for asking a maybe stupid question:

How exactly is const void* pImage Array supposed to look like? And how to create this array from a Java Image?
From the Python implementation, I can see that a 3 dimensional array is used, however in my understanding a Raster Image has only 2 dimensions?

Any hint or helping hand will be much appreciated, thank you!

This is my current understanding:

/*
num_chans must be 3 or 4. There must be w*3*h or w*4*h bytes pointed to by pImage.
The image row pitch is always w*3 or w*4 bytes.
There is no automatic determination if the image actually uses an alpha channel, so if you call it with 4 you will always get a 32bpp .PNG file.
*/

// read the Demo PNG into an Image
BufferedImage image = ImageIO.read(inputStream);

// Create the w*4*h byte array
final byte[] byteArr = new byte[image.getWidth()*4*image.getHeight()];
for (int y=0; y<image.getHeight(); y++) {
    for (int x=0; x<image.getWidth(); x++) {
        int argb = image.getRGB(x, y);

        final int alpha = 0xff & (argb >> 24);
        final int red = 0xff & (argb >> 16);
        final int green = 0xff & (argb >> 8);
        final int blue = 0xff & (argb >> 0);

        byteArr[x * 4 * y]= (byte) red;
        byteArr[x * 4 * y + 1]= (byte) green;
        byteArr[x * 4 * y + 2]= (byte) blue;
        byteArr[x * 4 * y + 3]= (byte) alpha;
    }
}

// Convert byte array to Pointer
Pointer pImage = new Memory(byteArr.length);
pImage.write(0, byteArr, 0, byteArr.length);

And pardon my ignorant question: Why declare an unspecific pointer const void* pImage instead of a pointer to a defined structure, which allows to understand what exactly is expected?

I have successfully wrapped the Encoder and can call it from Java. It produces a valid PNG byte stream.
However, the image is heavily distorted (simply garbage), meaning my understanding how pImage has to be construed is wrong.

Please help me out here with an explanation. Thanks a lot in advance.

extern "C" {
    void fpng_init() {
        return fpng::fpng_init();
    }

    typedef struct {
        uint8_t* data;
        size_t size;
    } ByteArray;

    void releaseVectorData(ByteArray data) {
        // Release the allocated memory when it's no longer needed
        delete[] data.data;
    }

    ByteArray* fpng_encode_image_to_memory1(const void* pImage, uint32_t w, uint32_t h, uint32_t num_chans,  uint32_t flags = 0) {
        // Vector frees itself
        std::vector<uint8_t> out_buf;

        //w=687 h=1012 channels=4 flags=0
        printf("w=%d h=%d channels=%d flags=%d\n", w, h, num_chans, flags);
        bool result = fpng::fpng_encode_image_to_memory( pImage, w, h, num_chans, out_buf, flags);

        printf("size=%d\n", out_buf.size());

        ByteArray* data = (ByteArray*)malloc(sizeof(ByteArray));
        data->size = out_buf.size();
        data->data = (uint8_t*) malloc( out_buf.size() * sizeof(uint8_t));

        // copy the uint8_t* array
        std::memcpy(data->data, out_buf.data(), out_buf.size());

        return data;
    }
}

Apologies for the noise.
I finally got it working:

BufferedImage image = ImageIO.read(inputStream);

            // Get image dimensions
            int width = image.getWidth();
            int height = image.getHeight();

            // Get the pixel data as an integer array
            int[] pixels = image.getRGB(0, 0, width, height, null, 0, width);

            // Create a byte array to store RGBA values
            byte[] rgbaArray = new byte[width * height * 4];

            // Extract RGBA values from each pixel
            for (int i = 0; i < pixels.length; i++) {
                int pixel = pixels[i];
                rgbaArray[i * 4] = (byte) ((pixel >> 16) & 0xFF); // Red
                rgbaArray[i * 4 + 1] = (byte) ((pixel >> 8) & 0xFF); // Green
                rgbaArray[i * 4 + 2] = (byte) (pixel & 0xFF); // Blue
                rgbaArray[i * 4 + 3] = (byte) ((pixel >> 24) & 0xFF); // Alpha
            }

With that Byte Array I can encode a proper PNG in Java, calling FPNG via JNA.