1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 import java.awt.Color;
  24 import java.awt.Graphics;
  25 import java.awt.GraphicsConfiguration;
  26 import java.awt.GraphicsDevice;
  27 import java.awt.Image;
  28 import java.awt.Rectangle;
  29 import java.awt.image.BufferedImage;
  30 import java.awt.image.BaseMultiResolutionImage;
  31 import static java.awt.RenderingHints.KEY_RESOLUTION_VARIANT;
  32 import static java.awt.RenderingHints.VALUE_RESOLUTION_VARIANT_BASE;
  33 import static java.awt.RenderingHints.VALUE_RESOLUTION_VARIANT_DPI_FIT;
  34 import static java.awt.RenderingHints.VALUE_RESOLUTION_VARIANT_SIZE_FIT;
  35 import static java.awt.RenderingHints.VALUE_RESOLUTION_VARIANT_DEFAULT;
  36 import java.awt.geom.AffineTransform;
  37 import java.awt.image.ColorModel;
  38 import java.awt.image.Raster;
  39 import sun.java2d.StateTrackable;
  40 import sun.java2d.SunGraphics2D;
  41 import sun.java2d.SurfaceData;
  42 import sun.java2d.loops.SurfaceType;
  43 
  44 /**
  45  * @test
  46  * @bug 8029339
  47  * @author Alexander Scherbatiy
  48  * @summary Custom MultiResolution image support on HiDPI displays
  49  * @modules java.desktop/sun.java2d
  50  * @run main MultiResolutionRenderingHintsTest
  51  */
  52 public class MultiResolutionRenderingHintsTest {
  53 
  54     private static final int BASE_SIZE = 200;
  55     private static final Color[] COLORS = {
  56         Color.CYAN, Color.GREEN, Color.BLUE, Color.ORANGE, Color.RED, Color.PINK
  57     };
  58 
  59     public static void main(String[] args) throws Exception {
  60 
  61         int length = COLORS.length;
  62         BufferedImage[] resolutionVariants = new BufferedImage[length];
  63         for (int i = 0; i < length; i++) {
  64             resolutionVariants[i] = createRVImage(getSize(i), COLORS[i]);
  65         }
  66 
  67         BaseMultiResolutionImage mrImage = new BaseMultiResolutionImage(
  68                 resolutionVariants);
  69 
  70         // base
  71         Color color = getImageColor(VALUE_RESOLUTION_VARIANT_BASE, mrImage, 2, 3);
  72         if (!getColorForScale(1).equals(color)) {
  73             throw new RuntimeException("Wrong base resolution variant!");
  74         }
  75 
  76         // dpi fit
  77         color = getImageColor(VALUE_RESOLUTION_VARIANT_DPI_FIT, mrImage, 2, 3);
  78         if (!getColorForScale(2).equals(color)) {
  79             throw new RuntimeException("Resolution variant is not based on dpi!");
  80         }
  81 
  82         // size fit
  83         color = getImageColor(VALUE_RESOLUTION_VARIANT_SIZE_FIT, mrImage, 2, 3);
  84         if (!getColorForScale(6).equals(color)) {
  85             throw new RuntimeException("Resolution variant is not based on"
  86                     + " rendered size!");
  87         }
  88 
  89         // default
  90         // depends on the policies of the platform
  91         // just check that exception is not thrown
  92         getImageColor(VALUE_RESOLUTION_VARIANT_DEFAULT, mrImage, 2, 3);
  93     }
  94 
  95     private static Color getColorForScale(int scale) {
  96         return COLORS[scale - 1];
  97     }
  98 
  99     private static Color getImageColor(final Object renderingHint, Image image,
 100             double configScale, double graphicsScale) {
 101 
 102         int width = image.getWidth(null);
 103         int height = image.getHeight(null);
 104 
 105         TestSurfaceData surface = new TestSurfaceData(width, height, configScale);
 106         SunGraphics2D g2d = new SunGraphics2D(surface,
 107                 Color.BLACK, Color.BLACK, null);
 108         g2d.setRenderingHint(KEY_RESOLUTION_VARIANT, renderingHint);
 109         g2d.scale(graphicsScale, graphicsScale);
 110         g2d.drawImage(image, 0, 0, null);
 111         g2d.dispose();
 112         return surface.getColor(width / 2, height / 2);
 113     }
 114 
 115     private static int getSize(int i) {
 116         return (i + 1) * BASE_SIZE;
 117     }
 118 
 119     private static BufferedImage createRVImage(int size, Color color) {
 120         BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
 121         Graphics g = image.createGraphics();
 122         g.setColor(Color.BLACK);
 123         g.fillRect(0, 0, size, size);
 124         g.setColor(color);
 125         g.fillOval(0, 0, size, size);
 126         g.dispose();
 127         return image;
 128     }
 129 
 130     static class TestGraphicsConfig extends GraphicsConfiguration {
 131 
 132         private final double scale;
 133 
 134         TestGraphicsConfig(double scale) {
 135             this.scale = scale;
 136         }
 137 
 138         @Override
 139         public GraphicsDevice getDevice() {
 140             throw new UnsupportedOperationException("Not supported yet.");
 141         }
 142 
 143         @Override
 144         public ColorModel getColorModel() {
 145             throw new UnsupportedOperationException("Not supported yet.");
 146         }
 147 
 148         @Override
 149         public ColorModel getColorModel(int transparency) {
 150             throw new UnsupportedOperationException("Not supported yet.");
 151         }
 152 
 153         @Override
 154         public AffineTransform getDefaultTransform() {
 155             return AffineTransform.getScaleInstance(scale, scale);
 156         }
 157 
 158         @Override
 159         public AffineTransform getNormalizingTransform() {
 160             throw new UnsupportedOperationException("Not supported yet.");
 161         }
 162 
 163         @Override
 164         public Rectangle getBounds() {
 165             throw new UnsupportedOperationException("Not supported yet.");
 166         }
 167     }
 168 
 169     static class TestSurfaceData extends SurfaceData {
 170 
 171         private final int width;
 172         private final int height;
 173         private final GraphicsConfiguration gc;
 174         private final BufferedImage buffImage;
 175         private final double scale;
 176 
 177         public TestSurfaceData(int width, int height, double scale) {
 178             super(StateTrackable.State.DYNAMIC, SurfaceType.Custom, ColorModel.getRGBdefault());
 179             this.scale = scale;
 180             gc = new TestGraphicsConfig(scale);
 181             this.width = (int) Math.ceil(scale * width);
 182             this.height = (int) Math.ceil(scale * height);
 183             buffImage = new BufferedImage(this.width, this.height,
 184                     BufferedImage.TYPE_INT_RGB);
 185         }
 186 
 187         Color getColor(int x, int y) {
 188             int sx = (int) Math.ceil(x * scale);
 189             int sy = (int) Math.ceil(y * scale);
 190             return new Color(buffImage.getRGB(sx, sy));
 191         }
 192 
 193         @Override
 194         public SurfaceData getReplacement() {
 195             throw new UnsupportedOperationException("Not supported yet.");
 196         }
 197 
 198         @Override
 199         public GraphicsConfiguration getDeviceConfiguration() {
 200             return gc;
 201         }
 202 
 203         @Override
 204         public Raster getRaster(int x, int y, int w, int h) {
 205             return buffImage.getRaster();
 206         }
 207 
 208         @Override
 209         public Rectangle getBounds() {
 210             return new Rectangle(0, 0, width, height);
 211         }
 212 
 213         @Override
 214         public Object getDestination() {
 215             throw new UnsupportedOperationException("Not supported yet.");
 216         }
 217     }
 218 }