LWJGL Forum
Programming => OpenGL => Topic started by: CodeBunny 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
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?
-
Hi,
what is the format of the image? PNG?
Regards,
WS
-
Yeah. I use a 24 x 33 png image.
-
something related to this post? http://lwjgl.org/forum/index.php/topic,3332.0.html
-
You could also use TWL's PNGDecoder (also available as separate JAR): http://www.javagaming.org/index.php/topic,22435.0.html (http://www.javagaming.org/index.php/topic,22435.0.html) - it is faster and easier to use.
-
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.
-
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.
-
Perhaps the width and height of the cursor needs to be in powers of 2?
-
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...
-
TWL contains code to change the cursor - and it seems to work on every system :) You can take a look at the code.
-
One question: It's called "PNGDecoder." Doesn't that mean it works only for PNG images?
-
sure - only PNGs :)
-
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.
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... ::)
-
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.
-
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?
-
Okay, I'm really confused here. ???
I changed the method I use to see if your method would work, but I get almost the same problem as before.
Here's what I do to load the IntBuffer for the cursor:
int numBytes = 3;
if(type == "PNG" || type == "TGA")
{
numBytes = 4;
}
int width = texture.getImageWidth();
int height = texture.getImageHeight();
int[] imageData = new int[width * height];
byte[] bytes = texture.getTextureData();
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
int i = y * width + x * numBytes;
int r = bytes[i] & 0xFF;
int g = bytes[i + 1] & 0xFF;
int b = bytes[i + 2] & 0xFF;
int a = 0xFF;
if(numBytes == 4)
{
a = bytes[i + 3] & 0xFF;
if(a > 0)
{
double ap = a / 255.0;
r = (int)Math.round(r * ap);
g = (int)Math.round(g * ap);
b = (int)Math.round(b * ap);
a = 0xFF;
}
}
imageData[y * width + x] = (a << 24) | (r << 16) | (g << 8) | (b);
}
}
return IntBuffer.wrap(imageData);
I tried the method with both a bmp and a png image and both became screwed up.
-
The only thing that I noticed wrong was your i variable, should be this:
int i = y * width * numBytes + x * numBytes;
Also, I forgot about texture.hasAlpha(), you can replace <code>type == "PNG" || type == "TGA"</code> with that.
If that still doesn't give you what you want, you can try an alpha tolerance as I suggested:
//parameter
double alphaTolerancePercentage; //between 0.0 and 1.0, play around with this; try 0.5 at first
//up at very top
int alphaTolerance = 256 * alphaTolerancePercentage; //256 so that "<" works
//replace in middle of code
if(numBytes == 4)
{
a = bytes[i + 3] & 0xFF;
if(a < alphaTolerance) {
a = 0;
}
else {
a = 0xFF;
}
}
-
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?
My machine only supports 1-bit alpha cursors. When trying a PNG with varying alpha values, everything that is below 0xFF is truncated to 0, so only one pixel in the center of the image is shown (the only pixel with 0xFF alpha).
Also, I used a 64x64 image for all of my tests, forgot to mention this, so you might want to try a 64x64 image until you get your code working, and I tested it with a BMP, TGA (with and without transparency), and PNG; all worked fine.
-
You're right -
When I test it as a 64 x 64, it works fine. If I use different dimensions, it blows up.
I checked 32 x 64, and that worked. It looks like it needs to be a power of two, for whatever reason.
-
You're right -
When I test it as a 64 x 64, it works fine. If I use different dimensions, it blows up.
I checked 32 x 64, and that worked. It looks like it needs to be a power of two, for whatever reason.
Since I had my testing project open, I tried a 32x40 image, and it worked. I think that there is just a minimum cursor size. I couldn't find an equivalent LWJGL method for this, but the Toolkit works fine:
Dimension d = Toolkit.getDefaultToolkit().getBestCursorSize(0,0);
On my machine, d.height and d.weight are 32, so I assume any height/width below 32 won't work, and from my testing, it doesn't need to be power of 2 (at least on my machine).
-
*sigh*
I found why it was locking to square roots of 2 - I needed to change
int i = y * width + x * numBytes;
To multiply y by the texture width, not the image width.
So, now the method sort of works for most images. There are two problems, though:
First, I'm getting the right colors but the alpha is still messed up somehow - sections that should be transparent appear black, and, more disturbingly, sections that should be opaque and colored in are showing images behind them.
Secondly, the method seems to break when the cursor gets very small for some reason. If I make the image something like 7 x 12, it starts getting wonky again.
If I make the cursor read the entire image, not just the image on top of it, it works better.
-
If I make the image something like 7 x 12, it starts getting wonky again.
Yes, this is probably due to the minimum cursor size I bet. When passing in an int buffer below 32x32, it's all "wonky" for me.
Yeah, completely forgot should be using texture width/height and not image width/height, but this does perplex me...as my 32x40 image worked using the image height/width and not the texture height/width. Your width still needs to be multiplied by numBytes though.
I'm not sure about your alpha problems, as it works fine for me. You could upload the image you're trying to your post and post the code for setting the cursor, and I could try it.
-
Since I had my testing project open, I tried a 32x40 image, and it worked. I think that there is just a minimum cursor size. I couldn't find an equivalent LWJGL method for this, but the Toolkit works fine:
Dimension d = Toolkit.getDefaultToolkit().getBestCursorSize(0,0);
http://lwjgl.org/javadoc/org/lwjgl/input/Cursor.html
get*CursorSize ?
-
http://lwjgl.org/javadoc/org/lwjgl/input/Cursor.html
get*CursorSize ?
Ah right, good call haha.
-
I've managed to fix the cursor code for all supplied images, finally. It'll work when the image is under size by just making the additional needed space have no alpha.
Thanks for the help. :) That was an annoying problem.