Skip to content

Commit be63525

Browse files
committed
8211999: Window positioning bugs due to overlapping GraphicsDevice bounds (Windows/HiDPI)
Reviewed-by: kizune, aivanov
1 parent 0a41ca6 commit be63525

File tree

35 files changed

+1117
-431
lines changed

35 files changed

+1117
-431
lines changed

src/java.desktop/share/classes/java/awt/Robot.java

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343
import sun.awt.image.SunWritableRaster;
4444
import sun.java2d.SunGraphicsEnvironment;
4545

46+
import static sun.java2d.SunGraphicsEnvironment.toDeviceSpace;
47+
import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs;
48+
4649
/**
4750
* This class is used to generate native system input events
4851
* for the purposes of test automation, self-running demos, and
@@ -385,13 +388,9 @@ private static void checkKeycodeArgument(int keycode) {
385388
*/
386389
public synchronized Color getPixelColor(int x, int y) {
387390
checkScreenCaptureAllowed();
388-
AffineTransform tx = GraphicsEnvironment.
389-
getLocalGraphicsEnvironment().getDefaultScreenDevice().
390-
getDefaultConfiguration().getDefaultTransform();
391-
x = (int) (x * tx.getScaleX());
392-
y = (int) (y * tx.getScaleY());
393-
Color color = new Color(peer.getRGBPixel(x, y));
394-
return color;
391+
Point point = peer.useAbsoluteCoordinates() ? toDeviceSpaceAbs(x, y)
392+
: toDeviceSpace(x, y);
393+
return new Color(peer.getRGBPixel(point.x, point.y));
395394
}
396395

397396
/**
@@ -523,17 +522,16 @@ public synchronized BufferedImage createScreenCapture(Rectangle screenRect) {
523522
imageArray[0] = highResolutionImage;
524523

525524
} else {
526-
527-
int sX = (int) Math.floor(screenRect.x * uiScaleX);
528-
int sY = (int) Math.floor(screenRect.y * uiScaleY);
529-
int sWidth = (int) Math.ceil(screenRect.width * uiScaleX);
530-
int sHeight = (int) Math.ceil(screenRect.height * uiScaleY);
531-
int[] temppixels;
532-
Rectangle scaledRect = new Rectangle(sX, sY, sWidth, sHeight);
533-
temppixels = peer.getRGBPixels(scaledRect);
534-
525+
Rectangle scaledRect;
526+
if (peer.useAbsoluteCoordinates()) {
527+
scaledRect = toDeviceSpaceAbs(gc, screenRect.x,
528+
screenRect.y, screenRect.width, screenRect.height);
529+
} else {
530+
scaledRect = toDeviceSpace(gc, screenRect.x,
531+
screenRect.y, screenRect.width, screenRect.height);
532+
}
535533
// HighResolutionImage
536-
pixels = temppixels;
534+
pixels = peer.getRGBPixels(scaledRect);
537535
buffer = new DataBufferInt(pixels, pixels.length);
538536
raster = Raster.createPackedRaster(buffer, scaledRect.width,
539537
scaledRect.height, scaledRect.width, bandmasks, null);

src/java.desktop/share/classes/java/awt/peer/RobotPeer.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -117,4 +117,14 @@ public interface RobotPeer
117117
* @see Robot#createScreenCapture(Rectangle)
118118
*/
119119
int[] getRGBPixels(Rectangle bounds);
120+
121+
/**
122+
* Determines if absolute coordinates should be used by this peer.
123+
*
124+
* @return {@code true} if absolute coordinates should be used,
125+
* {@code false} otherwise
126+
*/
127+
default boolean useAbsoluteCoordinates() {
128+
return false;
129+
}
120130
}

src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java

Lines changed: 98 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import java.awt.AWTError;
2929
import java.awt.Color;
30+
import java.awt.Dimension;
3031
import java.awt.Font;
3132
import java.awt.Graphics2D;
3233
import java.awt.GraphicsConfiguration;
@@ -349,53 +350,124 @@ public static GraphicsConfiguration getGraphicsConfigurationAtPoint(
349350
}
350351

351352
/**
352-
* Converts coordinates from the user's space to the device space using
353-
* appropriate device transformation.
353+
* Returns the bounds of the graphics configuration in device space.
354+
*
355+
* @param config the graphics configuration which bounds are requested
356+
* @return the bounds of the area covered by this
357+
* {@code GraphicsConfiguration} in device space (pixels)
358+
*/
359+
public static Rectangle getGCDeviceBounds(GraphicsConfiguration config) {
360+
AffineTransform tx = config.getDefaultTransform();
361+
Rectangle bounds = config.getBounds();
362+
bounds.width *= tx.getScaleX();
363+
bounds.height *= tx.getScaleY();
364+
return bounds;
365+
}
366+
367+
/**
368+
* Converts the size (w, h) from the device space to the user's space using
369+
* passed graphics configuration.
370+
*
371+
* @param gc the graphics configuration to be used for transformation
372+
* @param w the width in the device space
373+
* @param h the height in the device space
374+
* @return the size in the user's space
375+
*/
376+
public static Dimension toUserSpace(GraphicsConfiguration gc,
377+
int w, int h) {
378+
AffineTransform tx = gc.getDefaultTransform();
379+
return new Dimension(
380+
Region.clipRound(w / tx.getScaleX()),
381+
Region.clipRound(h / tx.getScaleY())
382+
);
383+
}
384+
385+
/**
386+
* Converts absolute coordinates from the user's space to the device space
387+
* using appropriate device transformation.
354388
*
355-
* @param x coordinate in the user space
356-
* @param y coordinate in the user space
357-
* @return the point which uses device space(pixels)
389+
* @param x absolute coordinate in the user's space
390+
* @param y absolute coordinate in the user's space
391+
* @return the point which uses device space (pixels)
358392
*/
359-
public static Point convertToDeviceSpace(double x, double y) {
393+
public static Point toDeviceSpaceAbs(int x, int y) {
360394
GraphicsConfiguration gc = getLocalGraphicsEnvironment()
361-
.getDefaultScreenDevice().getDefaultConfiguration();
395+
.getDefaultScreenDevice().getDefaultConfiguration();
362396
gc = getGraphicsConfigurationAtPoint(gc, x, y);
397+
return toDeviceSpaceAbs(gc, x, y, 0, 0).getLocation();
398+
}
363399

400+
/**
401+
* Converts the rectangle from the user's space to the device space using
402+
* appropriate device transformation.
403+
*
404+
* @param rect the rectangle in the user's space
405+
* @return the rectangle which uses device space (pixels)
406+
*/
407+
public static Rectangle toDeviceSpaceAbs(Rectangle rect) {
408+
GraphicsConfiguration gc = getLocalGraphicsEnvironment()
409+
.getDefaultScreenDevice().getDefaultConfiguration();
410+
gc = getGraphicsConfigurationAtPoint(gc, rect.x, rect.y);
411+
return toDeviceSpaceAbs(gc, rect.x, rect.y, rect.width, rect.height);
412+
}
413+
414+
/**
415+
* Converts absolute coordinates (x, y) and the size (w, h) from the user's
416+
* space to the device space using passed graphics configuration.
417+
*
418+
* @param gc the graphics configuration to be used for transformation
419+
* @param x absolute coordinate in the user's space
420+
* @param y absolute coordinate in the user's space
421+
* @param w the width in the user's space
422+
* @param h the height in the user's space
423+
* @return the rectangle which uses device space (pixels)
424+
*/
425+
public static Rectangle toDeviceSpaceAbs(GraphicsConfiguration gc,
426+
int x, int y, int w, int h) {
364427
AffineTransform tx = gc.getDefaultTransform();
365-
x = Region.clipRound(x * tx.getScaleX());
366-
y = Region.clipRound(y * tx.getScaleY());
367-
return new Point((int) x, (int) y);
428+
Rectangle screen = gc.getBounds();
429+
return new Rectangle(
430+
screen.x + Region.clipRound((x - screen.x) * tx.getScaleX()),
431+
screen.y + Region.clipRound((y - screen.y) * tx.getScaleY()),
432+
Region.clipRound(w * tx.getScaleX()),
433+
Region.clipRound(h * tx.getScaleY())
434+
);
368435
}
369436

370437
/**
371-
* Converts bounds from the user's space to the device space using
438+
* Converts coordinates from the user's space to the device space using
372439
* appropriate device transformation.
373440
*
374-
* @param bounds the rectangle in the user space
375-
* @return the rectangle which uses device space(pixels)
441+
* @param x coordinate in the user's space
442+
* @param y coordinate in the user's space
443+
* @return the point which uses device space (pixels)
376444
*/
377-
public static Rectangle convertToDeviceSpace(Rectangle bounds) {
445+
public static Point toDeviceSpace(int x, int y) {
378446
GraphicsConfiguration gc = getLocalGraphicsEnvironment()
379447
.getDefaultScreenDevice().getDefaultConfiguration();
380-
gc = getGraphicsConfigurationAtPoint(gc, bounds.x, bounds.y);
381-
return convertToDeviceSpace(gc, bounds);
448+
gc = getGraphicsConfigurationAtPoint(gc, x, y);
449+
return toDeviceSpace(gc, x, y, 0, 0).getLocation();
382450
}
383451

384452
/**
385-
* Converts bounds from the user's space to the device space using
386-
* appropriate device transformation of the passed graphics configuration.
453+
* Converts coordinates (x, y) and the size (w, h) from the user's
454+
* space to the device space using passed graphics configuration.
387455
*
388-
* @param bounds the rectangle in the user space
389-
* @return the rectangle which uses device space(pixels)
456+
* @param gc the graphics configuration to be used for transformation
457+
* @param x coordinate in the user's space
458+
* @param y coordinate in the user's space
459+
* @param w the width in the user's space
460+
* @param h the height in the user's space
461+
* @return the rectangle which uses device space (pixels)
390462
*/
391-
public static Rectangle convertToDeviceSpace(GraphicsConfiguration gc,
392-
Rectangle bounds) {
463+
public static Rectangle toDeviceSpace(GraphicsConfiguration gc,
464+
int x, int y, int w, int h) {
393465
AffineTransform tx = gc.getDefaultTransform();
394466
return new Rectangle(
395-
Region.clipRound(bounds.x * tx.getScaleX()),
396-
Region.clipRound(bounds.y * tx.getScaleY()),
397-
Region.clipRound(bounds.width * tx.getScaleX()),
398-
Region.clipRound(bounds.height * tx.getScaleY())
467+
Region.clipRound(x * tx.getScaleX()),
468+
Region.clipRound(y * tx.getScaleY()),
469+
Region.clipRound(w * tx.getScaleX()),
470+
Region.clipRound(h * tx.getScaleY())
399471
);
400472
}
401473
}

src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ public synchronized void setDisplayMode(DisplayMode dm) {
467467
// display mode
468468
Rectangle screenBounds = getDefaultConfiguration().getBounds();
469469
w.setBounds(screenBounds.x, screenBounds.y,
470-
dm.getWidth(), dm.getHeight());
470+
screenBounds.width, screenBounds.height);
471471
// Note: no call to replaceSurfaceData is required here since
472472
// replacement will be caused by an upcoming display change event
473473
} else {

src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,11 @@ public void run() {
533533

534534
@Override
535535
public boolean updateGraphicsData(GraphicsConfiguration gc) {
536+
var old = getGraphicsConfiguration().getDefaultTransform();
536537
winGraphicsConfig = (Win32GraphicsConfig)gc;
538+
if (gc != null && !old.equals(gc.getDefaultTransform())) {
539+
syncBounds(); // the bounds of the peer depend on the DPI
540+
}
537541
try {
538542
replaceSurfaceData();
539543
} catch (InvalidPipeException e) {
@@ -542,6 +546,14 @@ public boolean updateGraphicsData(GraphicsConfiguration gc) {
542546
return false;
543547
}
544548

549+
/**
550+
* Make sure that the native peer's coordinates are in sync with the target.
551+
*/
552+
void syncBounds() {
553+
Rectangle r = ((Component) target).getBounds();
554+
setBounds(r.x, r.y, r.width, r.height, SET_BOUNDS);
555+
}
556+
545557
//This will return null for Components not yet added to a Container
546558
@Override
547559
public ColorModel getColorModel() {

src/java.desktop/windows/classes/sun/awt/windows/WDialogPeer.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import sun.awt.AWTAccessor;
3737
import sun.awt.im.InputMethodManager;
3838

39+
import static sun.java2d.SunGraphicsEnvironment.toUserSpace;
40+
3941
final class WDialogPeer extends WWindowPeer implements DialogPeer {
4042
// Toolkit & peer internals
4143

@@ -117,8 +119,8 @@ public Dimension getMinimumSize() {
117119
if (((Dialog)target).isUndecorated()) {
118120
return super.getMinimumSize();
119121
}
120-
return new Dimension(scaleDownX(getSysMinWidth()),
121-
scaleDownY(getSysMinHeight()));
122+
return toUserSpace(getGraphicsConfiguration(),
123+
getSysMinWidth(), getSysMinHeight());
122124
}
123125

124126
@Override

src/java.desktop/windows/classes/sun/awt/windows/WFramePeer.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@
3838
import sun.awt.im.InputMethodManager;
3939
import sun.security.action.GetPropertyAction;
4040

41-
import static sun.java2d.SunGraphicsEnvironment.convertToDeviceSpace;
41+
import static sun.java2d.SunGraphicsEnvironment.getGCDeviceBounds;
42+
import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs;
43+
import static sun.java2d.SunGraphicsEnvironment.toUserSpace;
4244

4345
class WFramePeer extends WWindowPeer implements FramePeer {
4446

@@ -97,10 +99,9 @@ public final void setMaximizedBounds(Rectangle b) {
9799
*/
98100
private Rectangle adjustMaximizedBounds(Rectangle bounds) {
99101
// All calculations should be done in the device space
100-
bounds = convertToDeviceSpace(bounds);
101-
102+
bounds = toDeviceSpaceAbs(bounds);
102103
GraphicsConfiguration gc = getGraphicsConfiguration();
103-
Rectangle currentDevBounds = convertToDeviceSpace(gc, gc.getBounds());
104+
Rectangle currentDevBounds = getGCDeviceBounds(gc);
104105
// Prepare data for WM_GETMINMAXINFO message.
105106
// ptMaxPosition should be in coordinate system of the current monitor,
106107
// not the main monitor, or monitor on which we maximize the window.
@@ -148,13 +149,13 @@ public void reshape(int x, int y, int width, int height) {
148149

149150
@Override
150151
public final Dimension getMinimumSize() {
152+
GraphicsConfiguration gc = getGraphicsConfiguration();
151153
Dimension d = new Dimension();
152154
if (!((Frame)target).isUndecorated()) {
153-
d.setSize(scaleDownX(getSysMinWidth()),
154-
scaleDownY(getSysMinHeight()));
155+
d.setSize(toUserSpace(gc, getSysMinWidth(), getSysMinHeight()));
155156
}
156-
if (((Frame)target).getMenuBar() != null) {
157-
d.height += scaleDownY(getSysMenuHeight());
157+
if (((Frame) target).getMenuBar() != null) {
158+
d.height += toUserSpace(gc, 0, getSysMenuHeight()).height;
158159
}
159160
return d;
160161
}

src/java.desktop/windows/classes/sun/awt/windows/WRobotPeer.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@
2929
import java.awt.Rectangle;
3030
import java.awt.peer.RobotPeer;
3131

32-
import sun.java2d.SunGraphicsEnvironment;
32+
import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs;
3333

3434
final class WRobotPeer implements RobotPeer {
3535

3636
public native void mouseMoveImpl(int x, int y);
3737
@Override
3838
public void mouseMove(int x, int y) {
39-
Point point = SunGraphicsEnvironment.convertToDeviceSpace(x, y);
39+
Point point = toDeviceSpaceAbs(x, y);
4040
mouseMoveImpl(point.x, point.y);
4141
}
4242
@Override
@@ -64,5 +64,10 @@ public int getRGBPixel(int x, int y) {
6464
return pixelArray;
6565
}
6666

67+
@Override
68+
public boolean useAbsoluteCoordinates() {
69+
return true;
70+
}
71+
6772
private native void getRGBPixels(int x, int y, int width, int height, int[] pixelArray);
6873
}

0 commit comments

Comments
 (0)