# HG changeset patch # User alexsch # Date 1447419746 28800 # Node ID 259d6e4e09781bc8b2c5f95181fd466c4d8a88bc # Parent 171a07c89136161e44a9ff8a622d7a0623140c67 8073320: Windows HiDPI Graphics support Reviewed-by: flar, serb diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.base/windows/native/launcher/java.manifest --- a/jdk/src/java.base/windows/native/launcher/java.manifest Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/src/java.base/windows/native/launcher/java.manifest Fri Nov 13 05:02:26 2015 -0800 @@ -37,7 +37,7 @@ - true + true/PM diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java --- a/jdk/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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; } diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java --- a/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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, diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/share/classes/sun/awt/image/BufImgSurfaceData.java --- a/jdk/src/java.desktop/share/classes/sun/awt/image/BufImgSurfaceData.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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; diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java --- a/jdk/src/java.desktop/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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); } /** diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java --- a/jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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() { diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java --- a/jdk/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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(); } } diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java --- a/jdk/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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; } diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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, diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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() { @@ -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; + } + } } diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/share/classes/sun/java2d/SurfaceData.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/SurfaceData.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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; } } diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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 diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java --- a/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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); } /** diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java --- a/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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(); } /** diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java --- a/jdk/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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; } /** diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java --- a/jdk/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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 diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java --- a/jdk/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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); diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java --- a/jdk/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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 { diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java --- a/jdk/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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; } diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp --- a/jdk/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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. diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Choice.cpp Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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); diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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 diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/native/libawt/windows/awt_Component.h --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Component.h Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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 diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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); } diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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; diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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); } diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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, "", "(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; diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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 +#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(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 +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 diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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); }; diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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, "", "(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 */ diff -r 171a07c89136 -r 259d6e4e0978 jdk/src/java.desktop/windows/native/libawt/windows/awt_Window.h --- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Window.h Thu Nov 12 12:27:36 2015 -0600 +++ b/jdk/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; diff -r 171a07c89136 -r 259d6e4e0978 jdk/test/java/awt/Robot/HiDPIMouseClick/HiDPIRobotMouseClick.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/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!"); + } + } +} diff -r 171a07c89136 -r 259d6e4e0978 jdk/test/java/awt/Robot/HiDPIScreenCapture/HiDPIRobotScreenCaptureTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/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 diff -r 171a07c89136 -r 259d6e4e0978 jdk/test/java/awt/hidpi/properties/HiDPIPropertiesWindowsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/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); + } +} diff -r 171a07c89136 -r 259d6e4e0978 jdk/test/java/awt/image/MultiResolutionImage/MultiResolutionDrawImageWithTransformTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/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); + } + } +}