changeset 12778:7869179af058

8029339: Custom MultiResolution image support on HiDPI displays Reviewed-by: flar, serb
author alexsch
date Tue, 15 Sep 2015 15:31:34 +0400
parents 298cca968b4f
children 38169ef6e325
files src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java src/java.desktop/macosx/classes/sun/lwawt/macosx/CImage.java src/java.desktop/share/classes/java/awt/RenderingHints.java src/java.desktop/share/classes/java/awt/image/AbstractMultiResolutionImage.java src/java.desktop/share/classes/java/awt/image/BaseMultiResolutionImage.java src/java.desktop/share/classes/java/awt/image/MultiResolutionImage.java src/java.desktop/share/classes/sun/awt/SunHints.java src/java.desktop/share/classes/sun/awt/SunToolkit.java src/java.desktop/share/classes/sun/awt/image/AbstractMultiResolutionImage.java src/java.desktop/share/classes/sun/awt/image/MultiResolutionCachedImage.java src/java.desktop/share/classes/sun/awt/image/MultiResolutionImage.java src/java.desktop/share/classes/sun/awt/image/MultiResolutionToolkitImage.java src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java test/java/awt/Cursor/MultiResolutionCursorTest/MultiResolutionCursorTest.java test/java/awt/image/MultiResolutionImage/NSImageToMultiResolutionImageTest.java test/java/awt/image/MultiResolutionImageCommonTest.java test/java/awt/image/MultiResolutionImageTest.java test/java/awt/image/multiresolution/BaseMultiResolutionImageTest.java test/java/awt/image/multiresolution/MultiResolutionCachedImageTest.java test/java/awt/image/multiresolution/MultiResolutionRenderingHintsTest.java
diffstat 20 files changed, 942 insertions(+), 308 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java	Mon Sep 14 09:40:24 2015 -0700
+++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java	Tue Sep 15 15:31:34 2015 +0400
@@ -46,7 +46,7 @@
 import com.apple.laf.AquaIcon.SystemIcon;
 import com.apple.laf.AquaUtils.RecyclableObject;
 import com.apple.laf.AquaUtils.RecyclableSingleton;
-import sun.awt.image.MultiResolutionImage;
+import java.awt.image.MultiResolutionImage;
 import sun.awt.image.MultiResolutionCachedImage;
 
 public class AquaImageFactory {
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CImage.java	Mon Sep 14 09:40:24 2015 -0700
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CImage.java	Tue Sep 15 15:31:34 2015 +0400
@@ -31,7 +31,7 @@
 
 import java.util.Arrays;
 import java.util.List;
-import sun.awt.image.MultiResolutionImage;
+import java.awt.image.MultiResolutionImage;
 import sun.awt.image.MultiResolutionCachedImage;
 
 import sun.awt.image.SunWritableRaster;
--- a/src/java.desktop/share/classes/java/awt/RenderingHints.java	Mon Sep 14 09:40:24 2015 -0700
+++ b/src/java.desktop/share/classes/java/awt/RenderingHints.java	Tue Sep 15 15:31:34 2015 +0400
@@ -955,6 +955,64 @@
         SunHints.VALUE_STROKE_PURE;
 
     /**
+     * Image resolution variant hint key.
+     * The {@code RESOLUTION_VARIANT} hint controls which image resolution
+     * variant should be chosen for image drawing.
+     *
+     * <ul>
+     * <li>{@link #VALUE_RESOLUTION_VARIANT_DEFAULT}
+     * <li>{@link #VALUE_RESOLUTION_VARIANT_BASE}
+     * <li>{@link #VALUE_RESOLUTION_VARIANT_SIZE_FIT}
+     * <li>{@link #VALUE_RESOLUTION_VARIANT_DPI_FIT}
+     * </ul>
+     * @since 1.9
+     */
+    public static final Key KEY_RESOLUTION_VARIANT =
+        SunHints.KEY_RESOLUTION_VARIANT;
+
+    /**
+     * Image resolution variant hint value -- an image resolution variant is
+     * chosen based on a default heuristic which may depend on the policies
+     * of the platform
+     *
+     * @see #KEY_RESOLUTION_VARIANT
+     * @since 1.9
+     */
+    public static final Object VALUE_RESOLUTION_VARIANT_DEFAULT =
+        SunHints.VALUE_RESOLUTION_VARIANT_DEFAULT;
+
+    /**
+     * Image resolution variant hint value -- the standard resolution of an image
+     * is always used.
+     *
+     * @see #KEY_RESOLUTION_VARIANT
+     * @since 1.9
+     */
+    public static final Object VALUE_RESOLUTION_VARIANT_BASE =
+        SunHints.VALUE_RESOLUTION_VARIANT_BASE;
+
+    /**
+     * Image resolution variant hint value -- an image resolution variant is
+     * chosen based on the DPI of the screen and the transform in the Graphics2D
+     * context.
+     *
+     * @see #KEY_RESOLUTION_VARIANT
+     * @since 1.9
+     */
+    public static final Object VALUE_RESOLUTION_VARIANT_SIZE_FIT =
+        SunHints.VALUE_RESOLUTION_VARIANT_SIZE_FIT;
+
+    /**
+     * Image resolution variant hint value -- an image resolution variant is
+     * chosen based only on the DPI of the screen.
+     *
+     * @see #KEY_RESOLUTION_VARIANT
+     * @since 1.9
+     */
+    public static final Object VALUE_RESOLUTION_VARIANT_DPI_FIT =
+        SunHints.VALUE_RESOLUTION_VARIANT_DPI_FIT;
+
+    /**
      * Constructs a new object with keys and values initialized
      * from the specified Map object which may be null.
      * @param init a map of key/value pairs to initialize the hints
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.desktop/share/classes/java/awt/image/AbstractMultiResolutionImage.java	Tue Sep 15 15:31:34 2015 +0400
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package java.awt.image;
+
+import java.awt.Graphics;
+import java.awt.Image;
+
+/**
+ * This class provides default implementations of several {@code Image} methods
+ * for classes that want to implement the {@MultiResolutionImage} interface.
+ *
+ * For example,
+ * <pre> {@code
+ * public class CustomMultiResolutionImage extends AbstractMultiResolutionImage {
+ *
+ *     final Image[] resolutionVariants;
+ *
+ *     public CustomMultiResolutionImage(Image... resolutionVariants) {
+ *          this.resolutionVariants = resolutionVariants;
+ *     }
+ *
+ *     public Image getResolutionVariant(
+ *             double destImageWidth, double destImageHeight) {
+ *         // return a resolution variant based on the given destination image size
+ *     }
+ *
+ *     public List<Image> getResolutionVariants() {
+ *         return Collections.unmodifiableList(Arrays.asList(resolutionVariants));
+ *     }
+ *
+ *     protected Image getBaseImage() {
+ *         return resolutionVariants[0];
+ *     }
+ * }
+ * } </pre>
+ *
+ * @see java.awt.Image
+ * @see java.awt.image.MultiResolutionImage
+ *
+ * @since 1.9
+ */
+public abstract class AbstractMultiResolutionImage extends java.awt.Image
+        implements MultiResolutionImage {
+
+    @Override
+    public int getWidth(ImageObserver observer) {
+        return getBaseImage().getWidth(observer);
+    }
+
+    @Override
+    public int getHeight(ImageObserver observer) {
+        return getBaseImage().getHeight(observer);
+    }
+
+    @Override
+    public ImageProducer getSource() {
+        return getBaseImage().getSource();
+    }
+
+    @Override
+    public Graphics getGraphics() {
+        throw new UnsupportedOperationException("getGraphics() not supported"
+                + " on Multi-Resolution Images");
+    }
+
+    @Override
+    public Object getProperty(String name, ImageObserver observer) {
+        return getBaseImage().getProperty(name, observer);
+    }
+
+    /**
+     * Return the base image representing the best version of the image for
+     * rendering at the default width and height.
+     *
+     * @return the base image of the set of multi-resolution images
+     *
+     * @since 1.9
+     */
+    protected abstract Image getBaseImage();
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.desktop/share/classes/java/awt/image/BaseMultiResolutionImage.java	Tue Sep 15 15:31:34 2015 +0400
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package java.awt.image;
+
+import java.awt.Image;
+import java.util.List;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Objects;
+
+/**
+ * This class is an array-based implementation of
+ * the {@code AbstractMultiResolutionImage} class.
+ *
+ * This class will implement the
+ * {@code getResolutionVariant(double destImageWidth, double destImageHeight)}
+ * method using a simple algorithm which will return the first image variant
+ * in the array that is large enough to satisfy the rendering request. The
+ * last image in the array will be returned if no suitable image is found
+ * that is as large as the rendering request.
+ * <p>
+ * For best effect the array of images should be sorted with each image being
+ * both wider and taller than the previous image.  The base image need not be
+ * the first image in the array. No exception will be thrown if the images
+ * are not sorted as suggested.
+ *
+ * @see java.awt.Image
+ * @see java.awt.image.MultiResolutionImage
+ * @see java.awt.image.AbstractMultiResolutionImage
+ *
+ * @since 1.9
+ */
+public class BaseMultiResolutionImage extends AbstractMultiResolutionImage {
+
+    private final int baseImageIndex;
+    private final Image[] resolutionVariants;
+
+    /**
+     * Creates a multi-resolution image with the given resolution variants.
+     * The first resolution variant is used as the base image.
+     *
+     * @param resolutionVariants array of resolution variants sorted by image size
+     * @throws IllegalArgumentException if null or zero-length array is passed
+     * @throws NullPointerException if the specified {@code resolutionVariants}
+     *          contains one or more null elements
+     *
+     * @since 1.9
+     */
+    public BaseMultiResolutionImage(Image... resolutionVariants) {
+        this(0, resolutionVariants);
+    }
+
+    /**
+     * Creates a multi-resolution image with the given base image index and
+     * resolution variants.
+     *
+     * @param baseImageIndex the index of base image in the resolution variants
+     *        array
+     * @param resolutionVariants array of resolution variants sorted by image size
+     * @throws IllegalArgumentException if null or zero-length array is passed
+     * @throws NullPointerException if the specified {@code resolutionVariants}
+     *          contains one or more null elements
+     * @throws IndexOutOfBoundsException if {@code baseImageIndex} is
+     *          negative or greater than or equal to {@code resolutionVariants}
+     *          length.
+     *
+     * @since 1.9
+     */
+    public BaseMultiResolutionImage(int baseImageIndex,
+                                    Image... resolutionVariants) {
+
+        if (resolutionVariants == null || resolutionVariants.length == 0) {
+            throw new IllegalArgumentException(
+                    "Null or zero-length array is passed");
+        }
+
+        if (baseImageIndex < 0 || baseImageIndex >= resolutionVariants.length) {
+            throw new IndexOutOfBoundsException("Invalid base image index: "
+                    + baseImageIndex);
+        }
+
+        this.baseImageIndex = baseImageIndex;
+        this.resolutionVariants = Arrays.copyOf(resolutionVariants,
+                                                resolutionVariants.length);
+
+        for (Image resolutionVariant : this.resolutionVariants) {
+            Objects.requireNonNull(resolutionVariant,
+                                   "Resolution variant can't be null");
+        }
+    }
+
+    @Override
+    public Image getResolutionVariant(double destImageWidth,
+                                      double destImageHeight) {
+
+        checkSize(destImageWidth, destImageHeight);
+
+        for (Image rvImage : resolutionVariants) {
+            if (destImageWidth <= rvImage.getWidth(null)
+                    && destImageHeight <= rvImage.getHeight(null)) {
+                return rvImage;
+            }
+        }
+        return resolutionVariants[resolutionVariants.length - 1];
+    }
+
+    private static void checkSize(double width, double height) {
+        if (width <= 0 || height <= 0) {
+            throw new IllegalArgumentException(String.format(
+                    "Width (%s) or height (%s) cannot be <= 0", width, height));
+        }
+
+        if (!Double.isFinite(width) || !Double.isFinite(height)) {
+            throw new IllegalArgumentException(String.format(
+                    "Width (%s) or height (%s) is not finite", width, height));
+        }
+    }
+
+    @Override
+    public List<Image> getResolutionVariants() {
+        return Collections.unmodifiableList(Arrays.asList(resolutionVariants));
+    }
+
+    @Override
+    protected Image getBaseImage() {
+        return resolutionVariants[baseImageIndex];
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.desktop/share/classes/java/awt/image/MultiResolutionImage.java	Tue Sep 15 15:31:34 2015 +0400
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package java.awt.image;
+
+import java.awt.Image;
+import java.util.List;
+
+/**
+ * This interface is designed to be an optional additional API supported by
+ * some implementations of {@link java.awt.Image} to allow them to provide
+ * alternate images for various rendering resolutions. The various
+ * {@code Graphics.drawImage(...)} variant methods will consult the methods
+ * of this interface if it is implemented on the argument {@code Image} object
+ * in order to choose the best representation to use for each rendering operation.
+ * <p>
+ * The {@code MultiResolutionImage} interface should be implemented by any
+ * subclass of {@code java.awt.Image} whose instances are intended to provide
+ * image resolution variants according to the given image width and height.
+ * For convenience, toolkit images obtained from
+ * {@code Toolkit.getImage(String name)} and {@code Toolkit.getImage(URL url)}
+ * will implement this interface on platforms that support naming conventions
+ * for resolution variants of stored image media and the
+ * {@code AbstractMultiResolutionImage} and {@code BaseMultiResolutionImage}
+ * classes are provided to facilitate easy construction of custom multi-resolution
+ * images from a list of related images.
+ *
+ * @see java.awt.Image
+ * @see java.awt.image.AbstractMultiResolutionImage
+ * @see java.awt.image.BaseMultiResolutionImage
+ * @see java.awt.Toolkit#getImage(java.lang.String filename)
+ * @see java.awt.Toolkit#getImage(java.net.URL url)
+ *
+ * @since 1.9
+ */
+public interface MultiResolutionImage {
+
+    /**
+     * Gets a specific image that is the best variant to represent
+     * this logical image at the indicated size.
+     *
+     * @param destImageWidth the width of the destination image, in pixels.
+     * @param destImageHeight the height of the destination image, in pixels.
+     * @return image resolution variant.
+     * @throws IllegalArgumentException if {@code destImageWidth} or
+     *         {@code destImageHeight} is less than or equal to zero, infinity,
+     *         or NaN.
+     *
+     * @since 1.9
+     */
+    Image getResolutionVariant(double destImageWidth, double destImageHeight);
+
+    /**
+     * Gets a readable list of all resolution variants.
+     * The list must be nonempty and contain at least one resolution variant.
+     * <p>
+     * Note that many implementations might return an unmodifiable list.
+     * <p>
+     * @return list of resolution variants.
+     * @since 1.9
+     */
+    public List<Image> getResolutionVariants();
+}
\ No newline at end of file
--- a/src/java.desktop/share/classes/sun/awt/SunHints.java	Mon Sep 14 09:40:24 2015 -0700
+++ b/src/java.desktop/share/classes/sun/awt/SunHints.java	Tue Sep 15 15:31:34 2015 +0400
@@ -257,8 +257,10 @@
      */
     @Native public static final int INTKEY_RESOLUTION_VARIANT = 9;
     @Native public static final int INTVAL_RESOLUTION_VARIANT_DEFAULT = 0;
-    @Native public static final int INTVAL_RESOLUTION_VARIANT_OFF = 1;
-    @Native public static final int INTVAL_RESOLUTION_VARIANT_ON = 2;
+    @Native public static final int INTVAL_RESOLUTION_VARIANT_BASE = 1;
+    @Native public static final int INTVAL_RESOLUTION_VARIANT_SIZE_FIT = 2;
+    @Native public static final int INTVAL_RESOLUTION_VARIANT_DPI_FIT = 3;
+
     /**
      * LCD text contrast control hint key.
      * Value is "100" to make discontiguous with the others which
@@ -466,15 +468,23 @@
     public static final Object VALUE_RESOLUTION_VARIANT_DEFAULT =
         new SunHints.Value(KEY_RESOLUTION_VARIANT,
                            SunHints.INTVAL_RESOLUTION_VARIANT_DEFAULT,
-                           "Choose image resolutions based on a default heuristic");
-    public static final Object VALUE_RESOLUTION_VARIANT_OFF =
+                           "Choose image resolutions based on a default"
+                                   + "heuristic");
+    public static final Object VALUE_RESOLUTION_VARIANT_BASE =
         new SunHints.Value(KEY_RESOLUTION_VARIANT,
-                           SunHints.INTVAL_RESOLUTION_VARIANT_OFF,
+                           SunHints.INTVAL_RESOLUTION_VARIANT_BASE,
                            "Use only the standard resolution of an image");
-    public static final Object VALUE_RESOLUTION_VARIANT_ON =
+    public static final Object VALUE_RESOLUTION_VARIANT_SIZE_FIT =
         new SunHints.Value(KEY_RESOLUTION_VARIANT,
-                           SunHints.INTVAL_RESOLUTION_VARIANT_ON,
-                           "Always use resolution-specific variants of images");
+                           SunHints.INTVAL_RESOLUTION_VARIANT_SIZE_FIT,
+                           "Choose image resolutions based on the DPI"
+                                   + "of the screen and transform"
+                                   + "in the Graphics2D context");
+    public static final Object VALUE_RESOLUTION_VARIANT_DPI_FIT =
+        new SunHints.Value(KEY_RESOLUTION_VARIANT,
+                           SunHints.INTVAL_RESOLUTION_VARIANT_DPI_FIT,
+                           "Choose image resolutions based only on the DPI"
+                                   + " of the screen");
 
     public static class LCDContrastKey extends Key {
 
--- a/src/java.desktop/share/classes/sun/awt/SunToolkit.java	Mon Sep 14 09:40:24 2015 -0700
+++ b/src/java.desktop/share/classes/sun/awt/SunToolkit.java	Tue Sep 15 15:31:34 2015 +0400
@@ -60,7 +60,7 @@
 import sun.awt.image.ByteArrayImageSource;
 import sun.awt.image.FileImageSource;
 import sun.awt.image.ImageRepresentation;
-import sun.awt.image.MultiResolutionImage;
+import java.awt.image.MultiResolutionImage;
 import sun.awt.image.MultiResolutionToolkitImage;
 import sun.awt.image.ToolkitImage;
 import sun.awt.image.URLImageSource;
--- a/src/java.desktop/share/classes/sun/awt/image/AbstractMultiResolutionImage.java	Mon Sep 14 09:40:24 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +0,0 @@
-/*
- * 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.
- */
-package sun.awt.image;
-
-import java.awt.Graphics;
-import java.awt.Image;
-import java.awt.image.*;
-
-/**
- * This class provides default implementations for the
- * <code>MultiResolutionImage</code> interface. The developer needs only
- * to subclass this abstract class and define the <code>getResolutionVariant</code>,
- * <code>getResolutionVariants</code>, and <code>getBaseImage</code> methods.
- *
- *
- * For example,
- * {@code
- * public class CustomMultiResolutionImage extends AbstractMultiResolutionImage {
- *
- *     int baseImageIndex;
- *     Image[] resolutionVariants;
- *
- *     public CustomMultiResolutionImage(int baseImageIndex,
- *             Image... resolutionVariants) {
- *          this.baseImageIndex = baseImageIndex;
- *          this.resolutionVariants = resolutionVariants;
- *     }
- *
- *     @Override
- *     public Image getResolutionVariant(float logicalDPIX, float logicalDPIY,
- *             float baseImageWidth, float baseImageHeight,
- *             float destImageWidth, float destImageHeight) {
- *         // return a resolution variant based on the given logical DPI,
- *         // base image size, or destination image size
- *     }
- *
- *     @Override
- *     public List<Image> getResolutionVariants() {
- *         return Arrays.asList(resolutionVariants);
- *     }
- *
- *     protected Image getBaseImage() {
- *         return resolutionVariants[baseImageIndex];
- *     }
- * }
- * }
- *
- * @see java.awt.Image
- * @see java.awt.image.MultiResolutionImage
- *
- * @since 1.9
- */
-public abstract class AbstractMultiResolutionImage extends java.awt.Image
-        implements MultiResolutionImage {
-
-    /**
-     * @inheritDoc
-     */
-    @Override
-    public int getWidth(ImageObserver observer) {
-        return getBaseImage().getWidth(null);
-    }
-
-    /**
-     * @inheritDoc
-     */
-    @Override
-    public int getHeight(ImageObserver observer) {
-        return getBaseImage().getHeight(null);
-    }
-
-    /**
-     * @inheritDoc
-     */
-    @Override
-    public ImageProducer getSource() {
-        return getBaseImage().getSource();
-    }
-
-    /**
-     * @inheritDoc
-     */
-    @Override
-    public Graphics getGraphics() {
-        return getBaseImage().getGraphics();
-
-    }
-
-    /**
-     * @inheritDoc
-     */
-    @Override
-    public Object getProperty(String name, ImageObserver observer) {
-        return getBaseImage().getProperty(name, observer);
-    }
-
-    /**
-     * @return base image
-     */
-    protected abstract Image getBaseImage();
-}
--- a/src/java.desktop/share/classes/sun/awt/image/MultiResolutionCachedImage.java	Mon Sep 14 09:40:24 2015 -0700
+++ b/src/java.desktop/share/classes/sun/awt/image/MultiResolutionCachedImage.java	Tue Sep 15 15:31:34 2015 +0400
@@ -33,6 +33,7 @@
 import java.util.function.Function;
 import java.util.function.BiFunction;
 import java.util.stream.Collectors;
+import java.awt.image.AbstractMultiResolutionImage;
 
 public class MultiResolutionCachedImage extends AbstractMultiResolutionImage {
 
@@ -58,7 +59,10 @@
     }
 
     @Override
-    public Image getResolutionVariant(int width, int height) {
+    public Image getResolutionVariant(double destWidth, double destHeight) {
+        checkSize(destWidth, destHeight);
+        int width = (int) Math.ceil(destWidth);
+        int height = (int) Math.ceil(destHeight);
         ImageCache cache = ImageCache.getInstance();
         ImageCacheKey key = new ImageCacheKey(this, width, height);
         Image resolutionVariant = cache.getImage(key);
@@ -70,11 +74,23 @@
         return resolutionVariant;
     }
 
+    private static void checkSize(double width, double height) {
+        if (width <= 0 || height <= 0) {
+            throw new IllegalArgumentException(String.format(
+                    "Width (%s) or height (%s) cannot be <= 0", width, height));
+        }
+
+        if (!Double.isFinite(width) || !Double.isFinite(height)) {
+            throw new IllegalArgumentException(String.format(
+                    "Width (%s) or height (%s) is not finite", width, height));
+        }
+    }
+
     @Override
     public List<Image> getResolutionVariants() {
         return Arrays.stream(sizes).map((Function<Dimension2D, Image>) size
-                -> getResolutionVariant((int) size.getWidth(),
-                        (int) size.getHeight())).collect(Collectors.toList());
+                -> getResolutionVariant(size.getWidth(), size.getHeight()))
+                .collect(Collectors.toList());
     }
 
     public MultiResolutionCachedImage map(Function<Image, Image> mapper) {
--- a/src/java.desktop/share/classes/sun/awt/image/MultiResolutionImage.java	Mon Sep 14 09:40:24 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-/*
- * 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.
- */
-package sun.awt.image;
-
-import java.awt.Image;
-import java.util.List;
-
-/**
- * This interface is designed to provide a set of images at various resolutions.
- *
- * The <code>MultiResolutionImage</code> interface should be implemented by any
- * class whose instances are intended to provide image resolution variants
- * according to the given image width and height.
- *
- * For example,
- * <pre>
- * {@code
- *  public class ScaledImage extends BufferedImage
- *         implements MultiResolutionImage {
- *
- *    @Override
- *    public Image getResolutionVariant(int width, int height) {
- *      return ((width <= getWidth() && height <= getHeight()))
- *             ? this : highResolutionImage;
- *    }
- *
- *    @Override
- *    public List<Image> getResolutionVariants() {
- *        return Arrays.asList(this, highResolutionImage);
- *    }
- *  }
- * }</pre>
- *
- * It is recommended to cache image variants for performance reasons.
- *
- * <b>WARNING</b>: This class is an implementation detail. This API may change
- * between update release, and it may even be removed or be moved in some other
- * package(s)/class(es).
- */
-public interface MultiResolutionImage {
-
-    /**
-     * Provides an image with necessary resolution which best fits to the given
-     * image width and height.
-     *
-     * @param width the desired image resolution width.
-     * @param height the desired image resolution height.
-     * @return image resolution variant.
-     *
-     * @since 1.8
-     */
-    public Image getResolutionVariant(int width, int height);
-
-    /**
-     * Gets list of all resolution variants including the base image
-     *
-     * @return list of resolution variants.
-     * @since 1.8
-     */
-    public List<Image> getResolutionVariants();
-}
--- a/src/java.desktop/share/classes/sun/awt/image/MultiResolutionToolkitImage.java	Mon Sep 14 09:40:24 2015 -0700
+++ b/src/java.desktop/share/classes/sun/awt/image/MultiResolutionToolkitImage.java	Tue Sep 15 15:31:34 2015 +0400
@@ -26,6 +26,7 @@
 
 import java.awt.Image;
 import java.awt.image.ImageObserver;
+import java.awt.image.MultiResolutionImage;
 import java.util.Arrays;
 import java.util.List;
 import sun.misc.SoftCache;
@@ -40,11 +41,24 @@
     }
 
     @Override
-    public Image getResolutionVariant(int width, int height) {
-        return ((width <= getWidth() && height <= getHeight()))
+    public Image getResolutionVariant(double destWidth, double destHeight) {
+        checkSize(destWidth, destHeight);
+        return ((destWidth <= getWidth() && destHeight <= getHeight()))
                 ? this : resolutionVariant;
     }
 
+    private static void checkSize(double width, double height) {
+        if (width <= 0 || height <= 0) {
+            throw new IllegalArgumentException(String.format(
+                    "Width (%s) or height (%s) cannot be <= 0", width, height));
+        }
+
+        if (!Double.isFinite(width) || !Double.isFinite(height)) {
+            throw new IllegalArgumentException(String.format(
+                    "Width (%s) or height (%s) is not finite", width, height));
+        }
+    }
+
     public Image getResolutionVariant() {
         return resolutionVariant;
     }
--- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java	Mon Sep 14 09:40:24 2015 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java	Tue Sep 15 15:31:34 2015 +0400
@@ -94,7 +94,7 @@
 import sun.misc.PerformanceLogger;
 
 import java.lang.annotation.Native;
-import sun.awt.image.MultiResolutionImage;
+import java.awt.image.MultiResolutionImage;
 
 import static java.awt.geom.AffineTransform.TYPE_FLIP;
 import static java.awt.geom.AffineTransform.TYPE_MASK_SCALE;
@@ -3087,9 +3087,8 @@
 // end of text rendering methods
 
     private boolean isHiDPIImage(final Image img) {
-        return (SurfaceManager.getImageScale(img) != 1) ||
-               (resolutionVariantHint != SunHints.INTVAL_RESOLUTION_VARIANT_OFF
-                    && img instanceof MultiResolutionImage);
+        return (SurfaceManager.getImageScale(img) != 1)
+                || img instanceof MultiResolutionImage;
     }
 
     private boolean drawHiDPIImage(Image img, int dx1, int dy1, int dx2,
@@ -3175,25 +3174,42 @@
         int type = transform.getType();
         int dw = dx2 - dx1;
         int dh = dy2 - dy1;
-        double destRegionWidth;
-        double destRegionHeight;
-
-        if ((type & ~(TYPE_TRANSLATION | TYPE_FLIP)) == 0) {
-            destRegionWidth = dw;
-            destRegionHeight = dh;
-        } else if ((type & ~(TYPE_TRANSLATION | TYPE_FLIP | TYPE_MASK_SCALE)) == 0) {
-            destRegionWidth = dw * transform.getScaleX();
-            destRegionHeight = dh * transform.getScaleY();
+
+        double destImageWidth;
+        double destImageHeight;
+
+        if (resolutionVariantHint == SunHints.INTVAL_RESOLUTION_VARIANT_BASE) {
+            destImageWidth = srcWidth;
+            destImageHeight = srcHeight;
+        } else if (resolutionVariantHint == SunHints.INTVAL_RESOLUTION_VARIANT_DPI_FIT) {
+            AffineTransform configTransform = getDefaultTransform();
+            if (configTransform.isIdentity()) {
+                destImageWidth = srcWidth;
+                destImageHeight = srcHeight;
+            } else {
+                destImageWidth = srcWidth * configTransform.getScaleX();
+                destImageHeight = srcHeight * configTransform.getScaleY();
+            }
         } else {
-            destRegionWidth = dw * Math.hypot(
-                    transform.getScaleX(), transform.getShearY());
-            destRegionHeight = dh * Math.hypot(
-                    transform.getShearX(), transform.getScaleY());
+            double destRegionWidth;
+            double destRegionHeight;
+
+            if ((type & ~(TYPE_TRANSLATION | TYPE_FLIP)) == 0) {
+                destRegionWidth = dw;
+                destRegionHeight = dh;
+            } else if ((type & ~(TYPE_TRANSLATION | TYPE_FLIP | TYPE_MASK_SCALE)) == 0) {
+                destRegionWidth = dw * transform.getScaleX();
+                destRegionHeight = dh * transform.getScaleY();
+            } else {
+                destRegionWidth = dw * Math.hypot(
+                        transform.getScaleX(), transform.getShearY());
+                destRegionHeight = dh * Math.hypot(
+                        transform.getShearX(), transform.getScaleY());
+            }
+            destImageWidth = Math.abs(srcWidth * destRegionWidth / sw);
+            destImageHeight = Math.abs(srcHeight * destRegionHeight / sh);
         }
 
-        int destImageWidth = (int) Math.abs(srcWidth * destRegionWidth / sw);
-        int destImageHeight = (int) Math.abs(srcHeight * destRegionHeight / sh);
-
         Image resolutionVariant
                 = img.getResolutionVariant(destImageWidth, destImageHeight);
 
--- a/test/java/awt/Cursor/MultiResolutionCursorTest/MultiResolutionCursorTest.java	Mon Sep 14 09:40:24 2015 -0700
+++ b/test/java/awt/Cursor/MultiResolutionCursorTest/MultiResolutionCursorTest.java	Tue Sep 15 15:31:34 2015 +0400
@@ -25,19 +25,16 @@
 import java.awt.Cursor;
 import java.awt.Dialog;
 import java.awt.Frame;
-import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.Image;
 import java.awt.Label;
 import java.awt.Point;
 import java.awt.TextArea;
 import java.awt.Toolkit;
+import java.awt.image.BaseMultiResolutionImage;
 import java.awt.image.BufferedImage;
-import java.util.LinkedList;
-import java.util.List;
 import javax.swing.JApplet;
 import jdk.testlibrary.OSInfo;
-import sun.awt.image.MultiResolutionImage;
 
 /**
  * @test
@@ -52,7 +49,7 @@
 public class MultiResolutionCursorTest extends JApplet {
     //Declare things used in the test, like buttons and labels here
 
-    static final int sizes[] = {16, 32, 128};
+    static final int sizes[] = {8, 16, 32, 128};
     static final Color colors[] = {Color.WHITE, Color.RED, Color.GREEN, Color.BLUE};
 
     public void init() {
@@ -87,7 +84,12 @@
         setVisible(true);
         validate();
 
-        final Image image = new MultiResolutionCursor();
+        final Image image = new BaseMultiResolutionImage(
+                createResolutionVariant(0),
+                createResolutionVariant(1),
+                createResolutionVariant(2),
+                createResolutionVariant(3)
+        );
 
         int center = sizes[0] / 2;
         Cursor cursor = Toolkit.getDefaultToolkit().createCustomCursor(
@@ -101,53 +103,14 @@
         frame.setVisible(true);
     }// start()
 
-
-    static class MultiResolutionCursor extends BufferedImage implements MultiResolutionImage {
-
-        List<Image> highResolutionImages;
-
-        public MultiResolutionCursor() {
-            super(sizes[0], sizes[0], BufferedImage.TYPE_INT_RGB);
-
-            draw(getGraphics(), 0);
-            highResolutionImages = new LinkedList<>();
-            highResolutionImages.add(this);
-
-            for (int i = 1; i < sizes.length; i++) {
-                BufferedImage highResolutionImage =
-                        new BufferedImage(sizes[i], sizes[i], BufferedImage.TYPE_INT_RGB);
-                draw(highResolutionImage.getGraphics(), i);
-                highResolutionImages.add(highResolutionImage);
-            }
-        }
-
-        @Override
-        public Image getResolutionVariant(int width, int height) {
-
-            for (int i = 0; i < sizes.length; i++) {
-                Image image = highResolutionImages.get(i);
-                int w = image.getWidth(null);
-                int h = image.getHeight(null);
-
-                if (width <= w && height <= h) {
-                    return image;
-                }
-            }
-
-            return highResolutionImages.get(highResolutionImages.size() - 1);
-        }
-
-        void draw(Graphics graphics, int index) {
-            Graphics2D g2 = (Graphics2D) graphics;
-            Color color = colors[index];
-            g2.setColor(color);
-            g2.fillRect(0, 0, sizes[index], sizes[index]);
-        }
-
-        @Override
-        public List<Image> getResolutionVariants() {
-            return highResolutionImages;
-        }
+    static BufferedImage createResolutionVariant(int i) {
+        BufferedImage resolutionVariant = new BufferedImage(sizes[i], sizes[i],
+                BufferedImage.TYPE_INT_RGB);
+        Graphics2D g2 = resolutionVariant.createGraphics();
+        g2.setColor(colors[i]);
+        g2.fillRect(0, 0, sizes[i], sizes[i]);
+        g2.dispose();
+        return resolutionVariant;
     }
 }// class BlockedWindowTest
 
--- a/test/java/awt/image/MultiResolutionImage/NSImageToMultiResolutionImageTest.java	Mon Sep 14 09:40:24 2015 -0700
+++ b/test/java/awt/image/MultiResolutionImage/NSImageToMultiResolutionImageTest.java	Tue Sep 15 15:31:34 2015 +0400
@@ -23,15 +23,17 @@
 
 import java.awt.Image;
 import java.awt.Toolkit;
-import sun.awt.OSInfo;
-import sun.awt.image.MultiResolutionImage;
+import java.awt.image.MultiResolutionImage;
+import jdk.testlibrary.OSInfo;
+
 /*
  * @test
  * @bug 8033534 8035069
  * @summary [macosx] Get MultiResolution image from native system
  * @author Alexander Scherbatiy
- * @modules java.desktop/sun.awt
- *          java.desktop/sun.awt.image
+ * @modules java.desktop/sun.awt.image
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.OSInfo
  * @run main NSImageToMultiResolutionImageTest
  */
 
--- a/test/java/awt/image/MultiResolutionImageCommonTest.java	Mon Sep 14 09:40:24 2015 -0700
+++ b/test/java/awt/image/MultiResolutionImageCommonTest.java	Tue Sep 15 15:31:34 2015 +0400
@@ -25,12 +25,13 @@
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.Image;
+import java.awt.RenderingHints;
 import java.awt.image.BufferedImage;
 import sun.awt.SunHints;
 import java.awt.geom.AffineTransform;
 import java.util.Arrays;
 import java.util.List;
-import sun.awt.image.MultiResolutionImage;
+import java.awt.image.MultiResolutionImage;
 
 /**
  * @test @bug 8011059
@@ -173,8 +174,9 @@
         }
 
         @Override
-        public Image getResolutionVariant(int width, int height) {
-            return ((width <= getWidth() && height <= getHeight()))
+        public Image getResolutionVariant(
+                double destImageWidth, double destImageHeight) {
+            return ((destImageWidth <= getWidth() && destImageHeight <= getHeight()))
                     ? this : highResolutionImage;
         }
 
@@ -187,8 +189,8 @@
     static void setImageScalingHint(
             Graphics2D g2d, boolean enableImageScaling) {
         g2d.setRenderingHint(SunHints.KEY_RESOLUTION_VARIANT, enableImageScaling
-                ? SunHints.VALUE_RESOLUTION_VARIANT_ON
-                : SunHints.VALUE_RESOLUTION_VARIANT_OFF);
+                ? RenderingHints.VALUE_RESOLUTION_VARIANT_DEFAULT
+                : RenderingHints.VALUE_RESOLUTION_VARIANT_BASE);
     }
 
     static void checkColor(int rgb, boolean isImageScaled) {
--- a/test/java/awt/image/MultiResolutionImageTest.java	Mon Sep 14 09:40:24 2015 -0700
+++ b/test/java/awt/image/MultiResolutionImageTest.java	Tue Sep 15 15:31:34 2015 +0400
@@ -33,10 +33,11 @@
 import javax.imageio.ImageIO;
 import sun.awt.SunHints;
 import java.awt.MediaTracker;
+import java.awt.RenderingHints;
 import java.awt.image.ImageObserver;
 import javax.swing.JPanel;
 import jdk.testlibrary.Platform;
-import sun.awt.image.MultiResolutionImage;
+import java.awt.image.MultiResolutionImage;
 
 /**
  * @test @bug 8011059
@@ -339,8 +340,8 @@
     static void setImageScalingHint(Graphics2D g2d,
         boolean enableImageScaling) {
         g2d.setRenderingHint(SunHints.KEY_RESOLUTION_VARIANT, enableImageScaling
-            ? SunHints.VALUE_RESOLUTION_VARIANT_ON
-            : SunHints.VALUE_RESOLUTION_VARIANT_OFF);
+            ? RenderingHints.VALUE_RESOLUTION_VARIANT_DEFAULT
+            : RenderingHints.VALUE_RESOLUTION_VARIANT_BASE);
     }
 
     static void checkColor(int rgb, boolean isImageScaled) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/image/multiresolution/BaseMultiResolutionImageTest.java	Tue Sep 15 15:31:34 2015 +0400
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2015, 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.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.BaseMultiResolutionImage;
+import java.awt.image.MultiResolutionImage;
+import java.util.List;
+
+/**
+ * @test
+ * @bug 8029339
+ * @author Alexander Scherbatiy
+ * @summary Custom MultiResolution image support on HiDPI displays
+ * @run main BaseMultiResolutionImageTest
+ */
+public class BaseMultiResolutionImageTest {
+
+    public static void main(String[] args) {
+        testZeroRVIMages();
+        testNullRVIMages();
+        testNullRVIMage();
+        testIOOBException();
+        testRVSizes();
+        testBaseMRImage();
+    }
+
+    static void testZeroRVIMages() {
+        try {
+            new BaseMultiResolutionImage();
+        } catch (IllegalArgumentException ignored) {
+            return;
+        }
+        throw new RuntimeException("IllegalArgumentException is not thrown!");
+    }
+
+    static void testNullRVIMages() {
+        try {
+            new BaseMultiResolutionImage(null);
+        } catch (IllegalArgumentException ignored) {
+            return;
+        }
+        throw new RuntimeException("IllegalArgumentException is not thrown!");
+    }
+
+    static void testNullRVIMage() {
+
+        Image baseImage = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+
+        try {
+            new BaseMultiResolutionImage(baseImage, null);
+        } catch (NullPointerException ignored) {
+            return;
+        }
+        throw new RuntimeException("NullPointerException is not thrown!");
+    }
+
+    static void testIOOBException() {
+
+        for (int baseImageIndex : new int[]{-3, 2, 4}) {
+            try {
+                new BaseMultiResolutionImage(baseImageIndex,
+                        createRVImage(0), createRVImage(1));
+            } catch (IndexOutOfBoundsException ignored) {
+                continue;
+            }
+
+            throw new RuntimeException("IndexOutOfBoundsException is not thrown!");
+        }
+    }
+
+    static void testRVSizes() {
+
+        int imageSize = getSize(1);
+
+        double[][] sizeArray = {
+            {-imageSize, imageSize},
+            {2 * imageSize, -2 * imageSize},
+            {Double.POSITIVE_INFINITY, imageSize},
+            {Double.POSITIVE_INFINITY, -imageSize},
+            {imageSize, Double.NEGATIVE_INFINITY},
+            {-imageSize, Double.NEGATIVE_INFINITY},
+            {Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY},
+            {Double.NaN, imageSize},
+            {imageSize, Double.NaN},
+            {Double.NaN, Double.NaN},
+            {Double.POSITIVE_INFINITY, Double.NaN}
+        };
+
+        for (double[] sizes : sizeArray) {
+            try {
+                MultiResolutionImage mrImage = new BaseMultiResolutionImage(
+                        0, createRVImage(0), createRVImage(1));
+                mrImage.getResolutionVariant(sizes[0], sizes[1]);
+            } catch (IllegalArgumentException ignored) {
+                continue;
+            }
+
+            throw new RuntimeException("IllegalArgumentException is not thrown!");
+        }
+    }
+
+    static void testBaseMRImage() {
+        int baseIndex = 1;
+        int length = 3;
+        BufferedImage[] resolutionVariants = new BufferedImage[length];
+        for (int i = 0; i < length; i++) {
+            resolutionVariants[i] = createRVImage(i);
+        }
+
+        BaseMultiResolutionImage mrImage = new BaseMultiResolutionImage(baseIndex,
+                resolutionVariants);
+
+        List<Image> rvImageList = mrImage.getResolutionVariants();
+        if (rvImageList.size() != length) {
+            throw new RuntimeException("Wrong size of resolution variants list!");
+        }
+
+        for (int i = 0; i < length; i++) {
+            int imageSize = getSize(i);
+            Image testRVImage = mrImage.getResolutionVariant(imageSize, imageSize);
+
+            if (testRVImage != resolutionVariants[i]) {
+                throw new RuntimeException("Wrong resolution variant!");
+            }
+
+            if (rvImageList.get(i) != resolutionVariants[i]) {
+                throw new RuntimeException("Wrong resolution variant!");
+            }
+        }
+
+        BufferedImage baseImage = resolutionVariants[baseIndex];
+
+        if (baseImage.getWidth() != mrImage.getWidth(null)
+                || baseImage.getHeight() != mrImage.getHeight(null)) {
+            throw new RuntimeException("Base image is wrong!");
+        }
+
+        boolean passed = false;
+
+        try {
+            rvImageList.set(0, createRVImage(10));
+        } catch (Exception e) {
+            passed = true;
+        }
+
+        if (!passed) {
+            throw new RuntimeException("Resolution variants list is modifiable!");
+        }
+
+        passed = false;
+
+        try {
+            rvImageList.remove(0);
+        } catch (Exception e) {
+            passed = true;
+        }
+
+        if (!passed) {
+            throw new RuntimeException("Resolution variants list is modifiable!");
+        }
+
+        passed = false;
+
+        try {
+            rvImageList.add(0, createRVImage(10));
+        } catch (Exception e) {
+            passed = true;
+        }
+
+        if (!passed) {
+            throw new RuntimeException("Resolution variants list is modifiable!");
+        }
+    }
+
+    private static int getSize(int i) {
+        return 8 * (i + 1);
+    }
+
+    private static BufferedImage createRVImage(int i) {
+        return new BufferedImage(getSize(i), getSize(i),
+                BufferedImage.TYPE_INT_RGB);
+    }
+}
--- a/test/java/awt/image/multiresolution/MultiResolutionCachedImageTest.java	Mon Sep 14 09:40:24 2015 -0700
+++ b/test/java/awt/image/multiresolution/MultiResolutionCachedImageTest.java	Tue Sep 15 15:31:34 2015 +0400
@@ -98,7 +98,7 @@
         }
 
         @Override
-        public Image getResolutionVariant(int width, int height) {
+        public Image getResolutionVariant(double width, double height) {
             if (width == size || height == size) {
                 throw new RuntimeException("Base image is requested!");
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/image/multiresolution/MultiResolutionRenderingHintsTest.java	Tue Sep 15 15:31:34 2015 +0400
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2015, 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.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.awt.image.BaseMultiResolutionImage;
+import static java.awt.RenderingHints.KEY_RESOLUTION_VARIANT;
+import static java.awt.RenderingHints.VALUE_RESOLUTION_VARIANT_BASE;
+import static java.awt.RenderingHints.VALUE_RESOLUTION_VARIANT_DPI_FIT;
+import static java.awt.RenderingHints.VALUE_RESOLUTION_VARIANT_SIZE_FIT;
+import static java.awt.RenderingHints.VALUE_RESOLUTION_VARIANT_DEFAULT;
+import java.awt.geom.AffineTransform;
+import java.awt.image.ColorModel;
+import java.awt.image.Raster;
+import sun.java2d.StateTrackable;
+import sun.java2d.SunGraphics2D;
+import sun.java2d.SurfaceData;
+import sun.java2d.loops.SurfaceType;
+
+/**
+ * @test
+ * @bug 8029339
+ * @author Alexander Scherbatiy
+ * @summary Custom MultiResolution image support on HiDPI displays
+ * @modules java.desktop/sun.java2d
+ * @run main MultiResolutionRenderingHintsTest
+ */
+public class MultiResolutionRenderingHintsTest {
+
+    private static final int BASE_SIZE = 200;
+    private static final Color[] COLORS = {
+        Color.CYAN, Color.GREEN, Color.BLUE, Color.ORANGE, Color.RED, Color.PINK
+    };
+
+    public static void main(String[] args) throws Exception {
+
+        int length = COLORS.length;
+        BufferedImage[] resolutionVariants = new BufferedImage[length];
+        for (int i = 0; i < length; i++) {
+            resolutionVariants[i] = createRVImage(getSize(i), COLORS[i]);
+        }
+
+        BaseMultiResolutionImage mrImage = new BaseMultiResolutionImage(
+                resolutionVariants);
+
+        // base
+        Color color = getImageColor(VALUE_RESOLUTION_VARIANT_BASE, mrImage, 2, 3);
+        if (!getColorForScale(1).equals(color)) {
+            throw new RuntimeException("Wrong base resolution variant!");
+        }
+
+        // dpi fit
+        color = getImageColor(VALUE_RESOLUTION_VARIANT_DPI_FIT, mrImage, 2, 3);
+        if (!getColorForScale(2).equals(color)) {
+            throw new RuntimeException("Resolution variant is not based on dpi!");
+        }
+
+        // size fit
+        color = getImageColor(VALUE_RESOLUTION_VARIANT_SIZE_FIT, mrImage, 2, 3);
+        if (!getColorForScale(6).equals(color)) {
+            throw new RuntimeException("Resolution variant is not based on"
+                    + " rendered size!");
+        }
+
+        // default
+        // depends on the policies of the platform
+        // just check that exception is not thrown
+        getImageColor(VALUE_RESOLUTION_VARIANT_DEFAULT, mrImage, 2, 3);
+    }
+
+    private static Color getColorForScale(int scale) {
+        return COLORS[scale - 1];
+    }
+
+    private static Color getImageColor(final Object renderingHint, Image image,
+            double configScale, double graphicsScale) {
+
+        int width = image.getWidth(null);
+        int height = image.getHeight(null);
+
+        TestSurfaceData surface = new TestSurfaceData(width, height, configScale);
+        SunGraphics2D g2d = new SunGraphics2D(surface,
+                Color.BLACK, Color.BLACK, null);
+        g2d.setRenderingHint(KEY_RESOLUTION_VARIANT, renderingHint);
+        g2d.scale(graphicsScale, graphicsScale);
+        g2d.drawImage(image, 0, 0, null);
+        g2d.dispose();
+        return surface.getColor(width / 2, height / 2);
+    }
+
+    private static int getSize(int i) {
+        return (i + 1) * BASE_SIZE;
+    }
+
+    private static BufferedImage createRVImage(int size, Color color) {
+        BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
+        Graphics g = image.createGraphics();
+        g.setColor(Color.BLACK);
+        g.fillRect(0, 0, size, size);
+        g.setColor(color);
+        g.fillOval(0, 0, size, size);
+        g.dispose();
+        return image;
+    }
+
+    static class TestGraphicsConfig extends GraphicsConfiguration {
+
+        private final double scale;
+
+        TestGraphicsConfig(double scale) {
+            this.scale = scale;
+        }
+
+        @Override
+        public GraphicsDevice getDevice() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public ColorModel getColorModel() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public ColorModel getColorModel(int transparency) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public AffineTransform getDefaultTransform() {
+            return AffineTransform.getScaleInstance(scale, scale);
+        }
+
+        @Override
+        public AffineTransform getNormalizingTransform() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public Rectangle getBounds() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+    }
+
+    static class TestSurfaceData extends SurfaceData {
+
+        private final int width;
+        private final int height;
+        private final GraphicsConfiguration gc;
+        private final BufferedImage buffImage;
+        private final double scale;
+
+        public TestSurfaceData(int width, int height, double scale) {
+            super(StateTrackable.State.DYNAMIC, SurfaceType.Custom, ColorModel.getRGBdefault());
+            this.scale = scale;
+            gc = new TestGraphicsConfig(scale);
+            this.width = (int) Math.ceil(scale * width);
+            this.height = (int) Math.ceil(scale * height);
+            buffImage = new BufferedImage(this.width, this.height,
+                    BufferedImage.TYPE_INT_RGB);
+        }
+
+        Color getColor(int x, int y) {
+            int sx = (int) Math.ceil(x * scale);
+            int sy = (int) Math.ceil(y * scale);
+            return new Color(buffImage.getRGB(sx, sy));
+        }
+
+        @Override
+        public SurfaceData getReplacement() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public GraphicsConfiguration getDeviceConfiguration() {
+            return gc;
+        }
+
+        @Override
+        public Raster getRaster(int x, int y, int w, int h) {
+            return buffImage.getRaster();
+        }
+
+        @Override
+        public Rectangle getBounds() {
+            return new Rectangle(0, 0, width, height);
+        }
+
+        @Override
+        public Object getDestination() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+    }
+}