Skip to content

Commit ff5192d

Browse files
committed
Float Aware Geometry on Scaling #62
This commit introduces FloatAwareRectangle and FloatAwarePoint as an extension of Rectangle and Point respectively to improve scaling precision in the DPIUtil by using residuals obtained from rounding. Contributes to #62 and #128
1 parent e40ad29 commit ff5192d

File tree

8 files changed

+127
-35
lines changed

8 files changed

+127
-35
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.eclipse.swt.graphics;
2+
3+
/**
4+
* @since 3.130
5+
*/
6+
public sealed class FloatAwarePoint extends Point permits MonitorAwarePoint {
7+
8+
private static final long serialVersionUID = -1862062276431597053L;
9+
10+
public float residualX, residualY;
11+
12+
public FloatAwarePoint(int x, int y) {
13+
super(x, y);
14+
}
15+
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.eclipse.swt.graphics;
2+
3+
/**
4+
* @since 3.130
5+
*/
6+
public sealed class FloatAwareRectangle extends Rectangle permits MonitorAwareRectangle {
7+
8+
private static final long serialVersionUID = -3006999002677468391L;
9+
10+
public float residualX, residualY, residualWidth, residualHeight;
11+
12+
public FloatAwareRectangle(int x, int y, int width, int height) {
13+
super(x, y, width, height);
14+
}
15+
16+
}

bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/MonitorAwarePoint.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
* @since 3.129
2525
* @noreference This class is not intended to be referenced by clients
2626
*/
27-
public final class MonitorAwarePoint extends Point {
27+
public final class MonitorAwarePoint extends FloatAwarePoint {
2828

2929
private static final long serialVersionUID = 6077427420686999194L;
3030

bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/MonitorAwareRectangle.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
* @since 3.129
2525
* @noreference This class is not intended to be referenced by clients
2626
*/
27-
public final class MonitorAwareRectangle extends Rectangle {
27+
public final class MonitorAwareRectangle extends FloatAwareRectangle {
2828

2929
private static final long serialVersionUID = 5041911840525116925L;
3030

bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Point.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
* @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
4242
*/
4343

44-
public sealed class Point implements Serializable permits MonitorAwarePoint {
44+
public sealed class Point implements Serializable permits FloatAwarePoint {
4545

4646
/**
4747
* the x coordinate of the point

bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Rectangle.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
* @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
4646
*/
4747

48-
public sealed class Rectangle implements Serializable permits MonitorAwareRectangle {
48+
public sealed class Rectangle implements Serializable permits FloatAwareRectangle {
4949

5050
/**
5151
* the x coordinate of the rectangle

bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,17 @@ public static Point autoScaleDown(Point point) {
228228

229229
public static Point scaleDown(Point point, int zoom) {
230230
if (zoom == 100 || point == null) return point;
231+
FloatAwarePoint fPoint = FloatAwareGeometryFactory.createFloatAwarePoint(point);
231232
float scaleFactor = getScalingFactor(zoom);
232-
Point scaledPoint = new Point (0,0);
233-
scaledPoint.x = Math.round (point.x / scaleFactor);
234-
scaledPoint.y = Math.round (point.y / scaleFactor);
233+
FloatAwarePoint scaledPoint = new FloatAwarePoint(0,0);
234+
235+
float absX = (fPoint.x + fPoint.residualX) / scaleFactor;
236+
float absY = (fPoint.y + fPoint.residualY) / scaleFactor;
237+
238+
scaledPoint.x = Math.round (absX);
239+
scaledPoint.y = Math.round (absY);
240+
scaledPoint.residualX = absX - scaledPoint.x;
241+
scaledPoint.residualY = absY - scaledPoint.y;
235242
return scaledPoint;
236243
}
237244

@@ -255,16 +262,7 @@ public static Rectangle autoScaleDown(Rectangle rect) {
255262
}
256263

257264
public static Rectangle scaleDown(Rectangle rect, int zoom) {
258-
if (zoom == 100 || rect == null) return rect;
259-
Rectangle scaledRect = new Rectangle (0,0,0,0);
260-
Point scaledTopLeft = scaleDown(new Point (rect.x, rect.y), zoom);
261-
Point scaledBottomRight = scaleDown(new Point (rect.x + rect.width, rect.y + rect.height), zoom);
262-
263-
scaledRect.x = scaledTopLeft.x;
264-
scaledRect.y = scaledTopLeft.y;
265-
scaledRect.width = scaledBottomRight.x - scaledTopLeft.x;
266-
scaledRect.height = scaledBottomRight.y - scaledTopLeft.y;
267-
return scaledRect;
265+
return scaleBounds(rect, 100, zoom);
268266
}
269267
/**
270268
* Returns a new scaled down Rectangle if enabled for Drawable class.
@@ -333,12 +331,24 @@ public static boolean isSmoothScalingEnabled() {
333331
*/
334332
public static Rectangle scaleBounds (Rectangle rect, int targetZoom, int currentZoom) {
335333
if (rect == null || targetZoom == currentZoom) return rect;
334+
FloatAwareRectangle fRect = FloatAwareGeometryFactory.createFloatAwareRectangle(rect);
336335
float scaleFactor = ((float)targetZoom) / (float)currentZoom;
337-
Rectangle returnRect = new Rectangle (0,0,0,0);
338-
returnRect.x = Math.round (rect.x * scaleFactor);
339-
returnRect.y = Math.round (rect.y * scaleFactor);
340-
returnRect.width = Math.round (rect.width * scaleFactor);
341-
returnRect.height = Math.round (rect.height * scaleFactor);
336+
FloatAwareRectangle returnRect = new FloatAwareRectangle(0,0,0,0);
337+
338+
float absX = (fRect.x + fRect.residualX) * scaleFactor;
339+
float absY = (fRect.y + fRect.residualY) * scaleFactor;
340+
float absWidth = (fRect.width + fRect.residualWidth) * scaleFactor;
341+
float absHeight = (fRect.height + fRect.residualHeight) * scaleFactor;
342+
343+
returnRect.x = Math.round (absX);
344+
returnRect.y = Math.round (absY);
345+
returnRect.width = Math.round (absWidth);
346+
returnRect.height = Math.round (absHeight);
347+
348+
returnRect.residualX = absX - returnRect.x;
349+
returnRect.residualY = absY - returnRect.y;
350+
returnRect.residualWidth = absWidth - returnRect.width;
351+
returnRect.residualHeight = absHeight - returnRect.height;
342352
return returnRect;
343353
}
344354

@@ -436,10 +446,17 @@ public static Point autoScaleUp(Point point) {
436446

437447
public static Point scaleUp(Point point, int zoom) {
438448
if (zoom == 100 || point == null) return point;
449+
FloatAwarePoint fPoint = FloatAwareGeometryFactory.createFloatAwarePoint(point);
439450
float scaleFactor = getScalingFactor(zoom);
440-
Point scaledPoint = new Point(0,0);
441-
scaledPoint.x = Math.round (point.x * scaleFactor);
442-
scaledPoint.y = Math.round (point.y * scaleFactor);
451+
FloatAwarePoint scaledPoint = new FloatAwarePoint(0,0);
452+
453+
float absX = (fPoint.x + fPoint.residualX) * scaleFactor;
454+
float absY = (fPoint.y + fPoint.residualY) * scaleFactor;
455+
456+
scaledPoint.x = Math.round (absX);
457+
scaledPoint.y = Math.round (absY);
458+
scaledPoint.residualX = absX - scaledPoint.x;
459+
scaledPoint.residualY = absY - scaledPoint.y;
443460
return scaledPoint;
444461
}
445462

@@ -463,16 +480,7 @@ public static Rectangle autoScaleUp(Rectangle rect) {
463480
}
464481

465482
public static Rectangle scaleUp(Rectangle rect, int zoom) {
466-
if (zoom == 100 || rect == null) return rect;
467-
Rectangle scaledRect = new Rectangle(0,0,0,0);
468-
Point scaledTopLeft = scaleUp (new Point(rect.x, rect.y), zoom);
469-
Point scaledBottomRight = scaleUp (new Point(rect.x + rect.width, rect.y + rect.height), zoom);
470-
471-
scaledRect.x = scaledTopLeft.x;
472-
scaledRect.y = scaledTopLeft.y;
473-
scaledRect.width = scaledBottomRight.x - scaledTopLeft.x;
474-
scaledRect.height = scaledBottomRight.y - scaledTopLeft.y;
475-
return scaledRect;
483+
return scaleBounds(rect, zoom, 100);
476484
}
477485

478486
/**
@@ -751,4 +759,20 @@ public ImageData getImageData(int zoom) {
751759
return DPIUtil.scaleImageData(device, imageData, zoom, currentZoom);
752760
}
753761
}
762+
763+
private class FloatAwareGeometryFactory {
764+
static FloatAwareRectangle createFloatAwareRectangle(Rectangle rectangle) {
765+
if (rectangle instanceof FloatAwareRectangle) {
766+
return (FloatAwareRectangle) rectangle;
767+
}
768+
return new FloatAwareRectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
769+
}
770+
771+
static FloatAwarePoint createFloatAwarePoint(Point point) {
772+
if (point instanceof FloatAwarePoint) {
773+
return (FloatAwarePoint) point;
774+
}
775+
return new FloatAwarePoint(point.x, point.y);
776+
}
777+
}
754778
}

tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/DPIUtilTests.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,4 +318,40 @@ public void scaleUpRectangle() {
318318
scaledValue = DPIUtil.scaleUp((Device) null, valueAt100, 100);
319319
assertSame(valueAt100, scaledValue, "Scaling up Rectangle without zoom change with device failed");
320320
}
321+
322+
@Test
323+
public void scaleDownscaleUpRectangleInvertible() {
324+
int[] zooms = new int[] {25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 325, 350, 375, 400};
325+
for (int zoom1 : zooms) {
326+
for (int zoom2 : zooms) {
327+
for (int i = 1; i <= 10000; i++) {
328+
Rectangle rect = new Rectangle(0, 0, i, i);
329+
Rectangle scaleDown = DPIUtil.scaleDown(rect, zoom1);
330+
Rectangle scaleUp = DPIUtil.scaleUp(scaleDown, zoom2);
331+
scaleDown = DPIUtil.scaleDown(scaleUp, zoom2);
332+
scaleUp = DPIUtil.scaleUp(scaleDown, zoom1);
333+
assertEquals(rect.width, scaleUp.width);
334+
assertEquals(rect.height, scaleUp.height);
335+
}
336+
}
337+
}
338+
}
339+
340+
@Test
341+
public void scaleDownscaleUpPointInvertible() {
342+
int[] zooms = new int[] {25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 325, 350, 375, 400};
343+
for (int zoom1 : zooms) {
344+
for (int zoom2 : zooms) {
345+
for (int i = 1; i <= 10000; i++) {
346+
Point pt = new Point(i, i);
347+
Point scaleDown = DPIUtil.scaleDown(pt, zoom1);
348+
Point scaleUp = DPIUtil.scaleUp(scaleDown, zoom2);
349+
scaleDown = DPIUtil.scaleDown(scaleUp, zoom2);
350+
scaleUp = DPIUtil.scaleUp(scaleDown, zoom1);
351+
assertEquals(pt.x, scaleUp.x);
352+
assertEquals(pt.y, scaleUp.y);
353+
}
354+
}
355+
}
356+
}
321357
}

0 commit comments

Comments
 (0)