Endianness and row order for images

I’m reading in a gif with AWT/J2D and doing a color-space conversion from ARGB to RGBA (which JOGL likes better). There are two things I can fix with bit-shifting and array-copying, but I figure there must be nicer approaches to:

  1. The colors are right on Mac OS X, wrong on Windows. I suspect that because I’m sending an int[] to glDrawPixels like this:
    gl.glDrawPixels (width, height, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, myIntArray);
    that I’m suffering an endianness problem, ie, that on x86, the high 16 bits and low 16 bits of each integer trade places and I’m essentially sending “BARG” instead of “RGBA”.
    I could test this with java.nio.ByteOrder.nativeOrder and go through the array rotating bits if little-endian, but is there a prettier approach?

  2. My image is “upside down”, presumably because the java graphics world has 0,0 at top left, and OpenGL has 0,0 at bottom left, so the order of rows is correct, it’s just the coordinate space is different. Again, easy to fix by copying rows in reverse order to create a more OpenGL-friendly array, but is there another common approach, like using a transformation?

Thanks for ideas…
–invalidname

You might want to have a read through this post:

http://www.java-gaming.org/cgi-bin/JGNetForums/YaBB.cgi?board=jogl;action=display;num=1058674409;start=45

About a generic texture loader that gregory pierce started. The latest versions that I know of are here:

http://www.informatik.uni-oldenburg.de/~troggan/bodo/Texture.java
http://www.informatik.uni-oldenburg.de/~troggan/bodo/TextureLoader.java

There was some talk about getting them into the Jogl code base, but that doesn’t seem to have happened. This loader supports all images by creating a buffered image of the right type and just drawImage() into it…

Hope this helps,

Kev

You can draw the image from one buffered image to another with an AffineTransform, set to a scale of (1, -1).

How could that change the endianness?
I think defining a custom pixel format for the destination image is one way. But your transform will simply flip the image, not the RGB components.

[quote]2. My image is “upside down”, presumably because the java graphics world has 0,0 at top left, and OpenGL has 0,0 at bottom left, so the order of rows is correct, it’s just the coordinate space is different. Again, easy to fix by copying rows in reverse order to create a more OpenGL-friendly array, but is there another common approach, like using a transformation?
[/quote]
You could set up the projection matrix in opengl so that 0,0 is top left instead of bottom left. Use this code instead of glu.perspective(…).


//gl.frustum(left, right, bot, top, near, far);
float near = 1;
float top = n/2f;
float bot = -top;
float aspect = viewPortWidth /  viewPortHeight;
gl.frustum(-near*aspect/2f, near*aspect/2f, bot, top, near, far);

This will of course turn everything up side down :slight_smile: But I think it is more intuitive.

This is my basic texture loader class, and I insist on the term "basic"since it pales in comparaison to what Greg coded.
It also addresses the upside down symptom that you’re having.


class load_image
{
  java.awt.Image tex;
  byte[]         data;
  int            width,
                 height,
                 pixels[];

  load_image(){

    destroy();
  }

  void generate_texture_info(String texture_name, int type){

    try{
      tex = java.awt.Toolkit.getDefaultToolkit().getImage(texture_name);
    }
    catch(NullPointerException e){
      System.out.println(e);
    }

    while(width <= 0 || height <= 0){
      width      = tex.getWidth(null);
      height     = tex.getHeight(null);
    }

    pixels     = new int[width*height];
    extract_pixels();

    int pointer =0;

    switch(type){
      case  net.java.games.jogl.GL.GL_RGB:

        data = new byte[pixels.length*3];
        for(int y = height -1; y>=0; y--)
        for(int x = 0; x<width; x++){
          data[pointer+0] = (byte)((pixels[y*width + x] >> 16) & 0xFF);
          data[pointer+1] = (byte)((pixels[y*width + x] >>  8) & 0xFF);
          data[pointer+2] = (byte) (pixels[y*width + x]        & 0xFF);
          pointer+=3;
        }
      break;
      case net.java.games.jogl.GL.GL_RGBA:

        data = new byte[pixels.length*4];
        for(int y = height -1; y>=0; y--)
        for(int x = 0; x<width; x++){
          data[pointer+3] = (byte)((pixels[y*width + x] >> 24) & 0xFF);
          data[pointer+0] = (byte)((pixels[y*width + x] >> 16) & 0xFF);
          data[pointer+1] = (byte)((pixels[y*width + x] >>  8) & 0xFF);
          data[pointer+2] = (byte)( pixels[y*width + x]        & 0xFF);
          pointer+=4;
        }
      break;
      default: System.out.println("Unrecognized GL mode or not supported yet ^_^");
    }
  }

  void destroy(){

    tex    = null;
    data   = null;
    width  = -1;
    height = -1;
    pixels = null;
  }

  void extract_pixels(){

    try{
      new java.awt.image.PixelGrabber(tex,0,0,width,height,pixels,0,width).grabPixels();
    }
    catch (InterruptedException e) {}
  }
}


And this is how I call/use it in my demos:


  load_image     texture_loader = new load_image();
  String         texture_name[] = {"textures/floor.png",
                                   "textures/wall.png",
                                   "textures/ceiling.jpg"};
  float          x, y, z;
  int            index,
                 texture_id[] = new int[3];

    gl.glGenTextures(3, texture_id);

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

      texture_loader.generate_texture_info(texture_name[i], gl.GL_RGB);
      gl.glBindTexture(gl.GL_TEXTURE_2D, texture_id[i]);
      gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, 3,texture_loader.width, texture_loader.height,
                      0,gl.GL_RGB, gl.GL_UNSIGNED_BYTE, texture_loader.data);
      gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_MIN_FILTER,gl.GL_LINEAR);
      gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_MAG_FILTER,gl.GL_LINEAR);
      texture_loader.destroy();
    }


I wasn’t giving a solution to the endianness :stuck_out_tongue: But as InvalidName mentions, if you’re assuming that openGL has a coord system with the origin at the bottom left your textures will appear flipped. The affine transform is just an easy way to shuffle the rows around without doing it manually.

Thanks guys (and gals?). The suggestions were great. Greg’s texture-loader code made me realize I should use an interleaved color model, not direct/packed ARGB in an int[]. Also, the image flip (via a scale and translate AffineTransform) works for me. Maybe slower than munging arrays directly, but if it’s only done once, why not be a little pretty? (if nothing else, get it right and optimize last) So, both problems solved.

–invalidname