[FIXED] Bug in Cursor?

Started by MonsieurBlutbad, August 12, 2013, 10:22:03

Previous topic - Next topic

MonsieurBlutbad

Hi all,

when I try to create an animated mouse cursor with lwjgl Cursor I get the strangest results.


Here is the code I use:
private static Cursor getCursor() throws IOException, LWJGLException{
    // set width, height and amount of cursors
    int cursorWidth  = 32;
    int cursorHeight = 32;
    int cursorAmount = 16;

    // set local fields
    /* cursor images get prestored here */
    BufferedImage images      = ImageIO.read(new File("data/img/input/mouseCursor.png"));
    /* empty array needed for the images.getRGB method */
    int[]         rgbs        = new int[cursorWidth*cursorHeight];    
    /* this contains the rgb information for every pixel in every cursor */
    IntBuffer     imageBuffer = IntBuffer.allocate(images.getWidth()*images.getHeight());    
    /* this should only need a size of 16 (cursorAmount), as delayBuffer should store the delay between 2 different cursors.
     * however, if delayBuffer is smaller, the Cursor constructor throws the following error:
     * java.lang.IllegalArgumentException: width*height*numImages > images.remaining() 
    */
    IntBuffer     delayBuffer = IntBuffer.allocate(images.getWidth()*images.getHeight());
     
    // put images into imageBuffer
    for( int y = 0; y < images.getHeight()/cursorHeight; y++)
      for (int x = 0; x < images.getWidth()/cursorWidth; x++)
        if( y*(images.getWidth()/cursorWidth)+x < cursorAmount)
          imageBuffer.put( images.getRGB(x*cursorWidth, y*cursorHeight, cursorWidth, cursorHeight, rgbs, 0, cursorWidth));

    // put delay into delayBuffer
    for(int i = 0; i < cursorAmount; i++)
      delayBuffer.put(1000);  // changing this value does nothing at all
    
    // rewind buffers
    imageBuffer.rewind();
    delayBuffer.rewind();
    // return new cursor
    return new Cursor( cursorWidth,cursorHeight, // width and height of a single cursor image
                              0, cursorHeight-1,           // relative hotspot of cursor
                              cursorAmount,                // amounts of cursors (numImages)
                              imageBuffer,                   // imageBuffer containing the cursor images  
                              delayBuffer);                  // delayBuffer containing the delays between 2 different images
    
    /* note the following oddities:
     * - when cursorAmount is 1 and delayBuffer null the cursor is displayed correctly
     * - when imageBuffer and delayBuffer are swapped in the constructor (!), the different cursor images are displayed, but without delay between them. (changing the delay values doesn't do anything)
     * - when delayBuffer is allocated with a size smaller then width*height it throws this error: java.lang.IllegalArgumentException: width*height*numImages > images.remaining()
     */
  }


- This code is working as it should, only when I create a single image cursor (numImages = 1, delayBuffer = null).

- When I allocate the size for the delayBuffer with equivalent to the numImages (1 delayBuffer entry for each image, thats how it should be, right?), I get this error: "java.lang.IllegalArgumentException: width*height*numImages > images.remaining()". This is an error thrown by the Cursor constructor (see below), that should not be related to the delayBuffer Size at all!

- When I allocate the size for the delayBuffer equivalent to the allocated size for the imageBuffer, the code is compiling, but not drawing any cursor at all

- Here is the weirdest thing: When I switch delayBuffer with imageBuffer in the Constructor (meaning I give the imageBuffer I create to the Cursor Constructor as DelayBuffer and the delayBuffer as ImageBuffer), it is drawing all cursors from the imageBuffer, but without delay between them (they just flash). Nothing I do with the delayBuffer changes anything (I tried values from Integer.MIN_VALUE to Integer.MAX_VALUE, no difference at all, cursor images just flash through). To make it more clear, this happens, when I call the constructor like this:
new Cursor( cursorWidth,cursorHeight,  // width and height of a single cursor image
                 0, cursorHeight-1,            // relative hotspot of cursor
                 cursorAmount,                 // amounts of cursors (numImages)
                 delayBuffer,                    // imageBuffer containing the cursor images  
                 imageBuffer);                  // delayBuffer containing the delays between 2 different images





Here is the Cursor Constructor source code
public Cursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, IntBuffer delays) throws LWJGLException {
      synchronized (OpenGLPackageAccess.global_lock) {
         if ((getCapabilities() & CURSOR_ONE_BIT_TRANSPARENCY) == 0)
            throw new LWJGLException("Native cursors not supported");
         images = NondirectBufferWrapper.wrapBuffer(images, width*height*numImages);
         if (delays != null)
            delays = NondirectBufferWrapper.wrapBuffer(delays, numImages);
         if (!Mouse.isCreated())
            throw new IllegalStateException("Mouse must be created before creating cursor objects");
         if (width*height*numImages > images.remaining())
            throw new IllegalArgumentException("width*height*numImages > images.remaining()");
         if (xHotspot >= width || xHotspot < 0)
            throw new IllegalArgumentException("xHotspot > width || xHotspot < 0");
         if (yHotspot >= height || yHotspot < 0)
            throw new IllegalArgumentException("yHotspot > height || yHotspot < 0");

         Sys.initialize();

         // Hmm
         yHotspot = height - 1 - yHotspot;

         // create cursor (or cursors if multiple images supplied)
         cursors = createCursors(width, height, xHotspot, yHotspot, numImages, images, delays);
      }
  }


Please have a look at this, I am really growing desperate here

MonsieurBlutbad

The bug should be reproducable, as a friend of mine tried around with the Cursor constructor and got the same odd results.

Also, I don't know if I have pointed this out clear enough: It appears as somewhere in Cursor, the delayBuffer and the imageBuffer get mixed up, as it uses the data from the delayBuffer to create the cursor image. The imageBuffer seems to be not used at all, except when you use only a single image cursor without animation or delay.

MonsieurBlutbad


roguedjack

Hello,

I had the same exception thrown at me when trying to use animated cursors :
IllegalArgumentException("width*height*numImages > images.remaining()")


I found a fix but you have to recompile lwjgl Cursor.java. Just remove/comment out the code in Cursor constructor that wraps the IntBuffers.
public Cursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, IntBuffer delays) throws LWJGLException {
...
			// FIX			
			/*images = NondirectBufferWrapper.wrapBuffer(images, width*height*numImages);
			if (delays != null) {
				delays = NondirectBufferWrapper.wrapBuffer(delays, numImages);
			}*/
			// END FIX		
...
}


It works fine on Win 7 64 bits Java 6. No idea how viable is the fix though. And i feel dirty having to touch lwjgl code  :P

spasi

This should be fixed in the next build.