diff --git a/src/macosx/classes/com/apple/laf/AquaIcon.java b/src/macosx/classes/com/apple/laf/AquaIcon.java index fbc38d849d5d50ccf3b97642e083783458dac585..576661be4173b59eda4bff30380add6f3e33c459 100644 --- a/src/macosx/classes/com/apple/laf/AquaIcon.java +++ b/src/macosx/classes/com/apple/laf/AquaIcon.java @@ -295,7 +295,14 @@ public class AquaIcon { } Image createImage() { - return AquaUtils.getCImageCreator().createSystemImageFromSelector(selector, getIconWidth(), getIconHeight()); + int w = getIconWidth(); + int h = getIconHeight(); + return new AquaImageFactory.MultiResolutionIconImage( + AquaUtils.getCImageCreator().createSystemImageFromSelector( + selector, w, h), + AquaUtils.getCImageCreator().createSystemImageFromSelector( + selector, 2 * w, 2 * h) + ); } } } diff --git a/src/macosx/classes/com/apple/laf/AquaImageFactory.java b/src/macosx/classes/com/apple/laf/AquaImageFactory.java index 63ee3cfb5d3fcef852c5fcd5be01dc70c9d64e9d..7343ae3c4d6dcc46213b89ccd3c17af68f4c5e33 100644 --- a/src/macosx/classes/com/apple/laf/AquaImageFactory.java +++ b/src/macosx/classes/com/apple/laf/AquaImageFactory.java @@ -46,6 +46,9 @@ import com.apple.laf.AquaIcon.JRSUIControlSpec; import com.apple.laf.AquaIcon.SystemIcon; import com.apple.laf.AquaUtils.RecyclableObject; import com.apple.laf.AquaUtils.RecyclableSingleton; +import java.util.Arrays; +import java.util.List; +import sun.awt.image.MultiResolutionImage; public class AquaImageFactory { public static IconUIResource getConfirmImageIcon() { @@ -120,28 +123,48 @@ public class AquaImageFactory { private static final int kAlertIconSize = 64; static IconUIResource getAppIconCompositedOn(final Image background) { - final double scaleFactor = 1.0; // revise for HiDPI - final int kAlertSubIconSize = (int)(kAlertIconSize * 0.5 * scaleFactor); - final int kAlertSubIconInset = (int)(kAlertIconSize * scaleFactor) - kAlertSubIconSize; - final Icon smallAppIconScaled = new AquaIcon.CachingScalingIcon(kAlertSubIconSize, kAlertSubIconSize) { - Image createImage() { - return getThisApplicationsIcon(kAlertSubIconSize, kAlertSubIconSize); - } - }; + final BufferedImage iconImage = getAppIconImageCompositedOn(background, 1); + + if (background instanceof MultiResolutionIconImage) { + BufferedImage background2x + = ((MultiResolutionIconImage) background).resolutionVariant; + BufferedImage icon2xImage = getAppIconImageCompositedOn(background2x, 2); + + return new IconUIResource(new ImageIcon( + new MultiResolutionIconImage(iconImage, icon2xImage))); + } + return new IconUIResource(new ImageIcon(iconImage)); + } + + static BufferedImage getAppIconImageCompositedOn(final Image background, int scaleFactor) { + + final int scaledAlertIconSize = kAlertIconSize * scaleFactor; + final int kAlertSubIconSize = (int) (scaledAlertIconSize * 0.5); + final int kAlertSubIconInset = scaledAlertIconSize - kAlertSubIconSize; + final Icon smallAppIconScaled = new AquaIcon.CachingScalingIcon( + kAlertSubIconSize, kAlertSubIconSize) { + Image createImage() { + return getThisApplicationsIcon(kAlertSubIconSize, kAlertSubIconSize); + } + }; - final BufferedImage image = new BufferedImage(kAlertIconSize, kAlertIconSize, BufferedImage.TYPE_INT_ARGB); + final BufferedImage image = new BufferedImage(scaledAlertIconSize, + scaledAlertIconSize, BufferedImage.TYPE_INT_ARGB); final Graphics g = image.getGraphics(); - g.drawImage(background, 0, 0, (int)(kAlertIconSize * scaleFactor), (int)(kAlertIconSize * scaleFactor), null); + g.drawImage(background, 0, 0, + scaledAlertIconSize, scaledAlertIconSize, null); if (g instanceof Graphics2D) { // improves icon rendering quality in Quartz - ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); } - smallAppIconScaled.paintIcon(null, g, kAlertSubIconInset, kAlertSubIconInset); + smallAppIconScaled.paintIcon(null, g, + kAlertSubIconInset, kAlertSubIconInset); g.dispose(); - return new IconUIResource(new ImageIcon(image)); + return image; } public static IconUIResource getTreeFolderIcon() { @@ -484,4 +507,29 @@ public class AquaImageFactory { public static Color getSelectionInactiveForegroundColorUIResource() { return new SystemColorProxy(LWCToolkit.getAppleColor(LWCToolkit.INACTIVE_SELECTION_FOREGROUND_COLOR)); } + + static class MultiResolutionIconImage extends BufferedImage + implements MultiResolutionImage { + + BufferedImage resolutionVariant; + + public MultiResolutionIconImage(BufferedImage image, BufferedImage resolutionVariant) { + super(image.getWidth(), image.getHeight(), image.getType()); + this.resolutionVariant = resolutionVariant; + Graphics g = getGraphics(); + g.drawImage(image, 0, 0, null); + g.dispose(); + } + + @Override + public Image getResolutionVariant(int width, int height) { + return ((width <= getWidth() && height <= getHeight())) + ? this : resolutionVariant; + } + + @Override + public List getResolutionVariants() { + return Arrays.asList(this, resolutionVariant); + } + } } diff --git a/test/javax/swing/JOptionPane/8024926/bug8024926.html b/test/javax/swing/JOptionPane/8024926/bug8024926.html new file mode 100644 index 0000000000000000000000000000000000000000..e63e0ea768be7909a2c29c49887baa1fe2380136 --- /dev/null +++ b/test/javax/swing/JOptionPane/8024926/bug8024926.html @@ -0,0 +1,32 @@ + + + + + High resolution icon test, bug ID 8024926 + + + +

See the dialog box (usually in upper left corner) for instructions

+ + diff --git a/test/javax/swing/JOptionPane/8024926/bug8024926.java b/test/javax/swing/JOptionPane/8024926/bug8024926.java new file mode 100644 index 0000000000000000000000000000000000000000..42f976b03c009b785cd7624d5ba685695def7efb --- /dev/null +++ b/test/javax/swing/JOptionPane/8024926/bug8024926.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2013, 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. + */ +import java.awt.BorderLayout; +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.TextArea; +import javax.swing.JApplet; +import javax.swing.JOptionPane; +import sun.awt.OSInfo; + +/** + * @test + * @bug 8024926 + * @summary [macosx] AquaIcon HiDPI support + * @author Alexander Scherbatiy + * @run applet/manual=yesno bug8024926.html + */ +public class bug8024926 extends JApplet { + //Declare things used in the test, like buttons and labels here + + public void init() { + //Create instructions for the user here, as well as set up + // the environment -- set the layout manager, add buttons, + // etc. + this.setLayout(new BorderLayout()); + + + if (OSInfo.getOSType().equals(OSInfo.OSType.MACOSX)) { + String[] instructions = { + "Verify that high resolution system icons are used" + + " in JOptionPane on HiDPI displays.", + "1) Run the test on Retina display or enable the Quartz Debug" + + " and select the screen resolution with (HiDPI) label", + "2) Check that the error icon on the JOptionPane is smooth", + "If so, press PASS, else press FAIL." + }; + Sysout.createDialogWithInstructions(instructions); + + } else { + String[] instructions = { + "This test is not applicable to the current platform. Press PASS." + }; + Sysout.createDialogWithInstructions(instructions); + } + + + }//End init() + + public void start() { + //Get things going. Request focus, set size, et cetera + setSize(200, 200); + setVisible(true); + validate(); + EventQueue.invokeLater(new Runnable() { + + public void run() { + createAndShowGUI(); + } + }); + }// start() + + //The rest of this class is the actions which perform the test... + //Use Sysout.println to communicate with the user NOT System.out!! + //Sysout.println ("Something Happened!"); + private static void createAndShowGUI() { + JOptionPane.showMessageDialog(null, + "Icons should have high resolution.", + "High resolution icon test.", + JOptionPane.ERROR_MESSAGE); + } +}// class BlockedWindowTest + +/* Place other classes related to the test after this line */ +/** + * ************************************************** + * Standard Test Machinery DO NOT modify anything below -- it's a standard chunk + * of code whose purpose is to make user interaction uniform, and thereby make + * it simpler to read and understand someone else's test. + * ************************************************** + */ +/** + * This is part of the standard test machinery. It creates a dialog (with the + * instructions), and is the interface for sending text messages to the user. To + * print the instructions, send an array of strings to Sysout.createDialog + * WithInstructions method. Put one line of instructions per array entry. To + * display a message for the tester to see, simply call Sysout.println with the + * string to be displayed. This mimics System.out.println but works within the + * test harness as well as standalone. + */ +class Sysout { + + private static TestDialog dialog; + + public static void createDialogWithInstructions(String[] instructions) { + dialog = new TestDialog(new Frame(), "Instructions"); + dialog.printInstructions(instructions); + dialog.setVisible(true); + println("Any messages for the tester will display here."); + } + + public static void createDialog() { + dialog = new TestDialog(new Frame(), "Instructions"); + String[] defInstr = {"Instructions will appear here. ", ""}; + dialog.printInstructions(defInstr); + dialog.setVisible(true); + println("Any messages for the tester will display here."); + } + + public static void printInstructions(String[] instructions) { + dialog.printInstructions(instructions); + } + + public static void println(String messageIn) { + dialog.displayMessage(messageIn); + } +}// Sysout class + +/** + * This is part of the standard test machinery. It provides a place for the test + * instructions to be displayed, and a place for interactive messages to the + * user to be displayed. To have the test instructions displayed, see Sysout. To + * have a message to the user be displayed, see Sysout. Do not call anything in + * this dialog directly. + */ +class TestDialog extends Dialog { + + TextArea instructionsText; + TextArea messageText; + int maxStringLength = 80; + + //DO NOT call this directly, go through Sysout + public TestDialog(Frame frame, String name) { + super(frame, name); + int scrollBoth = TextArea.SCROLLBARS_BOTH; + instructionsText = new TextArea("", 15, maxStringLength, scrollBoth); + add("North", instructionsText); + + messageText = new TextArea("", 5, maxStringLength, scrollBoth); + add("Center", messageText); + + pack(); + + setVisible(true); + }// TestDialog() + + //DO NOT call this directly, go through Sysout + public void printInstructions(String[] instructions) { + //Clear out any current instructions + instructionsText.setText(""); + + //Go down array of instruction strings + + String printStr, remainingStr; + for (int i = 0; i < instructions.length; i++) { + //chop up each into pieces maxSringLength long + remainingStr = instructions[ i]; + while (remainingStr.length() > 0) { + //if longer than max then chop off first max chars to print + if (remainingStr.length() >= maxStringLength) { + //Try to chop on a word boundary + int posOfSpace = remainingStr.lastIndexOf(' ', maxStringLength - 1); + + if (posOfSpace <= 0) { + posOfSpace = maxStringLength - 1; + } + + printStr = remainingStr.substring(0, posOfSpace + 1); + remainingStr = remainingStr.substring(posOfSpace + 1); + } //else just print + else { + printStr = remainingStr; + remainingStr = ""; + } + + instructionsText.append(printStr + "\n"); + + }// while + + }// for + + }//printInstructions() + + //DO NOT call this directly, go through Sysout + public void displayMessage(String messageIn) { + messageText.append(messageIn + "\n"); + System.out.println(messageIn); + } +}// TestDialog class