Hi, AWT-dev.

I tried to get HiDPI icons working on my Swing application on Windows 10 today 
on OpenJDK 10.0.2, and ran into what seems to be an AWT bug. See the exhibit at 
https://github.com/eirikbakke/InvalidImageVariantBugExhibit/blob/master/src/invalidimagevariant/InvalidImageVariantBugExhibit.java

Loading two differently sized images via Toolkit.getImage() and putting them 
into a BaseMultiResolutionImage yields an "IllegalArgumentException: Invalid 
Image variant" exception from SurfaceManager.getManager when the 
MultiResolutionImage is used as an icon in a JButton and the JFrame is dragged 
onto my 200%-scaling HiDPI screen. See the full stack trace pasted at the end 
of this email.

The bug seems to be due to a race condition, where SunGraphics2D.drawHiDPIImage 
may return null even when the image is in fact a MultiResolutionImage, if the 
underlying image alternatives have not yet been fully loaded by the time the 
first paint is attempted.

Waiting for the image to load using MediaTracker.waitForAll() "fixes" the 
problem for the simplest case. This is not always an option in client code, 
however; there are other MultiResolutionImage instances being created behind 
the scenes for instance when a JButton is disabled and its icon needs to be 
greyed-out.

Note that there is a timing aspect to this bug, so not all configurations might 
expose it. The bug did not appear when I disconnected my external monitor and 
_just_ used the HiDPI laptop one (not sure if this is a timing issue or 
something else).

Also note: On MacOS, Toolkit.getImage() and friends have had the ability to 
automatically detect and load HiDPI images with a "@2x" suffix in their file 
name (JDK-8011059) . Am I correct that there is no equivalent mechanism on 
Windows?

-- Eirik

Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: 
Invalid Image variant
  at 
java.desktop/sun.awt.image.SurfaceManager.getManager(SurfaceManager.java:82)
  at 
java.desktop/sun.java2d.SurfaceData.getSourceSurfaceData(SurfaceData.java:218)
  at java.desktop/sun.java2d.pipe.DrawImage.renderImageScale(DrawImage.java:635)
  at java.desktop/sun.java2d.pipe.DrawImage.tryCopyOrScale(DrawImage.java:319)
  at java.desktop/sun.java2d.pipe.DrawImage.transformImage(DrawImage.java:258)
  at java.desktop/sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:76)
  at java.desktop/sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:1027)
  at java.desktop/sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3415)
  at java.desktop/sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3391)
  at java.desktop/javax.swing.ImageIcon.paintIcon(ImageIcon.java:425)
  at 
java.desktop/javax.swing.plaf.basic.BasicButtonUI.paintIcon(BasicButtonUI.java:358)
  at 
java.desktop/javax.swing.plaf.basic.BasicButtonUI.paint(BasicButtonUI.java:275)
  at 
java.desktop/com.sun.java.swing.plaf.windows.WindowsButtonUI.paint(WindowsButtonUI.java:167)
  at java.desktop/javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
  at java.desktop/javax.swing.JComponent.paintComponent(JComponent.java:797)
  at java.desktop/javax.swing.JComponent.paint(JComponent.java:1074)
  at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:907)
  at java.desktop/javax.swing.JComponent.paint(JComponent.java:1083)
  at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:907)
  at java.desktop/javax.swing.JComponent.paint(JComponent.java:1083)
  at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:907)
  at java.desktop/javax.swing.JComponent.paint(JComponent.java:1083)
  at java.desktop/javax.swing.JLayeredPane.paint(JLayeredPane.java:590)
  at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:907)
  at java.desktop/javax.swing.JComponent.paintToOffscreen(JComponent.java:5262)
  at 
java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBufferedImpl(RepaintManager.java:1633)
  at 
java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1608)
  at 
java.desktop/javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1546)
  at java.desktop/javax.swing.RepaintManager.paint(RepaintManager.java:1313)
  at java.desktop/javax.swing.JComponent.paint(JComponent.java:1060)
  at 
java.desktop/java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39)
  at 
java.desktop/sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:78)
  at 
java.desktop/sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:115)
  at java.desktop/java.awt.Container.paint(Container.java:2000)
  at java.desktop/java.awt.Window.paint(Window.java:3940)
  at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:868)
  at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:840)
  at java.base/java.security.AccessController.doPrivileged(Native Method)
  at 
java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
  at 
java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:840)
  at 
java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:815)
  at 
java.desktop/javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:764)
  at java.desktop/javax.swing.RepaintManager.access$1200(RepaintManager.java:69)
  at 
java.desktop/javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1880)
  at 
java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
  at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
  at java.desktop/java.awt.EventQueue.access$600(EventQueue.java:97)
  at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
  at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
  at java.base/java.security.AccessController.doPrivileged(Native Method)
  at 
java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
  at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
  at 
java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
  at 
java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
  at 
java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
  at 
java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
  at 
java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
  at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

Reply via email to