diff options
author | Anton Tarasov <anton.tarasov@jetbrains.com> | 2018-03-07 14:44:29 +0300 |
---|---|---|
committer | Anton Tarasov <anton.tarasov@jetbrains.com> | 2018-03-07 16:51:47 +0300 |
commit | b40ebad65e5204c53a8006f109fbe6dadf9638a1 (patch) | |
tree | 51cfbfa32a38839257f703b158193ada3ce79101 | |
parent | b578d33aac8f33cc0758b73ceb6484c686996b3a (diff) | |
download | jdk8u_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.java | 90 | ||||
-rw-r--r-- | src/share/classes/sun/java2d/SunGraphics2D.java | 2 | ||||
-rw-r--r-- | src/windows/classes/sun/awt/windows/WWindowPeer.java | 84 | ||||
-rw-r--r-- | test/java/awt/hidpi/DrawOnFrameGraphicsTest.java | 176 |
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 |