--- old/src/java.base/windows/native/launcher/java.manifest 2015-09-22 00:06:12.218289300 -0700 +++ new/src/java.base/windows/native/launcher/java.manifest 2015-09-22 00:06:11.983932800 -0700 @@ -37,7 +37,7 @@ - true + true/PM --- old/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java 2015-09-22 00:06:14.952861200 -0700 +++ new/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java 2015-09-22 00:06:14.671618800 -0700 @@ -166,7 +166,12 @@ } @Override - public int getDefaultScale() { + public double getDefaultScaleX() { + return scale; + } + + @Override + public double getDefaultScaleY() { return scale; } --- old/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java 2015-09-22 00:06:17.406197900 -0700 +++ new/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java 2015-09-22 00:06:17.140557500 -0700 @@ -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, --- old/src/java.desktop/share/classes/sun/awt/image/BufImgSurfaceData.java 2015-09-22 00:06:19.843870500 -0700 +++ new/src/java.desktop/share/classes/sun/awt/image/BufImgSurfaceData.java 2015-09-22 00:06:19.656410000 -0700 @@ -50,6 +50,8 @@ BufferedImage bufImg; private BufferedImageGraphicsConfig graphicsConfig; RenderLoops solidloops; + private double scaleX = 1; + private double scaleY = 1; 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,11 +379,16 @@ 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) { @@ -395,7 +429,8 @@ public synchronized GraphicsConfiguration getDeviceConfiguration() { if (graphicsConfig == null) { - graphicsConfig = BufferedImageGraphicsConfig.getConfig(bufImg); + graphicsConfig = BufferedImageGraphicsConfig + .getConfig(bufImg, scaleX, scaleY); } return graphicsConfig; } @@ -418,6 +453,16 @@ return bufImg; } + @Override + public double getDefaultScaleX() { + return scaleX; + } + + @Override + public double getDefaultScaleY() { + return scaleY; + } + public static final class ICMColorData { private long pData = 0L; --- old/src/java.desktop/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java 2015-09-22 00:06:22.484719500 -0700 +++ new/src/java.desktop/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java 2015-09-22 00:06:22.250325400 -0700 @@ -45,21 +45,29 @@ extends GraphicsConfiguration { private static final int numconfigs = BufferedImage.TYPE_BYTE_BINARY; - private static BufferedImageGraphicsConfig configs[] = - new BufferedImageGraphicsConfig[numconfigs]; + private static BufferedImageGraphicsConfig configs[][] = + new BufferedImageGraphicsConfig[numconfigs][2]; 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(); + int index = (scaleX == 1 && scaleY == 1) ? 0 : 1; if (type > 0 && type < numconfigs) { - ret = configs[type]; - if (ret != null) { + ret = configs[type][index]; + 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; + configs[type][index] = ret; } return ret; } @@ -68,8 +76,16 @@ ColorModel model; Raster raster; int width, height; + 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 { @@ -80,6 +96,8 @@ this.raster = bufImg.getRaster().createCompatibleWritableRaster(1, 1); this.width = bufImg.getWidth(); this.height = bufImg.getHeight(); + this.scaleX = scaleX; + this.scaleY = scaleY; } /** @@ -141,7 +159,7 @@ * For image buffers, this Transform will be the Identity transform. */ public AffineTransform getDefaultTransform() { - return new AffineTransform(); + return AffineTransform.getScaleInstance(scaleX, scaleY); } /** --- old/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java 2015-09-22 00:06:24.844279800 -0700 +++ new/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java 2015-09-22 00:06:24.609872000 -0700 @@ -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() { --- old/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java 2015-09-22 00:06:27.563244200 -0700 +++ new/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java 2015-09-22 00:06:27.360085900 -0700 @@ -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(); } } --- old/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java 2015-09-22 00:06:30.266563000 -0700 +++ new/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java 2015-09-22 00:06:30.032171300 -0700 @@ -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; } --- old/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java 2015-09-22 00:06:32.657393800 -0700 +++ new/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java 2015-09-22 00:06:32.423032100 -0700 @@ -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,22 +3086,30 @@ } // 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); + + return scaleImage(img, dx1, dy1, dx2, dy2, + sx1, sy1, sx2, sy2, + bgcolor, observer, xform); + } else if (resolutionVariantHint != SunHints.INTVAL_RESOLUTION_VARIANT_BASE + && (img instanceof MultiResolutionImage)) { // get scaled destination image size int width = img.getWidth(observer); @@ -3133,9 +3141,26 @@ observer = rvObserver; img = resolutionVariant; + return scaleImage(img, dx1, dy1, dx2, dy2, + sx1, sy1, sx2, sy2, + bgcolor, observer, xform); } } } + 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, + AffineTransform xform) { + + AffineTransform tx = null; + + if (xform != null) { + tx = new AffineTransform(transform); + transform(xform); + } try { return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1, sy1, @@ -3153,6 +3178,11 @@ } } finally { surfaceData.markDirty(); + + if (tx != null) { + transform.setTransform(tx); + invalidateTransform(); + } } } @@ -3277,9 +3307,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 +3355,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 +3412,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,16 +3498,13 @@ 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 { --- old/src/java.desktop/share/classes/sun/java2d/SurfaceData.java 2015-09-22 00:06:35.313852300 -0700 +++ new/src/java.desktop/share/classes/sun/java2d/SurfaceData.java 2015-09-22 00:06:35.032564900 -0700 @@ -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; } } --- old/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java 2015-09-22 00:06:37.907805200 -0700 +++ new/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java 2015-09-22 00:06:37.657775700 -0700 @@ -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 --- old/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java 2015-09-22 00:06:40.439254000 -0700 +++ new/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java 2015-09-22 00:06:40.204865000 -0700 @@ -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); } /** --- old/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java 2015-09-22 00:06:43.080071600 -0700 +++ new/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java 2015-09-22 00:06:42.876958300 -0700 @@ -37,13 +37,16 @@ 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.opengl.WGLGraphicsConfig; import sun.java2d.windows.WindowsFlags; +import sun.security.action.GetPropertyAction; /** * This is an implementation of a GraphicsDevice object for a single @@ -81,6 +84,9 @@ // activation/deactivation listener for the full-screen window private WindowListener fsWindowListener; + private float scaleX = 1.0f; + private float scaleY = 1.0f; + static { // 4455041 - Even when ddraw is disabled, ddraw.dll is loaded when @@ -97,6 +103,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 +119,7 @@ valid = true; initDevice(screennum); + initScaleFactors(); } /** @@ -128,6 +139,74 @@ return screen; } + public float getDefaultScaleX() { + return scaleX; + } + + public float getDefaultScaleY() { + return scaleY; + } + + private void initScaleFactors() { + boolean dpiEnabled = "true".equals(AccessController.doPrivileged( + new GetPropertyAction("sun.java2d.uiScale.enabled", "true"))); + + if (!dpiEnabled) { + return; + } + + scaleX = getScaleFactor("sun.java2d.win.uiScaleX"); + scaleY = getScaleFactor("sun.java2d.win.uiScaleY"); + + if (scaleX > 0 && scaleY > 0) { + setNativeScale(screen, scaleX, scaleY); + return; + } + + float scale = getScaleFactor("sun.java2d.win.uiScale"); + + if (scale > 0) { + scaleX = scale; + scaleY = scale; + setNativeScale(screen, scaleX, scaleY); + return; + } + + initNativeScale(screen); + scaleX = getNativeScaleX(screen); + scaleY = getNativeScaleY(screen); + } + + private static float getScaleFactor(String name) { + + String scaleFactor = AccessController.doPrivileged( + new GetPropertyAction(name, "-1")); + + if (scaleFactor == null || scaleFactor.equals("-1")) { + return -1; + } + try { + + if (scaleFactor.endsWith("dpi")) { + scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 3); + float scale = Float.parseFloat(scaleFactor); + return scale <= 0 ? -1 : scale / 96; + } + + if (scaleFactor.endsWith("%")) { + scaleFactor = scaleFactor.substring(0, scaleFactor.length() - 1); + float scale = Float.parseFloat(scaleFactor); + return scale <= 0 ? -1 : scale / 100; + } + + float scale = Float.parseFloat(scaleFactor); + return (scale <=0) ? -1 : scale; + + } catch (NumberFormatException ignore) { + } + return -1; + } + /** * Returns whether this is a valid devicie. Device can become * invalid as a result of device removal event. @@ -486,6 +565,7 @@ configs = null; // pass on to all top-level windows on this display topLevels.notifyListeners(); + initScaleFactors(); } /** --- old/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java 2015-09-22 00:06:45.377142300 -0700 +++ new/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java 2015-09-22 00:06:45.220914100 -0700 @@ -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 --- old/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java 2015-09-22 00:06:47.924199500 -0700 +++ new/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java 2015-09-22 00:06:47.689830000 -0700 @@ -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); --- old/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java 2015-09-22 00:06:50.596285900 -0700 +++ new/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java 2015-09-22 00:06:50.393195200 -0700 @@ -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 { --- old/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java 2015-09-22 00:06:53.080853900 -0700 +++ new/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java 2015-09-22 00:06:52.830845200 -0700 @@ -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; } --- old/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp 2015-09-22 00:06:55.799819100 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp 2015-09-22 00:06:55.549800500 -0700 @@ -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. --- old/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp 2015-09-22 00:06:58.518798600 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp 2015-09-22 00:06:58.300014700 -0700 @@ -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); --- old/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp 2015-09-22 00:07:01.034620700 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp 2015-09-22 00:07:00.815854900 -0700 @@ -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; @@ -4603,6 +4612,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); @@ -4896,8 +4933,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)) { @@ -4964,8 +5002,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); @@ -5471,7 +5511,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); @@ -7059,6 +7100,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)); @@ -7251,5 +7297,4 @@ removedDCs = removedDCs->next; delete tmpDCList; } -} - +} \ No newline at end of file --- old/src/java.desktop/windows/native/libawt/windows/awt_Component.h 2015-09-22 00:07:03.753569800 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_Component.h 2015-09-22 00:07:03.519176400 -0700 @@ -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 --- old/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp 2015-09-22 00:07:06.097502400 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp 2015-09-22 00:07:05.863138100 -0700 @@ -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); } --- old/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp 2015-09-22 00:07:08.503940000 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp 2015-09-22 00:07:08.269598800 -0700 @@ -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; --- old/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp 2015-09-22 00:07:11.051016600 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp 2015-09-22 00:07:10.816622200 -0700 @@ -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); } --- old/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp 2015-09-22 00:07:13.894990200 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp 2015-09-22 00:07:13.644970300 -0700 @@ -95,19 +95,31 @@ mid = env->GetMethodID(clazz, "", "(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; --- old/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp 2015-09-22 00:07:16.723339000 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp 2015-09-22 00:07:16.488973200 -0700 @@ -49,6 +49,12 @@ #include "dither.h" #include "img_util_md.h" #include "Devices.h" +#include +#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,98 @@ } /** + * 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 + char *uiScale = getenv("J2D_WIN_UISCALE"); + float scale = -1.0f; + + if (uiScale != NULL) { + scale = (float)strtod(uiScale, NULL); + if (errno != ERANGE && 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(x); + dpiY = static_cast(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 +1404,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 --- old/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h 2015-09-22 00:07:19.567309500 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h 2015-09-22 00:07:19.270412900 -0700 @@ -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); }; --- old/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp 2015-09-22 00:07:22.223784100 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp 2015-09-22 00:07:21.989403600 -0700 @@ -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" { /* @@ -3302,6 +3321,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, "", "(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 --- old/src/java.desktop/windows/native/libawt/windows/awt_Window.h 2015-09-22 00:07:24.755233800 -0700 +++ new/src/java.desktop/windows/native/libawt/windows/awt_Window.h 2015-09-22 00:07:24.489600600 -0700 @@ -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 2015-09-22 00:07:27.000000000 -0700 +++ new/test/java/awt/Robot/HiDPIMouseClick/HiDPIRobotMouseClick.java 2015-09-22 00:07:27.052289100 -0700 @@ -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 2015-09-22 00:07:29.000000000 -0700 +++ new/test/java/awt/Robot/HiDPIScreenCapture/HiDPIRobotScreenCaptureTest.java 2015-09-22 00:07:28.771177500 -0700 @@ -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 2015-09-22 00:07:31.000000000 -0700 +++ new/test/java/awt/hidpi/properties/HiDPIPropertiesTest.java 2015-09-22 00:07:30.630680400 -0700 @@ -0,0 +1,135 @@ +/* + * 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 + * HiDPIPropertiesTest UISCALE_DISABLED + * @run main/othervm -Dsun.java2d.uiScale.enabled=false + * -Dsun.java2d.win.uiScale=3 + * HiDPIPropertiesTest UISCALE_DISABLED + * @run main/othervm -Dsun.java2d.uiScale.enabled=false + * -Dsun.java2d.win.uiScale=3 + * -Dsun.java2d.win.uiScaleX=5 -Dsun.java2d.win.uiScaleY=6 + * HiDPIPropertiesTest UISCALE_DISABLED + * @run main/othervm -Dsun.java2d.uiScale.enabled=true + * -Dsun.java2d.win.uiScale=3 + * HiDPIPropertiesTest UISCALE_3 + * @run main/othervm -Dsun.java2d.uiScale.enabled=true + * -Dsun.java2d.win.uiScale=4 + * -Dsun.java2d.win.uiScaleX=2 -Dsun.java2d.win.uiScaleY=3 + * HiDPIPropertiesTest UISCALE_2X3 + * @run main/othervm -Dsun.java2d.uiScale.enabled=true + * -Dsun.java2d.win.uiScaleX=3 -Dsun.java2d.win.uiScaleY=2 + * HiDPIPropertiesTest UISCALE_3X2 + * @run main/othervm -Dsun.java2d.win.uiScale=4 + * HiDPIPropertiesTest UISCALE_4 + * @run main/othervm -Dsun.java2d.win.uiScale=4 + * -Dsun.java2d.win.uiScaleX=2 -Dsun.java2d.win.uiScaleY=3 + * HiDPIPropertiesTest UISCALE_2X3 + * @run main/othervm -Dsun.java2d.win.uiScaleX=4 -Dsun.java2d.win.uiScaleY=5 + * HiDPIPropertiesTest UISCALE_4X5 + * @run main/othervm -Dsun.java2d.win.uiScale=3 + * -Dsun.java2d.win.uiScaleX=0 -Dsun.java2d.win.uiScaleY=0 + * HiDPIPropertiesTest UISCALE_3 + * @run main/othervm -Dsun.java2d.win.uiScale=4 + * -Dsun.java2d.win.uiScaleX=-7 -Dsun.java2d.win.uiScaleY=-8 + * HiDPIPropertiesTest UISCALE_4 + * @run main/othervm -Dsun.java2d.win.uiScale=384dpi + * HiDPIPropertiesTest UISCALE_4 + * @run main/othervm -Dsun.java2d.win.uiScale=300% + * HiDPIPropertiesTest UISCALE_3 + * @run main/othervm -Dsun.java2d.win.uiScaleX=400% -Dsun.java2d.win.uiScaleY=500% + * HiDPIPropertiesTest UISCALE_4X5 + * @run main/othervm -Dsun.java2d.win.uiScaleX=288dpi -Dsun.java2d.win.uiScaleY=192dpi + * HiDPIPropertiesTest UISCALE_3X2 + * @run main/othervm -Dsun.java2d.win.uiScaleX=200% -Dsun.java2d.win.uiScaleY=288dpi + * HiDPIPropertiesTest UISCALE_2X3 + */ +public class HiDPIPropertiesTest { + + 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); + } +}