--- /dev/null 2013-11-11 19:47:25.000000000 +0400 +++ new/test/java/awt/image/MultiResolutionImageTest.java 2013-11-11 19:47:24.000000000 +0400 @@ -0,0 +1,347 @@ +/* + * 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.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.MediaTracker; +import java.awt.RenderingHints; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.io.File; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.Objects; +import javax.imageio.ImageIO; +import javax.swing.JPanel; +import sun.awt.OSInfo; +import sun.awt.SunHints; +import com.sun.awt.MultiResolutionImage; + +/** + * @test @bug 8011059 + * @author Alexander Scherbatiy + * @summary [macosx] Make JDK demos look perfect on retina displays + * @run main MultiResolutionImageTest + */ +public class MultiResolutionImageTest { + + private static final int WIDTH = 300; + private static final int HEIGHT = 200; + private static final Color COLOR_1X = Color.GREEN; + private static final Color COLOR_2X = Color.BLUE; + private static final String IMAGE_NAME_1X = "image.png"; + private static final String IMAGE_NAME_2X = "image@2x.png"; + + public static void main(String[] args) throws Exception { + testCustomScaledImage(); + + if (OSInfo.getOSType() == OSInfo.OSType.MACOSX) { + testToolkitScaledImage(); + testImageNameTo2xParsing(); + } + } + + public static void testCustomScaledImage() { + testCustomScaledImage(false); + testCustomScaledImage(true); + } + + public static void testCustomScaledImage(boolean enableImageScaling) { + + Image image = new MultiResolutionBufferedImage(); + + // Same image size + BufferedImage bufferedImage1x = new BufferedImage(WIDTH, HEIGHT, + BufferedImage.TYPE_INT_RGB); + Graphics2D g1x = (Graphics2D) bufferedImage1x.getGraphics(); + setImageScalingHint(g1x, enableImageScaling); + g1x.drawImage(image, 0, 0, null); + checkColor(bufferedImage1x.getRGB(3 * WIDTH / 4, 3 * HEIGHT / 4), false); + + // Twice image size + bufferedImage1x = new BufferedImage(2 * WIDTH, 2 * HEIGHT, + BufferedImage.TYPE_INT_RGB); + g1x = (Graphics2D) bufferedImage1x.getGraphics(); + setImageScalingHint(g1x, enableImageScaling); + g1x.drawImage(image, 0, 0, 2 * WIDTH, 2 * HEIGHT, 0, 0, WIDTH, HEIGHT, null); + checkColor(bufferedImage1x.getRGB(3 * WIDTH / 2, 3 * HEIGHT / 2), enableImageScaling); + } + + static class MultiResolutionBufferedImage extends BufferedImage + implements MultiResolutionImage { + + Image highResolutionImage; + + public MultiResolutionBufferedImage() { + super(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); + highResolutionImage = new BufferedImage( + 2 * WIDTH, 2 * HEIGHT, BufferedImage.TYPE_INT_RGB); + draw(getGraphics(), 1); + draw(highResolutionImage.getGraphics(), 2); + } + + void draw(Graphics graphics, float resolution) { + Graphics2D g2 = (Graphics2D) graphics; + g2.scale(resolution, resolution); + g2.setColor((resolution == 1) ? COLOR_1X : COLOR_2X); + g2.fillRect(0, 0, WIDTH, HEIGHT); + } + + @Override + public Image getResolutionVariant(int width, int height) { + return ((width <= getWidth() && height <= getHeight())) + ? this : highResolutionImage; + } + + } + + static void testToolkitScaledImage() throws Exception { + + generateImage(1); + generateImage(2); + + File imageFile = new File(IMAGE_NAME_1X); + Image image = Toolkit.getDefaultToolkit().getImage(imageFile.getAbsolutePath()); + testToolkitScaledImage(image, false); + testToolkitScaledImage(image, true); + + image = Toolkit.getDefaultToolkit().getImage(imageFile.toURI().toURL()); + testToolkitScaledImage(image, false); + testToolkitScaledImage(image, true); + } + + static void testToolkitScaledImage(Image image, boolean enableImageScaling) + throws Exception { + + MediaTracker tracker = new MediaTracker(new JPanel()); + tracker.addImage(image, 0); + tracker.waitForID(0); + if (tracker.isErrorAny()) { + throw new RuntimeException("Error during image loading"); + } + + final BufferedImage bufferedImage1x = new BufferedImage(WIDTH, HEIGHT, + BufferedImage.TYPE_INT_RGB); + Graphics2D g1x = (Graphics2D) bufferedImage1x.getGraphics(); + setImageScalingHint(g1x, false); + g1x.drawImage(image, 0, 0, null); + checkColor(bufferedImage1x.getRGB(3 * WIDTH / 4, 3 * HEIGHT / 4), false); + + Image scaledImage = ((MultiResolutionImage) image). + getResolutionVariant(2 * WIDTH, 2 * HEIGHT); + + if (scaledImage == null) { + throw new RuntimeException("Scaled image is null"); + } + + MediaTracker tracker2x = new MediaTracker(new JPanel()); + tracker2x.addImage(scaledImage, 0); + tracker2x.waitForID(0); + if (tracker2x.isErrorAny()) { + throw new RuntimeException("Error during scalable image loading"); + } + + final BufferedImage bufferedImage2x = new BufferedImage(2 * WIDTH, + 2 * HEIGHT, BufferedImage.TYPE_INT_RGB); + Graphics2D g2x = (Graphics2D) bufferedImage2x.getGraphics(); + setImageScalingHint(g2x, enableImageScaling); + g2x.drawImage(image, 0, 0, 2 * WIDTH, 2 * HEIGHT, 0, 0, WIDTH, HEIGHT, null); + checkColor(bufferedImage2x.getRGB(3 * WIDTH / 2, 3 * HEIGHT / 2), enableImageScaling); + } + + static void setImageScalingHint(Graphics2D g2d, boolean enableImageScaling) { + if (enableImageScaling) { + g2d.setRenderingHint(SunHints.KEY_RESOLUTION_VARIANT, + SunHints.VALUE_RESOLUTION_VARIANT_ON); + } + } + + static void checkColor(int rgb, boolean isImageScaled) { + + if (!isImageScaled && COLOR_1X.getRGB() != rgb) { + throw new RuntimeException("Wrong 1x color: " + new Color(rgb)); + } + + if (isImageScaled && COLOR_2X.getRGB() != rgb) { + throw new RuntimeException("Wrong 2x color" + new Color(rgb)); + } + } + + static void generateImage(int scale) throws Exception { + BufferedImage image = new BufferedImage(scale * WIDTH, scale * HEIGHT, + BufferedImage.TYPE_INT_RGB); + Graphics g = image.getGraphics(); + g.setColor(scale == 1 ? COLOR_1X : COLOR_2X); + g.fillRect(0, 0, scale * WIDTH, scale * HEIGHT); + File file = new File(scale == 1 ? IMAGE_NAME_1X : IMAGE_NAME_2X); + ImageIO.write(image, "png", file); + } + + static void testImageNameTo2xParsing() throws Exception { + + for (String[] testNames : TEST_FILE_NAMES) { + String testName = testNames[0]; + String goldenName = testNames[1]; + String resultName = getTestScaledImageName(testName); + + if (!isValidPath(testName) && resultName == null) { + continue; + } + + if (goldenName.equals(resultName)) { + continue; + } + + throw new RuntimeException("Test name " + testName + + ", result name: " + resultName); + } + + for (URL[] testURLs : TEST_URLS) { + URL testURL = testURLs[0]; + URL goldenURL = testURLs[1]; + URL resultURL = getTestScaledImageURL(testURL); + + if (!isValidPath(testURL.getPath()) && resultURL == null) { + continue; + } + + if (goldenURL.equals(resultURL)) { + continue; + } + + throw new RuntimeException("Test url: " + testURL + + ", result url: " + resultURL); + } + + } + + static URL getTestScaledImageURL(URL url) throws Exception { + Method method = getScalableImageMethod("getScaledImageURL", URL.class); + return (URL) method.invoke(null, url); + } + + static String getTestScaledImageName(String name) throws Exception { + Method method = getScalableImageMethod("getScaledImageName", String.class); + return (String) method.invoke(null, name); + } + + private static boolean isValidPath(String path) { + return !path.isEmpty() && !path.endsWith("/") && !path.endsWith("."); + } + + private static Method getScalableImageMethod(String name, + Class... parameterTypes) throws Exception { + + Toolkit toolkit = Toolkit.getDefaultToolkit(); + Class[] classes = toolkit.getClass().getClasses(); + Class scalableImageClass = null; + + for (Class cls : classes) { + if (cls.getName().endsWith("ScalableToolkitImage")) { + scalableImageClass = cls; + break; + } + } + + if (scalableImageClass == null) { + throw new RuntimeException("ScalableToolkitImage does not exist"); + } + Method method = scalableImageClass.getDeclaredMethod(name, parameterTypes); + method.setAccessible(true); + return method; + } + private static final String[][] TEST_FILE_NAMES; + private static final URL[][] TEST_URLS; + + static { + TEST_FILE_NAMES = new String[][]{ + {"", null}, + {".", null}, + {"..", null}, + {"/", null}, + {"/.", null}, + {"dir/", null}, + {"dir/.", null}, + {"image", "image@2x"}, + {"image.ext", "image@2x.ext"}, + {"image.aaa.ext", "image.aaa@2x.ext"}, + {"dir/image", "dir/image@2x"}, + {"dir/image.ext", "dir/image@2x.ext"}, + {"dir/image.aaa.ext", "dir/image.aaa@2x.ext"}, + {"dir/aaa.bbb/image", "dir/aaa.bbb/image@2x"}, + {"dir/aaa.bbb/image.ext", "dir/aaa.bbb/image@2x.ext"}, + {"dir/aaa.bbb/image.ccc.ext", "dir/aaa.bbb/image.ccc@2x.ext"}, + {"/dir/image", "/dir/image@2x"}, + {"/dir/image.ext", "/dir/image@2x.ext"}, + {"/dir/image.aaa.ext", "/dir/image.aaa@2x.ext"}, + {"/dir/aaa.bbb/image", "/dir/aaa.bbb/image@2x"}, + {"/dir/aaa.bbb/image.ext", "/dir/aaa.bbb/image@2x.ext"}, + {"/dir/aaa.bbb/image.ccc.ext", "/dir/aaa.bbb/image.ccc@2x.ext"} + }; + try { + TEST_URLS = new URL[][]{ + // file + {new URL("file:/aaa"), new URL("file:/aaa@2x")}, + {new URL("file:/aaa.ext"), new URL("file:/aaa@2x.ext")}, + {new URL("file:/aaa.bbb.ext"), new URL("file:/aaa.bbb@2x.ext")}, + {new URL("file:/ccc/aaa.bbb.ext"), + new URL("file:/ccc/aaa.bbb@2x.ext")}, + {new URL("file:/ccc.ddd/aaa.bbb.ext"), + new URL("file:/ccc.ddd/aaa.bbb@2x.ext")}, + {new URL("file:///~/image"), new URL("file:///~/image@2x")}, + {new URL("file:///~/image.ext"), + new URL("file:///~/image@2x.ext")}, + // http + {new URL("http://www.test.com"), null}, + {new URL("http://www.test.com/"), null}, + {new URL("http://www.test.com///"), null}, + {new URL("http://www.test.com/image"), + new URL("http://www.test.com/image@2x")}, + {new URL("http://www.test.com/image.ext"), + new URL("http://www.test.com/image@2x.ext")}, + {new URL("http://www.test.com/dir/image"), + new URL("http://www.test.com/dir/image@2x")}, + {new URL("http://www.test.com:80/dir/image.aaa.ext"), + new URL("http://www.test.com:80/dir/image.aaa@2x.ext")}, + {new URL("http://www.test.com:8080/dir/image.aaa.ext"), + new URL("http://www.test.com:8080/dir/image.aaa@2x.ext")}, + // jar + {new URL("jar:file:/dir/Java2D.jar!/image"), + new URL("jar:file:/dir/Java2D.jar!/image@2x")}, + {new URL("jar:file:/dir/Java2D.jar!/image.aaa.ext"), + new URL("jar:file:/dir/Java2D.jar!/image.aaa@2x.ext")}, + {new URL("jar:file:/dir/Java2D.jar!/images/image"), + new URL("jar:file:/dir/Java2D.jar!/images/image@2x")}, + {new URL("jar:file:/dir/Java2D.jar!/images/image.ext"), + new URL("jar:file:/dir/Java2D.jar!/images/image@2x.ext")}, + {new URL("jar:file:/aaa.bbb/Java2D.jar!/images/image.ext"), + new URL("jar:file:/aaa.bbb/Java2D.jar!/images/image@2x.ext")}, + {new URL("jar:file:/dir/Java2D.jar!/aaa.bbb/image.ext"), + new URL("jar:file:/dir/Java2D.jar!/aaa.bbb/image@2x.ext")},}; + } catch (Exception e) { + throw new RuntimeException(e); + } + } +}