8146321: [macosx] JInternalFrame frame icon in wrong position on Mac L&F if icon is not ImageIcon
authoraniyogi
Wed, 17 Feb 2016 11:44:07 +0530
changeset 36453 fdbaecdd2ace
parent 36452 a96686eca71d
child 36454 d2853d1fc614
8146321: [macosx] JInternalFrame frame icon in wrong position on Mac L&F if icon is not ImageIcon Reviewed-by: serb, alexsch, rchamyal
jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaInternalFrameBorder.java
jdk/test/javax/swing/JInternalFrame/8146321/JInternalFrameIconTest.java
--- a/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaInternalFrameBorder.java	Tue Feb 16 19:38:26 2016 -0600
+++ b/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaInternalFrameBorder.java	Wed Feb 17 11:44:07 2016 +0530
@@ -40,6 +40,7 @@
 
 import com.apple.laf.AquaUtils.RecyclableSingleton;
 import com.apple.laf.AquaInternalFrameBorderMetrics;
+import java.awt.geom.AffineTransform;
 
 public class AquaInternalFrameBorder implements Border, UIResource {
     private static final int kCloseButton = 0;
@@ -309,18 +310,40 @@
         return isInsideYButtonArea(i, y) && x >= startX && x <= endX;
     }
 
-    protected void paintTitleIcon(final Graphics g, final JInternalFrame frame, final int x, final int y) {
-        Icon icon = frame.getFrameIcon();
-        if (icon == null) icon = UIManager.getIcon("InternalFrame.icon");
-        if (icon == null) return;
+    protected void paintTitleIcon(final Graphics g, final JInternalFrame frame,
+            final int x, final int y) {
 
-        // Resize to 16x16 if necessary.
-        if (icon instanceof ImageIcon && (icon.getIconWidth() > sMaxIconWidth || icon.getIconHeight() > sMaxIconHeight)) {
-            final Image img = ((ImageIcon)icon).getImage();
-            ((ImageIcon)icon).setImage(img.getScaledInstance(sMaxIconWidth, sMaxIconHeight, Image.SCALE_SMOOTH));
+        Icon icon = frame.getFrameIcon();
+        if (icon == null) {
+            icon = UIManager.getIcon("InternalFrame.icon");
+        }
+
+        if (icon == null) {
+            return;
         }
 
-        icon.paintIcon(frame, g, x, y);
+        if (icon.getIconWidth() > sMaxIconWidth
+                || icon.getIconHeight() > sMaxIconHeight) {
+            final Graphics2D g2 = (Graphics2D) g;
+            final AffineTransform savedAT = g2.getTransform();
+            double xScaleFactor = (double) sMaxIconWidth / icon.getIconWidth();
+            double yScaleFactor = (double) sMaxIconHeight / icon.getIconHeight();
+
+            //Coordinates are after a translation hence relative origin shifts
+            g2.translate(x, y);
+
+            //scaling factor is needed to scale while maintaining aspect ratio
+            double scaleMaintainAspectRatio = Math.min(xScaleFactor, yScaleFactor);
+
+            //minimum value is taken to set to a maximum Icon Dimension
+            g2.scale(scaleMaintainAspectRatio, scaleMaintainAspectRatio);
+
+            icon.paintIcon(frame, g2, 0, 0);
+            g2.setTransform(savedAT);
+
+        } else {
+            icon.paintIcon(frame, g, x, y);
+        }
     }
 
     protected int getIconWidth(final JInternalFrame frame) {
@@ -330,9 +353,7 @@
         if (icon == null) {
             icon = UIManager.getIcon("InternalFrame.icon");
         }
-
-        if (icon != null && icon instanceof ImageIcon) {
-            // Resize to 16x16 if necessary.
+        if (icon != null) {
             width = Math.min(icon.getIconWidth(), sMaxIconWidth);
         }
 
@@ -346,9 +367,7 @@
         if (icon == null) {
             icon = UIManager.getIcon("InternalFrame.icon");
         }
-
-        if (icon != null && icon instanceof ImageIcon) {
-            // Resize to 16x16 if necessary.
+        if (icon != null) {
             height = Math.min(icon.getIconHeight(), sMaxIconHeight);
         }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JInternalFrame/8146321/JInternalFrameIconTest.java	Wed Feb 17 11:44:07 2016 +0530
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8146321
+ * @summary verifies JInternalFrame Icon and ImageIcon
+ * @library ../../regtesthelpers
+ * @build Util
+ * @run main JInternalFrameIconTest
+ */
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.image.BufferedImage;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JDesktopPane;
+import javax.swing.JFrame;
+import javax.swing.JInternalFrame;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+
+public class JInternalFrameIconTest {
+
+    private static JFrame frame;
+    private static JDesktopPane desktopPane;
+    private static JInternalFrame internalFrame;
+    private static ImageIcon titleImageIcon;
+    private static Icon titleIcon;
+    private static BufferedImage imageIconImage;
+    private static BufferedImage iconImage;
+
+    private static Robot robot;
+
+    public static void main(String[] args) throws Exception {
+        robot = new Robot();
+        robot.delay(2000);
+        UIManager.LookAndFeelInfo[] lookAndFeelArray
+                = UIManager.getInstalledLookAndFeels();
+        for (UIManager.LookAndFeelInfo lookAndFeelItem : lookAndFeelArray) {
+            executeCase(lookAndFeelItem.getClassName());
+        }
+
+    }
+
+    private static void executeCase(String lookAndFeelString) throws Exception {
+        if (tryLookAndFeel(lookAndFeelString)) {
+            createImageIconUI(lookAndFeelString);
+            robot.delay(1000);
+            getImageIconBufferedImage();
+            robot.waitForIdle();
+            cleanUp();
+            robot.waitForIdle();
+
+            createIconUI(lookAndFeelString);
+            robot.delay(1000);
+            getIconBufferedImage();
+            robot.waitForIdle();
+            cleanUp();
+            robot.waitForIdle();
+            testIfSame();
+            robot.waitForIdle();
+        }
+
+    }
+
+    private static void createImageIconUI(final String lookAndFeelString)
+            throws Exception {
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                desktopPane = new JDesktopPane();
+                internalFrame = new JInternalFrame();
+                frame = new JFrame();
+                internalFrame.setTitle(lookAndFeelString);
+                titleImageIcon = new ImageIcon() {
+                    @Override
+                    public int getIconWidth() {
+                        return 16;
+                    }
+
+                    @Override
+                    public int getIconHeight() {
+                        return 16;
+                    }
+
+                    @Override
+                    public void paintIcon(
+                            Component c, Graphics g, int x, int y) {
+                        g.setColor(java.awt.Color.black);
+                        g.fillRect(x, y, 16, 16);
+                    }
+                };
+                internalFrame.setFrameIcon(titleImageIcon);
+                internalFrame.setSize(500, 200);
+                internalFrame.setVisible(true);
+                desktopPane.add(internalFrame);
+
+                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+                frame.getContentPane().setLayout(new BorderLayout());
+                frame.getContentPane().add(desktopPane, "Center");
+                frame.setSize(500, 500);
+                frame.setLocationRelativeTo(null);
+                frame.setVisible(true);
+                frame.toFront();
+            }
+        });
+    }
+
+    private static void createIconUI(final String lookAndFeelString)
+            throws Exception {
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                desktopPane = new JDesktopPane();
+                internalFrame = new JInternalFrame();
+                frame = new JFrame();
+                internalFrame.setTitle(lookAndFeelString);
+                titleIcon = new Icon() {
+                    @Override
+                    public int getIconWidth() {
+                        return 16;
+                    }
+
+                    @Override
+                    public int getIconHeight() {
+                        return 16;
+                    }
+
+                    @Override
+                    public void paintIcon(
+                            Component c, Graphics g, int x, int y) {
+                        g.setColor(java.awt.Color.black);
+                        g.fillRect(x, y, 16, 16);
+                    }
+                };
+                internalFrame.setFrameIcon(titleIcon);
+                internalFrame.setSize(500, 200);
+                internalFrame.setVisible(true);
+                desktopPane.add(internalFrame);
+
+                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+                frame.getContentPane().setLayout(new BorderLayout());
+                frame.getContentPane().add(desktopPane, "Center");
+                frame.setSize(500, 500);
+                frame.setLocationRelativeTo(null);
+                frame.setVisible(true);
+                frame.toFront();
+            }
+        });
+    }
+
+    private static void getImageIconBufferedImage() throws Exception {
+        Point point = internalFrame.getLocationOnScreen();
+        Rectangle rect = internalFrame.getBounds();
+        Rectangle captureRect = new Rectangle(
+                point.x + internalFrame.getInsets().left,
+                point.y,
+                rect.width,
+                internalFrame.getInsets().top);
+        imageIconImage
+                = robot.createScreenCapture(captureRect);
+    }
+
+    private static void getIconBufferedImage() throws Exception {
+        Point point = internalFrame.getLocationOnScreen();
+        Rectangle rect = internalFrame.getBounds();
+        Rectangle captureRect = new Rectangle(
+                point.x + internalFrame.getInsets().left,
+                point.y,
+                rect.width,
+                internalFrame.getInsets().top);
+        iconImage
+                = robot.createScreenCapture(captureRect);
+    }
+
+    private static void testIfSame() throws Exception {
+        if (!bufferedImagesEqual(imageIconImage, iconImage)) {
+            System.err.println("ERROR: icon and imageIcon not same.");
+        } else {
+            System.out.println("SUCCESS: icon and imageIcon same.");
+        }
+    }
+
+    private static boolean bufferedImagesEqual(
+            BufferedImage bufferedImage1, BufferedImage bufferedImage2) {
+        boolean flag = true;
+
+        if (bufferedImage1.getWidth() == bufferedImage2.getWidth()
+                && bufferedImage1.getHeight() == bufferedImage2.getHeight()) {
+            final int colorTolerance = 25;
+            final int mismatchTolerance = (int) (0.1
+                    * bufferedImage1.getWidth() * bufferedImage1.getHeight());
+            int mismatchCounter = 0;
+            for (int x = 0; x < bufferedImage1.getWidth(); x++) {
+                for (int y = 0; y < bufferedImage1.getHeight(); y++) {
+
+                    int color1 = bufferedImage1.getRGB(x, y);
+                    int red1 = (color1 >> 16) & 0x000000FF;
+                    int green1 = (color1 >> 8) & 0x000000FF;
+                    int blue1 = (color1) & 0x000000FF;
+
+                    int color2 = bufferedImage2.getRGB(x, y);
+                    int red2 = (color2 >> 16) & 0x000000FF;
+                    int green2 = (color2 >> 8) & 0x000000FF;
+                    int blue2 = (color2) & 0x000000FF;
+                    if (red1 != red2 || green1 != green2 || blue1 != blue2) {
+                        ++mismatchCounter;
+                        if ((Math.abs(red1 - red2) > colorTolerance)
+                                || (Math.abs(green1 - green2) > colorTolerance)
+                                || (Math.abs(blue1 - blue2) > colorTolerance)) {
+
+                            flag = false;
+                        }
+                    }
+                }
+            }
+            if (mismatchCounter > mismatchTolerance) {
+                flag = false;
+            }
+        } else {
+            System.err.println("ERROR: size is different");
+            flag = false;
+        }
+        return flag;
+    }
+
+    private static void cleanUp() throws Exception {
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                frame.dispose();
+            }
+        });
+    }
+
+    private static boolean tryLookAndFeel(String lookAndFeelString)
+            throws Exception {
+        try {
+            UIManager.setLookAndFeel(
+                    lookAndFeelString);
+
+        } catch (UnsupportedLookAndFeelException
+                | ClassNotFoundException
+                | InstantiationException
+                | IllegalAccessException e) {
+            return false;
+        }
+        return true;
+    }
+}