From: Tuukka Lehtonen Date: Sun, 16 Sep 2018 12:25:59 +0000 (+0300) Subject: Small but effective HiDPI fixes for platform G2D X-Git-Tag: v1.43.0~136^2~373 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F95%2F2195%2F1;p=simantics%2Fplatform.git Small but effective HiDPI fixes for platform G2D * GalleryViewer now scales accoring to swt.autoScale setting * SWTPopupMenuParticipant now properly downscales pixel coordinates to SWT API coordinates * Default symbol title font size is now 12 which is more readable by default now that the font size is scaled more correctly than before gitlab #119 Change-Id: I9ced77879e367efd905138798b18c3fb908e5b21 --- diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/ui/SWTPopupMenuParticipant.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/ui/SWTPopupMenuParticipant.java index 588692b91..c489373b6 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/ui/SWTPopupMenuParticipant.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/ui/SWTPopupMenuParticipant.java @@ -27,7 +27,6 @@ import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; -import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.IWorkbenchPartSite; import org.simantics.g2d.canvas.ICanvasContext; @@ -42,6 +41,7 @@ import org.simantics.utils.datastructures.hints.HintListenerAdapter; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintListener; import org.simantics.utils.datastructures.hints.IHintObservable; +import org.simantics.utils.ui.SWTDPIUtil; /** * A participant that initializes an SWT pop-up menu and registers it with the @@ -221,20 +221,15 @@ public class SWTPopupMenuParticipant extends AbstractCanvasParticipant { * @param newValue * @thread canvas-thread (AWT) */ - protected void showPopup(Point2D newValue) { - final Point2D cp = (Point2D) newValue; + protected void showPopup(Point2D cp) { setHint(DiagramHints.POPUP_MENU_CONTROL_POSITION, cp); setHint(DiagramHints.POPUP_MENU_CANVAS_POSITION, trUtil.controlToCanvas(cp, null)); - display.asyncExec(new Runnable() { - @Override - public void run() { - if (control == null || control.isDisposed()) - return; - - Point p = control.toDisplay((int) cp.getX(), (int) cp.getY()); - menuManager.getMenu().setLocation(p); - menuManager.getMenu().setVisible(true); - } + display.asyncExec(() -> { + if (control == null || control.isDisposed()) + return; + Point p = control.toDisplay( SWTDPIUtil.downscaleSwtToInteger(cp) ); + menuManager.getMenu().setLocation(p); + menuManager.getMenu().setVisible(true); }); } diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/ui/e4/SWTPopupMenuParticipant.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/ui/e4/SWTPopupMenuParticipant.java index eb7fd9928..e6e4d33ee 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/ui/e4/SWTPopupMenuParticipant.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/ui/e4/SWTPopupMenuParticipant.java @@ -14,9 +14,6 @@ package org.simantics.diagram.ui.e4; import java.awt.geom.Point2D; import org.eclipse.e4.ui.model.application.ui.basic.MPart; -import org.eclipse.e4.ui.model.application.ui.menu.MMenu; -import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory; -import org.eclipse.e4.ui.model.application.ui.menu.MPopupMenu; import org.eclipse.jface.action.GroupMarker; import org.eclipse.jface.action.IMenuListener2; import org.eclipse.jface.action.IMenuManager; @@ -46,6 +43,7 @@ import org.simantics.utils.datastructures.hints.HintListenerAdapter; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintListener; import org.simantics.utils.datastructures.hints.IHintObservable; +import org.simantics.utils.ui.SWTDPIUtil; /** * A participant that initializes an SWT pop-up menu and registers it with the @@ -215,19 +213,15 @@ public class SWTPopupMenuParticipant extends AbstractCanvasParticipant { * @param newValue * @thread canvas-thread (AWT) */ - protected void showPopup(Point2D newValue) { - final Point2D cp = (Point2D) newValue; + protected void showPopup(Point2D cp) { setHint(DiagramHints.POPUP_MENU_CONTROL_POSITION, cp); setHint(DiagramHints.POPUP_MENU_CANVAS_POSITION, trUtil.controlToCanvas(cp, null)); - display.asyncExec(new Runnable() { - @Override - public void run() { - if (control == null || control.isDisposed()) - return; - Point p = control.toDisplay((int) cp.getX(), (int) cp.getY()); - menuManager.getMenu().setLocation(p); - menuManager.getMenu().setVisible(true); - } + display.asyncExec(() -> { + if (control == null || control.isDisposed()) + return; + Point p = control.toDisplay( SWTDPIUtil.downscaleSwtToInteger(cp) ); + menuManager.getMenu().setLocation(p); + menuManager.getMenu().setVisible(true); }); } diff --git a/bundles/org.simantics.g2d/plugin.xml b/bundles/org.simantics.g2d/plugin.xml index 10cb3b73f..85dfc192c 100644 --- a/bundles/org.simantics.g2d/plugin.xml +++ b/bundles/org.simantics.g2d/plugin.xml @@ -34,7 +34,7 @@ categoryId="org.simantics.g2d" id="org.simantics.gallery.itemfont" label="Symbol Font" - value="Tahoma-regular-8"> + value="Tahoma-regular-12"> The symbol font is used for labels of symbols in the symbol library view. diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/DiagramHints.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/DiagramHints.java index aded9d170..0c11d348a 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/DiagramHints.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/DiagramHints.java @@ -155,7 +155,7 @@ public class DiagramHints { /** * A Hint for other participants to use for showing the context menu at the - * contol position specified by the Point2D argument. + * control position specified by the Point2D argument. */ public static final Key SHOW_POPUP_MENU = new KeyOf(Point2D.class, "SHOW_POPUP_MENU_CMD"); diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/gallery/GalleryViewer.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/gallery/GalleryViewer.java index 2e8ad5b66..ce5687e84 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/gallery/GalleryViewer.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/gallery/GalleryViewer.java @@ -105,6 +105,7 @@ import org.simantics.utils.threads.logger.ITask; import org.simantics.utils.threads.logger.ThreadLogger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.simantics.utils.ui.SWTDPIUtil; /** * @author Toni Kalajainen @@ -165,22 +166,24 @@ public class GalleryViewer extends ContentViewer { chassis = new SWTChassis(composite, style) { @Override public Point computeSize(int wHint, int hHint, boolean changed) { -// System.out.println("chassis compute size: " + wHint + ", " + hHint + ", " + changed); - if (diagram == null) return super.computeSize(wHint, hHint, changed); + // Note: This code must take into account that FlowLayout expects to + // receive pixel coordinates, not SWT API coordinates. + Rectangle2D rect; // if (!changed) { // rect = ElementUtils.getSurroundingElementBoundsOnDiagram(diagram.getSnapshot()); // } // else { - Double wH = wHint==SWT.DEFAULT ? null : (double) wHint-vMargin-vMargin; - Double hH = hHint==SWT.DEFAULT ? null : (double) hHint-hMargin-hMargin; + Double wH = wHint==SWT.DEFAULT ? null : (double) SWTDPIUtil.upscaleSwt(wHint)-hMargin*2; + Double hH = hHint==SWT.DEFAULT ? null : (double) SWTDPIUtil.upscaleSwt(hHint)-vMargin*2; rect = fl.computeSize(diagram, wH, hH); + SWTDPIUtil.downscaleSwt(rect, rect); } - return new Point((int)rect.getMaxX()+hMargin*2, (int)rect.getMaxY()+vMargin*2); + return new Point((int)rect.getWidth()+hMargin*2, (int)rect.getHeight()+vMargin*2); } }; @@ -210,12 +213,7 @@ public class GalleryViewer extends ContentViewer { hintCtx.setHint(DiagramHints.KEY_DIAGRAM, diagram); // Force layout - ThreadUtils.asyncExec(swtThread, new Runnable() { - @Override - public void run() { - resized(false); - } - }); + ThreadUtils.asyncExec(swtThread, () -> resized(false)); }); chassis.addControlListener(new ControlListener() { @@ -255,12 +253,9 @@ public class GalleryViewer extends ContentViewer { fontRegistry.removeListener(fontRegistryListener); // Prevent memory leaks. - ThreadUtils.asyncExec(ctx.getThreadAccess(), new Runnable() { - @Override - public void run() { - chassis.getAWTComponent().setCanvasContext(null); - ctx.dispose(); - } + ThreadUtils.asyncExec(ctx.getThreadAccess(), () -> { + chassis.getAWTComponent().setCanvasContext(null); + ctx.dispose(); }); } }); @@ -274,12 +269,7 @@ public class GalleryViewer extends ContentViewer { currentItemFont = FontHelper.toAwt(fdn); itemClass.getSingleItem(GalleryItemSGNode.class).setFont(currentItemFont); // FIXME: a bug exists in this case. The group size will not be refreshed even though the sizes of the gallery items are recalculated and changed. - ThreadUtils.asyncExec(swtThread, new Runnable() { - @Override - public void run() { - resized(true); - } - }); + ThreadUtils.asyncExec(swtThread, () -> resized(true)); } }; @@ -324,26 +314,25 @@ public class GalleryViewer extends ContentViewer { //System.out.println(this + ".resized(" + refreshElementSizes + ")"); if (chassis.isDisposed()) return; - org.eclipse.swt.graphics.Rectangle b = chassis.getBounds(); + org.eclipse.swt.graphics.Rectangle b = SWTDPIUtil.upscaleSwt(chassis.getBounds()); + //System.out.println("chassis bounds: " + b); final Rectangle2D bounds = new Rectangle2D.Double(hMargin, vMargin, b.width-hMargin*2, b.height-vMargin*2); - ctx.getThreadAccess().asyncExec(new Runnable() { - @Override - public void run() { - if (ctx.isDisposed()) - return; - if (diagram == null) - return; - //System.out.println(this + ".resized(" + refreshElementSizes + ") AWT update"); - if (refreshElementSizes) - refreshElementSizes(); - fl.layout(diagram, bounds); - - // Makes sure RTreeNode is marked dirty and everything is - // properly repainted. - if (itemPainter != null) - itemPainter.updateAll(); - ctx.getContentContext().setDirty(); - }}); + ctx.getThreadAccess().asyncExec(() -> { + if (ctx.isDisposed()) + return; + if (diagram == null) + return; + //System.out.println(this + ".resized(" + refreshElementSizes + ") AWT update"); + if (refreshElementSizes) + refreshElementSizes(); + fl.layout(diagram, bounds); + + // Makes sure RTreeNode is marked dirty and everything is + // properly repainted. + if (itemPainter != null) + itemPainter.updateAll(); + ctx.getContentContext().setDirty(); + }); } /** @@ -392,12 +381,9 @@ public class GalleryViewer extends ContentViewer { // wrong thread (SWT) for AWTChassis. chassis.getAWTComponent().setCanvasContext(canvasContext); - swtThread.asyncExec(new Runnable() { - @Override - public void run() { - if (!chassis.isDisposed()) - chassis.setCanvasContext(canvasContext); - } + swtThread.asyncExec(() -> { + if (!chassis.isDisposed()) + chassis.setCanvasContext(canvasContext); }); canvasContext.assertParticipantDependencies(); @@ -573,12 +559,7 @@ public class GalleryViewer extends ContentViewer { // 3. Calculate maximum vertical space needed by current diagram element texts refreshElementSizes(); - ThreadUtils.asyncExec(swtThread, new Runnable() { - @Override - public void run() { - resized(false); - } - }); + ThreadUtils.asyncExec(swtThread, () -> resized(false)); // $AWT-Thread-End$ } }); @@ -609,9 +590,9 @@ public class GalleryViewer extends ContentViewer { // Calculate maximum vertical space needed by current diagram element texts FontMetrics metrics = awtComponent.getFontMetrics(currentItemFont); int fontHeight = metrics.getHeight(); - int maxWidth = (int) itemSize.getWidth(); - Rectangle2D size = itemSize; - java.awt.Point targetSize = new java.awt.Point((int) itemSize.getWidth(), (int) itemSize.getHeight()); + Rectangle2D size = SWTDPIUtil.upscaleSwt(itemSize); + int maxWidth = (int) size.getWidth(); + java.awt.Point targetSize = new java.awt.Point((int) size.getWidth(), (int) size.getHeight()); diagram.setHint(DiagramHints.KEY_ELEMENT_RASTER_TARGET_SIZE, targetSize); int maxLinesNeeded = 0; for (IElement el : diagram.getElements()) { @@ -619,7 +600,7 @@ public class GalleryViewer extends ContentViewer { // for caching rendered images in the correct size only. // NOTE: currently this is not used in GalleryItemPainter since the // target size is now propagated through the element class loading - // process through the diagram hint KEY_ELEMENT_RASTER_REFERENCE_SIZE. + // process through the diagram hint KEY_ELEMENT_RASTER_TARGET_SIZE. el.setHint(GalleryItemSGNode.KEY_TARGET_IMAGE_SIZE, targetSize); String text = ElementUtils.getText(el); @@ -647,14 +628,11 @@ public class GalleryViewer extends ContentViewer { if (image != i) continue; - ctx.getThreadAccess().asyncExec(new Runnable() { - @Override - public void run() { - //System.out.println(Thread.currentThread() + ": update scene graph(" + el + ")"); - // Update scene graph and repaint. - el.getElementClass().getSingleItem(GalleryItemSGNode.class).update(el); - ctx.getContentContext().setDirty(); - } + ctx.getThreadAccess().asyncExec(() -> { + //System.out.println(Thread.currentThread() + ": update scene graph(" + el + ")"); + // Update scene graph and repaint. + el.getElementClass().getSingleItem(GalleryItemSGNode.class).update(el); + ctx.getContentContext().setDirty(); }); break; } @@ -665,12 +643,9 @@ public class GalleryViewer extends ContentViewer { if (ctx.getThreadAccess().currentThreadAccess()) { ctx.add(p); } else { - ctx.getThreadAccess().asyncExec(new Runnable() { - @Override - public void run() { - if (!ctx.isDisposed()) - ctx.add(p); - } + ctx.getThreadAccess().asyncExec(() -> { + if (!ctx.isDisposed()) + ctx.add(p); }); } } @@ -679,12 +654,9 @@ public class GalleryViewer extends ContentViewer { if (ctx.getThreadAccess().currentThreadAccess()) { ctx.add(p); } else { - ctx.getThreadAccess().asyncExec(new Runnable() { - @Override - public void run() { - if (!ctx.isDisposed()) - ctx.add(p); - } + ctx.getThreadAccess().asyncExec(() -> { + if (!ctx.isDisposed()) + ctx.add(p); }); } } diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/utils/FontHelper.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/utils/FontHelper.java index 06bd80aa8..98d5c78f6 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/utils/FontHelper.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/utils/FontHelper.java @@ -12,11 +12,11 @@ package org.simantics.g2d.utils; import java.awt.Font; -import java.awt.Toolkit; import org.eclipse.jface.resource.FontRegistry; import org.eclipse.swt.graphics.FontData; import org.eclipse.ui.PlatformUI; +import org.simantics.utils.ui.SWTDPIUtil; public final class FontHelper { @@ -35,8 +35,7 @@ public final class FontHelper { } public static java.awt.Font toAwt(FontData fd) { - int resolution = Toolkit.getDefaultToolkit().getScreenResolution(); - int awtFontSize = (int) Math.round((double) fd.getHeight() * resolution / 72.0); + int awtFontSize = SWTDPIUtil.upscaleSwt(fd.getHeight()); // The style constants for SWT and AWT map exactly, and since they are int constants, they should // never change. So, the SWT style is passed through as the AWT style. Font font = new java.awt.Font(fd.getName(), fd.getStyle(), awtFontSize); diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/SWTDPIUtil.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/SWTDPIUtil.java new file mode 100644 index 000000000..962276432 --- /dev/null +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/SWTDPIUtil.java @@ -0,0 +1,232 @@ +package org.simantics.utils.ui; + +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.internal.DPIUtil; + +/** + * This class is needed to support {@link SWTAWTComponent} and HiDPI screens + * with different display zoom settings. + * + *

+ * See {@link DPIUtil} for explanations on what downscaling and upscaling are in + * this context. If user has zoom > 100% in use in the system display settings, + * SWT's API coordinates will be "downscaled" from actual internal HiDPI + * coordinates (pixels). + * + *

+ * This class contains methods to work around problems with e.g. opening context + * menu's in the correct location when using {@link SWTAWTComponent}. AWT always + * returns HiDPI pixel coordinates which need to be converted to SWT API + * coordinates before giving them to any SWT APIs. + * + * @author Tuukka Lehtonen + * @since 1.36.0 + */ +@SuppressWarnings("restriction") +public class SWTDPIUtil { + + private static boolean initialized = false; + private static int swtZoom; + private static boolean hasSwtScale; + + private static float fromSwtInternalScalingFactorF; + private static double fromSwtInternalScalingFactorD; + private static float toSwtInternalScalingFactorF; + private static double toSwtInternalScalingFactorD; + + private static void initialize() { + if (initialized) + return; + + swtZoom = DPIUtil.autoScaleUp(100); + hasSwtScale = swtZoom != 100; + + fromSwtInternalScalingFactorD = 100.0 / (double) swtZoom; + toSwtInternalScalingFactorD = (double) swtZoom / 100.0; + fromSwtInternalScalingFactorF = (float) fromSwtInternalScalingFactorD; + toSwtInternalScalingFactorF = (float) toSwtInternalScalingFactorD; + +// System.out.format("SWTDPIUtil:%n\tswt zoom = %d%n\tfrom swt internal scaling factor = %f%n\tto swt internal scaling factor = %f%n", +// swtZoom, +// fromSwtInternalScalingFactorD, +// toSwtInternalScalingFactorD, +// ); + + initialized = true; + } + + // Internals + + private static Rectangle scale(float s, Rectangle r, Rectangle target) { + if (s == 1.0f) { + if (r == target) + return r; + if (target == null) { + return new Rectangle(r.x, r.y, r.width, r.height); + } else { + target.x = r.x; + target.y = r.y; + target.width = r.width; + target.height = r.height; + return target; + } + } + if (target == null) { + return new Rectangle( + Math.round(r.x*s), + Math.round(r.y*s), + Math.round(r.width*s), + Math.round(r.height*s)); + } else { + target.x = Math.round(r.x*s); + target.y = Math.round(r.y*s); + target.width = Math.round(r.width*s); + target.height = Math.round(r.height*s); + return target; + } + } + + private static Rectangle2D scale(double s, Rectangle2D r, Rectangle2D target) { + if (s == 1.0) { + if (r == target) + return r; + if (target == null) + return (Rectangle2D) r.clone(); + target.setFrame(r); + return target; + } + if (target == null) + target = (Rectangle2D) r.clone(); + target.setFrame(r.getX()*s, r.getY()*s, r.getWidth()*s, r.getHeight()*s); + return target; + } + + // SWT API Coordinates <-> pixels + + public static double downscaleSwt(double x) { + initialize(); + return hasSwtScale ? x * fromSwtInternalScalingFactorD : x; + } + + public static int downscaleSwt(int x) { + if (!hasSwtScale) + return x; + return (int) Math.round(downscaleSwt((double) x)); + } + + public static Point2D downscaleSwt(double x, double y) { + initialize(); + if (!hasSwtScale) + return new Point2D.Double(x, y); + double s = fromSwtInternalScalingFactorD; + return new Point2D.Double(x * s, y * s); + } + + public static Point downscaleSwt(int x, int y) { + return new Point(downscaleSwt(x), downscaleSwt(y)); + } + + public static Point2D downscaleSwt(Point2D p) { + return downscaleSwt(p.getX(), p.getY()); + } + + public static Point downscaleSwtToInteger(Point2D p) { + return new Point( + (int) Math.round(downscaleSwt(p.getX())), + (int) Math.round(downscaleSwt(p.getY()))); + } + + public static Rectangle2D downscaleSwt(Rectangle2D r, Rectangle2D target) { + initialize(); + return scale(fromSwtInternalScalingFactorD, r, target); + } + + public static Rectangle2D downscaleSwt(Rectangle2D r) { + return downscaleSwt(r, null); + } + + public static Rectangle downscaleSwt(Rectangle r, Rectangle target) { + initialize(); + return scale(fromSwtInternalScalingFactorF, r, target); + } + + public static Rectangle downscaleSwt(Rectangle r) { + return downscaleSwt(r, null); + } + + public static Rectangle downscaleSwtToInteger(Rectangle2D r) { + return new Rectangle( + (int) Math.round(downscaleSwt(r.getMinX())), + (int) Math.round(downscaleSwt(r.getMinY())), + (int) Math.round(downscaleSwt(r.getWidth())), + (int) Math.round(downscaleSwt(r.getHeight()))); + } + + public static double upscaleSwt(double x) { + initialize(); + return hasSwtScale ? x * toSwtInternalScalingFactorD : x; + } + + public static int upscaleSwt(int x) { + initialize(); + if (!hasSwtScale) + return x; + return (int) Math.round((double) x * toSwtInternalScalingFactorD); + } + + public static Point2D upscaleSwt(double x, double y) { + if (!hasSwtScale) + return new Point2D.Double(x, y); + double s = toSwtInternalScalingFactorD; + return new Point2D.Double(x * s, y * s); + } + + public static Point upscaleSwt(int x, int y) { + return new Point(upscaleSwt(x), upscaleSwt(y)); + } + + public static Point2D upscaleSwt(Point2D p) { + return upscaleSwt(p.getX(), p.getY()); + } + + public static Point upscaleSwtToInteger(Point2D p) { + return new Point( + (int) Math.round(upscaleSwt(p.getX())), + (int) Math.round(upscaleSwt(p.getY()))); + } + + public static Point upscaleSwt(Point p) { + return upscaleSwt(p.x, p.y); + } + + public static Rectangle2D upscaleSwt(Rectangle2D r, Rectangle2D target) { + initialize(); + return scale(toSwtInternalScalingFactorD, r, target); + } + + public static Rectangle upscaleSwt(Rectangle r, Rectangle target) { + initialize(); + return scale(toSwtInternalScalingFactorF, r, target); + } + + public static Rectangle2D upscaleSwt(Rectangle2D r) { + return upscaleSwt(r, null); + } + + public static Rectangle upscaleSwt(Rectangle r) { + return upscaleSwt(r, null); + } + + public static Rectangle upscaleSwtToInteger(Rectangle2D r) { + return new Rectangle( + (int) Math.round(upscaleSwt(r.getMinX())), + (int) Math.round(upscaleSwt(r.getMinY())), + (int) Math.round(upscaleSwt(r.getWidth())), + (int) Math.round(upscaleSwt(r.getHeight()))); + } + +}