diff --git a/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java b/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java index 67520fcc30b..65c38cca879 100644 --- a/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java +++ b/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java @@ -105,38 +105,44 @@ final class AquaUtils { } 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 generateLightenedImage(Image image, ImageFilter filter) { + static Image generateFilteredImage(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 { IconImageFilter() { super(); diff --git a/jdk/src/java.desktop/share/classes/javax/swing/GrayFilter.java b/jdk/src/java.desktop/share/classes/javax/swing/GrayFilter.java index af5947e5eec..65fddf32df9 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/GrayFilter.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/GrayFilter.java @@ -26,6 +26,7 @@ package javax.swing; 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 @@ public class GrayFilter extends RGBImageFilter { * @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); diff --git a/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionCachedImage.java b/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionCachedImage.java index 8d8acdcf0a3..76bbfcff0f5 100644 --- a/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionCachedImage.java +++ b/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionCachedImage.java @@ -33,6 +33,7 @@ import java.util.List; 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 @@ public class MultiResolutionCachedImage extends AbstractMultiResolutionImage { private int availableInfo; public MultiResolutionCachedImage(int baseImageWidth, int baseImageHeight, - BiFunction mapper) { - this(baseImageWidth, baseImageHeight, new Dimension[]{new Dimension( - baseImageWidth, baseImageHeight) + BiFunction mapper) + { + this(baseImageWidth, baseImageHeight, + new Dimension[]{new Dimension( baseImageWidth, baseImageHeight) }, mapper); } public MultiResolutionCachedImage(int baseImageWidth, int baseImageHeight, - Dimension2D[] sizes, BiFunction mapper) { + Dimension2D[] sizes, + BiFunction mapper) + { + this(baseImageWidth, baseImageHeight, sizes, mapper, true); + } + + private MultiResolutionCachedImage(int baseImageWidth, int baseImageHeight, + Dimension2D[] sizes, + BiFunction 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 @@ public class MultiResolutionCachedImage extends AbstractMultiResolutionImage { mapper.apply(getResolutionVariant(width, height))); } + public static Image map(MultiResolutionImage mrImage, + Function mapper) { + + if (mrImage instanceof MultiResolutionToolkitImage) { + MultiResolutionToolkitImage mrtImage = + (MultiResolutionToolkitImage) mrImage; + return MultiResolutionToolkitImage.map(mrtImage, mapper); + } + + BiFunction 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); diff --git a/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionToolkitImage.java b/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionToolkitImage.java index 26a86916824..dfe08735a15 100644 --- a/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionToolkitImage.java +++ b/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionToolkitImage.java @@ -29,6 +29,7 @@ import java.awt.image.ImageObserver; 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 @@ public class MultiResolutionToolkitImage extends ToolkitImage implements MultiRe ? this : resolutionVariant; } + public static Image map(MultiResolutionToolkitImage mrImage, + Function 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( diff --git a/jdk/test/java/awt/image/MultiResolutionImage/MultiResolutionDisabledImageTest.java b/jdk/test/java/awt/image/MultiResolutionImage/MultiResolutionDisabledImageTest.java new file mode 100644 index 00000000000..ce5c44d0d8a --- /dev/null +++ b/jdk/test/java/awt/image/MultiResolutionImage/MultiResolutionDisabledImageTest.java @@ -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)); + } +} diff --git a/jdk/test/javax/swing/JButton/8151303/PressedIconTest.java b/jdk/test/javax/swing/JButton/8151303/PressedIconTest.java new file mode 100644 index 00000000000..9bf4065b099 --- /dev/null +++ b/jdk/test/javax/swing/JButton/8151303/PressedIconTest.java @@ -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; + } +}