changeset 13220:a8e9ad77ac81

8073320: Windows HiDPI Graphics support Reviewed-by: flar, serb
author alexsch
date Fri, 13 Nov 2015 05:02:26 -0800
parents 13a48e944151
children bc2d1130105f
files src/java.base/windows/native/launcher/java.manifest src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java src/java.desktop/share/classes/sun/awt/image/BufImgSurfaceData.java src/java.desktop/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java src/java.desktop/share/classes/sun/java2d/SurfaceData.java src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp src/java.desktop/windows/native/libawt/windows/awt_Component.cpp src/java.desktop/windows/native/libawt/windows/awt_Component.h src/java.desktop/windows/native/libawt/windows/awt_Font.cpp src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h src/java.desktop/windows/native/libawt/windows/awt_Window.cpp src/java.desktop/windows/native/libawt/windows/awt_Window.h test/java/awt/Robot/HiDPIMouseClick/HiDPIRobotMouseClick.java test/java/awt/Robot/HiDPIScreenCapture/HiDPIRobotScreenCaptureTest.java test/java/awt/hidpi/properties/HiDPIPropertiesWindowsTest.java test/java/awt/image/MultiResolutionImage/MultiResolutionDrawImageWithTransformTest.java
diffstat 35 files changed, 1502 insertions(+), 185 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/windows/native/launcher/java.manifest	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.base/windows/native/launcher/java.manifest	Fri Nov 13 05:02:26 2015 -0800
@@ -37,7 +37,7 @@
   <!-- Indicate JDK is high-dpi aware. -->
   <asmv3:application>
     <asmv3:windowsSettings  xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
-       <dpiAware>true</dpiAware>
+       <dpiAware>true/PM</dpiAware>
     </asmv3:windowsSettings>
   </asmv3:application>
 
--- a/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java	Fri Nov 13 05:02:26 2015 -0800
@@ -166,7 +166,12 @@
     }
 
     @Override
-    public int getDefaultScale() {
+    public double getDefaultScaleX() {
+        return scale;
+    }
+
+    @Override
+    public double getDefaultScaleY() {
         return scale;
     }
 
--- a/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java	Fri Nov 13 05:02:26 2015 -0800
@@ -1156,7 +1156,9 @@
             && !(dst instanceof NullSurfaceData)
             && !(src instanceof NullSurfaceData)
             && src.getSurfaceType().equals(dst.getSurfaceType())
-            && src.getDefaultScale() == dst.getDefaultScale()) {
+            && src.getDefaultScaleX() == dst.getDefaultScaleX()
+            && src.getDefaultScaleY() == dst.getDefaultScaleY())
+        {
             final Rectangle size = src.getBounds();
             final Blit blit = Blit.locate(src.getSurfaceType(),
                                           CompositeType.Src,
--- a/src/java.desktop/share/classes/sun/awt/image/BufImgSurfaceData.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/share/classes/sun/awt/image/BufImgSurfaceData.java	Fri Nov 13 05:02:26 2015 -0800
@@ -50,6 +50,8 @@
     BufferedImage bufImg;
     private BufferedImageGraphicsConfig graphicsConfig;
     RenderLoops solidloops;
+    private final double scaleX;
+    private final double scaleY;
 
     private static native void initIDs(Class<?> ICM, Class<?> ICMColorData);
 
@@ -73,6 +75,12 @@
     }
 
     public static SurfaceData createData(BufferedImage bufImg) {
+        return createData(bufImg, 1, 1);
+    }
+
+    public static SurfaceData createData(BufferedImage bufImg,
+                                         double scaleX, double scaleY)
+    {
         if (bufImg == null) {
             throw new NullPointerException("BufferedImage cannot be null");
         }
@@ -82,31 +90,36 @@
         // REMIND: Check the image type and pick an appropriate subclass
         switch (type) {
         case BufferedImage.TYPE_INT_BGR:
-            sData = createDataIC(bufImg, SurfaceType.IntBgr);
+            sData = createDataIC(bufImg, SurfaceType.IntBgr, scaleX, scaleY);
             break;
         case BufferedImage.TYPE_INT_RGB:
-            sData = createDataIC(bufImg, SurfaceType.IntRgb);
+            sData = createDataIC(bufImg, SurfaceType.IntRgb, scaleX, scaleY);
             break;
         case BufferedImage.TYPE_INT_ARGB:
-            sData = createDataIC(bufImg, SurfaceType.IntArgb);
+            sData = createDataIC(bufImg, SurfaceType.IntArgb, scaleX, scaleY);
             break;
         case BufferedImage.TYPE_INT_ARGB_PRE:
-            sData = createDataIC(bufImg, SurfaceType.IntArgbPre);
+            sData = createDataIC(bufImg, SurfaceType.IntArgbPre, scaleX, scaleY);
             break;
         case BufferedImage.TYPE_3BYTE_BGR:
-            sData = createDataBC(bufImg, SurfaceType.ThreeByteBgr, 2);
+            sData = createDataBC(bufImg, SurfaceType.ThreeByteBgr, 2,
+                                 scaleX, scaleY);
             break;
         case BufferedImage.TYPE_4BYTE_ABGR:
-            sData = createDataBC(bufImg, SurfaceType.FourByteAbgr, 3);
+            sData = createDataBC(bufImg, SurfaceType.FourByteAbgr, 3,
+                                 scaleX, scaleY);
             break;
         case BufferedImage.TYPE_4BYTE_ABGR_PRE:
-            sData = createDataBC(bufImg, SurfaceType.FourByteAbgrPre, 3);
+            sData = createDataBC(bufImg, SurfaceType.FourByteAbgrPre, 3,
+                                 scaleX, scaleY);
             break;
         case BufferedImage.TYPE_USHORT_565_RGB:
-            sData = createDataSC(bufImg, SurfaceType.Ushort565Rgb, null);
+            sData = createDataSC(bufImg, SurfaceType.Ushort565Rgb, null,
+                                 scaleX, scaleY);
             break;
         case BufferedImage.TYPE_USHORT_555_RGB:
-            sData = createDataSC(bufImg, SurfaceType.Ushort555Rgb, null);
+            sData = createDataSC(bufImg, SurfaceType.Ushort555Rgb, null,
+                                 scaleX, scaleY);
             break;
         case BufferedImage.TYPE_BYTE_INDEXED:
             {
@@ -128,14 +141,16 @@
                 default:
                     throw new InternalError("Unrecognized transparency");
                 }
-                sData = createDataBC(bufImg, sType, 0);
+                sData = createDataBC(bufImg, sType, 0, scaleX, scaleY);
             }
             break;
         case BufferedImage.TYPE_BYTE_GRAY:
-            sData = createDataBC(bufImg, SurfaceType.ByteGray, 0);
+            sData = createDataBC(bufImg, SurfaceType.ByteGray, 0,
+                                 scaleX, scaleY);
             break;
         case BufferedImage.TYPE_USHORT_GRAY:
-            sData = createDataSC(bufImg, SurfaceType.UshortGray, null);
+            sData = createDataSC(bufImg, SurfaceType.UshortGray, null,
+                                 scaleX, scaleY);
             break;
         case BufferedImage.TYPE_BYTE_BINARY:
             {
@@ -154,7 +169,7 @@
                 default:
                     throw new InternalError("Unrecognized pixel size");
                 }
-                sData = createDataBP(bufImg, sType);
+                sData = createDataBP(bufImg, sType, scaleX, scaleY);
             }
             break;
         case BufferedImage.TYPE_CUSTOM:
@@ -191,7 +206,7 @@
                             sType = SurfaceType.AnyDcm;
                         }
                     }
-                    sData = createDataIC(bufImg, sType);
+                    sData = createDataIC(bufImg, sType, scaleX, scaleY);
                     break;
                 } else if (raster instanceof ShortComponentRaster &&
                            raster.getNumDataElements() == 1 &&
@@ -233,11 +248,12 @@
                             icm = null;
                         }
                     }
-                    sData = createDataSC(bufImg, sType, icm);
+                    sData = createDataSC(bufImg, sType, icm, scaleX, scaleY);
                     break;
                 }
-                sData = new BufImgSurfaceData(raster.getDataBuffer(),
-                                              bufImg, SurfaceType.Custom);
+                sData = new BufImgSurfaceData(raster.getDataBuffer(), bufImg,
+                                              SurfaceType.Custom,
+                                              scaleX, scaleY);
             }
             break;
         }
@@ -250,11 +266,15 @@
     }
 
     public static SurfaceData createDataIC(BufferedImage bImg,
-                                           SurfaceType sType) {
+                                           SurfaceType sType,
+                                           double scaleX,
+                                           double scaleY)
+    {
         IntegerComponentRaster icRaster =
             (IntegerComponentRaster)bImg.getRaster();
         BufImgSurfaceData bisd =
-            new BufImgSurfaceData(icRaster.getDataBuffer(), bImg, sType);
+            new BufImgSurfaceData(icRaster.getDataBuffer(), bImg, sType,
+                                  scaleX, scaleY);
         bisd.initRaster(icRaster.getDataStorage(),
                         icRaster.getDataOffset(0) * 4, 0,
                         icRaster.getWidth(),
@@ -267,11 +287,14 @@
 
     public static SurfaceData createDataSC(BufferedImage bImg,
                                            SurfaceType sType,
-                                           IndexColorModel icm) {
+                                           IndexColorModel icm,
+                                           double scaleX, double scaleY)
+    {
         ShortComponentRaster scRaster =
             (ShortComponentRaster)bImg.getRaster();
         BufImgSurfaceData bisd =
-            new BufImgSurfaceData(scRaster.getDataBuffer(), bImg, sType);
+            new BufImgSurfaceData(scRaster.getDataBuffer(), bImg, sType,
+                                  scaleX, scaleY);
         bisd.initRaster(scRaster.getDataStorage(),
                         scRaster.getDataOffset(0) * 2, 0,
                         scRaster.getWidth(),
@@ -284,11 +307,14 @@
 
     public static SurfaceData createDataBC(BufferedImage bImg,
                                            SurfaceType sType,
-                                           int primaryBank) {
+                                           int primaryBank,
+                                           double scaleX, double scaleY)
+    {
         ByteComponentRaster bcRaster =
             (ByteComponentRaster)bImg.getRaster();
         BufImgSurfaceData bisd =
-            new BufImgSurfaceData(bcRaster.getDataBuffer(), bImg, sType);
+            new BufImgSurfaceData(bcRaster.getDataBuffer(), bImg, sType,
+                                  scaleX, scaleY);
         ColorModel cm = bImg.getColorModel();
         IndexColorModel icm = ((cm instanceof IndexColorModel)
                                ? (IndexColorModel) cm
@@ -304,11 +330,14 @@
     }
 
     public static SurfaceData createDataBP(BufferedImage bImg,
-                                           SurfaceType sType) {
+                                           SurfaceType sType,
+                                           double scaleX, double scaleY)
+    {
         BytePackedRaster bpRaster =
             (BytePackedRaster)bImg.getRaster();
         BufImgSurfaceData bisd =
-            new BufImgSurfaceData(bpRaster.getDataBuffer(), bImg, sType);
+            new BufImgSurfaceData(bpRaster.getDataBuffer(), bImg, sType,
+                                  scaleX, scaleY);
         ColorModel cm = bImg.getColorModel();
         IndexColorModel icm = ((cm instanceof IndexColorModel)
                                ? (IndexColorModel) cm
@@ -350,15 +379,22 @@
                                      IndexColorModel icm);
 
     public BufImgSurfaceData(DataBuffer db,
-                             BufferedImage bufImg, SurfaceType sType)
+                             BufferedImage bufImg,
+                             SurfaceType sType,
+                             double scaleX,
+                             double scaleY)
     {
         super(SunWritableRaster.stealTrackable(db),
               sType, bufImg.getColorModel());
         this.bufImg = bufImg;
+        this.scaleX = scaleX;
+        this.scaleY = scaleY;
     }
 
     protected BufImgSurfaceData(SurfaceType surfaceType, ColorModel cm) {
         super(surfaceType, cm);
+        this.scaleX = 1;
+        this.scaleY = 1;
     }
 
     public void initSolidLoops() {
@@ -395,7 +431,8 @@
 
     public synchronized GraphicsConfiguration getDeviceConfiguration() {
         if (graphicsConfig == null) {
-            graphicsConfig = BufferedImageGraphicsConfig.getConfig(bufImg);
+            graphicsConfig = BufferedImageGraphicsConfig
+                    .getConfig(bufImg, scaleX, scaleY);
         }
         return graphicsConfig;
     }
@@ -418,6 +455,16 @@
         return bufImg;
     }
 
+    @Override
+    public double getDefaultScaleX() {
+        return scaleX;
+    }
+
+    @Override
+    public double getDefaultScaleY() {
+        return scaleY;
+    }
+
     public static final class ICMColorData {
         private long pData = 0L;
 
--- a/src/java.desktop/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java	Fri Nov 13 05:02:26 2015 -0800
@@ -45,19 +45,32 @@
     extends GraphicsConfiguration
 {
     private static final int numconfigs = BufferedImage.TYPE_BYTE_BINARY;
-    private static BufferedImageGraphicsConfig configs[] =
+    private static BufferedImageGraphicsConfig standardConfigs[] =
+        new BufferedImageGraphicsConfig[numconfigs];
+    private static BufferedImageGraphicsConfig scaledConfigs[] =
         new BufferedImageGraphicsConfig[numconfigs];
 
     public static BufferedImageGraphicsConfig getConfig(BufferedImage bImg) {
+        return getConfig(bImg, 1, 1);
+    }
+
+    public static BufferedImageGraphicsConfig getConfig(BufferedImage bImg,
+                                                        double scaleX,
+                                                        double scaleY)
+    {
         BufferedImageGraphicsConfig ret;
         int type = bImg.getType();
+
+        BufferedImageGraphicsConfig[] configs = (scaleX == 1 && scaleY == 1)
+                ? standardConfigs : scaledConfigs;
+
         if (type > 0 && type < numconfigs) {
             ret = configs[type];
-            if (ret != null) {
+            if (ret != null && ret.scaleX == scaleX && ret.scaleY == scaleY) {
                 return ret;
             }
         }
-        ret = new BufferedImageGraphicsConfig(bImg, null);
+        ret = new BufferedImageGraphicsConfig(bImg, null, scaleX, scaleY);
         if (type > 0 && type < numconfigs) {
             configs[type] = ret;
         }
@@ -67,8 +80,16 @@
     GraphicsDevice gd;
     ColorModel model;
     Raster raster;
+    private final double scaleX;
+    private final double scaleY;
 
     public BufferedImageGraphicsConfig(BufferedImage bufImg, Component comp) {
+        this(bufImg, comp, 1, 1);
+    }
+
+    public BufferedImageGraphicsConfig(BufferedImage bufImg, Component comp,
+                                       double scaleX, double scaleY)
+    {
         if (comp == null) {
             this.gd = new BufferedImageDevice(this);
         } else {
@@ -77,6 +98,8 @@
         }
         this.model = bufImg.getColorModel();
         this.raster = bufImg.getRaster().createCompatibleWritableRaster(1, 1);
+        this.scaleX = scaleX;
+        this.scaleY = scaleY;
     }
 
     /**
@@ -138,7 +161,7 @@
      * For image buffers, this Transform will be the Identity transform.
      */
     public AffineTransform getDefaultTransform() {
-        return new AffineTransform();
+        return AffineTransform.getScaleInstance(scaleX, scaleY);
     }
 
     /**
--- a/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java	Fri Nov 13 05:02:26 2015 -0800
@@ -233,8 +233,17 @@
      * or a backup surface.
      */
     public BufferedImage getBackupImage() {
-        return graphicsConfig.createCompatibleImage(getWidth(), getHeight(),
-                                                    getTransparency());
+        return getBackupImage(1, 1);
+    }
+
+    /**
+     * This method creates a BufferedImage intended for use as a "snapshot"
+     * or a backup surface with the given horizontal and vertical scale factors.
+     */
+    public BufferedImage getBackupImage(double scaleX, double scaleY) {
+        int w = (int) Math.ceil(getWidth() * scaleX);
+        int h = (int) Math.ceil(getHeight() * scaleY);
+        return graphicsConfig.createCompatibleImage(w, h, getTransparency());
     }
 
     public BufferedImage getSnapshot() {
--- a/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java	Fri Nov 13 05:02:26 2015 -0800
@@ -290,16 +290,30 @@
     }
 
     /**
-     * Returns a scale factor of the image. This is utility method, which
-     * fetches information from the SurfaceData of the image.
+     * Returns a horizontal scale factor of the image. This is utility method,
+     * which fetches information from the SurfaceData of the image.
      *
-     * @see SurfaceData#getDefaultScale
+     * @see SurfaceData#getDefaultScaleX
      */
-    public static int getImageScale(final Image img) {
+    public static double getImageScaleX(final Image img) {
         if (!(img instanceof VolatileImage)) {
             return 1;
         }
         final SurfaceManager sm = getManager(img);
-        return sm.getPrimarySurfaceData().getDefaultScale();
+        return sm.getPrimarySurfaceData().getDefaultScaleX();
+    }
+
+    /**
+     * Returns a vertical scale factor of the image. This is utility method,
+     * which fetches information from the SurfaceData of the image.
+     *
+     * @see SurfaceData#getDefaultScaleY
+     */
+    public static double getImageScaleY(final Image img) {
+        if (!(img instanceof VolatileImage)) {
+            return 1;
+        }
+        final SurfaceManager sm = getManager(img);
+        return sm.getPrimarySurfaceData().getDefaultScaleY();
     }
 }
--- a/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java	Fri Nov 13 05:02:26 2015 -0800
@@ -25,18 +25,16 @@
 
 package sun.awt.image;
 
-import java.awt.Color;
 import java.awt.Graphics;
 import java.awt.GraphicsConfiguration;
 import java.awt.GraphicsEnvironment;
 import java.awt.ImageCapabilities;
+import java.awt.geom.AffineTransform;
 import java.awt.image.BufferedImage;
 import java.awt.image.VolatileImage;
 import sun.awt.DisplayChangedListener;
-import sun.awt.image.SunVolatileImage;
 import sun.java2d.SunGraphicsEnvironment;
 import sun.java2d.SurfaceData;
-import sun.java2d.loops.CompositeType;
 import static sun.java2d.pipe.hw.AccelSurface.*;
 
 /**
@@ -260,12 +258,16 @@
      */
     protected SurfaceData getBackupSurface() {
         if (sdBackup == null) {
-            BufferedImage bImg = vImg.getBackupImage();
+            GraphicsConfiguration gc = vImg.getGraphicsConfig();
+            AffineTransform tx = gc.getDefaultTransform();
+            double scaleX = tx.getScaleX();
+            double scaleY = tx.getScaleY();
+            BufferedImage bImg = vImg.getBackupImage(scaleX, scaleY);
             // Sabotage the acceleration capabilities of the BufImg surface
             SunWritableRaster.stealTrackable(bImg
                                              .getRaster()
                                              .getDataBuffer()).setUntrackable();
-            sdBackup = BufImgSurfaceData.createData(bImg);
+            sdBackup = BufImgSurfaceData.createData(bImg, scaleX, scaleY);
         }
         return sdBackup;
     }
--- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java	Fri Nov 13 05:02:26 2015 -0800
@@ -61,7 +61,6 @@
 import java.awt.Rectangle;
 import java.text.AttributedCharacterIterator;
 import java.awt.Font;
-import java.awt.Point;
 import java.awt.image.ImageObserver;
 import java.awt.Transparency;
 import java.awt.font.GlyphVector;
@@ -99,6 +98,7 @@
 import static java.awt.geom.AffineTransform.TYPE_FLIP;
 import static java.awt.geom.AffineTransform.TYPE_MASK_SCALE;
 import static java.awt.geom.AffineTransform.TYPE_TRANSLATION;
+import java.awt.image.VolatileImage;
 import sun.awt.image.MultiResolutionToolkitImage;
 import sun.awt.image.ToolkitImage;
 
@@ -3086,30 +3086,50 @@
     }
 // end of text rendering methods
 
-    private boolean isHiDPIImage(final Image img) {
-        return (SurfaceManager.getImageScale(img) != 1)
-                || img instanceof MultiResolutionImage;
-    }
-
-    private boolean drawHiDPIImage(Image img, int dx1, int dy1, int dx2,
-                                   int dy2, int sx1, int sy1, int sx2, int sy2,
-                                   Color bgcolor, ImageObserver observer) {
-
-        if (SurfaceManager.getImageScale(img) != 1) {  // Volatile Image
-            final int scale = SurfaceManager.getImageScale(img);
-            sx1 = Region.clipScale(sx1, scale);
-            sx2 = Region.clipScale(sx2, scale);
-            sy1 = Region.clipScale(sy1, scale);
-            sy2 = Region.clipScale(sy2, scale);
-        } else if (img instanceof MultiResolutionImage) {
+    private Boolean drawHiDPIImage(Image img,
+                                   int dx1, int dy1, int dx2, int dy2,
+                                   int sx1, int sy1, int sx2, int sy2,
+                                   Color bgcolor, ImageObserver observer,
+                                   AffineTransform xform) {
+
+        if (img instanceof VolatileImage) {
+            final SurfaceData sd = SurfaceManager.getManager(img)
+                    .getPrimarySurfaceData();
+            final double scaleX = sd.getDefaultScaleX();
+            final double scaleY = sd.getDefaultScaleY();
+            if (scaleX == 1 && scaleY == 1) {
+                return null;
+            }
+            sx1 = Region.clipScale(sx1, scaleX);
+            sx2 = Region.clipScale(sx2, scaleX);
+            sy1 = Region.clipScale(sy1, scaleY);
+            sy2 = Region.clipScale(sy2, scaleY);
+
+            AffineTransform tx = null;
+            if (xform != null) {
+                tx = new AffineTransform(transform);
+                transform(xform);
+            }
+            boolean result = scaleImage(img, dx1, dy1, dx2, dy2,
+                                        sx1, sy1, sx2, sy2,
+                                        bgcolor, observer);
+            if (tx != null) {
+                transform.setTransform(tx);
+                invalidateTransform();
+            }
+            return result;
+        } else if (resolutionVariantHint != SunHints.INTVAL_RESOLUTION_VARIANT_BASE
+                   && (img instanceof MultiResolutionImage)) {
             // get scaled destination image size
 
             int width = img.getWidth(observer);
             int height = img.getHeight(observer);
 
-            Image resolutionVariant = getResolutionVariant(
-                    (MultiResolutionImage) img, width, height,
-                    dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2);
+            MultiResolutionImage mrImage = (MultiResolutionImage) img;
+            Image resolutionVariant = getResolutionVariant(mrImage, width, height,
+                                                           dx1, dy1, dx2, dy2,
+                                                           sx1, sy1, sx2, sy2,
+                                                           xform);
 
             if (resolutionVariant != img && resolutionVariant != null) {
                 // recalculate source region for the resolution variant
@@ -3123,8 +3143,8 @@
 
                 if (0 < width && 0 < height && 0 < rvWidth && 0 < rvHeight) {
 
-                    float widthScale = ((float) rvWidth) / width;
-                    float heightScale = ((float) rvHeight) / height;
+                    double widthScale = ((double) rvWidth) / width;
+                    double heightScale = ((double) rvHeight) / height;
 
                     sx1 = Region.clipScale(sx1, widthScale);
                     sy1 = Region.clipScale(sy1, heightScale);
@@ -3133,10 +3153,29 @@
 
                     observer = rvObserver;
                     img = resolutionVariant;
+
+                    if (xform != null) {
+                        assert dx1 == 0 && dy1 == 0;
+                        assert dx2 == img.getWidth(observer);
+                        assert dy2 == img.getHeight(observer);
+                        AffineTransform renderTX = new AffineTransform(xform);
+                        renderTX.scale(1 / widthScale, 1 / heightScale);
+                        return transformImage(img, renderTX, observer);
+                    }
+
+                    return scaleImage(img, dx1, dy1, dx2, dy2,
+                                      sx1, sy1, sx2, sy2,
+                                      bgcolor, observer);
                 }
             }
         }
-
+        return null;
+    }
+
+    private boolean scaleImage(Image img, int dx1, int dy1, int dx2, int dy2,
+                               int sx1, int sy1, int sx2, int sy2,
+                               Color bgcolor, ImageObserver observer)
+    {
         try {
             return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1, sy1,
                                         sx2, sy2, bgcolor, observer);
@@ -3156,9 +3195,30 @@
         }
     }
 
+    private boolean transformImage(Image img,
+                                   AffineTransform xform,
+                                   ImageObserver observer)
+    {
+        try {
+            return imagepipe.transformImage(this, img, xform, observer);
+        } catch (InvalidPipeException e) {
+            try {
+                revalidateAll();
+                return imagepipe.transformImage(this, img, xform, observer);
+            } catch (InvalidPipeException e2) {
+                // Still catching the exception; we are not yet ready to
+                // validate the surfaceData correctly.  Fail for now and
+                // try again next time around.
+                return false;
+            }
+        } finally {
+            surfaceData.markDirty();
+        }
+    }
+
     private Image getResolutionVariant(MultiResolutionImage img,
             int srcWidth, int srcHeight, int dx1, int dy1, int dx2, int dy2,
-            int sx1, int sy1, int sx2, int sy2) {
+            int sx1, int sy1, int sx2, int sy2, AffineTransform xform) {
 
         if (srcWidth <= 0 || srcHeight <= 0) {
             return null;
@@ -3171,7 +3231,16 @@
             return null;
         }
 
-        int type = transform.getType();
+        AffineTransform tx;
+
+        if (xform == null) {
+            tx = transform;
+        } else {
+            tx = new AffineTransform(transform);
+            tx.concatenate(xform);
+        }
+
+        int type = tx.getType();
         int dw = dx2 - dx1;
         int dh = dy2 - dy1;
 
@@ -3198,13 +3267,13 @@
                 destRegionWidth = dw;
                 destRegionHeight = dh;
             } else if ((type & ~(TYPE_TRANSLATION | TYPE_FLIP | TYPE_MASK_SCALE)) == 0) {
-                destRegionWidth = dw * transform.getScaleX();
-                destRegionHeight = dh * transform.getScaleY();
+                destRegionWidth = dw * tx.getScaleX();
+                destRegionHeight = dh * tx.getScaleY();
             } else {
                 destRegionWidth = dw * Math.hypot(
-                        transform.getScaleX(), transform.getShearY());
+                        tx.getScaleX(), tx.getShearY());
                 destRegionHeight = dh * Math.hypot(
-                        transform.getShearX(), transform.getScaleY());
+                        tx.getShearX(), tx.getScaleY());
             }
             destImageWidth = Math.abs(srcWidth * destRegionWidth / sw);
             destImageHeight = Math.abs(srcHeight * destRegionHeight / sh);
@@ -3277,9 +3346,11 @@
 
         final int imgW = img.getWidth(null);
         final int imgH = img.getHeight(null);
-        if (isHiDPIImage(img)) {
-            return drawHiDPIImage(img, x, y, x + width, y + height, 0, 0, imgW,
-                                  imgH, bg, observer);
+        Boolean hidpiImageDrawn = drawHiDPIImage(img, x, y, x + width, y + height,
+                                                 0, 0, imgW, imgH, bg, observer,
+                                                 null);
+        if (hidpiImageDrawn != null) {
+            return hidpiImageDrawn;
         }
 
         if (width == imgW && height == imgH) {
@@ -3323,11 +3394,13 @@
             return true;
         }
 
-        if (isHiDPIImage(img)) {
-            final int imgW = img.getWidth(null);
-            final int imgH = img.getHeight(null);
-            return drawHiDPIImage(img, x, y, x + imgW, y + imgH, 0, 0, imgW,
-                                  imgH, bg, observer);
+        final int imgW = img.getWidth(null);
+        final int imgH = img.getHeight(null);
+        Boolean hidpiImageDrawn = drawHiDPIImage(img, x, y, x + imgW, y + imgH,
+                                                 0, 0, imgW, imgH, bg, observer,
+                                                 null);
+        if (hidpiImageDrawn != null) {
+            return hidpiImageDrawn;
         }
 
         try {
@@ -3378,9 +3451,12 @@
             return true;
         }
 
-        if (isHiDPIImage(img)) {
-            return drawHiDPIImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2,
-                                  bgcolor, observer);
+        Boolean hidpiImageDrawn = drawHiDPIImage(img, dx1, dy1, dx2, dy2,
+                                                 sx1, sy1, sx2, sy2,
+                                                 bgcolor, observer, null);
+
+        if (hidpiImageDrawn != null) {
+            return hidpiImageDrawn;
         }
 
         if (((sx2 - sx1) == (dx2 - dx1)) &&
@@ -3461,33 +3537,16 @@
             return drawImage(img, 0, 0, null, observer);
         }
 
-        if (isHiDPIImage(img)) {
-            final int w = img.getWidth(null);
-            final int h = img.getHeight(null);
-            final AffineTransform tx = new AffineTransform(transform);
-            transform(xform);
-            boolean result = drawHiDPIImage(img, 0, 0, w, h, 0, 0, w, h, null,
-                                            observer);
-            transform.setTransform(tx);
-            invalidateTransform();
-            return result;
+        final int w = img.getWidth(null);
+        final int h = img.getHeight(null);
+        Boolean hidpiImageDrawn = drawHiDPIImage(img, 0, 0, w, h, 0, 0, w, h,
+                                                 null, observer, xform);
+
+        if (hidpiImageDrawn != null) {
+            return hidpiImageDrawn;
         }
 
-        try {
-            return imagepipe.transformImage(this, img, xform, observer);
-        } catch (InvalidPipeException e) {
-            try {
-                revalidateAll();
-                return imagepipe.transformImage(this, img, xform, observer);
-            } catch (InvalidPipeException e2) {
-                // Still catching the exception; we are not yet ready to
-                // validate the surfaceData correctly.  Fail for now and
-                // try again next time around.
-                return false;
-            }
-        } finally {
-            surfaceData.markDirty();
-        }
+        return transformImage(img, xform, observer);
     }
 
     public void drawImage(BufferedImage bImg,
--- a/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java	Fri Nov 13 05:02:26 2015 -0800
@@ -66,6 +66,8 @@
 import sun.font.FontManagerFactory;
 import sun.font.FontManagerForSGE;
 import sun.font.NativeFont;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
 
 /**
  * This is an implementation of a GraphicsEnvironment object for the
@@ -80,6 +82,15 @@
     public static boolean isOpenSolaris;
     private static Font defaultFont;
 
+    private static final boolean uiScaleEnabled;
+    private static final double debugScale;
+
+    static {
+        uiScaleEnabled = "true".equals(AccessController.doPrivileged(
+                new GetPropertyAction("sun.java2d.uiScale.enabled", "true")));
+        debugScale = uiScaleEnabled ? getScaleFactor("sun.java2d.uiScale") : -1;
+    }
+
     public SunGraphicsEnvironment() {
         java.security.AccessController.doPrivileged(
                                     new java.security.PrivilegedAction<Object>() {
@@ -341,4 +352,41 @@
     public boolean isFlipStrategyPreferred(ComponentPeer peer) {
         return false;
     }
+
+    public static boolean isUIScaleEnabled() {
+        return uiScaleEnabled;
+    }
+
+    public static double getDebugScale() {
+        return debugScale;
+    }
+
+    public static double getScaleFactor(String propertyName) {
+
+        String scaleFactor = AccessController.doPrivileged(
+                new GetPropertyAction(propertyName, "-1"));
+
+        if (scaleFactor == null || scaleFactor.equals("-1")) {
+            return -1;
+        }
+
+        try {
+            double units = 1.0;
+
+            if (scaleFactor.endsWith("x")) {
+                scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 1);
+            } else if (scaleFactor.endsWith("dpi")) {
+                units = 96;
+                scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 3);
+            } else if (scaleFactor.endsWith("%")) {
+                units = 100;
+                scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 1);
+            }
+
+            double scale = Double.parseDouble(scaleFactor);
+            return scale <= 0 ? -1 : scale / units;
+        } catch (NumberFormatException ignored) {
+            return -1;
+        }
+    }
 }
--- a/src/java.desktop/share/classes/sun/java2d/SurfaceData.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/share/classes/sun/java2d/SurfaceData.java	Fri Nov 13 05:02:26 2015 -0800
@@ -1059,12 +1059,22 @@
     public abstract Object getDestination();
 
     /**
-     * Returns default scale factor of the destination surface. Scale factor
-     * describes the mapping between virtual and physical coordinates of the
+     * Returns default horizontal scale factor of the destination surface. Scale
+     * factor describes the mapping between virtual and physical coordinates of the
      * SurfaceData. If the scale is 2 then virtual pixel coordinates need to be
      * doubled for physical pixels.
      */
-    public int getDefaultScale() {
+    public double getDefaultScaleX() {
+        return 1;
+    }
+
+    /**
+     * Returns default vertical scale factor of the destination surface. Scale
+     * factor describes the mapping between virtual and physical coordinates of the
+     * SurfaceData. If the scale is 2 then virtual pixel coordinates need to be
+     * doubled for physical pixels.
+     */
+    public double getDefaultScaleY() {
         return 1;
     }
 }
--- a/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java	Fri Nov 13 05:02:26 2015 -0800
@@ -736,9 +736,10 @@
         atfm.scale(m00, m11);
         atfm.translate(srcX-sx1, srcY-sy1);
 
-        final int scale = SurfaceManager.getImageScale(img);
-        final int imgW = img.getWidth(null) * scale;
-        final int imgH = img.getHeight(null) * scale;
+        final double scaleX = SurfaceManager.getImageScaleX(img);
+        final double scaleY = SurfaceManager.getImageScaleY(img);
+        final int imgW = (int) Math.ceil(img.getWidth(null) * scaleX);
+        final int imgH = (int) Math.ceil(img.getHeight(null) * scaleY);
         srcW += srcX;
         srcH += srcY;
         // Make sure we are not out of bounds
--- a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java	Fri Nov 13 05:02:26 2015 -0800
@@ -106,7 +106,7 @@
     /**
      * Return the graphics device associated with this configuration.
      */
-    public GraphicsDevice getDevice() {
+    public Win32GraphicsDevice getDevice() {
         return screen;
     }
 
@@ -182,7 +182,9 @@
      * For image buffers, this Transform will be the Identity transform.
      */
     public AffineTransform getDefaultTransform() {
-        return new AffineTransform();
+        double scaleX = screen.getDefaultScaleX();
+        double scaleY = screen.getDefaultScaleY();
+        return AffineTransform.getScaleInstance(scaleX, scaleY);
     }
 
     /**
--- a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java	Fri Nov 13 05:02:26 2015 -0800
@@ -37,13 +37,19 @@
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.awt.event.WindowListener;
+import java.awt.geom.Point2D;
 import java.awt.image.ColorModel;
 import java.util.ArrayList;
 import java.util.Vector;
 import java.awt.peer.WindowPeer;
+import java.security.AccessController;
 import sun.awt.windows.WWindowPeer;
+import sun.java2d.SunGraphicsEnvironment;
 import sun.java2d.opengl.WGLGraphicsConfig;
 import sun.java2d.windows.WindowsFlags;
+import sun.security.action.GetPropertyAction;
+import static sun.awt.Win32GraphicsEnvironment.debugScaleX;
+import static sun.awt.Win32GraphicsEnvironment.debugScaleY;
 
 /**
  * This is an implementation of a GraphicsDevice object for a single
@@ -81,6 +87,9 @@
     // activation/deactivation listener for the full-screen window
     private WindowListener fsWindowListener;
 
+    private float scaleX;
+    private float scaleY;
+
     static {
 
         // 4455041 - Even when ddraw is disabled, ddraw.dll is loaded when
@@ -97,6 +106,10 @@
     private static native void initIDs();
 
     native void initDevice(int screen);
+    native void initNativeScale(int screen);
+    native void setNativeScale(int screen, float scaleX, float scaleY);
+    native float getNativeScaleX(int screen);
+    native float getNativeScaleY(int screen);
 
     public Win32GraphicsDevice(int screennum) {
         this.screen = screennum;
@@ -109,6 +122,7 @@
         valid = true;
 
         initDevice(screennum);
+        initScaleFactors();
     }
 
     /**
@@ -128,6 +142,31 @@
         return screen;
     }
 
+    public float getDefaultScaleX() {
+        return scaleX;
+    }
+
+    public float getDefaultScaleY() {
+        return scaleY;
+    }
+
+    private void initScaleFactors() {
+        if (SunGraphicsEnvironment.isUIScaleEnabled()) {
+            if (debugScaleX > 0 && debugScaleY > 0) {
+                scaleX = debugScaleX;
+                scaleY = debugScaleY;
+                setNativeScale(screen, scaleX, scaleY);
+            } else {
+                initNativeScale(screen);
+                scaleX = getNativeScaleX(screen);
+                scaleY = getNativeScaleY(screen);
+            }
+        } else {
+            scaleX = 1;
+            scaleY = 1;
+        }
+    }
+
     /**
      * Returns whether this is a valid devicie. Device can become
      * invalid as a result of device removal event.
@@ -486,6 +525,7 @@
         configs = null;
         // pass on to all top-level windows on this display
         topLevels.notifyListeners();
+        initScaleFactors();
     }
 
     /**
--- a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java	Fri Nov 13 05:02:26 2015 -0800
@@ -51,6 +51,9 @@
 
 public final class Win32GraphicsEnvironment extends SunGraphicsEnvironment {
 
+    static final float debugScaleX;
+    static final float debugScaleY;
+
     static {
         // Ensure awt is loaded already.  Also, this forces static init
         // of WToolkit and Toolkit, which we depend upon
@@ -61,6 +64,21 @@
 
         // Install correct surface manager factory.
         SurfaceManagerFactory.setInstance(new WindowsSurfaceManagerFactory());
+
+        double sx = -1;
+        double sy = -1;
+        if (isUIScaleEnabled()) {
+            sx = getScaleFactor("sun.java2d.win.uiScaleX");
+            sy = getScaleFactor("sun.java2d.win.uiScaleY");
+            if (sx <= 0 || sy <= 0) {
+                double s = getDebugScale();
+                sx = s;
+                sy = s;
+            }
+        }
+
+        debugScaleX = (float) sx;
+        debugScaleY = (float) sy;
     }
 
     /**
--- a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java	Fri Nov 13 05:02:26 2015 -0800
@@ -294,6 +294,12 @@
 
     synchronized native void reshapeFrame(int x, int y, int width, int height);
 
+    native Dimension getNativeWindowSize();
+
+    public Dimension getScaledWindowSize() {
+        return getNativeWindowSize();
+    }
+
     public boolean requestWindowFocus(CausedFocusEvent.Cause cause) {
         if (!focusAllowedFor()) {
             return false;
@@ -490,8 +496,7 @@
         }
 
         // get current GD
-        Win32GraphicsDevice oldDev = (Win32GraphicsDevice)winGraphicsConfig
-                                     .getDevice();
+        Win32GraphicsDevice oldDev = winGraphicsConfig.getDevice();
 
         Win32GraphicsDevice newDev;
         GraphicsDevice devs[] = GraphicsEnvironment
--- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java	Fri Nov 13 05:02:26 2015 -0800
@@ -63,9 +63,12 @@
 import static sun.java2d.pipe.hw.ExtendedBufferCapabilities.VSyncType.*;
 import sun.java2d.pipe.hw.ExtendedBufferCapabilities.VSyncType;
 import java.awt.BufferCapabilities.FlipContents;
+import java.awt.Dimension;
 import java.awt.Window;
+import java.awt.geom.AffineTransform;
 import sun.awt.SunToolkit;
 import sun.awt.image.SunVolatileImage;
+import sun.awt.windows.WWindowPeer;
 import sun.java2d.ScreenUpdateManager;
 import sun.java2d.StateTracker;
 import sun.java2d.SurfaceDataProxy;
@@ -162,6 +165,8 @@
 
     private int type;
     private int width, height;
+    private final double scaleX;
+    private final double scaleY;
     // these fields are set from the native code when the surface is
     // initialized
     private int nativeWidth, nativeHeight;
@@ -218,16 +223,29 @@
     {
         super(getCustomSurfaceType(type), cm);
         this.graphicsDevice = gc.getD3DDevice();
+        this.scaleX = type == TEXTURE ? 1 : graphicsDevice.getDefaultScaleX();
+        this.scaleY = type == TEXTURE ? 1 : graphicsDevice.getDefaultScaleY();
         this.peer = peer;
         this.type = type;
-        this.width = width;
-        this.height = height;
+
+        if (scaleX == 1 && scaleY == 1) {
+            this.width = width;
+            this.height = height;
+        } else if (peer instanceof WWindowPeer) {
+            Dimension scaledSize = ((WWindowPeer) peer).getScaledWindowSize();
+            this.width = scaledSize.width;
+            this.height = scaledSize.height;
+        } else {
+            this.width = (int) Math.ceil(width * scaleX);
+            this.height = (int) Math.ceil(height * scaleY);
+        }
+
         this.offscreenImage = image;
         this.backBuffersNum = numBackBuffers;
         this.swapEffect = swapEffect;
         this.syncType = vSyncType;
 
-        initOps(graphicsDevice.getScreen(), width, height);
+        initOps(graphicsDevice.getScreen(), this.width, this.height);
         if (type == WINDOW) {
             // we put the surface into the "lost"
             // state; it will be restored by the D3DScreenUpdateManager
@@ -241,6 +259,16 @@
     }
 
     @Override
+    public double getDefaultScaleX() {
+        return scaleX;
+    }
+
+    @Override
+    public double getDefaultScaleY() {
+        return scaleY;
+    }
+
+    @Override
     public SurfaceDataProxy makeProxyFor(SurfaceData srcData) {
         return D3DSurfaceDataProxy.
             createProxy(srcData,
@@ -777,8 +805,12 @@
 
     public Rectangle getBounds() {
         if (type == FLIP_BACKBUFFER || type == WINDOW) {
+            double scaleX = getDefaultScaleX();
+            double scaleY = getDefaultScaleY();
             Rectangle r = peer.getBounds();
             r.x = r.y = 0;
+            r.width = (int) Math.ceil(r.width * scaleX);
+            r.height = (int) Math.ceil(r.height * scaleY);
             return r;
         } else {
             return new Rectangle(width, height);
--- a/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java	Fri Nov 13 05:02:26 2015 -0800
@@ -31,8 +31,10 @@
 import java.awt.GraphicsEnvironment;
 import java.awt.Image;
 import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
 import java.awt.image.ColorModel;
 import sun.awt.SunToolkit;
+import sun.awt.Win32GraphicsDevice;
 import sun.awt.windows.WComponentPeer;
 import sun.java2d.SurfaceData;
 
@@ -40,6 +42,8 @@
 
     protected WComponentPeer peer;
     private WGLGraphicsConfig graphicsConfig;
+    protected double scaleX = 1;
+    protected double scaleY = 1;
 
     private native void initOps(long pConfigInfo, WComponentPeer peer,
                                 long hwnd);
@@ -50,6 +54,9 @@
         super(gc, cm, type);
         this.peer = peer;
         this.graphicsConfig = gc;
+        Win32GraphicsDevice device = gc.getDevice();
+        this.scaleX = type == TEXTURE ? 1 : device.getDefaultScaleX();
+        this.scaleY = type == TEXTURE ? 1 : device.getDefaultScaleY();
 
         long pConfigInfo = gc.getNativeConfigInfo();
         long hwnd = peer != null ? peer.getHWnd() : 0L;
@@ -57,6 +64,16 @@
         initOps(pConfigInfo, peer, hwnd);
     }
 
+    @Override
+    public double getDefaultScaleX() {
+        return scaleX;
+    }
+
+    @Override
+    public double getDefaultScaleY() {
+        return scaleY;
+    }
+
     public GraphicsConfiguration getDeviceConfiguration() {
         return graphicsConfig;
     }
@@ -148,6 +165,8 @@
         public Rectangle getBounds() {
             Rectangle r = peer.getBounds();
             r.x = r.y = 0;
+            r.width = (int) Math.ceil(r.width * scaleX);
+            r.height = (int) Math.ceil(r.height * scaleY);
             return r;
         }
 
@@ -208,11 +227,11 @@
         {
             super(peer, gc, cm, type);
 
-            this.width = width;
-            this.height = height;
+            this.width = (int) Math.ceil(width * scaleX);
+            this.height = (int) Math.ceil(height * scaleY);
             offscreenImage = image;
 
-            initSurface(width, height);
+            initSurface(this.width, this.height);
         }
 
         public SurfaceData getReplacement() {
@@ -222,6 +241,8 @@
         public Rectangle getBounds() {
             if (type == FLIP_BACKBUFFER) {
                 Rectangle r = peer.getBounds();
+                r.width = (int) Math.ceil(r.width * scaleX);
+                r.height = (int) Math.ceil(r.height * scaleY);
                 r.x = r.y = 0;
                 return r;
             } else {
--- a/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java	Fri Nov 13 05:02:26 2015 -0800
@@ -28,6 +28,7 @@
 import java.awt.Rectangle;
 import java.awt.GraphicsConfiguration;
 import java.awt.color.ColorSpace;
+import java.awt.geom.AffineTransform;
 import java.awt.image.ColorModel;
 import java.awt.image.ComponentColorModel;
 import java.awt.image.DirectColorModel;
@@ -77,6 +78,9 @@
 
     private static native void initIDs(Class<?> xorComp);
 
+    private final double scaleX;
+    private final double scaleY;
+
     static {
         initIDs(XORComposite.class);
         if (WindowsFlags.isGdiBlitEnabled()) {
@@ -265,13 +269,23 @@
         this.graphicsConfig =
             (Win32GraphicsConfig) peer.getGraphicsConfiguration();
         this.solidloops = graphicsConfig.getSolidLoops(sType);
-
-        Win32GraphicsDevice gd =
-            (Win32GraphicsDevice)graphicsConfig.getDevice();
+        Win32GraphicsDevice gd = graphicsConfig.getDevice();
+        scaleX = gd.getDefaultScaleX();
+        scaleY = gd.getDefaultScaleY();
         initOps(peer, depth, rMask, gMask, bMask, gd.getScreen());
         setBlitProxyKey(graphicsConfig.getProxyKey());
     }
 
+    @Override
+    public double getDefaultScaleX() {
+        return scaleX;
+    }
+
+    @Override
+    public double getDefaultScaleY() {
+        return scaleY;
+    }
+
     /**
      * {@inheritDoc}
      *
@@ -288,6 +302,8 @@
     public Rectangle getBounds() {
         Rectangle r = peer.getBounds();
         r.x = r.y = 0;
+        r.width = (int) Math.ceil(r.width * scaleX);
+        r.height = (int) Math.ceil(r.height * scaleY);
         return r;
     }
 
--- a/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp	Fri Nov 13 05:02:26 2015 -0800
@@ -94,12 +94,21 @@
         pointClass = (jclass)env->NewGlobalRef(pointClassLocal);
         env->DeleteLocalRef(pointClassLocal);
     }
+
+    int screen = AwtWin32GraphicsDevice::GetDefaultDeviceIndex();
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
+
     xID = env->GetFieldID(pointClass, "x", "I");
     CHECK_NULL_RETURN(xID, (jint)0);
     yID = env->GetFieldID(pointClass, "y", "I");
     CHECK_NULL_RETURN(yID, (jint)0);
-    env->SetIntField(point, xID, pt.x);
-    env->SetIntField(point, yID, pt.y);
+
+    int x = (device == NULL) ? pt.x : device->ScaleDownX(pt.x);
+    int y = (device == NULL) ? pt.y : device->ScaleDownY(pt.y);
+
+    env->SetIntField(point, xID, x);
+    env->SetIntField(point, yID, y);
 
     // Always return 0 on Windows: we assume there's always a
     // virtual screen device used.
--- a/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp	Fri Nov 13 05:02:26 2015 -0800
@@ -206,9 +206,10 @@
     int itemHeight =(int)::SendMessage(GetHWnd(), CB_GETITEMHEIGHT, (UINT)0,0);
     int numItemsToShow = (int)::SendMessage(GetHWnd(), CB_GETCOUNT, 0,0);
     numItemsToShow = min(MINIMUM_NUMBER_OF_VISIBLE_ITEMS, numItemsToShow);
+
     // drop-down height snaps to nearest line, so add a
     // fudge factor of 1/2 line to ensure last line shows
-    return itemHeight*numItemsToShow + itemHeight/2;
+    return ScaleDownY(itemHeight * numItemsToShow + itemHeight / 2);
 }
 
 // get the height of the field portion of the combobox
@@ -221,7 +222,7 @@
     // Win 4.x (3d edge) vs 3.x (1 pixel line)
     borderHeight = ::GetSystemMetrics(SM_CYEDGE);
     fieldHeight += borderHeight*2;
-    return fieldHeight;
+    return ScaleDownY(fieldHeight);
 }
 
 // gets the total height of the combobox, including drop down
@@ -325,8 +326,8 @@
      * Fix: Set the Choice to its actual size in the component.
      */
     ::GetClientRect(GetHWnd(), &rc);
-    env->SetIntField(target, AwtComponent::widthID,  (jint)rc.right);
-    env->SetIntField(target, AwtComponent::heightID, (jint)rc.bottom);
+    env->SetIntField(target, AwtComponent::widthID, ScaleDownX(rc.right));
+    env->SetIntField(target, AwtComponent::heightID, ScaleDownY(rc.bottom));
 
     env->DeleteLocalRef(target);
     env->DeleteLocalRef(parent);
--- a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp	Fri Nov 13 05:02:26 2015 -0800
@@ -963,6 +963,12 @@
     ::MapWindowPoints(HWND_DESKTOP, ::GetParent(GetHWnd()), (LPPOINT)&rc, 2);
     DTRACE_PRINTLN4("AwtComponent::Reshape from %d, %d, %d, %d", rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top);
 #endif
+
+    x = ScaleUpX(x);
+    y = ScaleUpY(y);
+    w = ScaleUpX(w);
+    h = ScaleUpY(h);
+
     AwtWindow* container = GetContainer();
     AwtComponent* parent = GetParent();
     if (container != NULL && container == parent) {
@@ -2212,8 +2218,11 @@
         }
         for(i = 0; i < 2; i++) {
             if (un[i] != 0) {
-                DoCallback("handleExpose", "(IIII)V", un[i]->left, un[i]->top,
-                    un[i]->right-un[i]->left, un[i]->bottom-un[i]->top);
+                DoCallback("handleExpose", "(IIII)V",
+                           ScaleDownX(un[i]->left),
+                           ScaleDownY(un[i]->top),
+                           ScaleDownX(un[i]->right - un[i]->left),
+                           ScaleDownY(un[i]->bottom - un[i]->top));
             }
         }
         delete [] buffer;
@@ -4608,6 +4617,34 @@
     }
 }
 
+int AwtComponent::ScaleUpX(int x) {
+    int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd());
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
+    return device == NULL ? x : device->ScaleUpX(x);
+}
+
+int AwtComponent::ScaleUpY(int y) {
+    int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd());
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
+    return device == NULL ? y : device->ScaleUpY(y);
+}
+
+int AwtComponent::ScaleDownX(int x) {
+    int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd());
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
+    return device == NULL ? x : device->ScaleDownX(x);
+}
+
+int AwtComponent::ScaleDownY(int y) {
+    int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd());
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
+    return device == NULL ? y : device->ScaleDownY(y);
+}
+
 jintArray AwtComponent::CreatePrintedPixels(SIZE &loc, SIZE &size, int alpha) {
     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 
@@ -4901,8 +4938,9 @@
     jobject mouseEvent = env->NewObject(mouseEventCls, mouseEventConst,
                                         target,
                                         id, when, modifiers,
-                                        x+insets.left, y+insets.top,
-                                        xAbs, yAbs,
+                                        ScaleDownX(x + insets.left),
+                                        ScaleDownY(y + insets.top),
+                                        ScaleDownX(xAbs), ScaleDownY(yAbs),
                                         clickCount, popupTrigger, button);
 
     if (safe_ExceptionOccurred(env)) {
@@ -4969,8 +5007,10 @@
                                              mouseWheelEventConst,
                                              target,
                                              id, when, modifiers,
-                                             x+insets.left, y+insets.top,
-                                             xAbs, yAbs,
+                                             ScaleDownX(x + insets.left),
+                                             ScaleDownY(y + insets.top),
+                                             ScaleDownX(xAbs),
+                                             ScaleDownY(yAbs),
                                              clickCount, popupTrigger,
                                              scrollType, scrollAmount,
                                              roundedWheelRotation, preciseWheelRotation);
@@ -5476,7 +5516,8 @@
         RECT rect;
         VERIFY(::GetWindowRect(p->GetHWnd(),&rect));
         result = JNU_NewObjectByName(env, "java/awt/Point", "(II)V",
-            rect.left, rect.top);
+                                     p->ScaleDownX(rect.left),
+                                     p->ScaleDownY(rect.top));
     }
 ret:
     env->DeleteGlobalRef(self);
@@ -7064,6 +7105,11 @@
         target = parent;
     }
 
+    x = ScaleUpX(x);
+    y = ScaleUpY(y);
+    width = ScaleUpX(width);
+    height = ScaleUpY(height);
+
     // Test whether component's bounds match the native window's
     RECT rect;
     VERIFY(::GetWindowRect(GetHWnd(), &rect));
@@ -7256,5 +7302,4 @@
         removedDCs = removedDCs->next;
         delete tmpDCList;
     }
-}
-
+}
\ No newline at end of file
--- a/src/java.desktop/windows/native/libawt/windows/awt_Component.h	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Component.h	Fri Nov 13 05:02:26 2015 -0800
@@ -746,6 +746,11 @@
     virtual void FillBackground(HDC hMemoryDC, SIZE &size);
     virtual void FillAlpha(void *bitmapBits, SIZE &size, BYTE alpha);
 
+    int ScaleUpX(int x);
+    int ScaleUpY(int y);
+    int ScaleDownX(int x);
+    int ScaleDownY(int y);
+
 private:
     /* A bitmask keeps the button's numbers as MK_LBUTTON, MK_MBUTTON, MK_RBUTTON
      * which are allowed to
--- a/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp	Fri Nov 13 05:02:26 2015 -0800
@@ -398,6 +398,38 @@
 
 }
 
+static int ScaleUpX(float x) {
+    int deviceIndex = AwtWin32GraphicsDevice::DeviceIndexForWindow(
+        ::GetDesktopWindow());
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice *device = devices->GetDevice(deviceIndex);
+    return device == NULL ? x : device->ScaleUpX(x);
+}
+
+static int ScaleUpY(int y) {
+    int deviceIndex = AwtWin32GraphicsDevice::DeviceIndexForWindow(
+        ::GetDesktopWindow());
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice *device = devices->GetDevice(deviceIndex);
+    return device == NULL ? y : device->ScaleUpY(y);
+}
+
+static int ScaleDownX(int x) {
+    int deviceIndex = AwtWin32GraphicsDevice::DeviceIndexForWindow(
+        ::GetDesktopWindow());
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice *device = devices->GetDevice(deviceIndex);
+    return device == NULL ? x : device->ScaleDownX(x);
+}
+
+static int ScaleDownY(int y) {
+    int deviceIndex = AwtWin32GraphicsDevice::DeviceIndexForWindow(
+        ::GetDesktopWindow());
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice *device = devices->GetDevice(deviceIndex);
+    return device == NULL ? y : device->ScaleDownY(y);
+}
+
 static HFONT CreateHFont_sub(LPCWSTR name, int style, int height,
                              int angle=0, float awScale=1.0f)
 {
@@ -424,7 +456,7 @@
     logFont.lfUnderline = 0;//(style & java_awt_Font_UNDERLINE) != 0;
 
     // Get point size
-    logFont.lfHeight = -height;
+    logFont.lfHeight = ScaleUpY(-height);
 
     // Set font name
     WCHAR tmpname[80];
@@ -451,7 +483,7 @@
             VERIFY(::DeleteObject(oldFont));
         }
         avgWidth = tm.tmAveCharWidth;
-        logFont.lfWidth = (LONG)((fabs)(avgWidth*awScale));
+        logFont.lfWidth = (LONG) ScaleUpX((fabs) (avgWidth * awScale));
         hFont = ::CreateFontIndirect(&logFont);
         DASSERT(hFont != NULL);
         VERIFY(::ReleaseDC(0, hDC));
@@ -535,19 +567,20 @@
     int ascent = metrics.tmAscent;
     int descent = metrics.tmDescent;
     int leading = metrics.tmExternalLeading;
-    env->SetIntField(fontMetrics, AwtFont::ascentID, ascent);
-    env->SetIntField(fontMetrics, AwtFont::descentID, descent);
-    env->SetIntField(fontMetrics, AwtFont::leadingID, leading);
-    env->SetIntField(fontMetrics, AwtFont::heightID, metrics.tmAscent +
-                     metrics.tmDescent + leading);
-    env->SetIntField(fontMetrics, AwtFont::maxAscentID, ascent);
-    env->SetIntField(fontMetrics, AwtFont::maxDescentID, descent);
+
+    env->SetIntField(fontMetrics, AwtFont::ascentID, ScaleDownY(ascent));
+    env->SetIntField(fontMetrics, AwtFont::descentID, ScaleDownY(descent));
+    env->SetIntField(fontMetrics, AwtFont::leadingID, ScaleDownX(leading));
+    env->SetIntField(fontMetrics, AwtFont::heightID,
+        ScaleDownY(metrics.tmAscent + metrics.tmDescent + leading));
+    env->SetIntField(fontMetrics, AwtFont::maxAscentID, ScaleDownY(ascent));
+    env->SetIntField(fontMetrics, AwtFont::maxDescentID, ScaleDownY(descent));
 
     int maxHeight =  ascent + descent + leading;
-    env->SetIntField(fontMetrics, AwtFont::maxHeightID, maxHeight);
+    env->SetIntField(fontMetrics, AwtFont::maxHeightID, ScaleDownY(maxHeight));
 
     int maxAdvance = metrics.tmMaxCharWidth;
-    env->SetIntField(fontMetrics, AwtFont::maxAdvanceID, maxAdvance);
+    env->SetIntField(fontMetrics, AwtFont::maxAdvanceID, ScaleDownX(maxAdvance));
 
     awtFont->m_overhang = metrics.tmOverhang;
 
@@ -818,6 +851,7 @@
     jobject font = env->GetObjectField(self, AwtFont::fontID);
 
     long ret = AwtFont::getMFStringWidth(hDC, font, str);
+    ret = ScaleDownX(ret);
     VERIFY(::ReleaseDC(0, hDC));
     return ret;
 
@@ -924,7 +958,7 @@
     }
 
     env->ReleasePrimitiveArrayCritical(str, pStrBody, 0);
-    return result;
+    return ScaleDownX(result);
 
     CATCH_BAD_ALLOC_RET(0);
 }
--- a/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp	Fri Nov 13 05:02:26 2015 -0800
@@ -80,6 +80,13 @@
                                      (PVOID)newSpeed,
                                      SPIF_SENDCHANGE);
 
+      int primaryIndex = AwtWin32GraphicsDevice::GetDefaultDeviceIndex();
+      Devices::InstanceAccess devices;
+      AwtWin32GraphicsDevice *device = devices->GetDevice(primaryIndex);
+
+      x = (device == NULL) ? x : device->ScaleUpX(x);
+      y = (device == NULL) ? y : device->ScaleUpY(y);
+
       POINT curPos;
       ::GetCursorPos(&curPos);
       x -= curPos.x;
@@ -217,11 +224,24 @@
         AwtWin32GraphicsDevice::SelectPalette(hdcMem, primaryIndex);
     AwtWin32GraphicsDevice::RealizePalette(hdcMem, primaryIndex);
 
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice *device = devices->GetDevice(primaryIndex);
+    int sWidth = (device == NULL) ? width : device->ScaleUpX(width);
+    int sHeight = (device == NULL) ? height : device->ScaleUpY(height);
+
     // copy screen image to offscreen bitmap
     // CAPTUREBLT flag is required to capture WS_EX_LAYERED windows' contents
     // correctly on Win2K/XP
-    VERIFY(::BitBlt(hdcMem, 0, 0, width, height, hdcScreen, x, y,
-                                                SRCCOPY|CAPTUREBLT) != 0);
+    if (width == sWidth && height == sHeight) {
+        VERIFY(::BitBlt(hdcMem, 0, 0, width, height, hdcScreen, x, y,
+               SRCCOPY | CAPTUREBLT) != 0);
+    } else {
+        int sX = (device == NULL) ? x : device->ScaleUpX(x);
+        int sY = (device == NULL) ? y : device->ScaleUpY(y);
+        VERIFY(::StretchBlt(hdcMem, 0, 0, width, height,
+               hdcScreen, sX, sY, sWidth, sHeight,
+               SRCCOPY | CAPTUREBLT) != 0);
+    }
 
     static const int BITS_PER_PIXEL = 32;
     static const int BYTES_PER_PIXEL = BITS_PER_PIXEL/8;
--- a/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp	Fri Nov 13 05:02:26 2015 -0800
@@ -2355,8 +2355,13 @@
 {
     TRY;
 
-    return ::GetSystemMetrics(SM_CXSCREEN);
-
+    int width = ::GetSystemMetrics(SM_CXSCREEN);
+
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice *device = devices->GetDevice(
+                        AwtWin32GraphicsDevice::GetDefaultDeviceIndex());
+
+    return (device == NULL) ? width : device->ScaleDownX(width);
     CATCH_BAD_ALLOC_RET(0);
 }
 
@@ -2370,7 +2375,12 @@
 {
     TRY;
 
-    return ::GetSystemMetrics(SM_CYSCREEN);
+    int height = ::GetSystemMetrics(SM_CYSCREEN);
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice *device = devices->GetDevice(
+                        AwtWin32GraphicsDevice::GetDefaultDeviceIndex());
+
+    return (device == NULL) ? height : device->ScaleDownY(height);
 
     CATCH_BAD_ALLOC_RET(0);
 }
--- a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp	Fri Nov 13 05:02:26 2015 -0800
@@ -95,19 +95,31 @@
     mid = env->GetMethodID(clazz, "<init>", "(IIII)V");
     if (mid != 0) {
         RECT rRW = {0, 0, 0, 0};
+        Devices::InstanceAccess devices;
+        AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
+
         if (TRUE == MonitorBounds(AwtWin32GraphicsDevice::GetMonitor(screen), &rRW)) {
-            bounds = env->NewObject(clazz, mid,
-                                    rRW.left, rRW.top,
-                                    rRW.right - rRW.left,
-                                    rRW.bottom - rRW.top);
+
+            int x = (device == NULL) ? rRW.left : device->ScaleDownX(rRW.left);
+            int y = (device == NULL) ? rRW.top  : device->ScaleDownY(rRW.top);
+            int w = (device == NULL) ? rRW.right - rRW.left
+                                     : device->ScaleDownX(rRW.right - rRW.left);
+            int h = (device == NULL) ? rRW.bottom - rRW.top
+                                     : device->ScaleDownY(rRW.bottom - rRW.top);
+
+            bounds = env->NewObject(clazz, mid, x, y, w, h);
+
         }
         else {
             // 4910760 - don't return a null bounds, return the bounds of the
             // primary screen
+            int w = ::GetSystemMetrics(SM_CXSCREEN);
+            int h = ::GetSystemMetrics(SM_CYSCREEN);
+
             bounds = env->NewObject(clazz, mid,
                                     0, 0,
-                                    ::GetSystemMetrics(SM_CXSCREEN),
-                                    ::GetSystemMetrics(SM_CYSCREEN));
+                                    device == NULL ? w : device->ScaleDownX(w),
+                                    device == NULL ? h : device->ScaleDownY(h));
         }
         if (safe_ExceptionOccurred(env)) {
            return 0;
--- a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp	Fri Nov 13 05:02:26 2015 -0800
@@ -49,6 +49,12 @@
 #include "dither.h"
 #include "img_util_md.h"
 #include "Devices.h"
+#include <d2d1.h>
+#pragma comment(lib, "d2d1")
+
+#ifndef MDT_Effective_DPI
+#define MDT_Effective_DPI 0
+#endif
 
 uns_ordered_dither_array img_oda_alpha;
 
@@ -74,6 +80,8 @@
 {
     this->screen  = screen;
     this->devicesArray = arr;
+    this->scaleX = 1;
+    this->scaleY = 1;
     javaDevice = NULL;
     colorData = new ImgColorData;
     colorData->grayscale = GS_NOTGRAY;
@@ -617,6 +625,104 @@
 }
 
 /**
+ * Sets horizontal and vertical scale factors
+ */
+void AwtWin32GraphicsDevice::SetScale(float sx, float sy)
+{
+    scaleX = sx;
+    scaleY = sy;
+}
+
+int AwtWin32GraphicsDevice::ScaleUpX(int x)
+{
+    return (int)ceil(x * scaleX);
+}
+
+int AwtWin32GraphicsDevice::ScaleUpY(int y)
+{
+    return (int)ceil(y * scaleY);
+}
+
+int AwtWin32GraphicsDevice::ScaleDownX(int x)
+{
+    return (int)ceil(x / scaleX);
+}
+
+int AwtWin32GraphicsDevice::ScaleDownY(int y)
+{
+    return (int)ceil(y / scaleY);
+}
+
+void AwtWin32GraphicsDevice::InitDesktopScales()
+{
+    unsigned x = 0;
+    unsigned y = 0;
+    float dpiX = -1.0f;
+    float dpiY = -1.0f;
+
+    // for debug purposes
+    static float scale = -2.0f;
+    if (scale == -2) {
+        scale = -1;
+        char *uiScale = getenv("J2D_UISCALE");
+        if (uiScale != NULL) {
+            scale = (float)strtod(uiScale, NULL);
+            if (errno == ERANGE || scale <= 0) {
+                scale = -1;
+            }
+        }
+    }
+
+    if (scale > 0) {
+        SetScale(scale, scale);
+        return;
+    }
+
+    typedef HRESULT(WINAPI GetDpiForMonitorFunc)(HMONITOR, int, UINT*, UINT*);
+    static HMODULE hLibSHCoreDll = NULL;
+    static GetDpiForMonitorFunc *lpGetDpiForMonitor = NULL;
+
+    if (hLibSHCoreDll == NULL) {
+        hLibSHCoreDll = JDK_LoadSystemLibrary("shcore.dll");
+        if (hLibSHCoreDll != NULL) {
+            lpGetDpiForMonitor = (GetDpiForMonitorFunc*)GetProcAddress(
+                hLibSHCoreDll, "GetDpiForMonitor");
+        }
+    }
+
+    if (lpGetDpiForMonitor != NULL) {
+        HRESULT hResult = lpGetDpiForMonitor(GetMonitor(),
+                                             MDT_Effective_DPI, &x, &y);
+        if (hResult == S_OK) {
+            dpiX = static_cast<float>(x);
+            dpiY = static_cast<float>(y);
+        }
+    } else {
+        ID2D1Factory* m_pDirect2dFactory;
+        HRESULT res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
+                                        &m_pDirect2dFactory);
+        if (res == S_OK) {
+            m_pDirect2dFactory->GetDesktopDpi(&dpiX, &dpiY);
+            m_pDirect2dFactory->Release();
+        }
+    }
+
+    if (dpiX > 0 && dpiY > 0) {
+        SetScale(dpiX / 96, dpiY / 96);
+    }
+}
+
+float AwtWin32GraphicsDevice::GetScaleX()
+{
+    return scaleX;
+}
+
+float AwtWin32GraphicsDevice::GetScaleY()
+{
+    return scaleY;
+}
+
+/**
  * Disables offscreen acceleration for this device.  This
  * sets a flag in the java object that is used to determine
  * whether offscreen surfaces can be created on the device.
@@ -1304,3 +1410,65 @@
     Devices::InstanceAccess devices;
     devices->GetDevice(screen)->SetJavaDevice(env, thisPtr);
 }
+
+/*
+ * Class:     sun_awt_Win32GraphicsDevice
+ * Method:    setNativeScale
+ * Signature: (I,F,F)V
+ */
+JNIEXPORT void JNICALL
+    Java_sun_awt_Win32GraphicsDevice_setNativeScale
+    (JNIEnv *env, jobject thisPtr, jint screen, jfloat scaleX, jfloat scaleY)
+{
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
+
+    if (device != NULL ) {
+        device->SetScale(scaleX, scaleY);
+    }
+}
+
+/*
+ * Class:     sun_awt_Win32GraphicsDevice
+ * Method:    getNativeScaleX
+ * Signature: (I)F
+ */
+JNIEXPORT jfloat JNICALL
+    Java_sun_awt_Win32GraphicsDevice_getNativeScaleX
+    (JNIEnv *env, jobject thisPtr, jint screen)
+{
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
+    return (device == NULL) ? 1 : device->GetScaleX();
+}
+
+/*
+ * Class:     sun_awt_Win32GraphicsDevice
+ * Method:    getNativeScaleY
+ * Signature: (I)F
+ */
+JNIEXPORT jfloat JNICALL
+    Java_sun_awt_Win32GraphicsDevice_getNativeScaleY
+    (JNIEnv *env, jobject thisPtr, jint screen)
+{
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
+    return (device == NULL) ? 1 : device->GetScaleY();
+}
+
+/*
+* Class:     sun_awt_Win32GraphicsDevice
+* Method:    initNativeScale
+* Signature: (I)V;
+*/
+JNIEXPORT void JNICALL
+Java_sun_awt_Win32GraphicsDevice_initNativeScale
+(JNIEnv *env, jobject thisPtr, jint screen)
+{
+    Devices::InstanceAccess devices;
+    AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
+
+    if (device != NULL) {
+        device->InitDesktopScales();
+    }
+}
\ No newline at end of file
--- a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h	Fri Nov 13 05:02:26 2015 -0800
@@ -66,6 +66,14 @@
     void                    Release();
     void                    DisableOffscreenAcceleration();
     void                    Invalidate(JNIEnv *env);
+    void                    InitDesktopScales();
+    void                    SetScale(float scaleX, float scaleY);
+    float                   GetScaleX();
+    float                   GetScaleY();
+    int                     ScaleUpX(int x);
+    int                     ScaleUpY(int y);
+    int                     ScaleDownX(int x);
+    int                     ScaleDownY(int y);
 
     static int              DeviceIndexForWindow(HWND hWnd);
     static jobject          GetColorModel(JNIEnv *env, jboolean dynamic,
@@ -107,6 +115,8 @@
     LPMONITORINFO           pMonitorInfo;
     jobject                 javaDevice;
     Devices                 *devicesArray;
+    float                   scaleX;
+    float                   scaleY;
 
     static HDC              MakeDCFromMonitor(HMONITOR);
 };
--- a/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp	Fri Nov 13 05:02:26 2015 -0800
@@ -1407,19 +1407,19 @@
     /* Get insets into our peer directly */
     jobject peerInsets = (env)->GetObjectField(peer, AwtPanel::insets_ID);
     DASSERT(!safe_ExceptionOccurred(env));
+
     if (peerInsets != NULL) { // may have been called during creation
-        (env)->SetIntField(peerInsets, AwtInsets::topID, m_insets.top);
-        (env)->SetIntField(peerInsets, AwtInsets::bottomID,
-                           m_insets.bottom);
-        (env)->SetIntField(peerInsets, AwtInsets::leftID, m_insets.left);
-        (env)->SetIntField(peerInsets, AwtInsets::rightID, m_insets.right);
+        (env)->SetIntField(peerInsets, AwtInsets::topID, ScaleDownY(m_insets.top));
+        (env)->SetIntField(peerInsets, AwtInsets::bottomID, ScaleDownY(m_insets.bottom));
+        (env)->SetIntField(peerInsets, AwtInsets::leftID, ScaleDownX(m_insets.left));
+        (env)->SetIntField(peerInsets, AwtInsets::rightID, ScaleDownX(m_insets.right));
     }
     /* Get insets into the Inset object (if any) that was passed */
     if (insets != NULL) {
-        (env)->SetIntField(insets, AwtInsets::topID, m_insets.top);
-        (env)->SetIntField(insets, AwtInsets::bottomID, m_insets.bottom);
-        (env)->SetIntField(insets, AwtInsets::leftID, m_insets.left);
-        (env)->SetIntField(insets, AwtInsets::rightID, m_insets.right);
+        (env)->SetIntField(insets, AwtInsets::topID, ScaleDownY(m_insets.top));
+        (env)->SetIntField(insets, AwtInsets::bottomID, ScaleDownY(m_insets.bottom));
+        (env)->SetIntField(insets, AwtInsets::leftID, ScaleDownX(m_insets.left));
+        (env)->SetIntField(insets, AwtInsets::rightID, ScaleDownX(m_insets.right));
     }
     env->DeleteLocalRef(peerInsets);
 
@@ -1735,10 +1735,10 @@
     RECT rect;
     ::GetWindowRect(GetHWnd(), &rect);
 
-    (env)->SetIntField(target, AwtComponent::xID, rect.left);
-    (env)->SetIntField(target, AwtComponent::yID, rect.top);
-    (env)->SetIntField(peer, AwtWindow::sysXID, rect.left);
-    (env)->SetIntField(peer, AwtWindow::sysYID, rect.top);
+    (env)->SetIntField(target, AwtComponent::xID, ScaleDownX(rect.left));
+    (env)->SetIntField(target, AwtComponent::yID, ScaleDownY(rect.top));
+    (env)->SetIntField(peer, AwtWindow::sysXID, ScaleDownX(rect.left));
+    (env)->SetIntField(peer, AwtWindow::sysYID, ScaleDownY(rect.top));
     SendComponentEvent(java_awt_event_ComponentEvent_COMPONENT_MOVED);
 
     env->DeleteLocalRef(target);
@@ -1803,12 +1803,12 @@
     int newWidth = w + m_insets.left + m_insets.right;
     int newHeight = h + m_insets.top + m_insets.bottom;
 
-    (env)->SetIntField(target, AwtComponent::widthID, newWidth);
-    (env)->SetIntField(target, AwtComponent::heightID, newHeight);
+    (env)->SetIntField(target, AwtComponent::widthID, ScaleDownX(newWidth));
+    (env)->SetIntField(target, AwtComponent::heightID, ScaleDownY(newHeight));
 
     jobject peer = GetPeer(env);
-    (env)->SetIntField(peer, AwtWindow::sysWID, newWidth);
-    (env)->SetIntField(peer, AwtWindow::sysHID, newHeight);
+    (env)->SetIntField(peer, AwtWindow::sysWID, ScaleDownX(newWidth));
+    (env)->SetIntField(peer, AwtWindow::sysHID, ScaleDownY(newHeight));
 
     if (!AwtWindow::IsResizing()) {
         WindowResized();
@@ -3072,6 +3072,25 @@
     delete data;
 }
 
+void AwtWindow::_GetNativeWindowSize(void* param) {
+
+    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
+
+    SizeStruct *ss = (SizeStruct *)param;
+    jobject self = ss->window;
+    AwtWindow *window = NULL;
+    PDATA pData;
+    JNI_CHECK_PEER_RETURN(self);
+    window = (AwtWindow *)pData;
+
+    RECT rc;
+    ::GetWindowRect(window->GetHWnd(), &rc);
+    ss->w = rc.right - rc.left;
+    ss->h = rc.bottom - rc.top;
+
+    env->DeleteGlobalRef(self);
+}
+
 extern "C" {
 
 /*
@@ -3303,6 +3322,46 @@
 
 /*
  * Class:     sun_awt_windows_WWindowPeer
+* Method:    getNativeWindowSize
+* Signature: ()Ljava/awt/Dimension;
+*/
+JNIEXPORT jobject JNICALL Java_sun_awt_windows_WWindowPeer_getNativeWindowSize
+(JNIEnv *env, jobject self) {
+
+    jobject res = NULL;
+    TRY;
+    SizeStruct *ss = new SizeStruct;
+    ss->window = env->NewGlobalRef(self);
+
+    AwtToolkit::GetInstance().SyncCall(AwtWindow::_GetNativeWindowSize, ss);
+
+    int w = ss->w;
+    int h = ss->h;
+
+    delete ss;
+    // global ref is deleted in _GetNativeWindowSize()
+
+    static jmethodID dimMID = NULL;
+    static jclass dimClassID = NULL;
+    if (dimClassID == NULL) {
+        jclass dimClassIDLocal = env->FindClass("java/awt/Dimension");
+        CHECK_NULL_RETURN(dimClassIDLocal, NULL);
+        dimClassID = (jclass)env->NewGlobalRef(dimClassIDLocal);
+        env->DeleteLocalRef(dimClassIDLocal);
+    }
+
+    if (dimMID == NULL) {
+        dimMID = env->GetMethodID(dimClassID, "<init>", "(II)V");
+        CHECK_NULL_RETURN(dimMID, NULL);
+    }
+
+    return env->NewObject(dimClassID, dimMID, w, h);
+
+    CATCH_BAD_ALLOC_RET(NULL);
+}
+
+/*
+ * Class:     sun_awt_windows_WWindowPeer
  * Method:    getSysMinWidth
  * Signature: ()I
  */
--- a/src/java.desktop/windows/native/libawt/windows/awt_Window.h	Thu Nov 12 12:27:36 2015 -0600
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Window.h	Fri Nov 13 05:02:26 2015 -0800
@@ -241,6 +241,7 @@
     static void _UpdateWindow(void* param);
     static void _RepositionSecurityWarning(void* param);
     static void _SetFullScreenExclusiveModeState(void* param);
+    static void _GetNativeWindowSize(void* param);
 
     inline static BOOL IsResizing() {
         return sm_resizing;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/Robot/HiDPIMouseClick/HiDPIRobotMouseClick.java	Fri Nov 13 05:02:26 2015 -0800
@@ -0,0 +1,87 @@
+/*
+ * 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.Frame;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import javax.swing.UIManager;
+
+/* @test
+ * @bug 8073320
+ * @summary  Windows HiDPI support
+ * @author Alexander Scherbatiy
+ * @requires (os.family == "windows")
+ * @run main/othervm -Dsun.java2d.win.uiScale=2 HiDPIRobotMouseClick
+ */
+public class HiDPIRobotMouseClick {
+
+    private static volatile int mouseX;
+    private static volatile int mouseY;
+
+    public static void main(String[] args) throws Exception {
+
+        try {
+            UIManager.setLookAndFeel(
+                    "com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
+        } catch (Exception e) {
+            return;
+        }
+
+        Frame frame = new Frame();
+        frame.setBounds(30, 20, 400, 300);
+        frame.setUndecorated(true);
+
+        frame.addMouseListener(new MouseAdapter() {
+
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                mouseX = e.getXOnScreen();
+                mouseY = e.getYOnScreen();
+            }
+        });
+
+        frame.setVisible(true);
+
+        Robot robot = new Robot();
+        robot.waitForIdle();
+        Thread.sleep(200);
+
+        Rectangle rect = frame.getBounds();
+        rect.setLocation(frame.getLocationOnScreen());
+
+        int x = (int) rect.getCenterX();
+        int y = (int) rect.getCenterY();
+
+        robot.mouseMove(x, y);
+        robot.mousePress(InputEvent.BUTTON1_MASK);
+        robot.mouseRelease(InputEvent.BUTTON1_MASK);
+        robot.waitForIdle();
+
+        if (x != mouseX || y != mouseY) {
+            throw new RuntimeException("Wrong mouse click point!");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/Robot/HiDPIScreenCapture/HiDPIRobotScreenCaptureTest.java	Fri Nov 13 05:02:26 2015 -0800
@@ -0,0 +1,115 @@
+/*
+ * 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.BorderLayout;
+import java.awt.Canvas;
+import java.awt.Color;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Panel;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.image.BufferedImage;
+import javax.swing.UIManager;
+
+/* @test
+ * @bug 8073320
+ * @summary  Windows HiDPI support
+ * @author Alexander Scherbatiy
+ * @requires (os.family == "windows")
+ * @run main/othervm -Dsun.java2d.win.uiScaleX=3 -Dsun.java2d.win.uiScaleY=2
+ *                    HiDPIRobotScreenCaptureTest
+ */
+public class HiDPIRobotScreenCaptureTest {
+
+    private static final Color[] COLORS = {
+        Color.GREEN, Color.BLUE, Color.ORANGE, Color.RED};
+
+    public static void main(String[] args) throws Exception {
+
+        try {
+            UIManager.setLookAndFeel(
+                    "com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
+        } catch (Exception e) {
+            return;
+        }
+
+        Frame frame = new Frame();
+        frame.setBounds(40, 30, 400, 300);
+        frame.setUndecorated(true);
+
+        Panel panel = new Panel(new BorderLayout());
+        Canvas canvas = new Canvas() {
+            @Override
+            public void paint(Graphics g) {
+                super.paint(g);
+                int w = getWidth();
+                int h = getHeight();
+                g.setColor(COLORS[0]);
+                g.fillRect(0, 0, w / 2, h / 2);
+                g.setColor(COLORS[1]);
+                g.fillRect(w / 2, 0, w / 2, h / 2);
+                g.setColor(COLORS[2]);
+                g.fillRect(0, h / 2, w / 2, h / 2);
+                g.setColor(COLORS[3]);
+                g.fillRect(w / 2, h / 2, w / 2, h / 2);
+            }
+        };
+
+        panel.add(canvas);
+        frame.add(panel);
+        frame.setVisible(true);
+        Robot robot = new Robot();
+        robot.waitForIdle();
+        Thread.sleep(200);
+
+        Rectangle rect = canvas.getBounds();
+        rect.setLocation(canvas.getLocationOnScreen());
+
+        BufferedImage image = robot.createScreenCapture(rect);
+        frame.dispose();
+
+        int w = image.getWidth();
+        int h = image.getHeight();
+
+        if (w != frame.getWidth() || h != frame.getHeight()) {
+            throw new RuntimeException("Wrong image size!");
+        }
+
+        if (image.getRGB(w / 4, h / 4) != COLORS[0].getRGB()) {
+            throw new RuntimeException("Wrong image color!");
+        }
+
+        if (image.getRGB(3 * w / 4, h / 4) != COLORS[1].getRGB()) {
+            throw new RuntimeException("Wrong image color!");
+        }
+
+        if (image.getRGB(w / 4, 3 * h / 4) != COLORS[2].getRGB()) {
+            throw new RuntimeException("Wrong image color!");
+        }
+
+        if (image.getRGB(3 * w / 4, 3 * h / 4) != COLORS[3].getRGB()) {
+            throw new RuntimeException("Wrong image color!");
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/hidpi/properties/HiDPIPropertiesWindowsTest.java	Fri Nov 13 05:02:26 2015 -0800
@@ -0,0 +1,139 @@
+/*
+ * 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.Dialog;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import javax.swing.UIManager;
+
+/* @test
+ * @bug 8073320
+ * @summary  Windows HiDPI support
+ * @author Alexander Scherbatiy
+ * @requires (os.family == "windows")
+ * @run main/othervm -Dsun.java2d.uiScale.enabled=false
+ *                   -Dsun.java2d.win.uiScaleX=3 -Dsun.java2d.win.uiScaleY=2
+ *                    HiDPIPropertiesWindowsTest UISCALE_DISABLED
+ * @run main/othervm -Dsun.java2d.uiScale.enabled=false
+ *                   -Dsun.java2d.uiScale=3
+ *                    HiDPIPropertiesWindowsTest UISCALE_DISABLED
+ * @run main/othervm -Dsun.java2d.uiScale.enabled=false
+ *                   -Dsun.java2d.uiScale=3
+ *                   -Dsun.java2d.win.uiScaleX=5 -Dsun.java2d.win.uiScaleY=6
+ *                    HiDPIPropertiesWindowsTest UISCALE_DISABLED
+ * @run main/othervm -Dsun.java2d.uiScale.enabled=true
+ *                   -Dsun.java2d.uiScale=3
+ *                    HiDPIPropertiesWindowsTest UISCALE_3
+ * @run main/othervm -Dsun.java2d.uiScale.enabled=true
+ *                   -Dsun.java2d.uiScale=4
+ *                   -Dsun.java2d.win.uiScaleX=2 -Dsun.java2d.win.uiScaleY=3
+ *                    HiDPIPropertiesWindowsTest UISCALE_2X3
+ * @run main/othervm -Dsun.java2d.uiScale.enabled=true
+ *                   -Dsun.java2d.win.uiScaleX=3 -Dsun.java2d.win.uiScaleY=2
+ *                    HiDPIPropertiesWindowsTest UISCALE_3X2
+ * @run main/othervm -Dsun.java2d.uiScale=4
+ *                    HiDPIPropertiesWindowsTest UISCALE_4
+ * @run main/othervm -Dsun.java2d.uiScale=4
+ *                   -Dsun.java2d.win.uiScaleX=2 -Dsun.java2d.win.uiScaleY=3
+ *                    HiDPIPropertiesWindowsTest UISCALE_2X3
+ * @run main/othervm -Dsun.java2d.win.uiScaleX=4 -Dsun.java2d.win.uiScaleY=5
+ *                    HiDPIPropertiesWindowsTest UISCALE_4X5
+ * @run main/othervm -Dsun.java2d.uiScale=3
+ *                   -Dsun.java2d.win.uiScaleX=0 -Dsun.java2d.win.uiScaleY=0
+ *                    HiDPIPropertiesWindowsTest UISCALE_3
+ * @run main/othervm -Dsun.java2d.uiScale=4
+ *                   -Dsun.java2d.win.uiScaleX=-7 -Dsun.java2d.win.uiScaleY=-8
+ *                    HiDPIPropertiesWindowsTest UISCALE_4
+ * @run main/othervm -Dsun.java2d.uiScale=4x
+ *                    HiDPIPropertiesWindowsTest UISCALE_4
+ * @run main/othervm -Dsun.java2d.win.uiScaleX=4x -Dsun.java2d.win.uiScaleY=5x
+ *                    HiDPIPropertiesWindowsTest UISCALE_4X5
+ * @run main/othervm -Dsun.java2d.uiScale=384dpi
+ *                    HiDPIPropertiesWindowsTest UISCALE_4
+ * @run main/othervm -Dsun.java2d.uiScale=300%
+ *                    HiDPIPropertiesWindowsTest UISCALE_3
+ * @run main/othervm -Dsun.java2d.win.uiScaleX=400% -Dsun.java2d.win.uiScaleY=500%
+ *                    HiDPIPropertiesWindowsTest UISCALE_4X5
+ * @run main/othervm -Dsun.java2d.win.uiScaleX=288dpi -Dsun.java2d.win.uiScaleY=192dpi
+ *                    HiDPIPropertiesWindowsTest UISCALE_3X2
+ * @run main/othervm -Dsun.java2d.win.uiScaleX=200% -Dsun.java2d.win.uiScaleY=288dpi
+ *                    HiDPIPropertiesWindowsTest UISCALE_2X3
+ */
+public class HiDPIPropertiesWindowsTest {
+
+    public static void main(String[] args) throws Exception {
+
+        try {
+            UIManager.setLookAndFeel(
+                    "com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
+        } catch (Exception e) {
+            return;
+        }
+
+        String testCase = args[0];
+        switch (testCase) {
+            case "UISCALE_DISABLED":
+                testScale(1.0, 1.0);
+                break;
+            case "UISCALE_3":
+                testScale(3.0, 3.0);
+                break;
+            case "UISCALE_4":
+                testScale(4.0, 4.0);
+                break;
+            case "UISCALE_2X3":
+                testScale(2.0, 3.0);
+                break;
+            case "UISCALE_3X2":
+                testScale(3.0, 2.0);
+                break;
+            case "UISCALE_4X5":
+                testScale(4.0, 5.0);
+                break;
+            default:
+                throw new RuntimeException("Unknown test case: " + testCase);
+        }
+    }
+
+    private static void testScale(double scaleX, double scaleY) {
+
+        Dialog dialog = new Dialog((Frame) null, true) {
+
+            @Override
+            public void paint(Graphics g) {
+                super.paint(g);
+                AffineTransform tx = ((Graphics2D) g).getTransform();
+                dispose();
+                if (scaleX != tx.getScaleX() || scaleY != tx.getScaleY()) {
+                    throw new RuntimeException(String.format("Wrong scale:"
+                            + "[%f, %f] instead of [%f, %f].",
+                            tx.getScaleX(), tx.getScaleY(), scaleX, scaleY));
+                }
+            }
+        };
+        dialog.setSize(200, 300);
+        dialog.setVisible(true);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/image/MultiResolutionImage/MultiResolutionDrawImageWithTransformTest.java	Fri Nov 13 05:02:26 2015 -0800
@@ -0,0 +1,248 @@
+/*
+ * 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.Frame;
+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_SIZE_FIT;
+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 8073320
+ * @author Alexander Scherbatiy
+ * @summary Windows HiDPI support
+ * @modules java.desktop/sun.java2d java.desktop/sun.java2d.loops
+ * @run main MultiResolutionDrawImageWithTransformTest
+ */
+public class MultiResolutionDrawImageWithTransformTest {
+
+    private static final int SCREEN_SIZE = 400;
+    private static final int IMAGE_SIZE = SCREEN_SIZE / 4;
+    private static final Color BACKGROUND_COLOR = Color.PINK;
+    private static final Color[] COLORS = {
+        Color.CYAN, Color.GREEN, Color.BLUE, Color.ORANGE
+    };
+
+    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);
+
+        // scale 1, transform 1, resolution variant 1
+        Color color = getImageColor(mrImage, 1, 1);
+        if (!getColorForScale(1).equals(color)) {
+            throw new RuntimeException("Wrong resolution variant!");
+        }
+
+        // scale 1, transform 2, resolution variant 2
+        color = getImageColor(mrImage, 1, 2);
+        if (!getColorForScale(2).equals(color)) {
+            throw new RuntimeException("Wrong resolution variant!");
+        }
+
+        // scale 2, transform 1, resolution variant 2
+        color = getImageColor(mrImage, 2, 1);
+        if (!getColorForScale(2).equals(color)) {
+            throw new RuntimeException("Wrong resolution variant!");
+        }
+
+        // scale 2, transform 2, resolution variant 4
+        color = getImageColor(mrImage, 2, 2);
+        if (!getColorForScale(4).equals(color)) {
+            throw new RuntimeException("Wrong resolution variant!");
+        }
+    }
+
+    private static Color getColorForScale(int scale) {
+        return COLORS[scale - 1];
+    }
+
+    private static Color getImageColor(Image image, double configScale,
+            double transformScale) {
+
+        TestSurfaceData surface = new TestSurfaceData(SCREEN_SIZE, SCREEN_SIZE,
+                configScale);
+        SunGraphics2D g2d = new SunGraphics2D(surface,
+                Color.BLACK, Color.BLACK, null);
+        g2d.setRenderingHint(KEY_RESOLUTION_VARIANT,
+                VALUE_RESOLUTION_VARIANT_SIZE_FIT);
+        AffineTransform tx = AffineTransform.getScaleInstance(transformScale,
+                transformScale);
+        g2d.drawImage(image, tx, null);
+        g2d.dispose();
+
+        int backgroundX = (int) (1.5 * image.getWidth(null) * transformScale);
+        int backgroundY = (int) (1.5 * image.getHeight(null) * transformScale);
+        Color backgroundColor = surface.getColor(backgroundX, backgroundY);
+        //surface.show(String.format("Config: %f, transform: %f", configScale, transformScale));
+        if (!BACKGROUND_COLOR.equals(backgroundColor)) {
+            throw new RuntimeException("Wrong background color!");
+        }
+        return surface.getColor(IMAGE_SIZE / 4, IMAGE_SIZE / 4);
+    }
+
+    private static int getSize(int i) {
+        return (i + 1) * IMAGE_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);
+        g.fillRect(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);
+
+            Graphics imageGraphics = buffImage.createGraphics();
+            imageGraphics.setColor(BACKGROUND_COLOR);
+            imageGraphics.fillRect(0, 0, this.width, this.height);
+            imageGraphics.dispose();
+        }
+
+        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.");
+        }
+
+        private void show(String title) {
+            Frame frame = new Frame() {
+
+                @Override
+                public void paint(Graphics g) {
+                    super.paint(g);
+                    g.drawImage(buffImage, 0, 0, this);
+                    g.setColor(Color.GRAY);
+                    g.drawRect(0, 0, width, height);
+                    g.drawRect(0, height / 2, width, height / 2);
+                    g.drawRect(width / 2, 0, width / 2, height);
+                }
+            };
+            frame.setTitle(title);
+            frame.setSize(width, height);
+            frame.setVisible(true);
+        }
+    }
+}