8151303: [macosx] [hidpi] JButton's low-res. icon is visible when clicking on it
8156182: [macosx] HiDPI/Retina icons do not work for disabled JButton/JMenuItem etc.
Reviewed-by: flar, prr
--- a/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java Fri Aug 19 12:22:23 2016 +0530
+++ b/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java Fri Aug 19 16:48:53 2016 +0400
@@ -105,36 +105,42 @@
}
static Image generateSelectedDarkImage(final Image image) {
- final ImageProducer prod = new FilteredImageSource(image.getSource(), new IconImageFilter() {
+ final ImageFilter filter = new IconImageFilter() {
@Override
int getGreyFor(final int gray) {
return gray * 75 / 100;
}
- });
- return Toolkit.getDefaultToolkit().createImage(prod);
+ };
+ return map(image, filter);
}
static Image generateDisabledImage(final Image image) {
- final ImageProducer prod = new FilteredImageSource(image.getSource(), new IconImageFilter() {
+ final ImageFilter filter = new IconImageFilter() {
@Override
int getGreyFor(final int gray) {
return 255 - ((255 - gray) * 65 / 100);
}
- });
- return Toolkit.getDefaultToolkit().createImage(prod);
+ };
+ return map(image, filter);
}
static Image generateLightenedImage(final Image image, final int percent) {
final GrayFilter filter = new GrayFilter(true, percent);
- return (image instanceof MultiResolutionCachedImage)
- ? ((MultiResolutionCachedImage) image).map(
- rv -> generateLightenedImage(rv, filter))
- : generateLightenedImage(image, filter);
+ return map(image, filter);
+ }
+
+ static Image generateFilteredImage(Image image, ImageFilter filter) {
+ final ImageProducer prod = new FilteredImageSource(image.getSource(), filter);
+ return Toolkit.getDefaultToolkit().createImage(prod);
}
- static Image generateLightenedImage(Image image, ImageFilter filter) {
- final ImageProducer prod = new FilteredImageSource(image.getSource(), filter);
- return Toolkit.getDefaultToolkit().createImage(prod);
+ private static Image map(Image image, ImageFilter filter) {
+ if (image instanceof MultiResolutionImage) {
+ return MultiResolutionCachedImage
+ .map((MultiResolutionImage) image,
+ (img) -> generateFilteredImage(img, filter));
+ }
+ return generateFilteredImage(image, filter);
}
private abstract static class IconImageFilter extends RGBImageFilter {
--- a/jdk/src/java.desktop/share/classes/javax/swing/GrayFilter.java Fri Aug 19 12:22:23 2016 +0530
+++ b/jdk/src/java.desktop/share/classes/javax/swing/GrayFilter.java Fri Aug 19 16:48:53 2016 +0400
@@ -26,6 +26,7 @@
import java.awt.*;
import java.awt.image.*;
+import sun.awt.image.MultiResolutionCachedImage;
/**
* An image filter that "disables" an image by turning
@@ -48,7 +49,16 @@
* @param i an {@code Image} to be created as disabled
* @return the new grayscale image created from {@code i}
*/
- public static Image createDisabledImage (Image i) {
+ public static Image createDisabledImage(Image i) {
+ if (i instanceof MultiResolutionImage) {
+ return MultiResolutionCachedImage
+ .map((MultiResolutionImage) i,
+ (img) -> createDisabledImageImpl(img));
+ }
+ return createDisabledImageImpl(i);
+ }
+
+ private static Image createDisabledImageImpl(Image i) {
GrayFilter filter = new GrayFilter(true, 50);
ImageProducer prod = new FilteredImageSource(i.getSource(), filter);
Image grayImage = Toolkit.getDefaultToolkit().createImage(prod);
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionCachedImage.java Fri Aug 19 12:22:23 2016 +0530
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionCachedImage.java Fri Aug 19 16:48:53 2016 +0400
@@ -33,6 +33,7 @@
import java.util.function.Function;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
+import java.awt.image.MultiResolutionImage;
import java.awt.image.AbstractMultiResolutionImage;
public class MultiResolutionCachedImage extends AbstractMultiResolutionImage {
@@ -44,17 +45,30 @@
private int availableInfo;
public MultiResolutionCachedImage(int baseImageWidth, int baseImageHeight,
- BiFunction<Integer, Integer, Image> mapper) {
- this(baseImageWidth, baseImageHeight, new Dimension[]{new Dimension(
- baseImageWidth, baseImageHeight)
+ BiFunction<Integer, Integer, Image> mapper)
+ {
+ this(baseImageWidth, baseImageHeight,
+ new Dimension[]{new Dimension( baseImageWidth, baseImageHeight)
}, mapper);
}
public MultiResolutionCachedImage(int baseImageWidth, int baseImageHeight,
- Dimension2D[] sizes, BiFunction<Integer, Integer, Image> mapper) {
+ Dimension2D[] sizes,
+ BiFunction<Integer, Integer, Image> mapper)
+ {
+ this(baseImageWidth, baseImageHeight, sizes, mapper, true);
+ }
+
+ private MultiResolutionCachedImage(int baseImageWidth, int baseImageHeight,
+ Dimension2D[] sizes,
+ BiFunction<Integer, Integer, Image> mapper,
+ boolean copySizes)
+ {
this.baseImageWidth = baseImageWidth;
this.baseImageHeight = baseImageHeight;
- this.sizes = (sizes == null) ? null : Arrays.copyOf(sizes, sizes.length);
+ this.sizes = (copySizes && sizes != null)
+ ? Arrays.copyOf(sizes, sizes.length)
+ : sizes;
this.mapper = mapper;
}
@@ -99,6 +113,35 @@
mapper.apply(getResolutionVariant(width, height)));
}
+ public static Image map(MultiResolutionImage mrImage,
+ Function<Image, Image> mapper) {
+
+ if (mrImage instanceof MultiResolutionToolkitImage) {
+ MultiResolutionToolkitImage mrtImage =
+ (MultiResolutionToolkitImage) mrImage;
+ return MultiResolutionToolkitImage.map(mrtImage, mapper);
+ }
+
+ BiFunction<Integer, Integer, Image> sizeMapper
+ = (w, h) -> mapper.apply(mrImage.getResolutionVariant(w, h));
+
+ if (mrImage instanceof MultiResolutionCachedImage) {
+ MultiResolutionCachedImage mrcImage
+ = (MultiResolutionCachedImage) mrImage;
+
+ return new MultiResolutionCachedImage(mrcImage.baseImageWidth,
+ mrcImage.baseImageHeight,
+ mrcImage.sizes,
+ sizeMapper,
+ false);
+ }
+
+ Image image = (Image) mrImage;
+ int width = image.getWidth(null);
+ int height = image.getHeight(null);
+ return new MultiResolutionCachedImage(width, height, sizeMapper);
+ }
+
@Override
public int getWidth(ImageObserver observer) {
updateInfo(observer, ImageObserver.WIDTH);
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionToolkitImage.java Fri Aug 19 12:22:23 2016 +0530
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionToolkitImage.java Fri Aug 19 16:48:53 2016 +0400
@@ -29,6 +29,7 @@
import java.awt.image.MultiResolutionImage;
import java.util.Arrays;
import java.util.List;
+import java.util.function.Function;
import sun.awt.SoftCache;
public class MultiResolutionToolkitImage extends ToolkitImage implements MultiResolutionImage {
@@ -47,6 +48,13 @@
? this : resolutionVariant;
}
+ public static Image map(MultiResolutionToolkitImage mrImage,
+ Function<Image, Image> mapper) {
+ Image baseImage = mapper.apply(mrImage);
+ Image rvImage = mapper.apply(mrImage.resolutionVariant);
+ return new MultiResolutionToolkitImage(baseImage, rvImage);
+ }
+
private static void checkSize(double width, double height) {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException(String.format(
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/image/MultiResolutionImage/MultiResolutionDisabledImageTest.java Fri Aug 19 16:48:53 2016 +0400
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2016, 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.Color;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.Toolkit;
+import java.awt.image.BaseMultiResolutionImage;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import javax.imageio.ImageIO;
+import javax.swing.GrayFilter;
+import java.awt.image.MultiResolutionImage;
+import javax.swing.JLabel;
+
+/**
+ * @test
+ * @bug 8156182
+ * @summary [macosx] HiDPI/Retina icons do not work for disabled
+ * JButton/JMenuItem etc.
+ * @run main/othervm -Dsun.java2d.uiScale=2 MultiResolutionDisabledImageTest
+ */
+public class MultiResolutionDisabledImageTest {
+
+ private static final String IMAGE_NAME_1X = "image.png";
+ private static final String IMAGE_NAME_2X = "image@2x.png";
+ private static final int IMAGE_SIZE = 100;
+ private static final Color COLOR_1X = Color.GREEN;
+ private static final Color COLOR_2X = Color.BLUE;
+
+ public static void main(String[] args) throws Exception {
+
+ Image baseMRImage = new BaseMultiResolutionImage(createImage(1),
+ createImage(2));
+ testMRDisabledImage(baseMRImage);
+
+ saveImages();
+ Image toolkitMRImage = Toolkit.getDefaultToolkit().getImage(IMAGE_NAME_1X);
+
+ if (toolkitMRImage instanceof MultiResolutionImage) {
+ testMRDisabledImage(toolkitMRImage);
+ }
+ }
+
+ private static void testMRDisabledImage(Image image) throws Exception {
+
+ Image disabledImage = GrayFilter.createDisabledImage(image);
+ MediaTracker mediaTracker = new MediaTracker(new JLabel());
+ mediaTracker.addImage(disabledImage, 0);
+ mediaTracker.waitForID(0);
+
+ BufferedImage buffImage = new BufferedImage(IMAGE_SIZE,
+ IMAGE_SIZE,
+ BufferedImage.TYPE_INT_RGB);
+
+ int x = IMAGE_SIZE / 2;
+ int y = IMAGE_SIZE / 2;
+
+ Graphics2D g = buffImage.createGraphics();
+
+ g.scale(1, 1);
+ g.drawImage(disabledImage, 0, 0, null);
+ int rgb1x = buffImage.getRGB(x, y);
+
+ g.scale(2, 2);
+ g.drawImage(disabledImage, 0, 0, null);
+ int rgb2x = buffImage.getRGB(x, y);
+
+ g.dispose();
+
+ if (rgb1x == rgb2x) {
+ throw new RuntimeException("Disabled image is the same for the base"
+ + "image and the resolution variant");
+ }
+
+ }
+
+ private static BufferedImage createImage(int scale) throws Exception {
+ BufferedImage image = new BufferedImage(scale * 200, scale * 300,
+ BufferedImage.TYPE_INT_RGB);
+ Graphics g = image.createGraphics();
+ g.setColor(scale == 1 ? COLOR_1X : COLOR_2X);
+ g.fillRect(0, 0, scale * 200, scale * 300);
+ g.dispose();
+ return image;
+ }
+
+ private static void saveImages() throws Exception {
+ saveImage(createImage(1), IMAGE_NAME_1X);
+ saveImage(createImage(2), IMAGE_NAME_2X);
+ }
+
+ private static void saveImage(BufferedImage image, String name) throws Exception {
+ ImageIO.write(image, "png", new File(name));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JButton/8151303/PressedIconTest.java Fri Aug 19 16:48:53 2016 +0400
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016, 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.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Robot;
+import java.awt.event.InputEvent;
+import java.awt.image.BaseMultiResolutionImage;
+import java.awt.image.BufferedImage;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JToggleButton;
+import javax.swing.SwingUtilities;
+
+/**
+ * @test
+ * @bug 8151303
+ * @summary [macosx] [hidpi] JButton's low-res. icon is visible when clicking on it
+ * @run main/othervm PressedIconTest
+ * @run main/othervm -Dsun.java2d.uiScale=2 PressedIconTest
+ */
+public class PressedIconTest {
+
+ private final static int IMAGE_SIZE = 300;
+
+ private final static Color COLOR_1X = Color.RED;
+ private final static Color COLOR_2X = Color.BLUE;
+ private static JFrame frame;
+ private static volatile double scale = -1;
+ private static volatile int centerX;
+ private static volatile int centerY;
+
+ public static void main(String[] args) throws Exception {
+ Robot robot = new Robot();
+ robot.setAutoDelay(50);
+
+ SwingUtilities.invokeAndWait(() -> createAndShowGUI());
+ robot.waitForIdle();
+
+ SwingUtilities.invokeAndWait(() -> {
+ scale = frame.getGraphicsConfiguration().getDefaultTransform()
+ .getScaleX();
+ Point location = frame.getLocation();
+ Dimension size = frame.getSize();
+ centerX = location.x + size.width / 2;
+ centerY = location.y + size.height / 2;
+ });
+ robot.waitForIdle();
+
+ robot.mouseMove(centerX, centerY);
+ robot.mousePress(InputEvent.BUTTON1_MASK);
+ robot.waitForIdle();
+ Thread.sleep(100);
+ Color color = robot.getPixelColor(centerX, centerY);
+ robot.mouseRelease(InputEvent.BUTTON1_MASK);
+
+ SwingUtilities.invokeAndWait(() -> frame.dispose());
+
+ if ((scale == 1 && !similar(color, COLOR_1X))
+ || (scale == 2 && !similar(color, COLOR_2X))) {
+ throw new RuntimeException("Colors are different!");
+ }
+ }
+
+ private static void createAndShowGUI() {
+ frame = new JFrame();
+ frame.setSize(IMAGE_SIZE, IMAGE_SIZE);
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ JPanel panel = new JPanel(new BorderLayout());
+
+ BufferedImage img1x = generateImage(1, COLOR_1X);
+
+ BufferedImage img2x = generateImage(2, COLOR_2X);
+ BaseMultiResolutionImage mri = new BaseMultiResolutionImage(
+ new BufferedImage[]{img1x, img2x});
+ Icon mrIcon = new ImageIcon(mri);
+
+ JToggleButton button = new JToggleButton();
+ button.setIcon(mrIcon);
+ panel.add(button, BorderLayout.CENTER);
+
+ frame.getContentPane().add(panel);
+ frame.setVisible(true);
+ }
+
+ private static boolean similar(Color c1, Color c2) {
+ return similar(c1.getRed(), c2.getRed())
+ && similar(c1.getGreen(), c2.getGreen())
+ && similar(c1.getBlue(), c2.getBlue());
+ }
+
+ private static boolean similar(int n, int m) {
+ return Math.abs(n - m) <= 50;
+ }
+
+ private static BufferedImage generateImage(int scale, Color c) {
+
+ int size = IMAGE_SIZE * scale;
+ BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
+ Graphics g = img.createGraphics();
+ g.setColor(c);
+ g.fillRect(0, 0, size, size);
+ g.dispose();
+ return img;
+ }
+}