diff --git a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java index 7926fb9e16c..759580dc17b 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java @@ -100,11 +100,11 @@ public void testCorrectScaleUpUsingDifferentSetBoundsMethod() { button.setBounds(new Rectangle(0, 47, 200, 47)); assertEquals("Control::setBounds(Rectangle) doesn't scale up correctly", - new Rectangle(0, 82, 350, 83), button.getBoundsInPixels()); + new Rectangle(0, 82, 350, 82), button.getBoundsInPixels()); button.setBounds(0, 47, 200, 47); assertEquals("Control::setBounds(int, int, int, int) doesn't scale up correctly", - new Rectangle(0, 82, 350, 83), button.getBoundsInPixels()); + new Rectangle(0, 82, 350, 82), button.getBoundsInPixels()); } record FontComparison(int originalFontHeight, int currentFontHeight) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/MonitorAwarePoint.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/MonitorAwarePoint.java index 5e73e051e69..88f504ad457 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/MonitorAwarePoint.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/MonitorAwarePoint.java @@ -26,10 +26,24 @@ */ public final class MonitorAwarePoint extends Point { - private static final long serialVersionUID = 6077427420686999194L; + private static final long serialVersionUID = 7516155847004716654L; + + public float residualX, residualY; private final Monitor monitor; + public MonitorAwarePoint(int x, int y) { + this(x, y, null); + } + + public MonitorAwarePoint(float x, float y, Monitor monitor) { + super(Math.round(x), Math.round(y)); + this.residualX = x - this.x; + this.residualY = y - this.y; + this.monitor = monitor; + } + + /** * Constructs a new MonitorAwarePoint * @@ -58,5 +72,22 @@ public boolean equals(Object object) { public int hashCode() { return super.hashCode(); } + + public float getX() { + return x + residualX; + } + + public float getY() { + return y + residualY; + } -} + public void setX(float x) { + this.x = Math.round(x); + this.residualX = x - this.x; + } + + public void setY(float y) { + this.y = Math.round(y); + this.residualY = y - this.y; + } +} \ No newline at end of file diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/MonitorAwareRectangle.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/MonitorAwareRectangle.java index ea0597621c3..bd4966136a0 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/MonitorAwareRectangle.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/MonitorAwareRectangle.java @@ -26,24 +26,34 @@ */ public final class MonitorAwareRectangle extends Rectangle { - private static final long serialVersionUID = 5041911840525116925L; + private static final long serialVersionUID = -54807918875027527L; + + private float residualX, residualY, residualWidth, residualHeight; private final Monitor monitor; - /** - * Constructs a new MonitorAwareRectangle - * - * @param x the x coordinate of the top left corner of the rectangle - * @param y the y coordinate of the top left corner of the rectangle - * @param width the width of the rectangle - * @param height the height of the rectangle - * @param monitor the monitor with whose context the rectangle is created - */ + public MonitorAwareRectangle(int x, int y, int width, int height) { + this(x, y, width, height, null); + } + + public MonitorAwareRectangle(float x, float y, float width, float height) { + this(x, y, width, height, null); + } + public MonitorAwareRectangle(int x, int y, int width, int height, Monitor monitor) { super(x, y, width, height); this.monitor = monitor; } + MonitorAwareRectangle(float x, float y, float width, float height, Monitor monitor) { + super(Math.round(x), Math.round(y), Math.round(width), Math.round(height)); + this.residualX = x - this.x; + this.residualY = y - this.y; + this.residualWidth = width - this.width; + this.residualHeight = height - this.height; + this.monitor = monitor; + } + /** * {@return the monitor with whose context the instance is created} */ @@ -60,10 +70,46 @@ public boolean equals(Object object) { public int hashCode() { return super.hashCode(); } - + @Override public MonitorAwareRectangle clone() { - return new MonitorAwareRectangle(x, y, width, height, monitor); + return new MonitorAwareRectangle(getX(), getY(), getWidth(), getHeight(), monitor); + } + + public float getX() { + return x + residualX; + } + + public float getY() { + return y + residualY; + } + + public float getWidth() { + return width + residualWidth; + } + + public float getHeight() { + return height + residualHeight; + } + + public void setX(float x) { + this.x = Math.round(x); + this.residualX = x - this.x; + } + + public void setY(float y) { + this.y = Math.round(y); + this.residualY = y - this.y; + } + + public void setWidth(float width) { + this.width = Math.round(width); + this.residualWidth = width - this.width; + } + + public void setHeight(float height) { + this.height = Math.round(height); + this.residualHeight = height - this.height; } -} +} \ No newline at end of file diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Rectangle.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Rectangle.java index b50cb13e6ac..341850cf316 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Rectangle.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Rectangle.java @@ -374,7 +374,7 @@ public Rectangle union (Rectangle rect) { */ public static Rectangle of(Point topLeft, int width, int height) { if (topLeft instanceof MonitorAwarePoint monitorAwareTopLeft) { - return new MonitorAwareRectangle(topLeft.x, topLeft.y, width, height, monitorAwareTopLeft.getMonitor()); + return new MonitorAwareRectangle(monitorAwareTopLeft.getX(), monitorAwareTopLeft.getY(), width, height, monitorAwareTopLeft.getMonitor()); } return new Rectangle(topLeft.x, topLeft.y, width, height); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java index a15f4683ae3..a5da23d2ce8 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java @@ -228,11 +228,11 @@ public static Point autoScaleDown(Point point) { public static Point scaleDown(Point point, int zoom) { if (zoom == 100 || point == null) return point; + MonitorAwarePoint fPoint = FloatAwareGeometryFactory.createFrom(point); float scaleFactor = getScalingFactor(zoom); - Point scaledPoint = new Point (0,0); - scaledPoint.x = Math.round (point.x / scaleFactor); - scaledPoint.y = Math.round (point.y / scaleFactor); - return scaledPoint; + float scaledX = fPoint.getX() / scaleFactor; + float scaledY = fPoint.getY() / scaleFactor; + return new MonitorAwarePoint(scaledX, scaledY, fPoint.getMonitor()); } /** @@ -255,16 +255,7 @@ public static Rectangle autoScaleDown(Rectangle rect) { } public static Rectangle scaleDown(Rectangle rect, int zoom) { - if (zoom == 100 || rect == null) return rect; - Rectangle scaledRect = new Rectangle (0,0,0,0); - Point scaledTopLeft = scaleDown(new Point (rect.x, rect.y), zoom); - Point scaledBottomRight = scaleDown(new Point (rect.x + rect.width, rect.y + rect.height), zoom); - - scaledRect.x = scaledTopLeft.x; - scaledRect.y = scaledTopLeft.y; - scaledRect.width = scaledBottomRight.x - scaledTopLeft.x; - scaledRect.height = scaledBottomRight.y - scaledTopLeft.y; - return scaledRect; + return scaleBounds(rect, 100, zoom); } /** * Returns a new scaled down Rectangle if enabled for Drawable class. @@ -333,13 +324,13 @@ public static boolean isSmoothScalingEnabled() { */ public static Rectangle scaleBounds (Rectangle rect, int targetZoom, int currentZoom) { if (rect == null || targetZoom == currentZoom) return rect; + MonitorAwareRectangle fRect = FloatAwareGeometryFactory.createFrom(rect); float scaleFactor = ((float)targetZoom) / (float)currentZoom; - Rectangle returnRect = new Rectangle (0,0,0,0); - returnRect.x = Math.round (rect.x * scaleFactor); - returnRect.y = Math.round (rect.y * scaleFactor); - returnRect.width = Math.round (rect.width * scaleFactor); - returnRect.height = Math.round (rect.height * scaleFactor); - return returnRect; + float scaledX = fRect.getX() * scaleFactor; + float scaledY = fRect.getY() * scaleFactor; + float scaledWidth = fRect.getWidth() * scaleFactor; + float scaledHeight = fRect.getHeight() * scaleFactor; + return new MonitorAwareRectangle(scaledX, scaledY, scaledWidth, scaledHeight); } /** @@ -436,11 +427,11 @@ public static Point autoScaleUp(Point point) { public static Point scaleUp(Point point, int zoom) { if (zoom == 100 || point == null) return point; + MonitorAwarePoint fPoint = FloatAwareGeometryFactory.createFrom(point); float scaleFactor = getScalingFactor(zoom); - Point scaledPoint = new Point(0,0); - scaledPoint.x = Math.round (point.x * scaleFactor); - scaledPoint.y = Math.round (point.y * scaleFactor); - return scaledPoint; + float scaledX = fPoint.getX() * scaleFactor; + float scaledY = fPoint.getY() * scaleFactor; + return new MonitorAwarePoint(scaledX, scaledY, fPoint.getMonitor()); } /** @@ -463,16 +454,7 @@ public static Rectangle autoScaleUp(Rectangle rect) { } public static Rectangle scaleUp(Rectangle rect, int zoom) { - if (zoom == 100 || rect == null) return rect; - Rectangle scaledRect = new Rectangle(0,0,0,0); - Point scaledTopLeft = scaleUp (new Point(rect.x, rect.y), zoom); - Point scaledBottomRight = scaleUp (new Point(rect.x + rect.width, rect.y + rect.height), zoom); - - scaledRect.x = scaledTopLeft.x; - scaledRect.y = scaledTopLeft.y; - scaledRect.width = scaledBottomRight.x - scaledTopLeft.x; - scaledRect.height = scaledBottomRight.y - scaledTopLeft.y; - return scaledRect; + return scaleBounds(rect, zoom, 100); } /** @@ -751,4 +733,20 @@ public ImageData getImageData(int zoom) { return DPIUtil.scaleImageData(device, imageData, zoom, currentZoom); } } + +private class FloatAwareGeometryFactory { + static MonitorAwareRectangle createFrom(Rectangle rectangle) { + if (rectangle instanceof MonitorAwareRectangle monitorAwareRectangle) { + return monitorAwareRectangle; + } + return new MonitorAwareRectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height); + } + + static MonitorAwarePoint createFrom(Point point) { + if (point instanceof MonitorAwarePoint monitorAwarePoint) { + return monitorAwarePoint; + } + return new MonitorAwarePoint(point.x, point.y); + } +} } \ No newline at end of file diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/DPIUtilTests.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/DPIUtilTests.java index d501c4d0a67..8453daa68f5 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/DPIUtilTests.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/DPIUtilTests.java @@ -296,7 +296,7 @@ public void scaleUpPoint() { @Test public void scaleUpRectangle() { Rectangle valueAt200 = new Rectangle(100, 150, 10, 14); - Rectangle valueAt150 = new Rectangle(75, 113, 8, 10); + Rectangle valueAt150 = new Rectangle(75, 113, 8, 11); Rectangle valueAt100 = new Rectangle(50, 75, 5, 7); Rectangle scaledValue = DPIUtil.autoScaleUp(valueAt100); @@ -319,4 +319,40 @@ public void scaleUpRectangle() { scaledValue = DPIUtil.scaleUp((Device) null, valueAt100, 100); assertSame(valueAt100, scaledValue, "Scaling up Rectangle without zoom change with device failed"); } + + @Test + public void scaleDownscaleUpRectangleInvertible() { + int[] zooms = new int[] {25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 325, 350, 375, 400}; + for (int zoom1 : zooms) { + for (int zoom2 : zooms) { + for (int i = 1; i <= 10000; i++) { + Rectangle rect = new Rectangle(0, 0, i, i); + Rectangle scaleDown = DPIUtil.scaleDown(rect, zoom1); + Rectangle scaleUp = DPIUtil.scaleUp(scaleDown, zoom2); + scaleDown = DPIUtil.scaleDown(scaleUp, zoom2); + scaleUp = DPIUtil.scaleUp(scaleDown, zoom1); + assertEquals(rect.width, scaleUp.width); + assertEquals(rect.height, scaleUp.height); + } + } + } + } + + @Test + public void scaleDownscaleUpPointInvertible() { + int[] zooms = new int[] {25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 325, 350, 375, 400}; + for (int zoom1 : zooms) { + for (int zoom2 : zooms) { + for (int i = 1; i <= 10000; i++) { + Point pt = new Point(i, i); + Point scaleDown = DPIUtil.scaleDown(pt, zoom1); + Point scaleUp = DPIUtil.scaleUp(scaleDown, zoom2); + scaleDown = DPIUtil.scaleDown(scaleUp, zoom2); + scaleUp = DPIUtil.scaleUp(scaleDown, zoom1); + assertEquals(pt.x, scaleUp.x); + assertEquals(pt.y, scaleUp.y); + } + } + } + } }