aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Tarasov <anton.tarasov@jetbrains.com>2018-03-07 14:44:29 +0300
committerAnton Tarasov <anton.tarasov@jetbrains.com>2018-03-07 16:51:47 +0300
commitb40ebad65e5204c53a8006f109fbe6dadf9638a1 (patch)
tree51cfbfa32a38839257f703b158193ada3ce79101
parentb578d33aac8f33cc0758b73ceb6484c686996b3a (diff)
downloadjdk8u_jdk-b40ebad65e5204c53a8006f109fbe6dadf9638a1.tar.gz
JRE-681 [windows] direct drawing into frame graphics may have wrong translatejb8u152-b1153.6
(cherry picked from commit ab6dee4)
-rw-r--r--src/share/classes/javax/swing/RepaintManager.java90
-rw-r--r--src/share/classes/sun/java2d/SunGraphics2D.java2
-rw-r--r--src/windows/classes/sun/awt/windows/WWindowPeer.java84
-rw-r--r--test/java/awt/hidpi/DrawOnFrameGraphicsTest.java176
4 files changed, 265 insertions, 87 deletions
diff --git a/src/share/classes/javax/swing/RepaintManager.java b/src/share/classes/javax/swing/RepaintManager.java
index 3f767e996d..0b50e7747d 100644
--- a/src/share/classes/javax/swing/RepaintManager.java
+++ b/src/share/classes/javax/swing/RepaintManager.java
@@ -1579,63 +1579,10 @@ public class RepaintManager
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 @@ public class RepaintManager
}
}
- /**
- * 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 6c0e4f2aae..39eeca1bda 100644
--- a/src/share/classes/sun/java2d/SunGraphics2D.java
+++ b/src/share/classes/sun/java2d/SunGraphics2D.java
@@ -372,7 +372,7 @@ public final class SunGraphics2D
// 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 df23c66035..0bc1875b77 100644
--- a/src/windows/classes/sun/awt/windows/WWindowPeer.java
+++ b/src/windows/classes/sun/awt/windows/WWindowPeer.java
@@ -26,6 +26,9 @@ package sun.awt.windows;
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.beans.*;
import java.util.*;
import java.util.List;
+
+import sun.java2d.SunGraphics2D;
import sun.util.logging.PlatformLogger;
import sun.awt.*;
@@ -862,4 +867,83 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
}
}
}
+
+ @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 0000000000..aa71e09785
--- /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