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 
  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 
  99     static boolean checkOS() {
 100         return OSInfo.getOSType() == OSInfo.OSType.MACOSX;
 101     }
 102 
 103     public static void testCustomMultiResolutionImage() {
 104         testCustomMultiResolutionImage(false);
 105         testCustomMultiResolutionImage(true);
 106     }
 107 
 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 
 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 
 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
 205         public Image getResolutionVariant(int width, int height) {
 206             return ((width <= getWidth() && height <= getHeight()))
 207                     ? this : highResolutionImage;
 208         }
 209 
 210         @Override
 211         public List<Image> getResolutionVariants() {
 212             return Arrays.asList(this, highResolutionImage);
 213         }
 214     }
 215 
 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 
 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 
 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 
 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
 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 
 288         public LoadImageObserver(Image image) {
 289             this.image = image;
 290         }
 291 
 292         @Override
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 516     static URL getTestScaledImageURL(URL url) throws Exception {
 517         Method method = getScalableImageMethod("getScaledImageURL", URL.class);
 518         return (URL) method.invoke(null, url);
 519     }
 520 
 521     static String getTestScaledImageName(String name) throws Exception {
 522         Method method = getScalableImageMethod("getScaledImageName", String.class);
 523         return (String) method.invoke(null, name);
 524     }
 525 
 526     private static boolean isValidPath(String path) {
 527         return !path.isEmpty() && !path.endsWith("/") && !path.endsWith(".")
 528                 && !path.contains("@2x");
 529     }
 530 
 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
 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 }