Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8211999: Window positioning bugs due to overlapping GraphicsDevice bounds (Windows/HiDPI) #375

Closed
wants to merge 14 commits into from
32 changes: 15 additions & 17 deletions src/java.desktop/share/classes/java/awt/Robot.java
Original file line number Diff line number Diff line change
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 @@ -385,13 +388,9 @@ private static 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 @@ -523,17 +522,16 @@ public synchronized BufferedImage createScreenCapture(Rectangle screenRect) {
imageArray[0] = highResolutionImage;

} 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
Original file line number Diff line number Diff line change
@@ -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;
}
}
122 changes: 97 additions & 25 deletions src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java
Original file line number Diff line number Diff line change
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 @@ -349,53 +350,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 x coordinate in the user space
* @param y coordinate in the user 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).
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* {@code GraphicsConfiguration} in device space(pixels).
* {@code GraphicsConfiguration} in device space (pixels).

If changed here, all other similar places should be updated too to keep doc comments consistent.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll fix it and also merge the master, then update the PR.

Copy link
Member

Choose a reason for hiding this comment

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

In this case, also consider adding a space between the word and the opening parenthesis in these coordinates (x, y) and size (w, h) and, probably, a space after comma.

Copy link
Member Author

Choose a reason for hiding this comment

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

Spaces are added.

*/
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 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);

AffineTransform tx = gc.getDefaultTransform();
x = Region.clipRound(x * tx.getScaleX());
y = Region.clipRound(y * tx.getScaleY());
return new Point((int) x, (int) y);
return toDeviceSpaceAbs(gc, x, y, 0, 0).getLocation();
}

/**
* Converts bounds from the user's space to the device space using
* Converts the rectangle from the user's space to the device space using
* appropriate device transformation.
*
* @param bounds the rectangle in the user space
* @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 convertToDeviceSpace(Rectangle bounds) {
public static Rectangle toDeviceSpaceAbs(GraphicsConfiguration gc,
int x, int y, int w, int h) {
AffineTransform tx = gc.getDefaultTransform();
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 absolute coordinates from the user's space to the device space
* using appropriate device transformation.
*
* @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 toDeviceSpace(int x, int y) {
mrserb marked this conversation as resolved.
Show resolved Hide resolved
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
* @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())
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,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
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,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 bound of the peer depends on the DPI
mrserb marked this conversation as resolved.
Show resolved Hide resolved
}
try {
replaceSurfaceData();
} catch (InvalidPipeException e) {
Expand All @@ -542,6 +546,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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
import java.awt.Rectangle;
import java.awt.peer.RobotPeer;

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

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 @@ -64,5 +64,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);
}
Loading