# HG changeset patch # User alexsch # Date 1442316694 -14400 # Node ID 6f1200d8999d12f489bbfcd5a4ffdc0bf0e4e2d9 # Parent 7fabd3486dea564db29da129cbd2c83be2b927bb 8029339: Custom MultiResolution image support on HiDPI displays Reviewed-by: flar, serb diff -r 7fabd3486dea -r 6f1200d8999d jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java --- a/jdk/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java Mon Sep 14 09:40:24 2015 -0700 +++ b/jdk/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 { diff -r 7fabd3486dea -r 6f1200d8999d jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CImage.java --- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/macosx/CImage.java Mon Sep 14 09:40:24 2015 -0700 +++ b/jdk/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; diff -r 7fabd3486dea -r 6f1200d8999d jdk/src/java.desktop/share/classes/java/awt/RenderingHints.java --- a/jdk/src/java.desktop/share/classes/java/awt/RenderingHints.java Mon Sep 14 09:40:24 2015 -0700 +++ b/jdk/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. + * + * + * @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 diff -r 7fabd3486dea -r 6f1200d8999d jdk/src/java.desktop/share/classes/java/awt/image/AbstractMultiResolutionImage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/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, + *
 {@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 getResolutionVariants() {
+ *         return Collections.unmodifiableList(Arrays.asList(resolutionVariants));
+ *     }
+ *
+ *     protected Image getBaseImage() {
+ *         return resolutionVariants[0];
+ *     }
+ * }
+ * } 
+ * + * @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 diff -r 7fabd3486dea -r 6f1200d8999d jdk/src/java.desktop/share/classes/java/awt/image/BaseMultiResolutionImage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/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. + *

+ * 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 getResolutionVariants() { + return Collections.unmodifiableList(Arrays.asList(resolutionVariants)); + } + + @Override + protected Image getBaseImage() { + return resolutionVariants[baseImageIndex]; + } +} \ No newline at end of file diff -r 7fabd3486dea -r 6f1200d8999d jdk/src/java.desktop/share/classes/java/awt/image/MultiResolutionImage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/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. + *

+ * 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. + *

+ * Note that many implementations might return an unmodifiable list. + *

+ * @return list of resolution variants. + * @since 1.9 + */ + public List getResolutionVariants(); +} \ No newline at end of file diff -r 7fabd3486dea -r 6f1200d8999d jdk/src/java.desktop/share/classes/sun/awt/SunHints.java --- a/jdk/src/java.desktop/share/classes/sun/awt/SunHints.java Mon Sep 14 09:40:24 2015 -0700 +++ b/jdk/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 { diff -r 7fabd3486dea -r 6f1200d8999d jdk/src/java.desktop/share/classes/sun/awt/SunToolkit.java --- a/jdk/src/java.desktop/share/classes/sun/awt/SunToolkit.java Mon Sep 14 09:40:24 2015 -0700 +++ b/jdk/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; diff -r 7fabd3486dea -r 6f1200d8999d jdk/src/java.desktop/share/classes/sun/awt/image/AbstractMultiResolutionImage.java --- a/jdk/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 - * MultiResolutionImage interface. The developer needs only - * to subclass this abstract class and define the getResolutionVariant, - * getResolutionVariants, and getBaseImage 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 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(); -} diff -r 7fabd3486dea -r 6f1200d8999d jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionCachedImage.java --- a/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionCachedImage.java Mon Sep 14 09:40:24 2015 -0700 +++ b/jdk/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 getResolutionVariants() { return Arrays.stream(sizes).map((Function) size - -> getResolutionVariant((int) size.getWidth(), - (int) size.getHeight())).collect(Collectors.toList()); + -> getResolutionVariant(size.getWidth(), size.getHeight())) + .collect(Collectors.toList()); } public MultiResolutionCachedImage map(Function mapper) { diff -r 7fabd3486dea -r 6f1200d8999d jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionImage.java --- a/jdk/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 MultiResolutionImage 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, - *

- * {@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 getResolutionVariants() {
- *        return Arrays.asList(this, highResolutionImage);
- *    }
- *  }
- * }
- * - * It is recommended to cache image variants for performance reasons. - * - * WARNING: 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 getResolutionVariants(); -} diff -r 7fabd3486dea -r 6f1200d8999d jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionToolkitImage.java --- a/jdk/src/java.desktop/share/classes/sun/awt/image/MultiResolutionToolkitImage.java Mon Sep 14 09:40:24 2015 -0700 +++ b/jdk/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; } diff -r 7fabd3486dea -r 6f1200d8999d jdk/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java Mon Sep 14 09:40:24 2015 -0700 +++ b/jdk/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); diff -r 7fabd3486dea -r 6f1200d8999d jdk/test/java/awt/Cursor/MultiResolutionCursorTest/MultiResolutionCursorTest.java --- a/jdk/test/java/awt/Cursor/MultiResolutionCursorTest/MultiResolutionCursorTest.java Mon Sep 14 09:40:24 2015 -0700 +++ b/jdk/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 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 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 diff -r 7fabd3486dea -r 6f1200d8999d jdk/test/java/awt/image/MultiResolutionImage/NSImageToMultiResolutionImageTest.java --- a/jdk/test/java/awt/image/MultiResolutionImage/NSImageToMultiResolutionImageTest.java Mon Sep 14 09:40:24 2015 -0700 +++ b/jdk/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 */ diff -r 7fabd3486dea -r 6f1200d8999d jdk/test/java/awt/image/MultiResolutionImageCommonTest.java --- a/jdk/test/java/awt/image/MultiResolutionImageCommonTest.java Mon Sep 14 09:40:24 2015 -0700 +++ b/jdk/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) { diff -r 7fabd3486dea -r 6f1200d8999d jdk/test/java/awt/image/MultiResolutionImageTest.java --- a/jdk/test/java/awt/image/MultiResolutionImageTest.java Mon Sep 14 09:40:24 2015 -0700 +++ b/jdk/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) { diff -r 7fabd3486dea -r 6f1200d8999d jdk/test/java/awt/image/multiresolution/BaseMultiResolutionImageTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/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 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); + } +} diff -r 7fabd3486dea -r 6f1200d8999d jdk/test/java/awt/image/multiresolution/MultiResolutionCachedImageTest.java --- a/jdk/test/java/awt/image/multiresolution/MultiResolutionCachedImageTest.java Mon Sep 14 09:40:24 2015 -0700 +++ b/jdk/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!"); } diff -r 7fabd3486dea -r 6f1200d8999d jdk/test/java/awt/image/multiresolution/MultiResolutionRenderingHintsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/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."); + } + } +}