--- /dev/null 2013-12-04 18:00:53.000000000 +0400 +++ new/test/java/awt/image/MultiResolutionImageTest.java 2013-12-04 18:00:53.000000000 +0400 @@ -0,0 +1,620 @@ +/* + * 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.Toolkit; +import java.awt.image.BufferedImage; +import java.io.File; +import java.lang.reflect.Method; +import java.net.URL; +import javax.imageio.ImageIO; +import sun.awt.OSInfo; +import sun.awt.SunHints; +import java.awt.MediaTracker; +import java.awt.geom.AffineTransform; +import java.awt.image.ImageObserver; +import java.util.Arrays; +import java.util.List; +import javax.swing.JPanel; +import sun.awt.SunToolkit; +import sun.awt.image.MultiResolutionImage; + +/** + * @test + * @bug 8011059 + * @author Alexander Scherbatiy + * @summary [macosx] Make JDK demos look perfect on retina displays + * @run main MultiResolutionImageTest CUSTOM + * @run main MultiResolutionImageTest TOOLKIT_PREPARE + * @run main MultiResolutionImageTest TOOLKIT_LOAD + * @run main MultiResolutionImageTest TOOLKIT + */ +public class MultiResolutionImageTest { + + private static final int IMAGE_WIDTH = 300; + private static final int IMAGE_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 { + + System.out.println("args: " + args.length); + + if (args.length == 0) { + throw new RuntimeException("Not found a test"); + } + + String test = args[0]; + + System.out.println("TEST: " + test); + System.out.println("CHECK OS: " + checkOS()); + + if ("CUSTOM".equals(test)) { + testCustomMultiResolutionImage(); + } else if (checkOS()) { + switch (test) { + case "CUSTOM": + break; + case "TOOLKIT_PREPARE": + testToolkitMultiResolutionImagePrepare(); + break; + case "TOOLKIT_LOAD": + testToolkitMultiResolutionImageLoad(); + break; + case "TOOLKIT": + testToolkitMultiResolutionImage(); + testImageNameTo2xParsing(); + break; + default: + throw new RuntimeException("Unknown test: " + test); + } + } + } + + static boolean checkOS() { + return OSInfo.getOSType() == OSInfo.OSType.MACOSX; + } + + public static void testCustomMultiResolutionImage() { + testCustomMultiResolutionImage(false); + testCustomMultiResolutionImage(true); + } + + public static void testCustomMultiResolutionImage(boolean enableImageScaling) { + + Image image = new MultiResolutionBufferedImage(); + + // Same image size + BufferedImage bufferedImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, + BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = (Graphics2D) bufferedImage.getGraphics(); + setImageScalingHint(g2d, enableImageScaling); + g2d.drawImage(image, 0, 0, null); + checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 4, 3 * IMAGE_HEIGHT / 4), false); + + // Twice image size + bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, + BufferedImage.TYPE_INT_RGB); + g2d = (Graphics2D) bufferedImage.getGraphics(); + setImageScalingHint(g2d, enableImageScaling); + g2d.drawImage(image, 0, 0, 2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null); + checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling); + + // Scale 2x + bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB); + g2d = (Graphics2D) bufferedImage.getGraphics(); + setImageScalingHint(g2d, enableImageScaling); + g2d.scale(2, 2); + g2d.drawImage(image, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null); + checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling); + + // Rotate + bufferedImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, + BufferedImage.TYPE_INT_RGB); + g2d = (Graphics2D) bufferedImage.getGraphics(); + setImageScalingHint(g2d, enableImageScaling); + g2d.drawImage(image, 0, 0, null); + g2d.rotate(Math.PI / 4); + checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 4, 3 * IMAGE_HEIGHT / 4), false); + + // Scale 2x and Rotate + bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB); + g2d = (Graphics2D) bufferedImage.getGraphics(); + setImageScalingHint(g2d, enableImageScaling); + g2d.scale(-2, 2); + g2d.rotate(-Math.PI / 10); + g2d.drawImage(image, -IMAGE_WIDTH, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null); + checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling); + + // General Transform + bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB); + g2d = (Graphics2D) bufferedImage.getGraphics(); + setImageScalingHint(g2d, enableImageScaling); + float delta = 0.05f; + float cos = 1 - delta * delta / 2; + float sin = 1 + delta; + AffineTransform transform = new AffineTransform(2 * cos, 0.1, 0.3, -2 * sin, 10, -5); + g2d.setTransform(transform); + g2d.drawImage(image, 0, -IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_HEIGHT, null); + checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling); + + int D = 10; + // From Source to small Destination region + bufferedImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB); + g2d = (Graphics2D) bufferedImage.getGraphics(); + setImageScalingHint(g2d, enableImageScaling); + g2d.drawImage(image, IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2, IMAGE_WIDTH - D, IMAGE_HEIGHT - D, + D, D, IMAGE_WIDTH - D, IMAGE_HEIGHT - D, null); + checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 4, 3 * IMAGE_HEIGHT / 4), false); + + // From Source to large Destination region + bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB); + g2d = (Graphics2D) bufferedImage.getGraphics(); + setImageScalingHint(g2d, enableImageScaling); + g2d.drawImage(image, D, D, 2 * IMAGE_WIDTH - D, 2 * IMAGE_HEIGHT - D, + IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2, IMAGE_WIDTH - D, IMAGE_HEIGHT - D, null); + checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling); + } + + static class MultiResolutionBufferedImage extends BufferedImage + implements MultiResolutionImage { + + Image highResolutionImage; + + public MultiResolutionBufferedImage() { + super(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB); + highResolutionImage = new BufferedImage( + 2 * IMAGE_WIDTH, 2 * IMAGE_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, IMAGE_WIDTH, IMAGE_HEIGHT); + } + + @Override + public Image getResolutionVariant(int width, int height) { + return ((width <= getWidth() && height <= getHeight())) + ? this : highResolutionImage; + } + + @Override + public List getResolutionVariants() { + return Arrays.asList(this, highResolutionImage); + } + } + + static void testToolkitMultiResolutionImagePrepare() throws Exception { + + generateImages(); + + File imageFile = new File(IMAGE_NAME_1X); + String fileName = imageFile.getAbsolutePath(); + + Image image = Toolkit.getDefaultToolkit().getImage(fileName); + + SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); + toolkit.prepareImage(image, IMAGE_WIDTH, IMAGE_HEIGHT, new LoadImageObserver(image)); + + testToolkitMultiResolutionImageLoad(image); + } + + static void testToolkitMultiResolutionImageLoad() throws Exception { + + generateImages(); + + File imageFile = new File(IMAGE_NAME_1X); + String fileName = imageFile.getAbsolutePath(); + Image image = Toolkit.getDefaultToolkit().getImage(fileName); + testToolkitMultiResolutionImageLoad(image); + } + + static void testToolkitMultiResolutionImageLoad(Image image) 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"); + } + tracker.removeImage(image, 0); + + testImageLoaded(image); + + int w = image.getWidth(null); + int h = image.getHeight(null); + + Image resolutionVariant = ((MultiResolutionImage) image) + .getResolutionVariant(2 * w, 2 * h); + + if (image == resolutionVariant) { + throw new RuntimeException("Resolution variant is not loaded"); + } + + testImageLoaded(resolutionVariant); + } + + static void testImageLoaded(Image image) { + + SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); + + int flags = toolkit.checkImage(image, IMAGE_WIDTH, IMAGE_WIDTH, new SilentImageObserver()); + if ((flags & (ImageObserver.FRAMEBITS | ImageObserver.ALLBITS)) == 0) { + throw new RuntimeException("Image is not loaded!"); + } + } + + static class SilentImageObserver implements ImageObserver { + + @Override + public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { + throw new RuntimeException("Observer should not be called!"); + } + } + + static class LoadImageObserver implements ImageObserver { + + Image image; + + public LoadImageObserver(Image image) { + this.image = image; + } + + @Override + public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { + + if (image != img) { + throw new RuntimeException("Original image is not passed to the observer"); + } + + if ((infoflags & ImageObserver.WIDTH) != 0) { + if (width != IMAGE_WIDTH) { + throw new RuntimeException("Original width is not passed to the observer"); + } + } + + if ((infoflags & ImageObserver.HEIGHT) != 0) { + if (height != IMAGE_HEIGHT) { + throw new RuntimeException("Original height is not passed to the observer"); + } + } + + return (infoflags & ALLBITS) == 0; + } + + } + + static void testToolkitMultiResolutionImage() throws Exception { + + generateImages(); + + File imageFile = new File(IMAGE_NAME_1X); + String fileName = imageFile.getAbsolutePath(); + URL url = imageFile.toURI().toURL(); + testToolkitMultiResolutionImageChache(fileName, url); + + Image image = Toolkit.getDefaultToolkit().getImage(fileName); + testToolkitImageObserver(image); + testToolkitMultiResolutionImage(image, false); + testToolkitMultiResolutionImage(image, true); + + image = Toolkit.getDefaultToolkit().getImage(url); + testToolkitImageObserver(image); + testToolkitMultiResolutionImage(image, false); + testToolkitMultiResolutionImage(image, true); + } + + static void testToolkitMultiResolutionImageChache(String fileName, URL url) { + + Image img1 = Toolkit.getDefaultToolkit().getImage(fileName); + if (!(img1 instanceof MultiResolutionImage)) { + throw new RuntimeException("Not a MultiResolutionImage"); + } + + Image img2 = Toolkit.getDefaultToolkit().getImage(fileName); + if (img1 != img2) { + throw new RuntimeException("Image is not cached"); + } + + img1 = Toolkit.getDefaultToolkit().getImage(url); + if (!(img1 instanceof MultiResolutionImage)) { + throw new RuntimeException("Not a MultiResolutionImage"); + } + + img2 = Toolkit.getDefaultToolkit().getImage(url); + if (img1 != img2) { + throw new RuntimeException("Image is not cached"); + } + } + + static void testToolkitMultiResolutionImage(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(IMAGE_WIDTH, IMAGE_HEIGHT, + BufferedImage.TYPE_INT_RGB); + Graphics2D g1x = (Graphics2D) bufferedImage1x.getGraphics(); + setImageScalingHint(g1x, false); + g1x.drawImage(image, 0, 0, null); + checkColor(bufferedImage1x.getRGB(3 * IMAGE_WIDTH / 4, 3 * IMAGE_HEIGHT / 4), false); + + Image resolutionVariant = ((MultiResolutionImage) image). + getResolutionVariant(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT); + + if (resolutionVariant == null) { + throw new RuntimeException("Resolution variant is null"); + } + + MediaTracker tracker2x = new MediaTracker(new JPanel()); + tracker2x.addImage(resolutionVariant, 0); + tracker2x.waitForID(0); + if (tracker2x.isErrorAny()) { + throw new RuntimeException("Error during scalable image loading"); + } + + final BufferedImage bufferedImage2x = new BufferedImage(2 * IMAGE_WIDTH, + 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB); + Graphics2D g2x = (Graphics2D) bufferedImage2x.getGraphics(); + setImageScalingHint(g2x, enableImageScaling); + g2x.drawImage(image, 0, 0, 2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null); + checkColor(bufferedImage2x.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling); + + if (!(image instanceof MultiResolutionImage)) { + throw new RuntimeException("Not a MultiResolutionImage"); + } + + MultiResolutionImage multiResolutionImage = (MultiResolutionImage) image; + + Image image1x = multiResolutionImage.getResolutionVariant(IMAGE_WIDTH, IMAGE_HEIGHT); + Image image2x = multiResolutionImage.getResolutionVariant(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT); + + if (image1x.getWidth(null) * 2 != image2x.getWidth(null) + || image1x.getHeight(null) * 2 != image2x.getHeight(null)) { + throw new RuntimeException("Wrong resolution variant size"); + } + } + + static void testToolkitImageObserver(final Image image) { + + ImageObserver observer = new ImageObserver() { + + @Override + public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { + + if (img != image) { + throw new RuntimeException("Wrong image in observer"); + } + + if ((infoflags & (ImageObserver.ERROR | ImageObserver.ABORT)) != 0) { + throw new RuntimeException("Error during image loading"); + } + + return (infoflags & ImageObserver.ALLBITS) == 0; + + } + }; + + final BufferedImage bufferedImage2x = new BufferedImage(2 * IMAGE_WIDTH, + 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB); + Graphics2D g2x = (Graphics2D) bufferedImage2x.getGraphics(); + setImageScalingHint(g2x, true); + + g2x.drawImage(image, 0, 0, 2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, observer); + + } + + static void setImageScalingHint(Graphics2D g2d, boolean enableImageScaling) { + g2d.setRenderingHint(SunHints.KEY_RESOLUTION_VARIANT, enableImageScaling + ? SunHints.VALUE_RESOLUTION_VARIANT_ON + : SunHints.VALUE_RESOLUTION_VARIANT_OFF); + } + + 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 generateImages() throws Exception { + if (!new File(IMAGE_NAME_1X).exists()) { + generateImage(1); + } + + if (!new File(IMAGE_NAME_2X).exists()) { + generateImage(2); + } + } + + static void generateImage(int scale) throws Exception { + BufferedImage image = new BufferedImage(scale * IMAGE_WIDTH, scale * IMAGE_HEIGHT, + BufferedImage.TYPE_INT_RGB); + Graphics g = image.getGraphics(); + g.setColor(scale == 1 ? COLOR_1X : COLOR_2X); + g.fillRect(0, 0, scale * IMAGE_WIDTH, scale * IMAGE_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(".") + && !path.contains("@2x"); + } + + private static Method getScalableImageMethod(String name, + Class... parameterTypes) throws Exception { + Toolkit toolkit = Toolkit.getDefaultToolkit(); + Method method = toolkit.getClass().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}, + {"aaa@2x.png", null}, + {"/dir/aaa@2x.png", 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); + } + } + + static class PreloadedImageObserver implements ImageObserver { + + @Override + public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { + throw new RuntimeException("Image should be already preloaded"); + } + } +}