Skip to content

Commit c924244

Browse files
amartya4256fedejeanne
authored andcommitted
Introduce internal OfFloat classes for better scaling #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 #62 and #128
1 parent 5ba5c97 commit c924244

File tree

4 files changed

+183
-42
lines changed

4 files changed

+183
-42
lines changed

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: 30 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 = createPrecisionPointFrom(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,27 @@ 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 = createPrecisionRectangleFrom(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);
334+
}
335+
336+
private static Rectangle.OfFloat createPrecisionRectangleFrom(Rectangle rectangle) {
337+
if (rectangle instanceof Rectangle.OfFloat) {
338+
return (Rectangle.OfFloat) rectangle;
339+
}
340+
return new Rectangle.OfFloat(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
341+
}
342+
343+
private static Point.OfFloat createPrecisionPointFrom(Point point) {
344+
if (point instanceof Point.OfFloat) {
345+
return (Point.OfFloat) point;
346+
}
347+
return new Point.OfFloat(point.x, point.y);
343348
}
344349

345350
/**
@@ -436,11 +441,11 @@ public static Point autoScaleUp(Point point) {
436441

437442
public static Point scaleUp(Point point, int zoom) {
438443
if (zoom == 100 || point == null) return point;
444+
Point.OfFloat fPoint = createPrecisionPointFrom(point);
439445
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;
446+
float scaledX = fPoint.getX() * scaleFactor;
447+
float scaledY = fPoint.getY() * scaleFactor;
448+
return new Point.OfFloat(scaledX, scaledY);
444449
}
445450

446451
/**
@@ -463,16 +468,7 @@ public static Rectangle autoScaleUp(Rectangle rect) {
463468
}
464469

465470
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;
471+
return scaleBounds(rect, zoom, 100);
476472
}
477473

478474
/**

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)