JRE-681 [windows] direct drawing into frame graphics may have wrong translate

(cherry picked from commit ab6dee4)
diff --git a/src/share/classes/javax/swing/RepaintManager.java b/src/share/classes/javax/swing/RepaintManager.java
index 3f767e9..0b50e77 100644
--- a/src/share/classes/javax/swing/RepaintManager.java
+++ b/src/share/classes/javax/swing/RepaintManager.java
@@ -1579,63 +1579,10 @@
                 Graphics g, int clipX, int clipY,
                 int clipW, int clipH)
         {
-            SunGraphics2D sg = (SunGraphics2D)g.create();
-            try {
-                // [tav] For the scaling graphics we need to compensate the toplevel insets rounding error
-                // to place [0, 0] of the client area in its correct device pixel.
-                if (sg.transformState == SunGraphics2D.TRANSFORM_TRANSLATESCALE) {
-                    Point2D err = getInsetsRoundingError(sg);
-                    double errX = err.getX();
-                    double errY = err.getY();
-                    if (errX != 0 || errY != 0) {
-                        // save the current tx
-                        AffineTransform tx = sg.transform;
-
-                        // translate the constrain
-                        Region constrainClip = sg.constrainClip;
-                        Shape usrClip = sg.usrClip;
-                        if (constrainClip != null) {
-                            // SunGraphics2D.constrain(..) rounds down x/y, so to compensate we need to round up
-                            int _errX = (int)Math.ceil(errX);
-                            int _errY = (int)Math.ceil(errY);
-                            if ((_errX | _errY) != 0) {
-                                // drop everything to default
-                                sg.constrainClip = null;
-                                sg.usrClip = null;
-                                sg.clipState = SunGraphics2D.CLIP_DEVICE;
-                                sg.transform = new AffineTransform();
-                                sg.setDevClip(sg.getSurfaceData().getBounds());
-
-                                Region r = constrainClip.getTranslatedRegion(_errX, _errY);
-                                sg.constrain(r.getLoX(), r.getLoY(), r.getWidth(), r.getHeight());
-                            }
-                        }
-
-                        // translate usrClip
-                        if (usrClip != null) {
-                            if (usrClip instanceof Rectangle2D) {
-                                Rectangle2D u = (Rectangle2D)usrClip;
-                                u.setRect(u.getX() + errX, u.getY() + errY, u.getWidth(), u.getHeight());
-                            } else {
-                                usrClip = AffineTransform.getTranslateInstance(errX, errY).createTransformedShape(usrClip);
-                            }
-                            sg.transform = new AffineTransform();
-                            sg.setClip(usrClip); // constrain clip is already valid
-                        }
-
-                        // finally translate the tx
-                        AffineTransform newTx = AffineTransform.getTranslateInstance(errX - sg.constrainX, errY - sg.constrainY);
-                        newTx.concatenate(tx);
-                        sg.setTransform(newTx);
-                    }
-                }
-                if (image instanceof VolatileImage && isPixelsCopying(c, g)) {
-                    paintDoubleBufferedFPScales(c, image, sg, clipX, clipY, clipW, clipH);
-                } else {
-                    paintDoubleBufferedImpl(c, image, sg, clipX, clipY, clipW, clipH);
-                }
-            } finally {
-                sg.dispose();
+            if (image instanceof VolatileImage && isPixelsCopying(c, g)) {
+                paintDoubleBufferedFPScales(c, image, g, clipX, clipY, clipW, clipH);
+            } else {
+                paintDoubleBufferedImpl(c, image, g, clipX, clipY, clipW, clipH);
             }
         }
 
@@ -1680,35 +1627,6 @@
             }
         }
 
-        /**
-         * For the scaling graphics and a decorated toplevel as the destination,
-         * calculates the rounding error of the toplevel insets.
-         *
-         * @return the left/top insets rounding error, in device space
-         */
-        private static Point2D getInsetsRoundingError(SunGraphics2D g) {
-            Point2D.Double err = new Point2D.Double(0, 0);
-            if (g.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) {
-                Object dst = g.getSurfaceData().getDestination();
-                if (dst instanceof Frame && !((Frame)dst).isUndecorated() ||
-                    dst instanceof Dialog && !((Dialog)dst).isUndecorated())
-                {
-                    Window wnd = (Window)dst;
-                    WindowPeer peer = (WindowPeer)wnd.getPeer();
-                    Insets sysInsets = peer != null ? peer.getSysInsets() : null;
-                    if (sysInsets != null) {
-                        Insets insets = wnd.getInsets();
-                        // insets.left/top is a scaled down rounded value
-                        // insets.left/top * tx.scale is a scaled up value (which contributes to graphics translate)
-                        // sysInsets.left/top is the precise system value
-                        err.x = sysInsets.left - insets.left * g.transform.getScaleX();
-                        err.y = sysInsets.top - insets.top * g.transform.getScaleY();
-                    }
-                }
-            }
-            return err;
-        }
-
         private void paintDoubleBufferedFPScales(JComponent c, Image image,
                                                  Graphics g, int clipX, int clipY,
                                                  int clipW, int clipH) {
diff --git a/src/share/classes/sun/java2d/SunGraphics2D.java b/src/share/classes/sun/java2d/SunGraphics2D.java
index 6c0e4f2..39eeca1 100644
--- a/src/share/classes/sun/java2d/SunGraphics2D.java
+++ b/src/share/classes/sun/java2d/SunGraphics2D.java
@@ -372,7 +372,7 @@
         // changes parameters according to the current scale and translate.
         final double scaleX = transform.getScaleX();
         final double scaleY = transform.getScaleY();
-        // [tav] rounding down affects aligning by insets in RepaintManager.paintDoubleBuffered
+        // [tav] rounding down affects aligning by insets in WWindowPeer.getGraphics
         x = constrainX = (int)Math.floor(transform.getTranslateX());
         y = constrainY = (int)Math.floor(transform.getTranslateY());
         w = Region.dimAdd(x, Region.clipScale(w, scaleX));
diff --git a/src/windows/classes/sun/awt/windows/WWindowPeer.java b/src/windows/classes/sun/awt/windows/WWindowPeer.java
index df23c66..0bc1875 100644
--- a/src/windows/classes/sun/awt/windows/WWindowPeer.java
+++ b/src/windows/classes/sun/awt/windows/WWindowPeer.java
@@ -26,6 +26,9 @@
 
 import java.awt.*;
 import java.awt.event.*;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
 import java.awt.image.*;
 import java.awt.peer.*;
 
@@ -33,6 +36,8 @@
 
 import java.util.*;
 import java.util.List;
+
+import sun.java2d.SunGraphics2D;
 import sun.util.logging.PlatformLogger;
 
 import sun.awt.*;
@@ -862,4 +867,83 @@
             }
         }
     }
+
+    @Override
+    public Graphics getGraphics() {
+        Graphics g = super.getGraphics();
+        if (!(g instanceof SunGraphics2D)) return g;
+        SunGraphics2D sg = (SunGraphics2D)g;
+
+        // [tav] For the scaling graphics we need to compensate the toplevel insets rounding error
+        // to place [0, 0] of the client area in its correct device pixel.
+        if (sg.transformState == SunGraphics2D.TRANSFORM_TRANSLATESCALE) {
+            Point2D err = getInsetsRoundingError(sg);
+            double errX = err.getX();
+            double errY = err.getY();
+            if (errX != 0 || errY != 0) {
+                // save the current tx
+                AffineTransform tx = sg.transform;
+
+                // translate the constrain
+                Region constrainClip = sg.constrainClip;
+                Shape usrClip = sg.usrClip;
+                if (constrainClip != null) {
+                    // SunGraphics2D.constrain(..) rounds down x/y, so to compensate we need to round up
+                    int _errX = (int) Math.ceil(errX);
+                    int _errY = (int) Math.ceil(errY);
+                    if ((_errX | _errY) != 0) {
+                        // drop everything to default
+                        sg.constrainClip = null;
+                        sg.usrClip = null;
+                        sg.clipState = SunGraphics2D.CLIP_DEVICE;
+                        sg.transform = new AffineTransform();
+                        sg.setDevClip(sg.getSurfaceData().getBounds());
+
+                        Region r = constrainClip.getTranslatedRegion(_errX, _errY);
+                        sg.constrain(r.getLoX(), r.getLoY(), r.getWidth(), r.getHeight());
+                    }
+                }
+
+                // translate usrClip
+                if (usrClip != null) {
+                    if (usrClip instanceof Rectangle2D) {
+                        Rectangle2D u = (Rectangle2D) usrClip;
+                        u.setRect(u.getX() + errX, u.getY() + errY, u.getWidth(), u.getHeight());
+                    } else {
+                        usrClip = AffineTransform.getTranslateInstance(errX, errY).createTransformedShape(usrClip);
+                    }
+                    sg.transform = new AffineTransform();
+                    sg.setClip(usrClip); // constrain clip is already valid
+                }
+
+                // finally translate the tx (in the device space, so via concatenate)
+                AffineTransform newTx = AffineTransform.getTranslateInstance(errX - sg.constrainX, errY - sg.constrainY);
+                newTx.concatenate(tx);
+                sg.setTransform(newTx);
+            }
+        }
+        return sg;
+    }
+
+    /**
+     * For the scaling graphics and a decorated toplevel as the destination,
+     * calculates the rounding error of the toplevel insets.
+     *
+     * @return the left/top insets rounding error, in device space
+     */
+    private Point2D getInsetsRoundingError(SunGraphics2D g) {
+        Point2D.Double err = new Point2D.Double(0, 0);
+        if (g.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE && !isTargetUndecorated()) {
+            Insets sysInsets = getSysInsets();
+            if (sysInsets != null) {
+                Insets insets = ((Window)target).getInsets();
+                // insets.left/top is a scaled down rounded value
+                // insets.left/top * tx.scale is a scaled up value (which contributes to graphics translate)
+                // sysInsets.left/top is the precise system value
+                err.x = sysInsets.left - insets.left * g.transform.getScaleX();
+                err.y = sysInsets.top - insets.top * g.transform.getScaleY();
+            }
+        }
+        return err;
+    }
 }
diff --git a/test/java/awt/hidpi/DrawOnFrameGraphicsTest.java b/test/java/awt/hidpi/DrawOnFrameGraphicsTest.java
new file mode 100644
index 0000000..aa71e09
--- /dev/null
+++ b/test/java/awt/hidpi/DrawOnFrameGraphicsTest.java
@@ -0,0 +1,176 @@
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.image.BufferedImage;
+import java.util.concurrent.CountDownLatch;
+
+/* @test
+ * bug JRE-681
+ * @summary Tests that drawing directly into frame's graphics doesn't shift relative to the frame's content.
+ * @author Anton Tarasov
+ * @requires (os.family == "windows")
+ * @run main/othervm -Dsun.java2d.uiScale.enabled=true
+ *                   -Dsun.java2d.uiScale=1.25
+ *                   -Dsun.java2d.d3d=false
+ *                    DrawOnFrameGraphicsTest
+ * @run main/othervm -Dsun.java2d.uiScale.enabled=true
+ *                   -Dsun.java2d.uiScale=1.5
+ *                   -Dsun.java2d.d3d=false
+ *                    DrawOnFrameGraphicsTest
+ * @run main/othervm -Dsun.java2d.uiScale.enabled=true
+ *                   -Dsun.java2d.uiScale=1.75
+ *                   -Dsun.java2d.d3d=false
+ *                    DrawOnFrameGraphicsTest
+ * @run main/othervm -Dsun.java2d.uiScale.enabled=true
+ *                   -Dsun.java2d.uiScale=2.0
+ *                   -Dsun.java2d.d3d=false
+ *                    DrawOnFrameGraphicsTest
+ * @run main/othervm -Dsun.java2d.uiScale.enabled=true
+ *                   -Dsun.java2d.uiScale=2.25
+ *                   -Dsun.java2d.d3d=false
+ *                    DrawOnFrameGraphicsTest
+ * @run main/othervm -Dsun.java2d.uiScale.enabled=true
+ *                   -Dsun.java2d.uiScale=2.5
+ *                   -Dsun.java2d.d3d=false
+ *                    DrawOnFrameGraphicsTest
+ * @run main/othervm -Dsun.java2d.uiScale.enabled=true
+ *                   -Dsun.java2d.uiScale=2.75
+ *                   -Dsun.java2d.d3d=false
+ *                    DrawOnFrameGraphicsTest
+ */
+// Note: -Dsun.java2d.d3d=false is the current IDEA mode.
+public class DrawOnFrameGraphicsTest {
+    static final int F_WIDTH = 300;
+    static final int F_HEIGHT = 200;
+
+    static final Color FRAME_BG = Color.GREEN;
+    static final Color RECT_COLOR_1 = Color.RED;
+    static final Color RECT_COLOR_2 = Color.BLUE;
+    static final int RECT_SIZE = 20;
+
+    static final Rectangle rect = new Rectangle(F_WIDTH/2 - RECT_SIZE/2, F_HEIGHT/2 - RECT_SIZE/2, RECT_SIZE, RECT_SIZE);
+    static JFrame frame;
+    static JComponent comp;
+
+    static volatile CountDownLatch latch = new CountDownLatch(1);
+    static volatile boolean framePainted = false;
+
+    static Robot robot;
+
+    public static void main(String[] args) throws AWTException, InterruptedException {
+        try {
+            robot = new Robot();
+        } catch (AWTException e) {
+            throw new RuntimeException(e);
+        }
+
+        EventQueue.invokeLater(DrawOnFrameGraphicsTest::show);
+
+        Timer timer = new Timer(100, (event) -> {
+            Point loc;
+            try {
+                loc = frame.getContentPane().getLocationOnScreen();
+            } catch (IllegalComponentStateException e) {
+                latch.countDown();
+                return;
+            }
+            BufferedImage capture = robot.createScreenCapture(
+                new Rectangle(loc.x + 50, loc.y + 50, 1, 1));
+            Color pixel = new Color(capture.getRGB(0, 0));
+            framePainted = FRAME_BG.equals(pixel);
+
+            latch.countDown();
+            return;
+        });
+
+        timer.setRepeats(false);
+        latch.await(); // wait for ACTIVATED
+        latch = new CountDownLatch(1);
+
+        //noinspection Duplicates
+        while (!framePainted) {
+            timer.start();
+            latch.await();
+            latch = new CountDownLatch(1);
+        }
+
+        //
+        // Draw on the frame
+        //
+        EventQueue.invokeLater(DrawOnFrameGraphicsTest::draw);
+        latch.await();
+
+        //
+        // Take the capture of the colored rect with some extra space
+        //
+        Point pt = comp.getLocationOnScreen();
+        BufferedImage capture = robot.createScreenCapture(
+            new Rectangle(pt.x + rect.x - 5, pt.y + rect.y - 5,
+                    rect.width + 10, rect.height + 10));
+
+        //
+        // Test RECT_COLOR_1 is fully covered with RECT_COLOR_2
+        //
+        boolean hasRectColor2 = false;
+        for (int x=0; x<capture.getWidth(); x++) {
+            for (int y = 0; y < capture.getHeight(); y++) {
+                Color pix = new Color(capture.getRGB(x, y));
+                if (RECT_COLOR_1.equals(pix)) {
+                    throw new RuntimeException("Test FAILED!");
+                }
+                hasRectColor2 |= RECT_COLOR_2.equals(pix);
+            }
+        }
+        if (!hasRectColor2) {
+            throw new RuntimeException("Test FAILED!");
+        }
+        System.out.println("Test PASSED");
+    }
+
+    static void show() {
+        frame = new JFrame("frame");
+        frame.setLocationRelativeTo(null);
+        frame.setBackground(FRAME_BG);
+        frame.getContentPane().setBackground(FRAME_BG);
+
+        comp = new JPanel() {
+            @Override
+            protected void paintComponent(Graphics g) {
+                Graphics2D g2d = (Graphics2D)g;
+                g2d.setColor(RECT_COLOR_1);
+                g2d.fill(rect);
+            }
+
+            @Override
+            public Dimension getPreferredSize() {
+                return new Dimension(F_WIDTH, F_HEIGHT);
+            }
+        };
+
+        frame.add(comp);
+        frame.pack();
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        frame.setLocationRelativeTo(null);
+        frame.setAlwaysOnTop(true);
+        frame.addWindowListener(new WindowAdapter() {
+            @Override
+            public void windowActivated(WindowEvent e) {
+                latch.countDown();
+            }
+        });
+        frame.setVisible(true);
+    }
+
+    static void draw() {
+        Graphics2D g2d = (Graphics2D)frame.getGraphics();
+        g2d.setColor(RECT_COLOR_2);
+
+        Point fpt = frame.getLocationOnScreen();
+        Point cpt = comp.getLocationOnScreen();
+        cpt.translate(-fpt.x, -fpt.y);
+
+        g2d.fill(new Rectangle(cpt.x + rect.x, cpt.y + rect.y, rect.width, rect.height));
+        latch.countDown();
+    }
+}
\ No newline at end of file