Hello Guest

Setting the Cursor

  • 24 Replies
  • 22600 Views
Setting the Cursor
« on: November 04, 2010, 21:44:26 »
I've tried to set the cursor using BufferedImage, and I run into serious problems with how the image is drawn.

I do the following:

Load a BufferedImage via ImageIO.read(url).
Paint that BufferedImage onto one with the data type BufferedImage.TYPE_INT_ARGB_PRE (don't know if this is necessary, thought that it would help stabilize the method for all image types compatible with BufferedImage).
Create a cursor using an IntBuffer obtained from the image via
Code: [Select]
IntBuffer.wrap(((DataBufferInt) image.getData().getDataBuffer()).getData());
I get a cursor of the right dimensions and all, but the image data is extremely messed up - patches of it have the wrong color, other patches have the wrong transparency.

I think the problem might be that my computer only supports one bit transparency for the cursor, but the returned intbuffer holds data for a full byte of alpha data, and that causes the problem somehow. Is this right? If so, how can I fix it?

Re: Setting the Cursor
« Reply #1 on: November 05, 2010, 06:24:03 »
Hi,
what is the format of the image? PNG?

Regards,
WS
S.

Re: Setting the Cursor
« Reply #2 on: November 05, 2010, 12:20:12 »
Yeah. I use a 24 x 33 png image.

Re: Setting the Cursor
« Reply #3 on: November 05, 2010, 14:13:51 »
S.

Re: Setting the Cursor
« Reply #4 on: November 05, 2010, 14:32:11 »
You could also use TWL's PNGDecoder (also available as separate JAR): http://www.javagaming.org/index.php/topic,22435.0.html - it is faster and easier to use.

Re: Setting the Cursor
« Reply #5 on: November 05, 2010, 20:51:15 »
Isn't that limited to just PNG images? I'm trying to create a method that will support as many image types as possible - PNG, bitmap, GIF, JPEG (though granted jpeg would be a bad choice for cursor format), etc. The simplest way to do that, code wise, seems to be to use Java2D's native methods for reading images, then dealing with the single format that results.

Re: Setting the Cursor
« Reply #6 on: November 06, 2010, 18:30:11 »
Does anyone know how to set the cursor with Java2D methods? I'm running into a bit of a wall here and could use some help.

Re: Setting the Cursor
« Reply #7 on: November 07, 2010, 06:04:45 »
Perhaps the width and height of the cursor needs to be in powers of 2?
cool story, bro

Re: Setting the Cursor
« Reply #8 on: November 07, 2010, 12:07:21 »
I just tried that and it didn't change anything... good idea, though.

This is really bugging me - has anyone gotten this to work? I believe bobjob said this was how he did it, and I tried to base the method on what he recommended...

Re: Setting the Cursor
« Reply #9 on: November 07, 2010, 13:42:12 »
TWL contains code to change the cursor - and it seems to work on every system :) You can take a look at the code.

Re: Setting the Cursor
« Reply #10 on: November 07, 2010, 14:13:21 »
One question: It's called "PNGDecoder." Doesn't that mean it works only for PNG images?

Re: Setting the Cursor
« Reply #11 on: November 07, 2010, 15:40:14 »
sure - only PNGs :)

Re: Setting the Cursor
« Reply #12 on: November 07, 2010, 19:33:57 »
This works perfectly for me for both PNG and BMP files.  In order to get PNG's to work with varying alpha values, I had to take a percentage of each red, green, and blue value based off of the alpha value.  This uses Slick-Util, but Slick-Util uses ImageIO anyway.  You should be able to figure out how to change this for your Buffered Images.

Code: [Select]
   Texture tex = TextureLoader.getTexture("BMP",new FileInputStream("test.bmp"));
    int byteCount = 3; //Set this to 4 for PNG, TGA, etc.
    int trueWidth = tex.getImageWidth() * byteCount; //For speed

    int[] array = new int[tex.getImageWidth() * tex.getImageHeight()];
    //If you call getTextureData() continuously in the loop, it's very slow.
    byte[] data = tex.getTextureData();

    for(int y = 0; y < tex.getImageHeight(); y++) {
      for(int x = 0; x < tex.getImageWidth(); x++) {
        int i = y * trueWidth + x * byteCount;

        //Without using "& 0xFF," there were some bad color problems.
        //This is due to conversion from byte to int (a byte is -128 to 127,
        //and we want 0 to 255).
        int r = data[i]     & 0xFF;
        int g = data[i + 1] & 0xFF;
        int b = data[i + 2] & 0xFF;
        int a = 0xFF;

        if(byteCount == 4) {
          a = data[i + 3] & 0xFF;

          if(a > 0) {
            double ap = a / 255.0; //alpha percentage
            r = (int)Math.round(r * ap);
            g = (int)Math.round(g * ap);
            b = (int)Math.round(b * ap);
            a = 0xFF;
          }
        }

        array[y * tex.getImageWidth() + x] =
          (a << 24) |
          (r << 16) |
          (g <<  8) |
          (b);
      }
    }

    Cursor cursor = new Cursor(tex.getImageWidth(),tex.getImageHeight(),0,
      tex.getImageHeight() - 1,1,IntBuffer.wrap(array),null);
    Mouse.setNativeCursor(cursor);


EDIT:  For a Targa (TGA) file with transparency, use a byteCount of 4; for a Targa file without transparency, use a byteCount of 3.  Slick-Util does not support Targa files that use RLE compression... ::)
« Last Edit: November 07, 2010, 22:57:20 by jediTofu »
cool story, bro

Re: Setting the Cursor
« Reply #13 on: November 08, 2010, 00:06:16 »
For a white background you could do the inverse of above...

You could just set a programmer-adjustable tolerance.  If alpha is below 50% (255 * 0.50), then set it to 0x00, else set it to 0xFF.  This is probably the best way, and just let the programmer know not to use varying degrees of alpha.
cool story, bro

Re: Setting the Cursor
« Reply #14 on: November 10, 2010, 16:06:23 »
Thanks for the example.

One question - if you try to supply an intbuffer that holds data for translucent images, and the computer only supports 1-bit alpha cursors, does it simply convert the image, or does it mess up the cursor?