JDK-6528166 : regression: calling drawImage() results in java.lang.NullPointerException in jdk7.0 b08
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 7
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2007-02-24
  • Updated: 2011-03-08
  • Resolved: 2011-03-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b10Fixed
Related Reports
Relates :  
Relates :  
Description
I have frame which has button to invoke a file dialog & select any image that can be draw on a canvas. When i try to display the animated image , i am getting java.lang.NullPointerException. This works fine jdk7.0 b07 , but fails in jdk7.0 b08. Hence this is a regression. This happens only in windows, it works fine in solaris.

The problem can be reproduced in two ways.
First method to Reproduce the problem
-----------------------------------
1) Run the attached program.
2) Click on "Get Dialog" button. Select animdog.gif file attached. Observe that animdog.gif is seen on the canvas.
3) Click on "Get Dialog" button. Select flower.gif file attached. Observe that flower is seen on the canvas.
4) Repeat step (2). If you see the java.lang.NullPointer exception , then the bug is reproduced.

Second method to Reproduce the problem
---------------------------------------
1) Run the attached program.
2) Click on "Get Dialog" button. Select animdog.gif file attached. Observe that animdog.gif is seen on the canvas.
3) Resize the frame. If you see the java.lang.NullPointer exception , then the bug is reproduced.
Java(TM) SE Runtime Environment (build 1.7.0-ea-b08)
Java HotSpot(TM) Client VM (build 1.7.0-ea-b08, mixed mode, sharing)

Exception occurred during event dispatching:
java.lang.NullPointerException
        at sun.java2d.windows.Win32SurfaceDataProxy$Bitmask.findUnusedPixelICM(Win32SurfaceDataProxy.java:363)
        at sun.java2d.windows.Win32SurfaceDataProxy$Bitmask.findTransparentPixel(Win32SurfaceDataProxy.java:330)
        at sun.java2d.windows.Win32SurfaceDataProxy$Bitmask.validateSurfaceData(Win32SurfaceDataProxy.java:274)
        at sun.java2d.SurfaceDataProxy.replaceData(SurfaceDataProxy.java:426)
        at sun.java2d.SurfaceData.getSourceSurfaceData(SurfaceData.java:209)
        at sun.java2d.pipe.DrawImage.renderImageCopy(DrawImage.java:545)
        at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:53)
        at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:990)
        at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3023)
        at sun.awt.image.ImageRepresentation.drawToBufImage(ImageRepresentation.java:746)
        at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:997)
        at sun.java2d.pipe.ValidatePipe.copyImage(ValidatePipe.java:168)
        at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3023)
        at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3008)
        at ReproduceException$ImageCanvas.paint(ReproduceException.java:64)
        at sun.awt.RepaintArea.paintComponent(RepaintArea.java:248)
        at sun.awt.RepaintArea.paint(RepaintArea.java:224)
        at sun.awt.windows.WComponentPeer.handleEvent(WComponentPeer.java:301)
        at java.awt.Component.dispatchEventImpl(Component.java:4481)
        at java.awt.Component.dispatchEvent(Component.java:4235)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:177)
        at java.awt.Dialog$1.run(Dialog.java:1045)
        at java.awt.Dialog$3.run(Dialog.java:1099)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.awt.Dialog.show(Dialog.java:1097)
        at java.awt.Component.show(Component.java:1414)
        at java.awt.Component.setVisible(Component.java:1367)
        at java.awt.Window.setVisible(Window.java:818)
        at java.awt.Dialog.setVisible(Dialog.java:984)
        at ReproduceException$1.actionPerformed(ReproduceException.java:18)
        at java.awt.Button.processActionEvent(Button.java:392)
        at java.awt.Button.processEvent(Button.java:360)
        at java.awt.Component.dispatchEventImpl(Component.java:4405)
        at java.awt.Component.dispatchEvent(Component.java:4235)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:173)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:121)

Machine details
---------------
Windows XP Home Edition
Version 2002
Service Pack 2
Intel Pentium 3 processor
730 MHz , 256 MB of RAM.
There is an another way to reproduce the same exception.
Lunch SwingSet2 in windows. You can see exception on the console.

Comments
EVALUATION Digging a little further shows some anomalies in the test case that contribute to making the root cause hard to distinguish. I instrumented the JDK a bit to see why we only ran into trouble on the second time we loaded the image and it turns out that the reason is that a new image is used every time. Actually, the fact that it is gotten from the file fresh each time shouldn't be the problem since the getImage() method tries to share images loaded from the same URL or file, but the fact that the image is greater than 75x75 and the code calls getScaledInstance on it creates a new Image instance each time it is set into the Canvas. The reason that the two images cause a problem is that the default imageUpdate method on "this" (Component.imageUpdate) will redraw the component for each newly loaded frame. When the first animated GIF is replaced, it continued to notify the component of new frames even though it wasn't the active image any more. When the second image was then created we then ended up in a situation where both were animating, and both were causing a repaint event for each frame, and so two attempts were made to render the image for each frame. As a result, the caching code started kicking in since it only attempts to cache for the second and subsequent redraw attempts per frame. And, those caching attempts led to the NPE. Resizing the frame can also cause multiple repaint events per frame of the image and thus kick in the caching code and cause the NPE as well, but they won't cause repeated NPEs like the case of loading the image twice. In any case, I was never able to reproduce the caching failure on my hardware, but the submitter took my build image and was able to demonstrate that the problem went away so it looks like the null check is the correct fix for the underlying problem. Note that I filed a few bugs on the way that we manage imageUpdate on Component based on some of the behavior I saw in this test case. One problem is that the test case constructs two "throw away" Frame objects to get the width and height. Unfortunately, these Frame objects will attach themselves as observers to the image and keep it alive (and possibly animating) forever despite the fact that they have nothing to do with the display of the image. See 6529740 for more info on this. Another problem is that the observers used in drawing the image do not fully manage the image. Using "this" as the observer works reasonably in most cases, but for an animated GIF the default implementation of imageUpdate on Component will keep the image animating forever as well. The test case should either override imageUpdate() to remove interest (return false) for any images that are not the current image being displayed, or it could call flush() on the image being replaced so that image is not animated any further.
01-03-2007

EVALUATION Well, I can see by inspecting the code that there should be a test for null in there, but the chances of getting a null at that point in the code should probably be very slim. To wit, I cannot reproduce the bug using any of the techniques mentioned in the description so I can't verify that the null test indeed fixes the bug. More information on the display environment for the test would help. What kind of framebuffer, resolution, screen depth, amount of VRAM, etc. My guess is that the theoretical conditions in which this might occur would be if we were to run out of VRAM at the moment when we are trying to cache a BITMASK transparent image, but that kind of a condition is hard to simulate reliably and it is suspicious that the submitter can so readily encounter such a situation (unless perhaps they have so little VRAM that they run out after only a very small number of images are cached...?).
28-02-2007

EVALUATION This regression was caused by the fixes for 6205557.
26-02-2007

EVALUATION It looks like if we are unable to create a cached surface on Win32 then the Bitmask caching code still tries to scan for an unused pixel. The scan requires a non-null cached surface so that it can determine which pixels will be unused on the cached surface - but the cached surface is null in this case. The Bitmask code needs to protect against a null cached surface before it calls the code to find the unused pixel.
25-02-2007