Skip to content

Commit 0ebab19

Browse files
amartya4256fedejeanne
authored andcommitted
Introduce OfFloat classes for better scaling eclipse-platform#62
This commit introduces Rectangle.OfFloat and Point.OfFloat classes as an extension of Rectangle and Point respectively to improve scaling precision in the DPIUtil by using residuals obtained from rounding. Contributes to eclipse-platform#62 and eclipse-platform#128
1 parent 5b9f3a0 commit 0ebab19

File tree

5 files changed

+187
-44
lines changed

5 files changed

+187
-44
lines changed

bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,11 @@ public void testCorrectScaleUpUsingDifferentSetBoundsMethod() {
100100

101101
button.setBounds(new Rectangle(0, 47, 200, 47));
102102
assertEquals("Control::setBounds(Rectangle) doesn't scale up correctly",
103-
new Rectangle(0, 82, 350, 83), button.getBoundsInPixels());
103+
new Rectangle(0, 82, 350, 82), button.getBoundsInPixels());
104104

105105
button.setBounds(0, 47, 200, 47);
106106
assertEquals("Control::setBounds(int, int, int, int) doesn't scale up correctly",
107-
new Rectangle(0, 82, 350, 83), button.getBoundsInPixels());
107+
new Rectangle(0, 82, 350, 82), button.getBoundsInPixels());
108108
}
109109

110110
record FontComparison(int originalFontHeight, int currentFontHeight) {

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

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
* @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
4444
*/
4545

46-
public sealed class Point implements Serializable permits Point.WithMonitor {
46+
public sealed class Point implements Serializable permits Point.OfFloat {
4747

4848
/**
4949
* the x coordinate of the point
@@ -120,14 +120,56 @@ public String toString () {
120120

121121
/**
122122
* Instances of this class represent {@link org.eclipse.swt.graphics.Point}
123+
* objects with the fields capable of storing more precise value in float.
124+
*
125+
* @since 3.131
126+
* @noreference This class is not intended to be referenced by clients
127+
*/
128+
public static sealed class OfFloat extends Point permits Point.WithMonitor {
129+
130+
private static final long serialVersionUID = -1862062276431597053L;
131+
132+
public float residualX, residualY;
133+
134+
public OfFloat(int x, int y) {
135+
super(x, y);
136+
}
137+
138+
public OfFloat(float x, float y) {
139+
super(Math.round(x), Math.round(y));
140+
this.residualX = x - this.x;
141+
this.residualY = y - this.y;
142+
}
143+
144+
public float getX() {
145+
return x + residualX;
146+
}
147+
148+
public float getY() {
149+
return y + residualY;
150+
}
151+
152+
public void setX(float x) {
153+
this.x = Math.round(x);
154+
this.residualX = x - this.x;
155+
}
156+
157+
public void setY(float y) {
158+
this.y = Math.round(y);
159+
this.residualY = y - this.y;
160+
}
161+
}
162+
163+
/**
164+
* Instances of this class represent {@link org.eclipse.swt.graphics.Point.OfFloat}
123165
* objects along with the context of the monitor in relation to which they are
124166
* placed on the display. The monitor awareness makes it easy to scale and
125167
* translate the points between pixels and points.
126168
*
127169
* @since 3.131
128170
* @noreference This class is not intended to be referenced by clients
129171
*/
130-
public static final class WithMonitor extends Point {
172+
public static final class WithMonitor extends Point.OfFloat {
131173

132174
private static final long serialVersionUID = 6077427420686999194L;
133175

@@ -155,4 +197,3 @@ public Monitor getMonitor() {
155197
}
156198

157199
}
158-

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

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
* @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
4747
*/
4848

49-
public sealed class Rectangle implements Serializable, Cloneable permits Rectangle.WithMonitor {
49+
public sealed class Rectangle implements Serializable, Cloneable permits Rectangle.OfFloat {
5050

5151
/**
5252
* the x coordinate of the rectangle
@@ -375,7 +375,7 @@ public Rectangle union (Rectangle rect) {
375375
*/
376376
public static Rectangle of(Point topLeft, int width, int height) {
377377
if (topLeft instanceof Point.WithMonitor monitorAwareTopLeft) {
378-
return new Rectangle.WithMonitor(topLeft.x, topLeft.y, width, height, monitorAwareTopLeft.getMonitor());
378+
return new Rectangle.WithMonitor(monitorAwareTopLeft.getX(), monitorAwareTopLeft.getY(), width, height, monitorAwareTopLeft.getMonitor());
379379
}
380380
return new Rectangle(topLeft.x, topLeft.y, width, height);
381381
}
@@ -400,14 +400,77 @@ public Rectangle clone() {
400400

401401
/**
402402
* Instances of this class represent {@link org.eclipse.swt.graphics.Rectangle}
403+
* objects which supports values of Float type for it's fields
404+
*
405+
* @since 3.131
406+
* @noreference This class is not intended to be referenced by clients
407+
*/
408+
public static sealed class OfFloat extends Rectangle permits Rectangle.WithMonitor {
409+
410+
private static final long serialVersionUID = -3006999002677468391L;
411+
412+
private float residualX, residualY, residualWidth, residualHeight;
413+
414+
public OfFloat(int x, int y, int width, int height) {
415+
super(x, y, width, height);
416+
}
417+
418+
public OfFloat(float x, float y, float width, float height) {
419+
super(Math.round(x), Math.round(y), Math.round(width), Math.round(height));
420+
this.residualX = x - this.x;
421+
this.residualY = y - this.y;
422+
this.residualWidth = width - this.width;
423+
this.residualHeight = height - this.height;
424+
}
425+
426+
public float getX() {
427+
return x + residualX;
428+
}
429+
430+
public float getY() {
431+
return y + residualY;
432+
}
433+
434+
public float getWidth() {
435+
return width + residualWidth;
436+
}
437+
438+
public float getHeight() {
439+
return height + residualHeight;
440+
}
441+
442+
public void setX(float x) {
443+
this.x = Math.round(x);
444+
this.residualX = x - this.x;
445+
}
446+
447+
public void setY(float y) {
448+
this.y = Math.round(y);
449+
this.residualY = y - this.y;
450+
}
451+
452+
public void setWidth(float width) {
453+
this.width = Math.round(width);
454+
this.residualWidth = width - this.width;
455+
}
456+
457+
public void setHeight(float height) {
458+
this.height = Math.round(height);
459+
this.residualHeight = height - this.height;
460+
}
461+
462+
}
463+
464+
/**
465+
* Instances of this class represent {@link org.eclipse.swt.graphics.Rectangle.OfFloat}
403466
* objects along with the context of the monitor in relation to which they are
404467
* placed on the display. The monitor awareness makes it easy to scale and
405468
* translate the rectangles between pixels and points.
406469
*
407470
* @since 3.131
408471
* @noreference This class is not intended to be referenced by clients
409472
*/
410-
public static final class WithMonitor extends Rectangle {
473+
public static final class WithMonitor extends Rectangle.OfFloat {
411474

412475
private static final long serialVersionUID = 5041911840525116925L;
413476

@@ -427,6 +490,11 @@ public WithMonitor(int x, int y, int width, int height, Monitor monitor) {
427490
this.monitor = monitor;
428491
}
429492

493+
private WithMonitor(float x, float y, float width, float height, Monitor monitor) {
494+
super(x, y, width, height);
495+
this.monitor = monitor;
496+
}
497+
430498
/**
431499
* {@return the monitor with whose context the instance is created}
432500
*/
@@ -436,8 +504,8 @@ public Monitor getMonitor() {
436504

437505
@Override
438506
public Rectangle.WithMonitor clone() {
439-
return new Rectangle.WithMonitor(x, y, width, height, monitor);
507+
return new Rectangle.WithMonitor(getX(), getY(), getWidth(), getHeight(), monitor);
440508
}
441509

442510
}
443-
}
511+
}

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

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -228,11 +228,11 @@ 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+
Point.OfFloat fPoint = FloatAwareGeometryFactory.createFrom(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);
235-
return scaledPoint;
233+
float scaledX = fPoint.getX() / scaleFactor;
234+
float scaledY = fPoint.getY() / scaleFactor;
235+
return new Point.OfFloat(scaledX, scaledY);
236236
}
237237

238238
/**
@@ -255,16 +255,7 @@ public static Rectangle autoScaleDown(Rectangle rect) {
255255
}
256256

257257
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;
258+
return scaleBounds(rect, 100, zoom);
268259
}
269260
/**
270261
* Returns a new scaled down Rectangle if enabled for Drawable class.
@@ -333,13 +324,13 @@ public static boolean isSmoothScalingEnabled() {
333324
*/
334325
public static Rectangle scaleBounds (Rectangle rect, int targetZoom, int currentZoom) {
335326
if (rect == null || targetZoom == currentZoom) return rect;
327+
Rectangle.OfFloat fRect = FloatAwareGeometryFactory.createFrom(rect);
336328
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);
342-
return returnRect;
329+
float scaledX = fRect.getX() * scaleFactor;
330+
float scaledY = fRect.getY() * scaleFactor;
331+
float scaledWidth = fRect.getWidth() * scaleFactor;
332+
float scaledHeight = fRect.getHeight() * scaleFactor;
333+
return new Rectangle.OfFloat(scaledX, scaledY, scaledWidth, scaledHeight);
343334
}
344335

345336
/**
@@ -436,11 +427,11 @@ public static Point autoScaleUp(Point point) {
436427

437428
public static Point scaleUp(Point point, int zoom) {
438429
if (zoom == 100 || point == null) return point;
430+
Point.OfFloat fPoint = FloatAwareGeometryFactory.createFrom(point);
439431
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);
443-
return scaledPoint;
432+
float scaledX = fPoint.getX() * scaleFactor;
433+
float scaledY = fPoint.getY() * scaleFactor;
434+
return new Point.OfFloat(scaledX, scaledY);
444435
}
445436

446437
/**
@@ -463,16 +454,7 @@ public static Rectangle autoScaleUp(Rectangle rect) {
463454
}
464455

465456
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;
457+
return scaleBounds(rect, zoom, 100);
476458
}
477459

478460
/**
@@ -751,4 +733,20 @@ public ImageData getImageData(int zoom) {
751733
return DPIUtil.scaleImageData(device, imageData, zoom, currentZoom);
752734
}
753735
}
736+
737+
private class FloatAwareGeometryFactory {
738+
static Rectangle.OfFloat createFrom(Rectangle rectangle) {
739+
if (rectangle instanceof Rectangle.OfFloat) {
740+
return (Rectangle.OfFloat) rectangle;
741+
}
742+
return new Rectangle.OfFloat(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
743+
}
744+
745+
static Point.OfFloat createFrom(Point point) {
746+
if (point instanceof Point.OfFloat) {
747+
return (Point.OfFloat) point;
748+
}
749+
return new Point.OfFloat(point.x, point.y);
750+
}
751+
}
754752
}

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

0 commit comments

Comments
 (0)