--- old/src/java.desktop/share/classes/java/awt/Robot.java 2017-02-09 15:00:22.609640001 +0530 +++ new/src/java.desktop/share/classes/java/awt/Robot.java 2017-02-09 15:00:22.397640001 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2017, 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 @@ -27,6 +27,9 @@ import java.awt.event.InputEvent; import java.awt.event.KeyEvent; +import java.awt.geom.AffineTransform; +import java.awt.image.BaseMultiResolutionImage; +import java.awt.image.MultiResolutionImage; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.awt.image.DirectColorModel; @@ -390,6 +393,11 @@ * @return Color of the pixel */ public synchronized Color getPixelColor(int x, int y) { + AffineTransform tx = GraphicsEnvironment. + getLocalGraphicsEnvironment().getDefaultScreenDevice(). + getDefaultConfiguration().getDefaultTransform(); + x = (int) (x * tx.getScaleX()); + y = (int) (y * tx.getScaleY()); Color color = new Color(peer.getRGBPixel(x, y)); return color; } @@ -405,13 +413,68 @@ * @see AWTPermission */ public synchronized BufferedImage createScreenCapture(Rectangle screenRect) { + return createCompatibleImage(screenRect, false)[0]; + } + + /** + * Creates an image containing pixels read from the screen. + * This image does not include the mouse cursor. + * This method can be used in case there is a scaling transform + * from user space to screen (device) space. + * Typically this means that the display is a high resolution screen, + * although strictly it means any case in which there is such a transform. + * Returns a {@link java.awt.image.MultiResolutionImage}. + *
+ * For a non-scaled display, the {@code MultiResolutionImage} + * will have one image variant: + *
+ * For a high resolution display where there is a scaling transform, + * the {@code MultiResolutionImage} will have two image variants: + *
+ * Example: + *
+ * Image nativeResImage; + * Image baseResImage; + * MultiResolutionImage mrImage = robot.createMultiResolutionScreenCapture(frame.getBounds()); + * List+ * @param screenRect Rect to capture in screen coordinates + * @return The captured image + * @throws IllegalArgumentException if {@code screenRect} width and height are not greater than zero + * @throws SecurityException if {@code readDisplayPixels} permission is not granted + * @see SecurityManager#checkPermission + * @see AWTPermission + */ + public synchronized MultiResolutionImage + createMultiResolutionScreenCapture(Rectangle screenRect) { + + return new BaseMultiResolutionImage( + createCompatibleImage(screenRect, true)); + } + + private synchronized BufferedImage[] + createCompatibleImage(Rectangle screenRect, boolean isHiDPI) { + checkScreenCaptureAllowed(); checkValidRect(screenRect); - BufferedImage image; + BufferedImage lowResolutionImage; + BufferedImage highResolutionImage; DataBufferInt buffer; WritableRaster raster; + BufferedImage[] imageArray; if (screenCapCM == null) { /* @@ -421,31 +484,92 @@ */ screenCapCM = new DirectColorModel(24, - /* red mask */ 0x00FF0000, - /* green mask */ 0x0000FF00, - /* blue mask */ 0x000000FF); + /* red mask */ 0x00FF0000, + /* green mask */ 0x0000FF00, + /* blue mask */ 0x000000FF); } + int[] bandmasks = new int[3]; + bandmasks[0] = screenCapCM.getRedMask(); + bandmasks[1] = screenCapCM.getGreenMask(); + bandmasks[2] = screenCapCM.getBlueMask(); + // need to sync the toolkit prior to grabbing the pixels since in some // cases rendering to the screen may be delayed Toolkit.getDefaultToolkit().sync(); - + AffineTransform tx = GraphicsEnvironment. + getLocalGraphicsEnvironment().getDefaultScreenDevice(). + getDefaultConfiguration().getDefaultTransform(); + double uiScaleX = tx.getScaleX(); + double uiScaleY = tx.getScaleY(); int pixels[]; - int[] bandmasks = new int[3]; - pixels = peer.getRGBPixels(screenRect); - buffer = new DataBufferInt(pixels, pixels.length); + if (uiScaleX == 1 && uiScaleY == 1) { - bandmasks[0] = screenCapCM.getRedMask(); - bandmasks[1] = screenCapCM.getGreenMask(); - bandmasks[2] = screenCapCM.getBlueMask(); + pixels = peer.getRGBPixels(screenRect); + buffer = new DataBufferInt(pixels, pixels.length); - raster = Raster.createPackedRaster(buffer, screenRect.width, screenRect.height, screenRect.width, bandmasks, null); - SunWritableRaster.makeTrackable(buffer); + bandmasks[0] = screenCapCM.getRedMask(); + bandmasks[1] = screenCapCM.getGreenMask(); + bandmasks[2] = screenCapCM.getBlueMask(); + + raster = Raster.createPackedRaster(buffer, screenRect.width, + screenRect.height, screenRect.width, bandmasks, null); + SunWritableRaster.makeTrackable(buffer); + + highResolutionImage = new BufferedImage(screenCapCM, raster, + false, null); + imageArray = new BufferedImage[1]; + imageArray[0] = highResolutionImage; + + } else { + + int sX = (int) Math.floor(screenRect.x * uiScaleX); + int sY = (int) Math.floor(screenRect.y * uiScaleY); + int sWidth = (int) Math.ceil(screenRect.width * uiScaleX); + int sHeight = (int) Math.ceil(screenRect.height * uiScaleY); + int temppixels[]; + Rectangle scaledRect = new Rectangle(sX, sY, sWidth, sHeight); + temppixels = peer.getRGBPixels(scaledRect); + + // HighResolutionImage + pixels = temppixels; + buffer = new DataBufferInt(pixels, pixels.length); + raster = Raster.createPackedRaster(buffer, scaledRect.width, + scaledRect.height, scaledRect.width, bandmasks, null); + SunWritableRaster.makeTrackable(buffer); + + highResolutionImage = new BufferedImage(screenCapCM, raster, + false, null); + + + // LowResolutionImage + lowResolutionImage = new BufferedImage(screenRect.width, + screenRect.height, highResolutionImage.getType()); + Graphics2D g = lowResolutionImage.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g.drawImage(highResolutionImage, 0, 0, + screenRect.width, screenRect.height, + 0, 0, scaledRect.width, scaledRect.height, null); + g.dispose(); + + if(!isHiDPI) { + imageArray = new BufferedImage[1]; + imageArray[0] = lowResolutionImage; + } else { + imageArray = new BufferedImage[2]; + imageArray[0] = lowResolutionImage; + imageArray[1] = highResolutionImage; + } - image = new BufferedImage(screenCapCM, raster, false, null); + } - return image; + return imageArray; } private static void checkValidRect(Rectangle rect) { --- old/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp 2017-02-09 15:00:23.105640001 +0530 +++ new/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp 2017-02-09 15:00:22.933640001 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2017, 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 @@ -224,24 +224,11 @@ 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 - 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); - } + VERIFY(::BitBlt(hdcMem, 0, 0, width, height, hdcScreen, x, y, + SRCCOPY | CAPTUREBLT) != 0); static const int BITS_PER_PIXEL = 32; static const int BYTES_PER_PIXEL = BITS_PER_PIXEL/8; --- old/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java 2017-02-09 15:00:23.589640001 +0530 +++ new/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java 2017-02-09 15:00:23.393640001 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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 @@ -115,8 +115,7 @@ @Override public int getRGBPixel(int x, int y) { int pixelArray[] = new int[1]; - getRGBPixelsImpl(xgc, x, y, 1, 1, xgc.getScale(), pixelArray, - useGtk); + getRGBPixelsImpl(xgc, x, y, 1, 1, pixelArray, useGtk); return pixelArray[0]; } @@ -124,7 +123,7 @@ public int [] getRGBPixels(Rectangle bounds) { int pixelArray[] = new int[bounds.width*bounds.height]; getRGBPixelsImpl(xgc, bounds.x, bounds.y, bounds.width, bounds.height, - xgc.getScale(), pixelArray, useGtk); + pixelArray, useGtk); return pixelArray; } @@ -140,6 +139,5 @@ private static synchronized native void keyReleaseImpl(int keycode); private static synchronized native void getRGBPixelsImpl(X11GraphicsConfig xgc, - int x, int y, int width, int height, int scale, - int pixelArray[], boolean isGtkSupported); + int x, int y, int width, int height, int pixelArray[], boolean isGtkSupported); } --- old/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c 2017-02-09 15:00:24.077640001 +0530 +++ new/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c 2017-02-09 15:00:23.901640001 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2017, 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 @@ -276,7 +276,6 @@ jint jy, jint jwidth, jint jheight, - jint scale, jintArray pixelArray, jboolean useGtk) { XImage *image; @@ -298,11 +297,6 @@ AWT_LOCK(); - jint sx = jx * scale; - jint sy = jy * scale; - jint swidth = jwidth * scale; - jint sheight = jheight * scale; - rootWindow = XRootWindow(awt_display, adata->awt_visInfo.screen); if (!useGtk) { @@ -314,10 +308,10 @@ } if (!XGetWindowAttributes(awt_display, rootWindow, &attr) - || sx + swidth <= attr.x - || attr.x + attr.width <= sx - || sy + sheight <= attr.y - || attr.y + attr.height <= sy) { + || jx + jwidth <= attr.x + || attr.x + attr.width <= jx + || jy + jheight <= attr.y + || attr.y + attr.height <= jy) { AWT_UNLOCK(); return; // Does not intersect with root window @@ -326,26 +320,25 @@ gboolean gtk_failed = TRUE; jint _x, _y; - jint x = MAX(sx, attr.x); - jint y = MAX(sy, attr.y); - jint width = MIN(sx + swidth, attr.x + attr.width) - x; - jint height = MIN(sy + sheight, attr.y + attr.height) - y; - + jint x = MAX(jx, attr.x); + jint y = MAX(jy, attr.y); + jint width = MIN(jx + jwidth, attr.x + attr.width) - x; + jint height = MIN(jy + jheight, attr.y + attr.height) - y; - int dx = attr.x > sx ? attr.x - sx : 0; - int dy = attr.y > sy ? attr.y - sy : 0; + int dx = attr.x > jx ? attr.x - jx : 0; + int dy = attr.y > jy ? attr.y - jy : 0; int index; if (useGtk) { gtk->gdk_threads_enter(); gtk_failed = gtk->get_drawable_data(env, pixelArray, x, y, width, - height, jwidth, dx, dy, scale); + height, jwidth, dx, dy, 1); gtk->gdk_threads_leave(); } if (gtk_failed) { - image = getWindowImage(awt_display, rootWindow, sx, sy, swidth, sheight); + image = getWindowImage(awt_display, rootWindow, x, y, width, height); ary = (*env)->GetPrimitiveArrayCritical(env, pixelArray, NULL); @@ -355,15 +348,10 @@ return; } - dx /= scale; - dy /= scale; - width /= scale; - height /= scale; - /* convert to Java ARGB pixels */ for (_y = 0; _y < height; _y++) { for (_x = 0; _x < width; _x++) { - jint pixel = (jint) XGetPixel(image, _x * scale, _y * scale); + jint pixel = (jint) XGetPixel(image, _x, _y); /* Note ignore upper * 32-bits on 64-bit * OSes. --- old/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m 2017-02-09 15:00:24.521640001 +0530 +++ new/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m 2017-02-09 15:00:24.365640001 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -276,7 +276,7 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CRobot_nativeGetScreenPixels (JNIEnv *env, jobject peer, - jint x, jint y, jint width, jint height, jintArray pixels) + jint x, jint y, jint width, jint height, jdouble scale, jintArray pixels) { JNF_COCOA_ENTER(env); @@ -285,10 +285,11 @@ jint picWidth = width; jint picHeight = height; - CGRect screenRect = CGRectMake(picX, picY, picWidth, picHeight); + CGRect screenRect = CGRectMake(picX / scale, picY / scale, + picWidth / scale, picHeight / scale); CGImageRef screenPixelsImage = CGWindowListCreateImage(screenRect, kCGWindowListOptionOnScreenOnly, - kCGNullWindowID, kCGWindowImageDefault); + kCGNullWindowID, kCGWindowImageBestResolution); if (screenPixelsImage == NULL) { return; --- old/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java 2017-02-09 15:00:25.073640001 +0530 +++ new/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java 2017-02-09 15:00:24.861640001 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2017, 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 @@ -174,7 +174,8 @@ @Override public int getRGBPixel(int x, int y) { int c[] = new int[1]; - getScreenPixels(new Rectangle(x, y, 1, 1), c); + double scale = fDevice.getScaleFactor(); + getScreenPixels(new Rectangle(x, y, (int) scale, (int) scale), c); return c[0]; } @@ -185,7 +186,7 @@ */ @Override public int [] getRGBPixels(final Rectangle bounds) { - int c[] = new int[bounds.width * bounds.height]; + int c[] = new int[bounds.width * bounds.height]; getScreenPixels(bounds, c); return c; @@ -198,7 +199,8 @@ boolean isMouseMove); private native void keyEvent(int javaKeyCode, boolean keydown); private void getScreenPixels(Rectangle r, int[] pixels){ - nativeGetScreenPixels(r.x, r.y, r.width, r.height, pixels); + double scale = fDevice.getScaleFactor(); + nativeGetScreenPixels(r.x, r.y, r.width, r.height, scale, pixels); } - private native void nativeGetScreenPixels(int x, int y, int width, int height, int[] pixels); + private native void nativeGetScreenPixels(int x, int y, int width, int height, double scale, int[] pixels); } --- /dev/null 2017-02-09 11:50:18.248520000 +0530 +++ new/test/java/awt/Robot/HiDPIScreenCapture/ScreenCaptureTest.java 2017-02-09 15:00:25.465640001 +0530 @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2017, 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. + */ + + /* + * @test + * @bug 8162959 + * @summary Validate output of createMultiResolutionScreenCapture + * new API which returns MultiResolutionImage. + * @run main/othervm -Dsun.java2d.uiScale=1 ScreenCaptureTest + * @run main/othervm -Dsun.java2d.uiScale=2 ScreenCaptureTest + */ +import java.awt.Dimension; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.MultiResolutionImage; +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.Robot; +import java.util.List; + +public class ScreenCaptureTest { + + private static Robot robot; + private static Frame frame; + private static boolean isHiDPI = true; + private static final Color[] COLORS = { + Color.GREEN, Color.BLUE, Color.ORANGE, Color.RED}; + + public static void main(String[] args) throws Exception { + + frame = new Frame(); + frame.setBounds(0, 0, 400, 400); + frame.setUndecorated(true); + robot = new Robot(); + Panel panel = new Panel(new BorderLayout()); + Canvas canvas = new Canvas() { + 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.delay(500); + robot.waitForIdle(); + + int w = frame.getWidth(); + int h = frame.getHeight(); + + // getPixelColor Test + // Check pixel color in first quardant GREEN; x=100, y=100 + if (!robot.getPixelColor(w / 4, h / 4).equals(COLORS[0])) { + throw new RuntimeException("Wrong Pixel Color! Expected GREEN"); + } + // Check pixel color in second quardant; BLUE, x=300, y=100 + if (!robot.getPixelColor(3 * w / 4, h / 4).equals(COLORS[1])) { + throw new RuntimeException("Wrong Pixel Color! Expected BLUE"); + } + // Check pixel color in third quardant; ORANGE, x=100, y=300 + if (!robot.getPixelColor(w / 4, 3 * h / 4).equals(COLORS[2])) { + throw new RuntimeException("Wrong Pixel Color! Expected ORANGE"); + } + // Check pixel color in fourth quardant; RED, x=300, y=300 + if (!robot.getPixelColor(3 * w / 4, 3 * h / 4).equals(COLORS[3])) { + throw new RuntimeException("Wrong Pixel Color! Expected RED"); + } + + // createScreenCaptureTest + AffineTransform tx = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration() + .getDefaultTransform(); + + if (tx.getScaleX() == 1 && tx.getScaleY() == 1) { + isHiDPI = false; + } + + MultiResolutionImage image + = robot.createMultiResolutionScreenCapture(frame.getBounds()); + ListresolutionVariants = mrImage.getResolutionVariants(); + * if (resolutionVariants.size() > 1) { + * nativeResImage = resolutionVariants.get(1); + * } else { + * baseResImage = resolutionVariants.get(0); + * }