8069348: SunGraphics2D.copyArea() does not properly work for scaled graphics in D3D
authoralexsch
Fri, 11 Mar 2016 21:57:43 +0400
changeset 36874 09abfda883d2
parent 36873 ed3edebf7d37
child 36875 e49f14b66c89
child 36878 1da705f6ced2
8069348: SunGraphics2D.copyArea() does not properly work for scaled graphics in D3D Reviewed-by: flar, serb
jdk/src/java.desktop/macosx/classes/sun/java2d/OSXOffScreenSurfaceData.java
jdk/src/java.desktop/macosx/classes/sun/java2d/OSXSurfaceData.java
jdk/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java
jdk/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java
jdk/src/java.desktop/share/classes/sun/java2d/SurfaceData.java
jdk/src/java.desktop/share/classes/sun/java2d/opengl/OGLSurfaceData.java
jdk/src/java.desktop/unix/classes/sun/java2d/x11/X11SurfaceData.java
jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceData.java
jdk/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java
jdk/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java
jdk/test/java/awt/Graphics/CopyScaledArea/CopyScaledAreaTest.java
jdk/test/javax/swing/JInternalFrame/8069348/bug8069348.java
--- a/jdk/src/java.desktop/macosx/classes/sun/java2d/OSXOffScreenSurfaceData.java	Fri Mar 11 19:45:03 2016 +0300
+++ b/jdk/src/java.desktop/macosx/classes/sun/java2d/OSXOffScreenSurfaceData.java	Fri Mar 11 21:57:43 2016 +0400
@@ -478,13 +478,9 @@
         // <rdar://problem/4488745> For the Sun2D renderer we should rely on the implementation of the super class.
         // BufImageSurfaceData.java doesn't have an implementation of copyArea() and relies on the super class.
 
-        int offsetX = 0;
-        int offsetY = 0;
-        if (sg2d.transformState == SunGraphics2D.TRANSFORM_ANY_TRANSLATE ||
-                    sg2d.transformState == SunGraphics2D.TRANSFORM_INT_TRANSLATE) {
-            offsetX = (int) sg2d.transform.getTranslateX();
-            offsetY = (int) sg2d.transform.getTranslateY();
-        } else if (sg2d.transformState != SunGraphics2D.TRANSFORM_ISIDENT) { return false; }
+        if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) {
+            return false;
+        }
 
         // reset the clip (this is how it works on windows)
         // we actually can handle a case with any clips but windows ignores the light clip
@@ -498,18 +494,23 @@
             return true;
         }
 
-        // the rectangle returned from clipCopyArea() is in the coordinate space of the surface (image)
-        // we need to substract the offsetX and offsetY to move it to the coordinate space of the graphics2d.
-        // sg2d.drawImage expects the destination rect to be in the coord space of the graphics2d. <rdar://3746194>
-        // (vm)
-        x = clippedCopyAreaRect.x - offsetX;
-        y = clippedCopyAreaRect.y - offsetY;
+        // the rectangle returned from clipCopyArea() is in the coordinate space
+        // of the surface (image)
+        x = clippedCopyAreaRect.x;
+        y = clippedCopyAreaRect.y;
         w = clippedCopyAreaRect.width;
         h = clippedCopyAreaRect.height;
 
-        // copy (dst coordinates are in the coord space of the graphics2d, and src coordinates are
-        // in the coordinate space of the image)
-        sg2d.drawImage(this.bim, x + dx, y + dy, x + dx + w, y + dy + h, x + offsetX, y + offsetY, x + w + offsetX, y + h + offsetY, null);
+        // copy (dst coordinates are in the coord space of the graphics2d, and
+        // src coordinates are in the coordinate space of the image)
+        // sg2d.drawImage expects the destination rect to be in the coord space
+        // of the graphics2d. <rdar://3746194> (vm)
+        // we need to substract the transX and transY to move it
+        // to the coordinate space of the graphics2d.
+        int dstX = x + dx - sg2d.transX;
+        int dstY = y + dy - sg2d.transY;
+        sg2d.drawImage(this.bim, dstX, dstY, dstX + w, dstY + h,
+                       x, y, x + w, y + h, null);
 
         // restore the clip
         sg2d.setClip(clip);
--- a/jdk/src/java.desktop/macosx/classes/sun/java2d/OSXSurfaceData.java	Fri Mar 11 19:45:03 2016 +0300
+++ b/jdk/src/java.desktop/macosx/classes/sun/java2d/OSXSurfaceData.java	Fri Mar 11 21:57:43 2016 +0400
@@ -1094,19 +1094,13 @@
     }
 
     /**
-     * Clips the copy area to the heavywieght bounds and returns the cliped rectangle. The tricky part here is the
-     * passed arguments x, y are in the coordinate space of the sg2d/lightweight comp. In order to do the clipping we
-     * translate them to the coordinate space of the surface, and the returned clipped rectangle is in the coordinate
-     * space of the surface.
+     * Clips the copy area to the heavyweight bounds and returns the clipped rectangle.
+     * The returned clipped rectangle is in the coordinate space of the surface.
      */
     protected Rectangle clipCopyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) {
         // we need to clip against the heavyweight bounds
         copyAreaBounds.setBounds(sg2d.devClip.getLoX(), sg2d.devClip.getLoY(), sg2d.devClip.getWidth(), sg2d.devClip.getHeight());
 
-        // put src rect into surface coordinate space
-        x += sg2d.transX;
-        y += sg2d.transY;
-
         // clip src rect
         srcCopyAreaRect.setBounds(x, y, w, h);
         intersection(srcCopyAreaRect, copyAreaBounds, srcCopyAreaRect);
--- a/jdk/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java	Fri Mar 11 19:45:03 2016 +0300
+++ b/jdk/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLSurfaceData.java	Fri Mar 11 21:57:43 2016 +0400
@@ -175,31 +175,6 @@
         return scale;
     }
 
-    @Override
-    public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w, int h,
-                            int dx, int dy) {
-        final int state = sg2d.transformState;
-        if (state > SunGraphics2D.TRANSFORM_TRANSLATESCALE
-            || sg2d.compositeState >= SunGraphics2D.COMP_XOR) {
-            return false;
-        }
-        if (state <= SunGraphics2D.TRANSFORM_ANY_TRANSLATE) {
-            x += sg2d.transX;
-            y += sg2d.transY;
-        } else if (state == SunGraphics2D.TRANSFORM_TRANSLATESCALE) {
-            final double[] coords = {x, y, x + w, y + h, x + dx, y + dy};
-            sg2d.transform.transform(coords, 0, coords, 0, 3);
-            x = (int) Math.ceil(coords[0] - 0.5);
-            y = (int) Math.ceil(coords[1] - 0.5);
-            w = ((int) Math.ceil(coords[2] - 0.5)) - x;
-            h = ((int) Math.ceil(coords[3] - 0.5)) - y;
-            dx = ((int) Math.ceil(coords[4] - 0.5)) - x;
-            dy = ((int) Math.ceil(coords[5] - 0.5)) - y;
-        }
-        oglRenderPipe.copyArea(sg2d, x, y, w, h, dx, dy);
-        return true;
-    }
-
     protected native void clearWindow();
 
     public static class CGLWindowSurfaceData extends CGLSurfaceData {
--- a/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java	Fri Mar 11 19:45:03 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java	Fri Mar 11 21:57:43 2016 +0400
@@ -2101,13 +2101,39 @@
         if (w <= 0 || h <= 0) {
             return;
         }
+
+        if (transformState == SunGraphics2D.TRANSFORM_ISIDENT) {
+            // do nothing
+        } else if (transformState <= SunGraphics2D.TRANSFORM_ANY_TRANSLATE) {
+            x += transX;
+            y += transY;
+        } else if (transformState == SunGraphics2D.TRANSFORM_TRANSLATESCALE) {
+            final double[] coords = {x, y, x + w, y + h, x + dx, y + dy};
+            transform.transform(coords, 0, coords, 0, 3);
+            x = (int) Math.ceil(coords[0] - 0.5);
+            y = (int) Math.ceil(coords[1] - 0.5);
+            w = ((int) Math.ceil(coords[2] - 0.5)) - x;
+            h = ((int) Math.ceil(coords[3] - 0.5)) - y;
+            dx = ((int) Math.ceil(coords[4] - 0.5)) - x;
+            dy = ((int) Math.ceil(coords[5] - 0.5)) - y;
+            // In case of negative scale transform, reflect the rect coords.
+            if (w < 0) {
+                w = -w;
+                x -= w;
+            }
+            if (h < 0) {
+                h = -h;
+                y -= h;
+            }
+        } else {
+            throw new InternalError("transformed copyArea not implemented yet");
+        }
+
         SurfaceData theData = surfaceData;
         if (theData.copyArea(this, x, y, w, h, dx, dy)) {
             return;
         }
-        if (transformState > TRANSFORM_TRANSLATESCALE) {
-            throw new InternalError("transformed copyArea not implemented yet");
-        }
+
         // REMIND: This method does not deal with missing data from the
         // source object (i.e. it does not send exposure events...)
 
@@ -2126,26 +2152,6 @@
             lastCAcomp = comp;
         }
 
-        double[] coords = {x, y, x + w, y + h, x + dx, y + dy};
-        transform.transform(coords, 0, coords, 0, 3);
-
-        x = (int)Math.ceil(coords[0] - 0.5);
-        y = (int)Math.ceil(coords[1] - 0.5);
-        w = ((int)Math.ceil(coords[2] - 0.5)) - x;
-        h = ((int)Math.ceil(coords[3] - 0.5)) - y;
-        dx = ((int)Math.ceil(coords[4] - 0.5)) - x;
-        dy = ((int)Math.ceil(coords[5] - 0.5)) - y;
-
-        // In case of negative scale transform, reflect the rect coords.
-        if (w < 0) {
-            w *= -1;
-            x -= w;
-        }
-        if (h < 0) {
-            h *= -1;
-            y -= h;
-        }
-
         Blit ob = lastCAblit;
         if (dy == 0 && dx > 0 && dx < w) {
             while (w > 0) {
@@ -2167,7 +2173,7 @@
             }
             return;
         }
-        ob.Blit(theData, theData, comp, clip, x, y, x+dx, y+dy, w, h);
+            ob.Blit(theData, theData, comp, clip, x, y, x+dx, y+dy, w, h);
     }
 
     /*
--- a/jdk/src/java.desktop/share/classes/sun/java2d/SurfaceData.java	Fri Mar 11 19:45:03 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/SurfaceData.java	Fri Mar 11 21:57:43 2016 +0400
@@ -1039,6 +1039,11 @@
      * Performs a copyarea within this surface.  Returns
      * false if there is no algorithm to perform the copyarea
      * given the current settings of the SunGraphics2D.
+     *
+     * @param x the x coordinate of the area in device space
+     * @param y the y coordinate of the area in device space
+     * @param w the width of the area in device space
+     * @param h the height of the area in device space
      */
     public boolean copyArea(SunGraphics2D sg2d,
                             int x, int y, int w, int h, int dx, int dy)
--- a/jdk/src/java.desktop/share/classes/sun/java2d/opengl/OGLSurfaceData.java	Fri Mar 11 19:45:03 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/opengl/OGLSurfaceData.java	Fri Mar 11 21:57:43 2016 +0400
@@ -542,20 +542,14 @@
         return super.getMaskFill(sg2d);
     }
 
-    public boolean copyArea(SunGraphics2D sg2d,
-                            int x, int y, int w, int h, int dx, int dy)
-    {
-        if (sg2d.transformState < SunGraphics2D.TRANSFORM_TRANSLATESCALE &&
-            sg2d.compositeState < SunGraphics2D.COMP_XOR)
-        {
-            x += sg2d.transX;
-            y += sg2d.transY;
-
-            oglRenderPipe.copyArea(sg2d, x, y, w, h, dx, dy);
-
-            return true;
+    @Override
+    public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w, int h,
+                            int dx, int dy) {
+        if (sg2d.compositeState >= SunGraphics2D.COMP_XOR) {
+            return false;
         }
-        return false;
+        oglRenderPipe.copyArea(sg2d, x, y, w, h, dx, dy);
+        return true;
     }
 
     public void flush() {
--- a/jdk/src/java.desktop/unix/classes/sun/java2d/x11/X11SurfaceData.java	Fri Mar 11 19:45:03 2016 +0300
+++ b/jdk/src/java.desktop/unix/classes/sun/java2d/x11/X11SurfaceData.java	Fri Mar 11 21:57:43 2016 +0400
@@ -487,12 +487,9 @@
             makePipes();
         }
         CompositeType comptype = sg2d.imageComp;
-        if (sg2d.transformState < SunGraphics2D.TRANSFORM_TRANSLATESCALE &&
-            (CompositeType.SrcOverNoEa.equals(comptype) ||
+        if ((CompositeType.SrcOverNoEa.equals(comptype) ||
              CompositeType.SrcNoEa.equals(comptype)))
         {
-            x += sg2d.transX;
-            y += sg2d.transY;
             SunToolkit.awtLock();
             try {
                 boolean needExposures = canSourceSendExposures(x, y, w, h);
--- a/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceData.java	Fri Mar 11 19:45:03 2016 +0300
+++ b/jdk/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceData.java	Fri Mar 11 21:57:43 2016 +0400
@@ -365,12 +365,9 @@
             makePipes();
         }
         CompositeType comptype = sg2d.imageComp;
-        if (sg2d.transformState < SunGraphics2D.TRANSFORM_TRANSLATESCALE &&
-            (CompositeType.SrcOverNoEa.equals(comptype) ||
-             CompositeType.SrcNoEa.equals(comptype)))
+        if (CompositeType.SrcOverNoEa.equals(comptype) ||
+             CompositeType.SrcNoEa.equals(comptype))
         {
-            x += sg2d.transX;
-            y += sg2d.transY;
             try {
                 SunToolkit.awtLock();
                 boolean needExposures = canSourceSendExposures(x, y, w, h);
--- a/jdk/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java	Fri Mar 11 19:45:03 2016 +0300
+++ b/jdk/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java	Fri Mar 11 21:57:43 2016 +0400
@@ -703,20 +703,13 @@
     }
 
     @Override
-    public boolean copyArea(SunGraphics2D sg2d,
-                            int x, int y, int w, int h, int dx, int dy)
-    {
-        if (sg2d.transformState < SunGraphics2D.TRANSFORM_TRANSLATESCALE &&
-            sg2d.compositeState < SunGraphics2D.COMP_XOR)
-        {
-            x += sg2d.transX;
-            y += sg2d.transY;
-
-            d3dRenderPipe.copyArea(sg2d, x, y, w, h, dx, dy);
-
-            return true;
+    public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w, int h,
+                            int dx, int dy) {
+        if (sg2d.compositeState >= SunGraphics2D.COMP_XOR) {
+            return false;
         }
-        return false;
+        d3dRenderPipe.copyArea(sg2d, x, y, w, h, dx, dy);
+        return true;
     }
 
     @Override
--- a/jdk/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java	Fri Mar 11 19:45:03 2016 +0300
+++ b/jdk/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java	Fri Mar 11 21:57:43 2016 +0400
@@ -311,13 +311,10 @@
                             int x, int y, int w, int h, int dx, int dy)
     {
         CompositeType comptype = sg2d.imageComp;
-        if (sg2d.transformState < SunGraphics2D.TRANSFORM_TRANSLATESCALE &&
-            sg2d.clipState != SunGraphics2D.CLIP_SHAPE &&
+        if (sg2d.clipState != SunGraphics2D.CLIP_SHAPE &&
             (CompositeType.SrcOverNoEa.equals(comptype) ||
              CompositeType.SrcNoEa.equals(comptype)))
         {
-            x += sg2d.transX;
-            y += sg2d.transY;
             int dstx1 = x + dx;
             int dsty1 = y + dy;
             int dstx2 = dstx1 + w;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Graphics/CopyScaledArea/CopyScaledAreaTest.java	Fri Mar 11 21:57:43 2016 +0400
@@ -0,0 +1,164 @@
+/*
+ * 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.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.VolatileImage;
+import static sun.awt.OSInfo.*;
+
+/**
+ * @test
+ * @bug 8069348
+ * @summary SunGraphics2D.copyArea() does not properly work for scaled graphics
+ * @modules java.desktop/sun.awt
+ * @run main/othervm -Dsun.java2d.uiScale=2 CopyScaledAreaTest
+ * @run main/othervm -Dsun.java2d.opengl=true -Dsun.java2d.uiScale=2 CopyScaledAreaTest
+ * @run main/othervm -Dsun.java2d.d3d=true    -Dsun.java2d.uiScale=2 CopyScaledAreaTest
+ * @run main/othervm -Dsun.java2d.d3d=false   -Dsun.java2d.opengl=false
+ *                   -Dsun.java2d.uiScale=2 CopyScaledAreaTest
+ */
+public class CopyScaledAreaTest {
+
+    private static final int IMAGE_WIDTH = 800;
+    private static final int IMAGE_HEIGHT = 800;
+    private static final int X = 50;
+    private static final int Y = 50;
+    private static final int W = 100;
+    private static final int H = 75;
+    private static final int DX = 15;
+    private static final int DY = 10;
+    private static final int N = 3;
+    private static final Color BACKGROUND_COLOR = Color.YELLOW;
+    private static final Color FILL_COLOR = Color.ORANGE;
+    private static final double[][] SCALES = {{1.3, 1.4}, {0.3, 2.3}, {2.7, 0.1}};
+
+    private static boolean isSupported() {
+        String d3d = System.getProperty("sun.java2d.d3d");
+        return !Boolean.getBoolean(d3d) || getOSType() == OSType.WINDOWS;
+    }
+
+    private static int scale(int x, double scale) {
+        return (int) Math.floor(x * scale);
+    }
+
+    private static VolatileImage createVolatileImage(GraphicsConfiguration conf) {
+        return conf.createCompatibleVolatileImage(IMAGE_WIDTH, IMAGE_HEIGHT);
+    }
+
+    // rendering to the image
+    private static void renderOffscreen(VolatileImage vImg,
+                                        GraphicsConfiguration conf,
+                                        double scaleX,
+                                        double scaleY)
+    {
+        int attempts = 0;
+        do {
+
+            if (attempts > 10) {
+                throw new RuntimeException("Too many attempts!");
+            }
+
+            if (vImg.validate(conf) == VolatileImage.IMAGE_INCOMPATIBLE) {
+                // old vImg doesn't work with new GraphicsConfig; re-create it
+                vImg = createVolatileImage(conf);
+            }
+            Graphics2D g = vImg.createGraphics();
+            //
+            // miscellaneous rendering commands...
+            //
+            g.setColor(BACKGROUND_COLOR);
+            g.fillRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
+            g.scale(scaleX, scaleY);
+
+            g.setColor(FILL_COLOR);
+            g.fillRect(X, Y, W, H);
+
+            for (int i = 0; i < N; i++) {
+                g.copyArea(X + i * DX, Y + i * DY, W, H, DX, DY);
+            }
+            g.dispose();
+            attempts++;
+        } while (vImg.contentsLost());
+    }
+
+    public static void main(String[] args) throws Exception {
+
+        if (!isSupported()) {
+            return;
+        }
+
+        GraphicsConfiguration graphicsConfiguration =
+                GraphicsEnvironment.getLocalGraphicsEnvironment()
+                .getDefaultScreenDevice().getDefaultConfiguration();
+
+        for(double[] scales: SCALES){
+            testScale(scales[0], scales[1], graphicsConfiguration);
+        }
+    }
+
+    private static void testScale(double scaleX, double scaleY,
+                                  GraphicsConfiguration gc) throws Exception
+    {
+
+        BufferedImage buffImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT,
+                                                    BufferedImage.TYPE_INT_RGB);
+        Graphics g = buffImage.createGraphics();
+
+        VolatileImage vImg = createVolatileImage(gc);
+
+        int attempts = 0;
+        do {
+
+            if (attempts > 10) {
+                throw new RuntimeException("Too many attempts!");
+            }
+
+            int returnCode = vImg.validate(gc);
+            if (returnCode == VolatileImage.IMAGE_RESTORED) {
+                // Contents need to be restored
+                renderOffscreen(vImg, gc, scaleX, scaleY); // restore contents
+            } else if (returnCode == VolatileImage.IMAGE_INCOMPATIBLE) {
+                // old vImg doesn't work with new GraphicsConfig; re-create it
+                vImg = createVolatileImage(gc);
+                renderOffscreen(vImg, gc, scaleX, scaleY);
+            }
+            g.drawImage(vImg, 0, 0, null);
+            attempts++;
+        } while (vImg.contentsLost());
+
+        g.dispose();
+
+        int x = scale(X + N * DX, scaleX) + 1;
+        int y = scale(Y + N * DY, scaleY) + 1;
+        int w = scale(W, scaleX) - 2;
+        int h = scale(H, scaleY) - 2;
+
+        for (int i = x; i < x + w; i++) {
+            for (int j = y; j < y + h; j++) {
+                if (buffImage.getRGB(i, j) != FILL_COLOR.getRGB()) {
+                    throw new RuntimeException("Wrong rectangle color!");
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JInternalFrame/8069348/bug8069348.java	Fri Mar 11 21:57:43 2016 +0400
@@ -0,0 +1,144 @@
+/*
+ * 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.Color;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.event.InputEvent;
+import javax.swing.JDesktopPane;
+import javax.swing.JFrame;
+import javax.swing.JInternalFrame;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import static sun.awt.OSInfo.*;
+
+/**
+ * @test
+ * @bug 8069348
+ * @summary SunGraphics2D.copyArea() does not properly work for scaled graphics
+ * @author Alexandr Scherbatiy
+ * @modules java.desktop/sun.awt
+ * @run main/othervm -Dsun.java2d.uiScale=2 bug8069348
+ * @run main/othervm -Dsun.java2d.opengl=true -Dsun.java2d.uiScale=2 bug8069348
+ * @run main/othervm -Dsun.java2d.d3d=true -Dsun.java2d.uiScale=2 bug8069348
+ */
+public class bug8069348 {
+
+    private static final int WIN_WIDTH = 500;
+    private static final int WIN_HEIGHT = 500;
+
+    private static final Color DESKTOPPANE_COLOR = Color.YELLOW;
+    private static final Color FRAME_COLOR = Color.ORANGE;
+
+    private static JFrame frame;
+    private static JInternalFrame internalFrame;
+
+    public static void main(String[] args) throws Exception {
+
+        if (!isSupported()) {
+            return;
+        }
+
+        try {
+
+            SwingUtilities.invokeAndWait(bug8069348::createAndShowGUI);
+
+            Robot robot = new Robot();
+            robot.setAutoDelay(50);
+            robot.waitForIdle();
+
+            Rectangle screenBounds = getInternalFrameScreenBounds();
+
+            int x = screenBounds.x + screenBounds.width / 2;
+            int y = screenBounds.y + 10;
+            int dx = screenBounds.width / 2;
+            int dy = screenBounds.height / 2;
+
+            robot.mouseMove(x, y);
+            robot.waitForIdle();
+
+            robot.mousePress(InputEvent.BUTTON1_MASK);
+            robot.mouseMove(x + dx, y + dy);
+            robot.mouseRelease(InputEvent.BUTTON1_MASK);
+            robot.waitForIdle();
+
+            int cx = screenBounds.x + screenBounds.width + dx / 2;
+            int cy = screenBounds.y + screenBounds.height + dy / 2;
+
+            robot.mouseMove(cx, cy);
+            if (!FRAME_COLOR.equals(robot.getPixelColor(cx, cy))) {
+                throw new RuntimeException("Internal frame is not correctly dragged!");
+            }
+        } finally {
+            if (frame != null) {
+                frame.dispose();
+            }
+        }
+    }
+
+    private static boolean isSupported() {
+        String d3d = System.getProperty("sun.java2d.d3d");
+        return !Boolean.getBoolean(d3d) || getOSType() == OSType.WINDOWS;
+    }
+
+    private static Rectangle getInternalFrameScreenBounds() throws Exception {
+        Rectangle[] points = new Rectangle[1];
+        SwingUtilities.invokeAndWait(() -> {
+            points[0] = new Rectangle(internalFrame.getLocationOnScreen(),
+                    internalFrame.getSize());
+        });
+        return points[0];
+    }
+
+    private static void createAndShowGUI() {
+
+        frame = new JFrame();
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+        JDesktopPane desktopPane = new JDesktopPane();
+        desktopPane.setBackground(DESKTOPPANE_COLOR);
+
+        internalFrame = new JInternalFrame("Test") {
+
+            @Override
+            public void paint(Graphics g) {
+                super.paint(g);
+                g.setColor(FRAME_COLOR);
+                g.fillRect(0, 0, getWidth(), getHeight());
+            }
+        };
+        internalFrame.setSize(WIN_WIDTH / 3, WIN_HEIGHT / 3);
+        internalFrame.setVisible(true);
+        desktopPane.add(internalFrame);
+
+        JPanel panel = new JPanel();
+        panel.setLayout(new BorderLayout());
+        panel.add(desktopPane, BorderLayout.CENTER);
+        frame.add(panel);
+        frame.setSize(WIN_WIDTH, WIN_HEIGHT);
+        frame.setVisible(true);
+        frame.requestFocus();
+    }
+}