openglgraphicspnglwjgl

PNGDecoder in LWJGL returning few negative values


I am using PNGDecoder to decode a png that is a height map. Howerver, when I am just printing the values returned in console I see few of the rgb values are returned as negative

-128, -128, -128, -1
-128, -128, -128, -1
-128, -128, -128, -1
-124, -124, -124, -1
-119, -119, -119, -1
-118, -118, -118, -1

The code that I'm using to decode and then read is below

public static ByteBuffer decodeImage(String path) throws IOException {
        InputStream stream = new FileInputStream(path);
        PNGDecoder decoder = new PNGDecoder(stream);
        ByteBuffer decodedImageData = ByteBuffer.allocateDirect(4 * decoder.getWidth() * decoder.getHeight());
        decoder.decodeFlipped(decodedImageData, decoder.getWidth() * 4, PNGDecoder.Format.RGBA);
        decodedImageData.flip();
        return decodedImageData;
    }

private void applyHeightMap (String heightMapPath) throws IOException {
        float vertices[] = plane.getVertices();
        ByteBuffer buffer = Texture.decodeImage(heightMapPath);
        for (int i = 0; i < 2624 * 1756; i++) {
            byte r = buffer.get();
            byte g = buffer.get();
            byte b = buffer.get();
            byte a = buffer.get();
            if(r < 0 || b < 0 || g < 0) {
                System.out.println(r + ", " + g + ", " + b+", "+a);
            }
        }
    }

Not sure why there are few negative values and also why the alpha channel is read as -1

The image I'm using is https://learnopengl.com/img/guest/2021/tessellation/height_map/iceland_heightmap.png


Solution

  • Java's byte type is a signed 8-bit type. This means that the 8bits represents values from -128 to 127. Images on the other hand are stored in a unsigned 8-bit type per channel, representing [0, 255].

    When you look at the printed values -128, -128, -128, -1, you actually have the binary data 10000000 for -128 and 11111111 for -1. When converting this into unsigned 8-bits, it's 10000000 = 128 and 11111111 = 255.

    Java doesn't have unsigned types, but I assume that lwjgl will only care about the binary data, not the numeric representation when passing the image to OpenGL.

    If you want to print the values, you need to convert the byte into a datatype that is large enough to represent the [0,255] range, for example, into an int. Something like the following code should print correct values:

    int r = buffer.get() & 0xff;
    int g = buffer.get() & 0xff;
    int b = buffer.get() & 0xff;
    int a = buffer.get() & 0xff;
    System.out.println(r + ", " + g + ", " + b + ", " + a);
    

    Conversion taken from https://mkyong.com/java/java-convert-bytes-to-unsigned-bytes/#:~:text=In%20Java%2C%20byte%20is%20an,bytes%20(0%20to%20255).