Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8211999: Window positioning bugs due to overlapping GraphicsDevice bo…
…unds (Windows/HiDPI)

Reviewed-by: serb
Backport-of: be63525
  • Loading branch information
TheRealMDoerr committed Oct 15, 2021
1 parent 99ff268 commit fcb396f
Show file tree
Hide file tree
Showing 35 changed files with 1,117 additions and 429 deletions.
30 changes: 15 additions & 15 deletions src/java.desktop/share/classes/java/awt/Robot.java
Expand Up @@ -43,6 +43,9 @@
import sun.awt.image.SunWritableRaster;
import sun.java2d.SunGraphicsEnvironment;

import static sun.java2d.SunGraphicsEnvironment.toDeviceSpace;
import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs;

/**
* This class is used to generate native system input events
* for the purposes of test automation, self-running demos, and
Expand Down Expand Up @@ -377,13 +380,9 @@ private void checkKeycodeArgument(int keycode) {
*/
public synchronized Color getPixelColor(int x, int y) {
checkScreenCaptureAllowed();
AffineTransform tx = GraphicsEnvironment.
getLocalGraphicsEnvironment().getDefaultScreenDevice().
getDefaultConfiguration().getDefaultTransform();
x = (int) (x * tx.getScaleX());
y = (int) (y * tx.getScaleY());
Color color = new Color(peer.getRGBPixel(x, y));
return color;
Point point = peer.useAbsoluteCoordinates() ? toDeviceSpaceAbs(x, y)
: toDeviceSpace(x, y);
return new Color(peer.getRGBPixel(point.x, point.y));
}

/**
Expand Down Expand Up @@ -516,16 +515,17 @@ public synchronized BufferedImage createScreenCapture(Rectangle screenRect) {

} else {

int sX = (int) Math.floor(screenRect.x * uiScaleX);
int sY = (int) Math.floor(screenRect.y * uiScaleY);
int sWidth = (int) Math.ceil(screenRect.width * uiScaleX);
int sHeight = (int) Math.ceil(screenRect.height * uiScaleY);
int temppixels[];
Rectangle scaledRect = new Rectangle(sX, sY, sWidth, sHeight);
temppixels = peer.getRGBPixels(scaledRect);
Rectangle scaledRect;
if (peer.useAbsoluteCoordinates()) {
scaledRect = toDeviceSpaceAbs(gc, screenRect.x,
screenRect.y, screenRect.width, screenRect.height);
} else {
scaledRect = toDeviceSpace(gc, screenRect.x,
screenRect.y, screenRect.width, screenRect.height);
}

// HighResolutionImage
pixels = temppixels;
pixels = peer.getRGBPixels(scaledRect);
buffer = new DataBufferInt(pixels, pixels.length);
raster = Raster.createPackedRaster(buffer, scaledRect.width,
scaledRect.height, scaledRect.width, bandmasks, null);
Expand Down
12 changes: 11 additions & 1 deletion src/java.desktop/share/classes/java/awt/peer/RobotPeer.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2020, 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
Expand Down Expand Up @@ -117,4 +117,14 @@ public interface RobotPeer
* @see Robot#createScreenCapture(Rectangle)
*/
int[] getRGBPixels(Rectangle bounds);

/**
* Determines if absolute coordinates should be used by this peer.
*
* @return {@code true} if absolute coordinates should be used,
* {@code false} otherwise
*/
default boolean useAbsoluteCoordinates() {
return false;
}
}
124 changes: 98 additions & 26 deletions src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java
Expand Up @@ -27,6 +27,7 @@

import java.awt.AWTError;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
Expand Down Expand Up @@ -401,53 +402,124 @@ public static GraphicsConfiguration getGraphicsConfigurationAtPoint(
}

/**
* Converts coordinates from the user's space to the device space using
* appropriate device transformation.
* Returns the bounds of the graphics configuration in device space.
*
* @param config the graphics configuration which bounds are requested
* @return the bounds of the area covered by this
* {@code GraphicsConfiguration} in device space (pixels)
*/
public static Rectangle getGCDeviceBounds(GraphicsConfiguration config) {
AffineTransform tx = config.getDefaultTransform();
Rectangle bounds = config.getBounds();
bounds.width *= tx.getScaleX();
bounds.height *= tx.getScaleY();
return bounds;
}

/**
* Converts the size (w, h) from the device space to the user's space using
* passed graphics configuration.
*
* @param gc the graphics configuration to be used for transformation
* @param w the width in the device space
* @param h the height in the device space
* @return the size in the user's space
*/
public static Dimension toUserSpace(GraphicsConfiguration gc,
int w, int h) {
AffineTransform tx = gc.getDefaultTransform();
return new Dimension(
Region.clipRound(w / tx.getScaleX()),
Region.clipRound(h / tx.getScaleY())
);
}

/**
* Converts absolute coordinates from the user's space to the device space
* using appropriate device transformation.
*
* @param x coordinate in the user space
* @param y coordinate in the user space
* @return the point which uses device space(pixels)
* @param x absolute coordinate in the user's space
* @param y absolute coordinate in the user's space
* @return the point which uses device space (pixels)
*/
public static Point convertToDeviceSpace(double x, double y) {
public static Point toDeviceSpaceAbs(int x, int y) {
GraphicsConfiguration gc = getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();
.getDefaultScreenDevice().getDefaultConfiguration();
gc = getGraphicsConfigurationAtPoint(gc, x, y);
return toDeviceSpaceAbs(gc, x, y, 0, 0).getLocation();
}

/**
* Converts the rectangle from the user's space to the device space using
* appropriate device transformation.
*
* @param rect the rectangle in the user's space
* @return the rectangle which uses device space (pixels)
*/
public static Rectangle toDeviceSpaceAbs(Rectangle rect) {
GraphicsConfiguration gc = getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();
gc = getGraphicsConfigurationAtPoint(gc, rect.x, rect.y);
return toDeviceSpaceAbs(gc, rect.x, rect.y, rect.width, rect.height);
}

/**
* Converts absolute coordinates (x, y) and the size (w, h) from the user's
* space to the device space using passed graphics configuration.
*
* @param gc the graphics configuration to be used for transformation
* @param x absolute coordinate in the user's space
* @param y absolute coordinate in the user's space
* @param w the width in the user's space
* @param h the height in the user's space
* @return the rectangle which uses device space (pixels)
*/
public static Rectangle toDeviceSpaceAbs(GraphicsConfiguration gc,
int x, int y, int w, int h) {
AffineTransform tx = gc.getDefaultTransform();
x = Region.clipRound(x * tx.getScaleX());
y = Region.clipRound(y * tx.getScaleY());
return new Point((int) x, (int) y);
Rectangle screen = gc.getBounds();
return new Rectangle(
screen.x + Region.clipRound((x - screen.x) * tx.getScaleX()),
screen.y + Region.clipRound((y - screen.y) * tx.getScaleY()),
Region.clipRound(w * tx.getScaleX()),
Region.clipRound(h * tx.getScaleY())
);
}

/**
* Converts bounds from the user's space to the device space using
* Converts coordinates from the user's space to the device space using
* appropriate device transformation.
*
* @param bounds the rectangle in the user space
* @return the rectangle which uses device space(pixels)
* @param x coordinate in the user's space
* @param y coordinate in the user's space
* @return the point which uses device space (pixels)
*/
public static Rectangle convertToDeviceSpace(Rectangle bounds) {
public static Point toDeviceSpace(int x, int y) {
GraphicsConfiguration gc = getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();
gc = getGraphicsConfigurationAtPoint(gc, bounds.x, bounds.y);
return convertToDeviceSpace(gc, bounds);
gc = getGraphicsConfigurationAtPoint(gc, x, y);
return toDeviceSpace(gc, x, y, 0, 0).getLocation();
}

/**
* Converts bounds from the user's space to the device space using
* appropriate device transformation of the passed graphics configuration.
* Converts coordinates (x, y) and the size (w, h) from the user's
* space to the device space using passed graphics configuration.
*
* @param bounds the rectangle in the user space
* @return the rectangle which uses device space(pixels)
* @param gc the graphics configuration to be used for transformation
* @param x coordinate in the user's space
* @param y coordinate in the user's space
* @param w the width in the user's space
* @param h the height in the user's space
* @return the rectangle which uses device space (pixels)
*/
public static Rectangle convertToDeviceSpace(GraphicsConfiguration gc,
Rectangle bounds) {
public static Rectangle toDeviceSpace(GraphicsConfiguration gc,
int x, int y, int w, int h) {
AffineTransform tx = gc.getDefaultTransform();
return new Rectangle(
Region.clipRound(bounds.x * tx.getScaleX()),
Region.clipRound(bounds.y * tx.getScaleY()),
Region.clipRound(bounds.width * tx.getScaleX()),
Region.clipRound(bounds.height * tx.getScaleY())
Region.clipRound(x * tx.getScaleX()),
Region.clipRound(y * tx.getScaleY()),
Region.clipRound(w * tx.getScaleX()),
Region.clipRound(h * tx.getScaleY())
);
}
}
Expand Up @@ -463,7 +463,7 @@ public synchronized void setDisplayMode(DisplayMode dm) {
// display mode
Rectangle screenBounds = getDefaultConfiguration().getBounds();
w.setBounds(screenBounds.x, screenBounds.y,
dm.getWidth(), dm.getHeight());
screenBounds.width, screenBounds.height);
// Note: no call to replaceSurfaceData is required here since
// replacement will be caused by an upcoming display change event
} else {
Expand Down
Expand Up @@ -520,7 +520,11 @@ public void run() {

@Override
public boolean updateGraphicsData(GraphicsConfiguration gc) {
var old = getGraphicsConfiguration().getDefaultTransform();
winGraphicsConfig = (Win32GraphicsConfig)gc;
if (gc != null && !old.equals(gc.getDefaultTransform())) {
syncBounds(); // the bounds of the peer depend on the DPI
}
try {
replaceSurfaceData();
} catch (InvalidPipeException e) {
Expand All @@ -529,6 +533,14 @@ public boolean updateGraphicsData(GraphicsConfiguration gc) {
return false;
}

/**
* Make sure that the native peer's coordinates are in sync with the target.
*/
void syncBounds() {
Rectangle r = ((Component) target).getBounds();
setBounds(r.x, r.y, r.width, r.height, SET_BOUNDS);
}

//This will return null for Components not yet added to a Container
@Override
public ColorModel getColorModel() {
Expand Down
Expand Up @@ -36,6 +36,8 @@
import sun.awt.AWTAccessor;
import sun.awt.im.InputMethodManager;

import static sun.java2d.SunGraphicsEnvironment.toUserSpace;

final class WDialogPeer extends WWindowPeer implements DialogPeer {
// Toolkit & peer internals

Expand Down Expand Up @@ -117,8 +119,8 @@ public Dimension getMinimumSize() {
if (((Dialog)target).isUndecorated()) {
return super.getMinimumSize();
}
return new Dimension(scaleDownX(getSysMinWidth()),
scaleDownY(getSysMinHeight()));
return toUserSpace(getGraphicsConfiguration(),
getSysMinWidth(), getSysMinHeight());
}

@Override
Expand Down
17 changes: 9 additions & 8 deletions src/java.desktop/windows/classes/sun/awt/windows/WFramePeer.java
Expand Up @@ -38,7 +38,9 @@
import sun.awt.im.InputMethodManager;
import sun.security.action.GetPropertyAction;

import static sun.java2d.SunGraphicsEnvironment.convertToDeviceSpace;
import static sun.java2d.SunGraphicsEnvironment.getGCDeviceBounds;
import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs;
import static sun.java2d.SunGraphicsEnvironment.toUserSpace;

class WFramePeer extends WWindowPeer implements FramePeer {

Expand Down Expand Up @@ -97,10 +99,9 @@ public final void setMaximizedBounds(Rectangle b) {
*/
private Rectangle adjustMaximizedBounds(Rectangle bounds) {
// All calculations should be done in the device space
bounds = convertToDeviceSpace(bounds);

bounds = toDeviceSpaceAbs(bounds);
GraphicsConfiguration gc = getGraphicsConfiguration();
Rectangle currentDevBounds = convertToDeviceSpace(gc, gc.getBounds());
Rectangle currentDevBounds = getGCDeviceBounds(gc);
// Prepare data for WM_GETMINMAXINFO message.
// ptMaxPosition should be in coordinate system of the current monitor,
// not the main monitor, or monitor on which we maximize the window.
Expand Down Expand Up @@ -148,13 +149,13 @@ public void reshape(int x, int y, int width, int height) {

@Override
public final Dimension getMinimumSize() {
GraphicsConfiguration gc = getGraphicsConfiguration();
Dimension d = new Dimension();
if (!((Frame)target).isUndecorated()) {
d.setSize(scaleDownX(getSysMinWidth()),
scaleDownY(getSysMinHeight()));
d.setSize(toUserSpace(gc, getSysMinWidth(), getSysMinHeight()));
}
if (((Frame)target).getMenuBar() != null) {
d.height += scaleDownY(getSysMenuHeight());
if (((Frame) target).getMenuBar() != null) {
d.height += toUserSpace(gc, 0, getSysMenuHeight()).height;
}
return d;
}
Expand Down
Expand Up @@ -30,7 +30,7 @@
import java.awt.Rectangle;
import java.awt.peer.RobotPeer;

import sun.java2d.SunGraphicsEnvironment;
import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs;

final class WRobotPeer implements RobotPeer {

Expand All @@ -40,7 +40,7 @@ final class WRobotPeer implements RobotPeer {
public native void mouseMoveImpl(int x, int y);
@Override
public void mouseMove(int x, int y) {
Point point = SunGraphicsEnvironment.convertToDeviceSpace(x, y);
Point point = toDeviceSpaceAbs(x, y);
mouseMoveImpl(point.x, point.y);
}
@Override
Expand Down Expand Up @@ -68,5 +68,10 @@ public int getRGBPixel(int x, int y) {
return pixelArray;
}

@Override
public boolean useAbsoluteCoordinates() {
return true;
}

private native void getRGBPixels(int x, int y, int width, int height, int pixelArray[]);
}

1 comment on commit fcb396f

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.