Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8313698

BaseMultiResolutionImage doesn't work as icon of java.awt.Window on Linux

XMLWordPrintable

    • x86_64
    • linux

      ADDITIONAL SYSTEM INFORMATION :
      Tested on:

      * Linux:
      ** Java 11.0.19 ❌ Fail
      ** Java 17.0.7 ❌ Fail
      ** Java 19.0.2 ❌ Fail
      ** Java 20.0.1-testing ❌ Fail
      * Windows:
      ** 11.0.17 ✅ Success
      ** 15.0.2 ✅ Success
      ** 18.0.2.1 ✅ Success
      * macOS: not applicable, because JFrame's icons aren't rendered there. Instead, only Taskbar.setIconImage is rendered in the Dock.

      A DESCRIPTION OF THE PROBLEM :
      When a {{BaseMultiResolutionImage}} is used as a parameter to method {{java.awt.Window#setIconImage}}, the resources of the underlying images don't get loaded properly, and the icon falls back to the default Duke icon. This is only observed for the very first call to method {{setIconImage}}.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Launch class MultiResolutionImageBug from the reproducer
      2. Click on button "Show dialog with BaseMultiResolutionImage"
      3. Observe the icon of the dialog window
      4. Click the button "Show dialog with BaseMultiResolutionImage" again
      5. Observe the icon in the second dialog window

      The reproducer code is also [available on GitHub|https://github.com/rybak/foobar], with necessary resource files present.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The icon from the resource files is seen in steps 3 and 5.
      ACTUAL -
      Default Duke mascot icon is used in step 3. The proper icon is only used when the icon is being used the second time (step 5) and any subsequent times.

      ---------- BEGIN SOURCE ----------
      package swing;

      import org.jetbrains.annotations.NotNull;

      import javax.swing.*;
      import java.awt.*;
      import java.awt.event.ActionEvent;
      import java.awt.event.KeyEvent;
      import java.awt.image.BaseMultiResolutionImage;
      import java.net.URL;
      import java.util.Objects;

      /**
       * Tested on:
       * <ul>
       * <li>
       * Linux:
       * <ul>
       * <li>Java 11.0.19 ❌ Fail</li>
       * <li>Java 17.0.7 ❌ Fail</li>
       * <li>Java 19.0.2 ❌ Fail</li>
       * <li>Java 20.0.1-testing ❌ Fail</li>
       * </ul>
       * </li>
       * <li>
       * Windows:
       * <ul>
       * <li>11.0.17 ✅ Success</li>
       * <li>15.0.2 ✅ Success</li>
       * <li>18.0.2.1 ✅ Success</li>
       * </ul>
       * </li>
       * <li>
       * macOS: not applicable, because JFrame's icons aren't rendered there.
       * Instead, only {@link Taskbar#setIconImage} is rendered in the Dock.
       * </li>
       * </ul>
       */
      public class MultiResolutionImageBug {
      public static final String ICON_64 = "icon64x64.png";
      public static final String ICON_32 = "icon32x32.png";
      private int attemptsCounter;
      private JLabel counterLabel;
      private JFrame mainWindow;

      public static void main(String[] args) {
      SwingUtilities.invokeLater(() -> new MultiResolutionImageBug().go());
      }

      /**
      * On Linux, first attempt always fails, because image isn't loaded yet,
      * and {@code sun.awt.IconInfo#isValid} returns {@code false}.
      */
      private void showDialog(JFrame mainWindow, boolean multi) {
      attemptsCounter++;
      counterLabel.setText(getLabelText());
      JDialog dialog = new JDialog(mainWindow, "Dialog attempt #" + attemptsCounter, Dialog.ModalityType.MODELESS);
      dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

      dialog.setIconImage(multi ? getMultiResolutionImage() : getFixedResolutionImage());

      dialog.setLocation(200, 200);
      dialog.setSize(400, 400);
      final String explanation;
      if (!multi) {
      explanation = "Fixed resolution works fine.";
      } else {
      if (attemptsCounter == 1) {
      explanation = "On Linux, on the first attempt, the Duke icon is shown.";
      } else {
      explanation = "On subsequent attempts, the correct icon is shown.";
      }
      }
      JTextArea textDisplay = new JTextArea(explanation);
      dialog.getContentPane().add(textDisplay);
      setUpEscapeKeyClosing(dialog, textDisplay);
      dialog.setVisible(true);
      }

      private void go() {
      attemptsCounter = 0;
      String windowTitle = "Bug demo on Java version " + System.getProperty("java.version");
      System.out.println(windowTitle);
      mainWindow = new JFrame(windowTitle);
      mainWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      JPanel contentPane = new JPanel(new GridLayout(3, 1));
      mainWindow.setContentPane(contentPane);

      counterLabel = new JLabel(getLabelText());
      contentPane.add(counterLabel);

      JButton showMultiIconDialogButton = new JButton("Show dialog with BaseMultiResolutionImage");
      showMultiIconDialogButton.addActionListener(ignored -> showDialog(mainWindow, true));
      contentPane.add(showMultiIconDialogButton);
      JButton showFixedIconDialogButton = new JButton("Show dialog with fixed resolution Image");
      showFixedIconDialogButton.addActionListener(ignored -> showDialog(mainWindow, false));
      contentPane.add(showFixedIconDialogButton);

      mainWindow.setSize(400, 400);
      mainWindow.setVisible(true);
      }

      @NotNull
      private String getLabelText() {
      return "Attempts: " + attemptsCounter;
      }

      private Image getMultiResolutionImage() {
      Image icon32 = getFixedResolutionImage(ICON_32);
      Image icon64 = getFixedResolutionImage(ICON_64);
      return new BaseMultiResolutionImage(icon32, icon64);
      }

      private Image getFixedResolutionImage() {
      return getFixedResolutionImage(ICON_64);
      }

      private Image getFixedResolutionImage(String filename) {
      URL url = Objects.requireNonNull(getClass().getResource(filename));
      return Toolkit.getDefaultToolkit().getImage(url);
      }

      private void setUpEscapeKeyClosing(JDialog d, JComponent c) {
      Object escapeCloseActionKey = new Object();
      c.getActionMap().put(escapeCloseActionKey, new AbstractAction() {
      @Override
      public void actionPerformed(ActionEvent e) {
      d.dispose();
      }
      });
      c.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), escapeCloseActionKey);
      }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Method {{java.awt.Window#setIconImages}} with takes a {{List}} of images isn't affected.

      FREQUENCY : always


        1. Capture_Ubuntu.PNG
          34 kB
          Praveen Narayanaswamy
        2. Capture_Windows.PNG
          31 kB
          Praveen Narayanaswamy
        3. icon32x32.png
          6 kB
          Praveen Narayanaswamy
        4. icon64x64.png
          5 kB
          Praveen Narayanaswamy
        5. MultiResolutionImageBug.java
          4 kB
          Praveen Narayanaswamy

            rkannathpari Renjith Kannath Pariyangad
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: