changeset 16717:c0cfc1420a14

8162959: [HiDPI] screenshot artifacts using AWT Robot Reviewed-by: alexsch, prr
author pkbalakr
date Thu, 16 Feb 2017 12:05:40 +0530
parents 656ccef366ac
children 460f7166ddc9
files src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m src/java.desktop/share/classes/java/awt/Robot.java src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp test/java/awt/Robot/HiDPIScreenCapture/ScreenCaptureResolutionTest.java test/java/awt/Robot/HiDPIScreenCapture/ScreenCaptureTest.java
diffstat 8 files changed, 613 insertions(+), 72 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java	Wed Feb 15 18:22:39 2017 -0800
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java	Thu Feb 16 12:05:40 2017 +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];
     }
 
@@ -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);
 }
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m	Wed Feb 15 18:22:39 2017 -0800
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m	Thu Feb 16 12:05:40 2017 +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;
--- a/src/java.desktop/share/classes/java/awt/Robot.java	Wed Feb 15 18:22:39 2017 -0800
+++ b/src/java.desktop/share/classes/java/awt/Robot.java	Thu Feb 16 12:05:40 2017 +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;
@@ -391,6 +394,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;
     }
@@ -406,13 +414,69 @@
      * @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}.
+     * <p>
+     * For a non-scaled display, the {@code MultiResolutionImage}
+     * will have one image variant:
+     * <ul>
+     * <li> Base Image with user specified size.
+     * </ul>
+     * <p>
+     * For a high resolution display where there is a scaling transform,
+     * the {@code MultiResolutionImage} will have two image variants:
+     * <ul>
+     * <li> Base Image with user specified size. This is scaled from the screen.
+     * <li> Native device resolution image with device size pixels.
+     * </ul>
+     * <p>
+     * Example:
+     * <pre>
+     *      Image nativeResImage;
+     *      MultiResolutionImage mrImage = robot.createMultiResolutionScreenCapture(frame.getBounds());
+     *      List<Image> resolutionVariants = mrImage.getResolutionVariants();
+     *      if (resolutionVariants.size() > 1) {
+     *          nativeResImage = resolutionVariants.get(1);
+     *      } else {
+     *          nativeResImage = resolutionVariants.get(0);
+     *      } </pre>
+     * @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
+     *
+     * @since 9
+     */
+    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) {
             /*
@@ -422,31 +486,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 pixels[];
-        int[] bandmasks = new int[3];
+        if (uiScaleX == 1 && uiScaleY == 1) {
 
-        pixels = peer.getRGBPixels(screenRect);
-        buffer = new DataBufferInt(pixels, pixels.length);
+            pixels = peer.getRGBPixels(screenRect);
+            buffer = new DataBufferInt(pixels, pixels.length);
 
-        bandmasks[0] = screenCapCM.getRedMask();
-        bandmasks[1] = screenCapCM.getGreenMask();
-        bandmasks[2] = screenCapCM.getBlueMask();
+            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);
+            raster = Raster.createPackedRaster(buffer, screenRect.width,
+                    screenRect.height, screenRect.width, bandmasks, null);
+            SunWritableRaster.makeTrackable(buffer);
 
-        image = new BufferedImage(screenCapCM, raster, false, null);
+            highResolutionImage = new BufferedImage(screenCapCM, raster,
+                    false, null);
+            imageArray = new BufferedImage[1];
+            imageArray[0] = highResolutionImage;
 
-        return image;
+        } 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;
+            }
+
+        }
+
+        return imageArray;
     }
 
     private static void checkValidRect(Rectangle rect) {
--- a/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java	Wed Feb 15 18:22:39 2017 -0800
+++ b/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java	Thu Feb 16 12:05:40 2017 +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);
 }
--- a/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c	Wed Feb 15 18:22:39 2017 -0800
+++ b/src/java.desktop/unix/native/libawt_xawt/awt/awt_Robot.c	Thu Feb 16 12:05:40 2017 +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.
--- a/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp	Wed Feb 15 18:22:39 2017 -0800
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Robot.cpp	Thu Feb 16 12:05:40 2017 +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;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/Robot/HiDPIScreenCapture/ScreenCaptureResolutionTest.java	Thu Feb 16 12:05:40 2017 +0530
@@ -0,0 +1,258 @@
+/*
+ * 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 Visually validate multiresolution screencapture.
+ * @run main/othervm/manual -Dsun.java2d.uiScale=2 ScreenCaptureResolutionTest
+ */
+import java.awt.AWTException;
+import java.awt.Color;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.util.concurrent.CountDownLatch;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.SwingUtilities;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.concurrent.TimeUnit;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import java.awt.Robot;
+import java.awt.image.BufferedImage;
+import java.util.List;
+import java.awt.Image;
+import java.awt.image.MultiResolutionImage;
+
+public class ScreenCaptureResolutionTest {
+
+    public static void main(String args[]) throws Exception {
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        TestUI test = new TestUI(latch);
+
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    test.createUI();
+                } catch (Exception ex) {
+                    throw new RuntimeException("Exception while creating UI");
+                }
+            }
+        });
+        latch.await(3, TimeUnit.SECONDS);
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    test.validateScreenCapture();
+                } catch (Exception ex) {
+                    throw new RuntimeException("Exception while"
+                        + " validating ScreenCapture");
+                }
+            }
+        });
+        boolean status = latch.await(5, TimeUnit.MINUTES);
+        if (!status) {
+            System.out.println("Test timed out.");
+        }
+
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    test.disposeUI();
+                } catch (Exception ex) {
+                    throw new RuntimeException("Exception while disposing UI");
+                }
+            }
+        });
+
+        if (test.testResult == false) {
+            throw new RuntimeException("Test Failed.");
+        }
+    }
+}
+
+class TestUI {
+
+    private static JFrame mainFrame;
+    private static JFrame outputImageFrame;
+    private static JFrame inputImageFrame;
+    private static JPanel outputControlPanel;
+    private static JPanel mainControlPanel;
+
+    private static JTextArea instructionTextArea;
+
+    private static JPanel inputImagePanel;
+    private static JPanel resultButtonPanel;
+    private static JButton passButton;
+    private static JButton failButton;
+
+    private static GridBagLayout layout;
+    private final CountDownLatch latch;
+    public boolean testResult = false;
+
+    public TestUI(CountDownLatch latch) throws Exception {
+        this.latch = latch;
+    }
+
+    public void validateScreenCapture() throws AWTException {
+        Robot robot = new Robot();
+        outputControlPanel = new JPanel(layout);
+        GridBagConstraints ogbc = new GridBagConstraints();
+
+        MultiResolutionImage image
+                = robot.createMultiResolutionScreenCapture(inputImageFrame.getBounds());
+        List<Image> imageList = image.getResolutionVariants();
+        int size = imageList.size();
+        BufferedImage lowResImage = (BufferedImage) imageList.get(0);
+        BufferedImage highResImage = (BufferedImage) imageList.get(1);
+
+        outputImageFrame = new JFrame("Output");
+        outputImageFrame.getContentPane().setLayout(new GridBagLayout());
+        ogbc.gridx = 0;
+        ogbc.gridy = 0;
+        ogbc.fill = GridBagConstraints.HORIZONTAL;
+        outputControlPanel.add(new JLabel(new ImageIcon(lowResImage)), ogbc);
+        int width = lowResImage.getWidth();
+        int height = lowResImage.getHeight();
+        JLabel labelImg1 = new JLabel("LEFT:Width: " + width
+                                      + " Height: " + height);
+        ogbc.gridx = 0;
+        ogbc.gridy = 1;
+        outputControlPanel.add(labelImg1, ogbc);
+        ogbc.gridx = 1;
+        ogbc.gridy = 0;
+        outputControlPanel.add(new JLabel(new ImageIcon(highResImage)), ogbc);
+        width = highResImage.getWidth();
+        height = highResImage.getHeight();
+        JLabel labelImg2 = new JLabel("RIGHT:Width: " + width
+                                      + " Height: " + height);
+        ogbc.gridx = 1;
+        ogbc.gridy = 1;
+        outputControlPanel.add(labelImg2, ogbc);
+        outputControlPanel.setBackground(Color.GRAY);
+        outputImageFrame.add(outputControlPanel);
+
+        outputImageFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        outputImageFrame.setBounds(600, 0, 400, 300);
+        outputImageFrame.setLocationRelativeTo(null);
+        outputImageFrame.setVisible(true);
+    }
+
+    public final void createUI() throws Exception {
+
+        mainFrame = new JFrame("ScreenCaptureResolutionTest");
+
+        layout = new GridBagLayout();
+        mainControlPanel = new JPanel(layout);
+        resultButtonPanel = new JPanel(layout);
+
+        GridBagConstraints gbc = new GridBagConstraints();
+
+        // Create Test instructions
+        String instructions
+                = "INSTRUCTIONS:"
+                + "\n Test to Visually validate MultiResolutionn Image. "
+                + "\n 1. Check if output window contains two screenshot "
+                + "\n    of input window. "
+                + "\n 2. Right image should be twice the size of Left. "
+                + "\n 3. Quality of Right image should be better than Left. "
+                + "\n If all the three conditons are satisfied, then "
+                + "\n click pass else fail.";
+        instructionTextArea = new JTextArea();
+        instructionTextArea.setText(instructions);
+        instructionTextArea.setEnabled(false);
+        instructionTextArea.setDisabledTextColor(Color.black);
+        instructionTextArea.setBackground(Color.white);
+
+        gbc.gridx = 0;
+        gbc.gridy = 0;
+        gbc.fill = GridBagConstraints.HORIZONTAL;
+        mainControlPanel.add(instructionTextArea, gbc);
+
+        gbc.gridx = 0;
+        gbc.gridy = 1;
+
+        inputImagePanel = new JPanel(layout);
+        JLabel label = new JLabel("Resolution");
+        inputImagePanel.add(label);
+        inputImageFrame = new JFrame("Input");
+        inputImageFrame.add(inputImagePanel);
+
+        inputImageFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        inputImageFrame.setBounds(100, 200, 100, 100);
+        inputImageFrame.setVisible(true);
+
+        passButton = new JButton("Pass");
+        passButton.setActionCommand("Pass");
+        passButton.addActionListener((ActionEvent e) -> {
+            outputImageFrame.dispose();
+            inputImageFrame.dispose();
+            testResult = true;
+            mainFrame.dispose();
+            latch.countDown();
+
+        });
+        failButton = new JButton("Fail");
+        failButton.setActionCommand("Fail");
+        failButton.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                outputImageFrame.dispose();
+                inputImageFrame.dispose();
+                testResult = false;
+                mainFrame.dispose();
+                latch.countDown();
+            }
+        });
+        gbc.gridx = 0;
+        gbc.gridy = 0;
+        resultButtonPanel.add(passButton, gbc);
+        gbc.gridx = 1;
+        gbc.gridy = 0;
+        resultButtonPanel.add(failButton, gbc);
+
+        gbc.gridx = 0;
+        gbc.gridy = 2;
+        mainControlPanel.add(resultButtonPanel, gbc);
+
+        mainFrame.add(mainControlPanel);
+        mainFrame.pack();
+        mainFrame.setVisible(true);
+    }
+
+    public void disposeUI() {
+        outputImageFrame.dispose();
+        inputImageFrame.dispose();
+        mainFrame.setVisible(false);
+        mainFrame.dispose();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/Robot/HiDPIScreenCapture/ScreenCaptureTest.java	Thu Feb 16 12:05:40 2017 +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());
+        List<Image> imageList = image.getResolutionVariants();
+        int size = imageList.size();
+        BufferedImage lowResImage;
+        BufferedImage highResImage;
+
+        if (!isHiDPI) {
+            // Check if output is MultiResolutionImage with one variant
+            if (size != 1) {
+                throw new RuntimeException(" Invalid variant size");
+            }
+
+            lowResImage = (BufferedImage) imageList.get(0);
+            System.out.println(frame.getBounds());
+            System.out.println(lowResImage.getWidth()+" "+lowResImage.getHeight());
+            if (frame.getWidth() != lowResImage.getWidth()
+                        || frame.getHeight() != lowResImage.getHeight()) {
+                throw new RuntimeException(" Invalid Image size");
+            }
+
+        } else {
+            // Check if output contains two variants.
+            if (size != 2) {
+                throw new RuntimeException(" Invalid variant size");
+            }
+
+            // Check if hight resolution image size is scale times low resolution image.
+            lowResImage = (BufferedImage) imageList.get(0);
+            highResImage = (BufferedImage) imageList.get(1);
+
+            int lW = (int) lowResImage.getWidth();
+            int lH = (int) lowResImage.getHeight();
+            int hW = (int) highResImage.getWidth();
+            int hH = (int) highResImage.getHeight();
+
+            if ( hW != (tx.getScaleX() * lW) || hH != (tx.getScaleY() * lH)) {
+                throw new RuntimeException(" Invalid Resolution Variants");
+            }
+
+            // Check if both image colors are same at some location.
+            if (lowResImage.getRGB(lW / 4, lH / 4)
+                    != highResImage.getRGB(hW / 4, hH / 4)) {
+                throw new RuntimeException("Wrong image color!");
+            }
+
+            if (lowResImage.getRGB(3 * lW / 4, lH / 4)
+                    != highResImage.getRGB(3 * hW / 4, hH / 4)) {
+                throw new RuntimeException("Wrong image color!");
+            }
+
+            if (lowResImage.getRGB(lW / 4, 3 * lH / 4)
+                    != highResImage.getRGB(hW / 4, 3 * hH / 4)) {
+                throw new RuntimeException("Wrong image color!");
+            }
+
+            if (lowResImage.getRGB(3 * lW / 4, 3 * lH / 4)
+                    != highResImage.getRGB(3 * hW / 4, 3 * hH / 4)) {
+                throw new RuntimeException("Wrong image color!");
+            }
+
+        }
+
+        frame.dispose();
+    }
+
+}