8032667: [macosx] Components cannot be rendered in HiDPI to BufferedImage
Reviewed-by: serb, pchelko
--- a/jdk/src/macosx/classes/com/apple/laf/AquaPainter.java Fri Mar 28 17:31:16 2014 +0400
+++ b/jdk/src/macosx/classes/com/apple/laf/AquaPainter.java Fri Mar 28 18:56:25 2014 +0400
@@ -148,34 +148,44 @@
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 @@
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 @@
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 @@
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;
}
--- a/jdk/src/share/classes/sun/awt/image/MultiResolutionBufferedImage.java Fri Mar 28 17:31:16 2014 +0400
+++ b/jdk/src/share/classes/sun/awt/image/MultiResolutionBufferedImage.java Fri Mar 28 18:56:25 2014 +0400
@@ -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;
@@ -43,6 +44,13 @@
private int availableInfo;
public MultiResolutionBufferedImage(Image baseImage,
+ BiFunction<Integer, Integer, Image> mapper) {
+ this(baseImage, new Dimension[]{new Dimension(
+ baseImage.getWidth(null), baseImage.getHeight(null))
+ }, mapper);
+ }
+
+ public MultiResolutionBufferedImage(Image baseImage,
Dimension2D[] sizes, BiFunction<Integer, Integer, Image> mapper) {
super(baseImage.getWidth(null), baseImage.getHeight(null),
BufferedImage.TYPE_INT_ARGB_PRE);
@@ -115,7 +123,7 @@
}
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;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JCheckBox/8032667/bug8032667.html Fri Mar 28 18:56:25 2014 +0400
@@ -0,0 +1,36 @@
+<!--
+ 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.
+-->
+
+<html>
+<body>
+
+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.
+
+<applet code="bug8032667.class" width=400 height=400></applet>
+
+</body>
+</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JCheckBox/8032667/bug8032667.java Fri Mar 28 18:56:25 2014 +0400
@@ -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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JCheckBox/8032667/bug8032667_image_diff.java Fri Mar 28 18:56:25 2014 +0400
@@ -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;
+ }
+}