1 /*
2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 import java.awt.Color;
25 import java.awt.Graphics;
26 import java.awt.Graphics2D;
27 import java.awt.Image;
28 import java.awt.Toolkit;
29 import java.awt.image.BufferedImage;
30 import java.io.File;
31 import java.lang.reflect.Method;
32 import java.net.URL;
33 import javax.imageio.ImageIO;
34 import sun.awt.OSInfo;
35 import sun.awt.SunHints;
36 import java.awt.MediaTracker;
37 import java.awt.geom.AffineTransform;
38 import java.awt.image.ImageObserver;
39 import java.util.Arrays;
40 import java.util.List;
41 import javax.swing.JPanel;
42 import sun.awt.SunToolkit;
43 import sun.awt.image.MultiResolutionImage;
44 
45 /**
46  * @test
47  * @bug 8011059
48  * @author Alexander Scherbatiy
49  * @summary [macosx] Make JDK demos look perfect on retina displays
50  * @run main MultiResolutionImageTest CUSTOM
51  * @run main MultiResolutionImageTest TOOLKIT_PREPARE
52  * @run main MultiResolutionImageTest TOOLKIT_LOAD
53  * @run main MultiResolutionImageTest TOOLKIT
54  */
55 public class MultiResolutionImageTest {
56 
57     private static final int IMAGE_WIDTH = 300;
58     private static final int IMAGE_HEIGHT = 200;
59     private static final Color COLOR_1X = Color.GREEN;
60     private static final Color COLOR_2X = Color.BLUE;
61     private static final String IMAGE_NAME_1X = "image.png";
62     private static final String IMAGE_NAME_2X = "image@2x.png";
63 
main(String[] args)64     public static void main(String[] args) throws Exception {
65 
66         System.out.println("args: " + args.length);
67 
68         if (args.length == 0) {
69             throw new RuntimeException("Not found a test");
70         }
71 
72         String test = args[0];
73 
74         System.out.println("TEST: " + test);
75         System.out.println("CHECK OS: " + checkOS());
76 
77         if ("CUSTOM".equals(test)) {
78             testCustomMultiResolutionImage();
79         } else if (checkOS()) {
80             switch (test) {
81                 case "CUSTOM":
82                     break;
83                 case "TOOLKIT_PREPARE":
84                     testToolkitMultiResolutionImagePrepare();
85                     break;
86                 case "TOOLKIT_LOAD":
87                     testToolkitMultiResolutionImageLoad();
88                     break;
89                 case "TOOLKIT":
90                     testToolkitMultiResolutionImage();
91                     testImageNameTo2xParsing();
92                     break;
93                 default:
94                     throw new RuntimeException("Unknown test: " + test);
95             }
96         }
97     }
98 
checkOS()99     static boolean checkOS() {
100         return OSInfo.getOSType() == OSInfo.OSType.MACOSX;
101     }
102 
testCustomMultiResolutionImage()103     public static void testCustomMultiResolutionImage() {
104         testCustomMultiResolutionImage(false);
105         testCustomMultiResolutionImage(true);
106     }
107 
testCustomMultiResolutionImage(boolean enableImageScaling)108     public static void testCustomMultiResolutionImage(boolean enableImageScaling) {
109 
110         Image image = new MultiResolutionBufferedImage();
111 
112         // Same image size
113         BufferedImage bufferedImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT,
114                 BufferedImage.TYPE_INT_RGB);
115         Graphics2D g2d = (Graphics2D) bufferedImage.getGraphics();
116         setImageScalingHint(g2d, enableImageScaling);
117         g2d.drawImage(image, 0, 0, null);
118         checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 4, 3 * IMAGE_HEIGHT / 4), false);
119 
120         // Twice image size
121         bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT,
122                 BufferedImage.TYPE_INT_RGB);
123         g2d = (Graphics2D) bufferedImage.getGraphics();
124         setImageScalingHint(g2d, enableImageScaling);
125         g2d.drawImage(image, 0, 0, 2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null);
126         checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling);
127 
128         // Scale 2x
129         bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
130         g2d = (Graphics2D) bufferedImage.getGraphics();
131         setImageScalingHint(g2d, enableImageScaling);
132         g2d.scale(2, 2);
133         g2d.drawImage(image, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null);
134         checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling);
135 
136         // Rotate
137         bufferedImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT,
138                 BufferedImage.TYPE_INT_RGB);
139         g2d = (Graphics2D) bufferedImage.getGraphics();
140         setImageScalingHint(g2d, enableImageScaling);
141         g2d.drawImage(image, 0, 0, null);
142         g2d.rotate(Math.PI / 4);
143         checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 4, 3 * IMAGE_HEIGHT / 4), false);
144 
145         // Scale 2x and Rotate
146         bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
147         g2d = (Graphics2D) bufferedImage.getGraphics();
148         setImageScalingHint(g2d, enableImageScaling);
149         g2d.scale(-2, 2);
150         g2d.rotate(-Math.PI / 10);
151         g2d.drawImage(image, -IMAGE_WIDTH, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null);
152         checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling);
153 
154         // General Transform
155         bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
156         g2d = (Graphics2D) bufferedImage.getGraphics();
157         setImageScalingHint(g2d, enableImageScaling);
158         float delta = 0.05f;
159         float cos = 1 - delta * delta / 2;
160         float sin = 1 + delta;
161         AffineTransform transform = new AffineTransform(2 * cos, 0.1, 0.3, -2 * sin, 10, -5);
162         g2d.setTransform(transform);
163         g2d.drawImage(image, 0, -IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_HEIGHT, null);
164         checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling);
165 
166         int D = 10;
167         // From Source to small Destination region
168         bufferedImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
169         g2d = (Graphics2D) bufferedImage.getGraphics();
170         setImageScalingHint(g2d, enableImageScaling);
171         g2d.drawImage(image, IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2, IMAGE_WIDTH - D, IMAGE_HEIGHT - D,
172                 D, D, IMAGE_WIDTH - D, IMAGE_HEIGHT - D, null);
173         checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 4, 3 * IMAGE_HEIGHT / 4), false);
174 
175         // From Source to large Destination region
176         bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
177         g2d = (Graphics2D) bufferedImage.getGraphics();
178         setImageScalingHint(g2d, enableImageScaling);
179         g2d.drawImage(image, D, D, 2 * IMAGE_WIDTH - D, 2 * IMAGE_HEIGHT - D,
180                 IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2, IMAGE_WIDTH - D, IMAGE_HEIGHT - D, null);
181         checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling);
182     }
183 
184     static class MultiResolutionBufferedImage extends BufferedImage
185             implements MultiResolutionImage {
186 
187         Image highResolutionImage;
188 
MultiResolutionBufferedImage()189         public MultiResolutionBufferedImage() {
190             super(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
191             highResolutionImage = new BufferedImage(
192                     2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
193             draw(getGraphics(), 1);
194             draw(highResolutionImage.getGraphics(), 2);
195         }
196 
draw(Graphics graphics, float resolution)197         void draw(Graphics graphics, float resolution) {
198             Graphics2D g2 = (Graphics2D) graphics;
199             g2.scale(resolution, resolution);
200             g2.setColor((resolution == 1) ? COLOR_1X : COLOR_2X);
201             g2.fillRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
202         }
203 
204         @Override
getResolutionVariant(int width, int height)205         public Image getResolutionVariant(int width, int height) {
206             return ((width <= getWidth() && height <= getHeight()))
207                     ? this : highResolutionImage;
208         }
209 
210         @Override
getResolutionVariants()211         public List<Image> getResolutionVariants() {
212             return Arrays.asList(this, highResolutionImage);
213         }
214     }
215 
testToolkitMultiResolutionImagePrepare()216     static void testToolkitMultiResolutionImagePrepare() throws Exception {
217 
218         generateImages();
219 
220         File imageFile = new File(IMAGE_NAME_1X);
221         String fileName = imageFile.getAbsolutePath();
222 
223         Image image = Toolkit.getDefaultToolkit().getImage(fileName);
224 
225         SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit();
226         toolkit.prepareImage(image, IMAGE_WIDTH, IMAGE_HEIGHT, new LoadImageObserver(image));
227 
228         testToolkitMultiResolutionImageLoad(image);
229     }
230 
testToolkitMultiResolutionImageLoad()231     static void testToolkitMultiResolutionImageLoad() throws Exception {
232 
233         generateImages();
234 
235         File imageFile = new File(IMAGE_NAME_1X);
236         String fileName = imageFile.getAbsolutePath();
237         Image image = Toolkit.getDefaultToolkit().getImage(fileName);
238         testToolkitMultiResolutionImageLoad(image);
239     }
240 
testToolkitMultiResolutionImageLoad(Image image)241     static void testToolkitMultiResolutionImageLoad(Image image) throws Exception {
242 
243         MediaTracker tracker = new MediaTracker(new JPanel());
244         tracker.addImage(image, 0);
245         tracker.waitForID(0);
246         if (tracker.isErrorAny()) {
247             throw new RuntimeException("Error during image loading");
248         }
249         tracker.removeImage(image, 0);
250 
251         testImageLoaded(image);
252 
253         int w = image.getWidth(null);
254         int h = image.getHeight(null);
255 
256         Image resolutionVariant = ((MultiResolutionImage) image)
257                 .getResolutionVariant(2 * w, 2 * h);
258 
259         if (image == resolutionVariant) {
260             throw new RuntimeException("Resolution variant is not loaded");
261         }
262 
263         testImageLoaded(resolutionVariant);
264     }
265 
testImageLoaded(Image image)266     static void testImageLoaded(Image image) {
267 
268         SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit();
269 
270         int flags = toolkit.checkImage(image, IMAGE_WIDTH, IMAGE_WIDTH, new SilentImageObserver());
271         if ((flags & (ImageObserver.FRAMEBITS | ImageObserver.ALLBITS)) == 0) {
272             throw new RuntimeException("Image is not loaded!");
273         }
274     }
275 
276     static class SilentImageObserver implements ImageObserver {
277 
278         @Override
imageUpdate(Image img, int infoflags, int x, int y, int width, int height)279         public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
280             throw new RuntimeException("Observer should not be called!");
281         }
282     }
283 
284     static class LoadImageObserver implements ImageObserver {
285 
286         Image image;
287 
LoadImageObserver(Image image)288         public LoadImageObserver(Image image) {
289             this.image = image;
290         }
291 
292         @Override
imageUpdate(Image img, int infoflags, int x, int y, int width, int height)293         public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
294 
295             if (image != img) {
296                 throw new RuntimeException("Original image is not passed to the observer");
297             }
298 
299             if ((infoflags & ImageObserver.WIDTH) != 0) {
300                 if (width != IMAGE_WIDTH) {
301                     throw new RuntimeException("Original width is not passed to the observer");
302                 }
303             }
304 
305             if ((infoflags & ImageObserver.HEIGHT) != 0) {
306                 if (height != IMAGE_HEIGHT) {
307                     throw new RuntimeException("Original height is not passed to the observer");
308                 }
309             }
310 
311             return (infoflags & ALLBITS) == 0;
312         }
313 
314     }
315 
testToolkitMultiResolutionImage()316     static void testToolkitMultiResolutionImage() throws Exception {
317 
318         generateImages();
319 
320         File imageFile = new File(IMAGE_NAME_1X);
321         String fileName = imageFile.getAbsolutePath();
322         URL url = imageFile.toURI().toURL();
323         testToolkitMultiResolutionImageChache(fileName, url);
324 
325         Image image = Toolkit.getDefaultToolkit().getImage(fileName);
326         testToolkitImageObserver(image);
327         testToolkitMultiResolutionImage(image, false);
328         testToolkitMultiResolutionImage(image, true);
329 
330         image = Toolkit.getDefaultToolkit().getImage(url);
331         testToolkitImageObserver(image);
332         testToolkitMultiResolutionImage(image, false);
333         testToolkitMultiResolutionImage(image, true);
334     }
335 
testToolkitMultiResolutionImageChache(String fileName, URL url)336     static void testToolkitMultiResolutionImageChache(String fileName, URL url) {
337 
338         Image img1 = Toolkit.getDefaultToolkit().getImage(fileName);
339         if (!(img1 instanceof MultiResolutionImage)) {
340             throw new RuntimeException("Not a MultiResolutionImage");
341         }
342 
343         Image img2 = Toolkit.getDefaultToolkit().getImage(fileName);
344         if (img1 != img2) {
345             throw new RuntimeException("Image is not cached");
346         }
347 
348         img1 = Toolkit.getDefaultToolkit().getImage(url);
349         if (!(img1 instanceof MultiResolutionImage)) {
350             throw new RuntimeException("Not a MultiResolutionImage");
351         }
352 
353         img2 = Toolkit.getDefaultToolkit().getImage(url);
354         if (img1 != img2) {
355             throw new RuntimeException("Image is not cached");
356         }
357     }
358 
testToolkitMultiResolutionImage(Image image, boolean enableImageScaling)359     static void testToolkitMultiResolutionImage(Image image, boolean enableImageScaling)
360             throws Exception {
361 
362         MediaTracker tracker = new MediaTracker(new JPanel());
363         tracker.addImage(image, 0);
364         tracker.waitForID(0);
365         if (tracker.isErrorAny()) {
366             throw new RuntimeException("Error during image loading");
367         }
368 
369         final BufferedImage bufferedImage1x = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT,
370                 BufferedImage.TYPE_INT_RGB);
371         Graphics2D g1x = (Graphics2D) bufferedImage1x.getGraphics();
372         setImageScalingHint(g1x, false);
373         g1x.drawImage(image, 0, 0, null);
374         checkColor(bufferedImage1x.getRGB(3 * IMAGE_WIDTH / 4, 3 * IMAGE_HEIGHT / 4), false);
375 
376         Image resolutionVariant = ((MultiResolutionImage) image).
377                 getResolutionVariant(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT);
378 
379         if (resolutionVariant == null) {
380             throw new RuntimeException("Resolution variant is null");
381         }
382 
383         MediaTracker tracker2x = new MediaTracker(new JPanel());
384         tracker2x.addImage(resolutionVariant, 0);
385         tracker2x.waitForID(0);
386         if (tracker2x.isErrorAny()) {
387             throw new RuntimeException("Error during scalable image loading");
388         }
389 
390         final BufferedImage bufferedImage2x = new BufferedImage(2 * IMAGE_WIDTH,
391                 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
392         Graphics2D g2x = (Graphics2D) bufferedImage2x.getGraphics();
393         setImageScalingHint(g2x, enableImageScaling);
394         g2x.drawImage(image, 0, 0, 2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null);
395         checkColor(bufferedImage2x.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling);
396 
397         if (!(image instanceof MultiResolutionImage)) {
398             throw new RuntimeException("Not a MultiResolutionImage");
399         }
400 
401         MultiResolutionImage multiResolutionImage = (MultiResolutionImage) image;
402 
403         Image image1x = multiResolutionImage.getResolutionVariant(IMAGE_WIDTH, IMAGE_HEIGHT);
404         Image image2x = multiResolutionImage.getResolutionVariant(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT);
405 
406         if (image1x.getWidth(null) * 2 != image2x.getWidth(null)
407                 || image1x.getHeight(null) * 2 != image2x.getHeight(null)) {
408             throw new RuntimeException("Wrong resolution variant size");
409         }
410     }
411 
testToolkitImageObserver(final Image image)412     static void testToolkitImageObserver(final Image image) {
413 
414         ImageObserver observer = new ImageObserver() {
415 
416             @Override
417             public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
418 
419                 if (img != image) {
420                     throw new RuntimeException("Wrong image in observer");
421                 }
422 
423                 if ((infoflags & (ImageObserver.ERROR | ImageObserver.ABORT)) != 0) {
424                     throw new RuntimeException("Error during image loading");
425                 }
426 
427                 return (infoflags & ImageObserver.ALLBITS) == 0;
428 
429             }
430         };
431 
432         final BufferedImage bufferedImage2x = new BufferedImage(2 * IMAGE_WIDTH,
433                 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
434         Graphics2D g2x = (Graphics2D) bufferedImage2x.getGraphics();
435         setImageScalingHint(g2x, true);
436 
437         g2x.drawImage(image, 0, 0, 2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, observer);
438 
439     }
440 
setImageScalingHint(Graphics2D g2d, boolean enableImageScaling)441     static void setImageScalingHint(Graphics2D g2d, boolean enableImageScaling) {
442         g2d.setRenderingHint(SunHints.KEY_RESOLUTION_VARIANT, enableImageScaling
443                 ? SunHints.VALUE_RESOLUTION_VARIANT_ON
444                 : SunHints.VALUE_RESOLUTION_VARIANT_OFF);
445     }
446 
checkColor(int rgb, boolean isImageScaled)447     static void checkColor(int rgb, boolean isImageScaled) {
448 
449         if (!isImageScaled && COLOR_1X.getRGB() != rgb) {
450             throw new RuntimeException("Wrong 1x color: " + new Color(rgb));
451         }
452 
453         if (isImageScaled && COLOR_2X.getRGB() != rgb) {
454             throw new RuntimeException("Wrong 2x color" + new Color(rgb));
455         }
456     }
457 
generateImages()458     static void generateImages() throws Exception {
459         if (!new File(IMAGE_NAME_1X).exists()) {
460             generateImage(1);
461         }
462 
463         if (!new File(IMAGE_NAME_2X).exists()) {
464             generateImage(2);
465         }
466     }
467 
generateImage(int scale)468     static void generateImage(int scale) throws Exception {
469         BufferedImage image = new BufferedImage(scale * IMAGE_WIDTH, scale * IMAGE_HEIGHT,
470                 BufferedImage.TYPE_INT_RGB);
471         Graphics g = image.getGraphics();
472         g.setColor(scale == 1 ? COLOR_1X : COLOR_2X);
473         g.fillRect(0, 0, scale * IMAGE_WIDTH, scale * IMAGE_HEIGHT);
474         File file = new File(scale == 1 ? IMAGE_NAME_1X : IMAGE_NAME_2X);
475         ImageIO.write(image, "png", file);
476     }
477 
testImageNameTo2xParsing()478     static void testImageNameTo2xParsing() throws Exception {
479 
480         for (String[] testNames : TEST_FILE_NAMES) {
481             String testName = testNames[0];
482             String goldenName = testNames[1];
483             String resultName = getTestScaledImageName(testName);
484 
485             if (!isValidPath(testName) && resultName == null) {
486                 continue;
487             }
488 
489             if (goldenName.equals(resultName)) {
490                 continue;
491             }
492 
493             throw new RuntimeException("Test name " + testName
494                     + ", result name: " + resultName);
495         }
496 
497         for (URL[] testURLs : TEST_URLS) {
498             URL testURL = testURLs[0];
499             URL goldenURL = testURLs[1];
500             URL resultURL = getTestScaledImageURL(testURL);
501 
502             if (!isValidPath(testURL.getPath()) && resultURL == null) {
503                 continue;
504             }
505 
506             if (goldenURL.equals(resultURL)) {
507                 continue;
508             }
509 
510             throw new RuntimeException("Test url: " + testURL
511                     + ", result url: " + resultURL);
512         }
513 
514     }
515 
getTestScaledImageURL(URL url)516     static URL getTestScaledImageURL(URL url) throws Exception {
517         Method method = getScalableImageMethod("getScaledImageURL", URL.class);
518         return (URL) method.invoke(null, url);
519     }
520 
getTestScaledImageName(String name)521     static String getTestScaledImageName(String name) throws Exception {
522         Method method = getScalableImageMethod("getScaledImageName", String.class);
523         return (String) method.invoke(null, name);
524     }
525 
isValidPath(String path)526     private static boolean isValidPath(String path) {
527         return !path.isEmpty() && !path.endsWith("/") && !path.endsWith(".")
528                 && !path.contains("@2x");
529     }
530 
getScalableImageMethod(String name, Class... parameterTypes)531     private static Method getScalableImageMethod(String name,
532             Class... parameterTypes) throws Exception {
533         Toolkit toolkit = Toolkit.getDefaultToolkit();
534         Method method = toolkit.getClass().getDeclaredMethod(name, parameterTypes);
535         method.setAccessible(true);
536         return method;
537     }
538     private static final String[][] TEST_FILE_NAMES;
539     private static final URL[][] TEST_URLS;
540 
541     static {
542         TEST_FILE_NAMES = new String[][]{
543             {"", null},
544             {".", null},
545             {"..", null},
546             {"/", null},
547             {"/.", null},
548             {"dir/", null},
549             {"dir/.", null},
550             {"aaa@2x.png", null},
551             {"/dir/aaa@2x.png", null},
552             {"image", "image@2x"},
553             {"image.ext", "image@2x.ext"},
554             {"image.aaa.ext", "image.aaa@2x.ext"},
555             {"dir/image", "dir/image@2x"},
556             {"dir/image.ext", "dir/image@2x.ext"},
557             {"dir/image.aaa.ext", "dir/image.aaa@2x.ext"},
558             {"dir/aaa.bbb/image", "dir/aaa.bbb/image@2x"},
559             {"dir/aaa.bbb/image.ext", "dir/aaa.bbb/image@2x.ext"},
560             {"dir/aaa.bbb/image.ccc.ext", "dir/aaa.bbb/image.ccc@2x.ext"},
561             {"/dir/image", "/dir/image@2x"},
562             {"/dir/image.ext", "/dir/image@2x.ext"},
563             {"/dir/image.aaa.ext", "/dir/image.aaa@2x.ext"},
564             {"/dir/aaa.bbb/image", "/dir/aaa.bbb/image@2x"},
565             {"/dir/aaa.bbb/image.ext", "/dir/aaa.bbb/image@2x.ext"},
566             {"/dir/aaa.bbb/image.ccc.ext", "/dir/aaa.bbb/image.ccc@2x.ext"}
567         };
568         try {
569             TEST_URLS = new URL[][]{
570                 // file
571                 {new URL("file:/aaa"), new URL("file:/aaa@2x")},
572                 {new URL("file:/aaa.ext"), new URL("file:/aaa@2x.ext")},
573                 {new URL("file:/aaa.bbb.ext"), new URL("file:/aaa.bbb@2x.ext")},
574                 {new URL("file:/ccc/aaa.bbb.ext"),
575                     new URL("file:/ccc/aaa.bbb@2x.ext")},
576                 {new URL("file:/ccc.ddd/aaa.bbb.ext"),
577                     new URL("file:/ccc.ddd/aaa.bbb@2x.ext")},
578                 {new URL("file:///~/image"), new URL("file:///~/image@2x")},
579                 {new URL("file:///~/image.ext"),
580                     new URL("file:///~/image@2x.ext")},
581                 // http
582                 {new URL("http://www.test.com"), null},
583                 {new URL("http://www.test.com/"), null},
584                 {new URL("http://www.test.com///"), null},
585                 {new URL("http://www.test.com/image"),
586                     new URL("http://www.test.com/image@2x")},
587                 {new URL("http://www.test.com/image.ext"),
588                     new URL("http://www.test.com/image@2x.ext")},
589                 {new URL("http://www.test.com/dir/image"),
590                     new URL("http://www.test.com/dir/image@2x")},
591                 {new URL("http://www.test.com:80/dir/image.aaa.ext"),
592                     new URL("http://www.test.com:80/dir/image.aaa@2x.ext")},
593                 {new URL("http://www.test.com:8080/dir/image.aaa.ext"),
594                     new URL("http://www.test.com:8080/dir/image.aaa@2x.ext")},
595                 // jar
596                 {new URL("jar:file:/dir/Java2D.jar!/image"),
597                     new URL("jar:file:/dir/Java2D.jar!/image@2x")},
598                 {new URL("jar:file:/dir/Java2D.jar!/image.aaa.ext"),
599                     new URL("jar:file:/dir/Java2D.jar!/image.aaa@2x.ext")},
600                 {new URL("jar:file:/dir/Java2D.jar!/images/image"),
601                     new URL("jar:file:/dir/Java2D.jar!/images/image@2x")},
602                 {new URL("jar:file:/dir/Java2D.jar!/images/image.ext"),
603                     new URL("jar:file:/dir/Java2D.jar!/images/image@2x.ext")},
604                 {new URL("jar:file:/aaa.bbb/Java2D.jar!/images/image.ext"),
605                     new URL("jar:file:/aaa.bbb/Java2D.jar!/images/image@2x.ext")},
606                 {new URL("jar:file:/dir/Java2D.jar!/aaa.bbb/image.ext"),
607                     new URL("jar:file:/dir/Java2D.jar!/aaa.bbb/image@2x.ext")},};
608         } catch (Exception e) {
609             throw new RuntimeException(e);
610         }
611     }
612 
613     static class PreloadedImageObserver implements ImageObserver {
614 
615         @Override
imageUpdate(Image img, int infoflags, int x, int y, int width, int height)616         public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
617             throw new RuntimeException("Image should be already preloaded");
618         }
619     }
620 }
621