Question

I've been trying to encode some data (represented as array of byte values 0-255) in a PNG image format with Java. The data is read out using HTML canvas element getImageData() method in JavaScript (similar to this: http://blog.nihilogic.dk/2008/05/compression-using-canvas-and-png.html).

However, the output data is not always identical to the input. Some of the values appear to differ from the input. It seems to work encoding as an image 1 pixel high, and is only incorrect for an image with multiple rows. I had the idea it could be caused by the line-by-line filtering for PNG images somehow but don't really know.

It seems that each incorrect value is only ever wrong by 1 or 2.

Here's the Java code but I'm wondering if it could also be an issue with the ImageIO api, in particular its PNG encoder?

public static File encodeInPng(byte[] data, String filename) throws java.io.IOException{
    int width = (int)Math.ceil(Math.sqrt(data.length));
    int height = (int)Math.ceil((double)data.length/width);

    BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);

    int x = 0, y = 0;
    for (byte b : data) {
        bufImg.getRaster().setPixel(x, y, new int[]{b&0xFF});
        x++;
        if (x == width) {
            x = 0;
            y ++;
        }
    }

    File f = new File(filename);
    ImageIO.write(bufImg, "png", f);

    return f;
}

Edit: The problem only appears for PNG files over a certain size (around 50 kB, or possibly 256x256px).

Was it helpful?

Solution 2

Ok, so I discovered that the problem only occurred for images larger than 256x256. I did a bit of research and discovered existing (although different) problems that people had experienced in Chrome (with the canvas element and this size image). So I tried using Firefox, and no errors!

Seems like the Chrome canvas element is far from perfect.

[Using Chrome Version 32.0.1700.77]

Edit: Also, setting the chrome://flags "Disable accelerated 2D canvas" makes it work in Chrome.

OTHER TIPS

The calculations of dimensions is rather ugly, and some parts of the code could be slightly more efficient and clean - some polishing comes below. But your code seems basically correct, and it works for me. Can you pass an example where "output data is not identical to the input"?

public static File encodeInPng(byte[] data, String filename) 
         throws java.io.IOException {
    int width = (int) Math.ceil(Math.sqrt(data.length));
    int height = data.length / width;
    BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
    int[] pix = new int[1];
    int pos = 0;
    for( int y = 0; y < height; y++ ) {
        for( int x = 0; x < width; x++ ) {
            pix[0] = data[pos++] & 0xFF;
            bufImg.getRaster().setPixel(x, y, pix);
        }
    }
    File f = new File(filename);
    ImageIO.write(bufImg, "png", f);
    return f;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top