diff --git a/src/macosx/classes/com/apple/laf/AquaPainter.java b/src/macosx/classes/com/apple/laf/AquaPainter.java index 38884ac7e8f3775bd69c9360a7fc6955a5859b83..a5bc8fb25dd67924c314945f770027612e4ea84c 100644 --- a/src/macosx/classes/com/apple/laf/AquaPainter.java +++ b/src/macosx/classes/com/apple/laf/AquaPainter.java @@ -148,34 +148,44 @@ abstract class AquaPainter { return; } - int scale = 1; - if (g instanceof SunGraphics2D) { - scale = ((SunGraphics2D) g).surfaceData.getDefaultScale(); - } final GraphicsConfiguration config = g.getDeviceConfiguration(); final ImageCache cache = ImageCache.getInstance(); - final int imgW = bounds.width * scale; - final int imgH = bounds.height * scale; + final int width = bounds.width; + final int height = bounds.height; AquaPixelsKey key = new AquaPixelsKey(config, - imgW, imgH, scale, controlState); - BufferedImage img = (BufferedImage) cache.getImage(key); + width, height, bounds, controlState); + Image img = (BufferedImage) cache.getImage(key); if (img == null) { - img = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB_PRE); + + Image baseImage = createImage(width, height, bounds, control, + controlState); + + img = new MultiResolutionBufferedImage(baseImage, + (rvWidth, rvHeight) -> createImage(rvWidth, rvHeight, + bounds, control, controlState)); + if (!controlState.is(JRSUIConstants.Animating.YES)) { cache.setImage(key, img); } - - final WritableRaster raster = img.getRaster(); - final DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer(); - - control.set(controlState); - control.paint(SunWritableRaster.stealData(buffer, 0), - imgW, imgH, 0, 0, bounds.width, bounds.height); - SunWritableRaster.markDirty(buffer); } g.drawImage(img, bounds.x, bounds.y, bounds.width, bounds.height, null); } + + private static Image createImage(int imgW, int imgH, final Rectangle bounds, + final JRSUIControl control, JRSUIState controlState) { + BufferedImage img = new BufferedImage(imgW, imgH, + BufferedImage.TYPE_INT_ARGB_PRE); + + final WritableRaster raster = img.getRaster(); + final DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer(); + + control.set(controlState); + control.paint(SunWritableRaster.stealData(buffer, 0), + imgW, imgH, 0, 0, bounds.width, bounds.height); + SunWritableRaster.markDirty(buffer); + return img; + } } private static class AquaPixelsKey implements ImageCache.PixelsKey { @@ -187,17 +197,17 @@ abstract class AquaPainter { private final GraphicsConfiguration config; private final int w; private final int h; - private final int scale; + private final Rectangle bounds; private final JRSUIState state; AquaPixelsKey(final GraphicsConfiguration config, - final int w, final int h, final int scale, + final int w, final int h, final Rectangle bounds, final JRSUIState state) { this.pixelCount = w * h; this.config = config; this.w = w; this.h = h; - this.scale = scale; + this.bounds = bounds; this.state = state; this.hash = hash(); } @@ -210,7 +220,7 @@ abstract class AquaPainter { int hash = config != null ? config.hashCode() : 0; hash = 31 * hash + w; hash = 31 * hash + h; - hash = 31 * hash + scale; + hash = 31 * hash + bounds.hashCode(); hash = 31 * hash + state.hashCode(); return hash; } @@ -225,7 +235,7 @@ abstract class AquaPainter { if (obj instanceof AquaPixelsKey) { AquaPixelsKey key = (AquaPixelsKey) obj; return config == key.config && w == key.w && h == key.h - && scale == key.scale && state.equals(key.state); + && bounds.equals(key.bounds) && state.equals(key.state); } return false; } diff --git a/src/share/classes/sun/awt/image/MultiResolutionBufferedImage.java b/src/share/classes/sun/awt/image/MultiResolutionBufferedImage.java index 283095c3588fe38a6ea5691a4953acbfb3a6d8d7..74db82734168ec37296c9405c95eab4e618cc34a 100644 --- a/src/share/classes/sun/awt/image/MultiResolutionBufferedImage.java +++ b/src/share/classes/sun/awt/image/MultiResolutionBufferedImage.java @@ -24,6 +24,7 @@ */ package sun.awt.image; +import java.awt.Dimension; import java.awt.Image; import java.awt.Graphics; import java.awt.geom.Dimension2D; @@ -42,6 +43,13 @@ public class MultiResolutionBufferedImage extends BufferedImage private final Dimension2D[] sizes; private int availableInfo; + public MultiResolutionBufferedImage(Image baseImage, + BiFunction mapper) { + this(baseImage, new Dimension[]{new Dimension( + baseImage.getWidth(null), baseImage.getHeight(null)) + }, mapper); + } + public MultiResolutionBufferedImage(Image baseImage, Dimension2D[] sizes, BiFunction mapper) { super(baseImage.getWidth(null), baseImage.getHeight(null), @@ -115,7 +123,7 @@ public class MultiResolutionBufferedImage extends BufferedImage } private static void preload(Image image, int availableInfo) { - if (image instanceof ToolkitImage) { + if (availableInfo != 0 && image instanceof ToolkitImage) { ((ToolkitImage) image).preload(new ImageObserver() { int flags = availableInfo; diff --git a/test/javax/swing/JCheckBox/8032667/bug8032667.html b/test/javax/swing/JCheckBox/8032667/bug8032667.html new file mode 100644 index 0000000000000000000000000000000000000000..33cc90e77cf624672ab8a716b2530c8b4196dcd8 --- /dev/null +++ b/test/javax/swing/JCheckBox/8032667/bug8032667.html @@ -0,0 +1,36 @@ + + + + + +Verify that scaled components are rendered smoothly to image. + +1. Run the test. +2. Check that Selected and Deselected JCheckBox icons are drawn smoothly. +If so, press PASS, else press FAIL. + + + + + diff --git a/test/javax/swing/JCheckBox/8032667/bug8032667.java b/test/javax/swing/JCheckBox/8032667/bug8032667.java new file mode 100644 index 0000000000000000000000000000000000000000..fda8852e520098beed8144f40740a0b644b11bfe --- /dev/null +++ b/test/javax/swing/JCheckBox/8032667/bug8032667.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014, 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.Canvas; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import javax.swing.JApplet; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; + +/* @test + * @bug 8032667 + * @summary [macosx] Components cannot be rendered in HiDPI to BufferedImage + * @run applet/manual=yesno bug8032667.html + */ +public class bug8032667 extends JApplet { + + static final int scale = 2; + static final int width = 130; + static final int height = 50; + static final int scaledWidth = scale * width; + static final int scaledHeight = scale * height; + + @Override + public void init() { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + + final Image image1 = getImage(getCheckBox("Deselected", false)); + final Image image2 = getImage(getCheckBox("Selected", true)); + + Canvas canvas = new Canvas() { + + @Override + public void paint(Graphics g) { + super.paint(g); + g.drawImage(image1, 0, 0, scaledWidth, scaledHeight, this); + g.drawImage(image2, 0, scaledHeight + 5, + scaledWidth, scaledHeight, this); + } + }; + + getContentPane().add(canvas, BorderLayout.CENTER); + } + }); + } + + static JCheckBox getCheckBox(String text, boolean selected) { + JCheckBox checkBox = new JCheckBox(text); + checkBox.setSelected(selected); + checkBox.setSize(new Dimension(width, height)); + return checkBox; + } + + static Image getImage(JComponent component) { + final BufferedImage image = new BufferedImage( + scaledWidth, scaledHeight, BufferedImage.TYPE_INT_ARGB); + final Graphics g = image.getGraphics(); + ((Graphics2D) g).scale(scale, scale); + component.paint(g); + g.dispose(); + + return image; + } +} diff --git a/test/javax/swing/JCheckBox/8032667/bug8032667_image_diff.java b/test/javax/swing/JCheckBox/8032667/bug8032667_image_diff.java new file mode 100644 index 0000000000000000000000000000000000000000..09e6b623784165200cb2b97410ae596f0dcbe8c9 --- /dev/null +++ b/test/javax/swing/JCheckBox/8032667/bug8032667_image_diff.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2014, 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.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; +import sun.awt.OSInfo; + +/* @test + * @bug 8032667 + * @summary [macosx] Components cannot be rendered in HiDPI to BufferedImage + * @run main bug8032667_image_diff + */ +public class bug8032667_image_diff { + + static final int IMAGE_WIDTH = 130; + static final int IMAGE_HEIGHT = 50; + + public static void main(String[] args) throws Exception { + + if(!OSInfo.OSType.MACOSX.equals(OSInfo.getOSType())){ + return; + } + + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + + JCheckBox checkBox = new JCheckBox(); + checkBox.setSelected(true); + checkBox.setSize(new Dimension(IMAGE_WIDTH, IMAGE_HEIGHT)); + + final BufferedImage image1 = getHiDPIImage(checkBox); + final BufferedImage image2 = getScaledImage(checkBox); + + if(equal(image1, image2)){ + throw new RuntimeException("2x image equals to non smooth image"); + } + } + }); + } + + static boolean equal(BufferedImage image1, BufferedImage image2) { + + int w = image1.getWidth(); + int h = image1.getHeight(); + + if (w != image2.getWidth() || h != image2.getHeight()) { + return false; + } + + for (int i = 0; i < w; i++) { + for (int j = 0; j < h; j++) { + int color1 = image1.getRGB(i, j); + int color2 = image2.getRGB(i, j); + + if (color1 != color2) { + return false; + } + } + } + return true; + } + + static BufferedImage getHiDPIImage(JComponent component) { + return getImage(component, 2, IMAGE_WIDTH, IMAGE_HEIGHT); + } + + static BufferedImage getScaledImage(JComponent component) { + Image image1x = getImage(component, 1, IMAGE_WIDTH, IMAGE_HEIGHT); + final BufferedImage image2x = new BufferedImage( + 2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_ARGB); + final Graphics g = image2x.getGraphics(); + ((Graphics2D) g).scale(2, 2); + g.drawImage(image1x, 0, 0, null); + g.dispose(); + return image2x; + } + + static BufferedImage getImage(JComponent component, int scale, int width, int height) { + final BufferedImage image = new BufferedImage( + scale * width, scale * height, BufferedImage.TYPE_INT_ARGB); + final Graphics g = image.getGraphics(); + ((Graphics2D) g).scale(scale, scale); + component.paint(g); + g.dispose(); + return image; + } +}