Bug 493462: [HiDPI] Improve autoScale method, default deviceZoom, and system properties

Change-Id: I5c87c4378092770abb8991936cd7ea6a51fcb2af
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java
index 1054282..43da3ca 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java
@@ -724,7 +724,7 @@
 	final ImageData imageData = image.getImageDataAtCurrentZoom();
 	imageData.transparentPixel = imageData.palette.getPixel(transparent);
 	image.dispose();
-	image = new Image(display, new AutoScaleImageDataProvider(imageData, DPIUtil.getDeviceZoom()));
+	image = new Image(display, new AutoScaleImageDataProvider(display, imageData, DPIUtil.getDeviceZoom()));
 	return image;
 }
 void createItem (CTabItem item, int index) {
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TableDragSourceEffect.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TableDragSourceEffect.java
index 5eb5319..5774b4a 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TableDragSourceEffect.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TableDragSourceEffect.java
@@ -144,7 +144,8 @@
 					} else {
 						data.transparentPixel = shdi.crColorKey << 8;
 					}
-					dragSourceImage = new Image(control.getDisplay(), new AutoScaleImageDataProvider(data, DPIUtil.getDeviceZoom()));
+					Display display = control.getDisplay();
+					dragSourceImage = new Image(display, new AutoScaleImageDataProvider(display, data, DPIUtil.getDeviceZoom()));
 					OS.SelectObject (memHdc, oldMemBitmap);
 					OS.DeleteDC (memHdc);
 					OS.DeleteObject (memDib);
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TreeDragSourceEffect.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TreeDragSourceEffect.java
index a0e6e99..ea6f9ef 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TreeDragSourceEffect.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TreeDragSourceEffect.java
@@ -143,7 +143,8 @@
 					} else {
 						data.transparentPixel = shdi.crColorKey << 8;
 					}
-					dragSourceImage = new Image (control.getDisplay (), new AutoScaleImageDataProvider(data, DPIUtil.getDeviceZoom()));
+					Display display = control.getDisplay ();
+					dragSourceImage = new Image (display, new AutoScaleImageDataProvider(display, data, DPIUtil.getDeviceZoom()));
 					OS.SelectObject (memHdc, oldMemBitmap);
 					OS.DeleteDC (memHdc);
 					OS.DeleteObject (memDib);
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java
index b3320fc..6e0df6a 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java
@@ -971,7 +971,7 @@
 			NSBitmapImageRep imageRep = getRepresentation();
 			return _getImageData(imageRep);
 		}
-		return DPIUtil.autoScaleImageData(getImageData(), DPIUtil.getDeviceZoom(), 100);
+		return DPIUtil.autoScaleImageData(device, getImageData(), DPIUtil.getDeviceZoom(), 100);
 	} finally {
 		if (pool != null) pool.release();
 	}
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 60901a7b..90dca7f 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
@@ -30,35 +30,68 @@
  */
 public class DPIUtil {
 
-	/* DPI Constants */
-	static final int DPI_ZOOM_100 = 96;
-	private static final double MIN_ZOOM_INTERVAL = 25;
+	private static final int DPI_ZOOM_100 = 96;
 
-	private static boolean autoScaleEnable = true;
 	private static int deviceZoom = 100;
 
-	/*
-	 * The AutoScale functionality is enabled by default on HighDPI monitors &
-	 * can be disabled by setting below system property to "false"(Ignore case).
+	private static enum AutoScaleMethod { AUTO, NEAREST, SMOOTH }
+	private static AutoScaleMethod autoScaleMethodSetting = AutoScaleMethod.AUTO;
+	private static AutoScaleMethod autoScaleMethod = AutoScaleMethod.NEAREST;
+
+	/**
+	 * System property that controls the autoScale functionality.
+	 * <ul>
+	 * <li><b>false</b>: deviceZoom is set to 100%</li>
+	 * <li><b>integer</b>: deviceZoom depends on the current display resolution,
+	 *     but only uses integer multiples of 100%. The detected native zoom is
+	 *     generally rounded down (e.g. at 150%, will use 100%), unless close to
+	 *     the next integer multiple (currently at 175%, will use 200%).</li>
+	 * <li><b>quarter</b>: deviceZoom depends on the current display resolution,
+	 *     but only uses integer multiples of 25%. The detected native zoom is
+	 *     rounded to the closest permissible value.</li>
+	 * <li><b>exact</b>: deviceZoom uses the native zoom (with 1% as minimal
+	 *     step).</li>
+	 * <li><i>&lt;value&gt;</i>: deviceZoom uses the given integer value in
+	 *     percent as zoom level.</li>
+	 * </ul>
+	 * The current default is "integer".
 	 */
-	static final String SWT_ENABLE_AUTOSCALE = "swt.enable.autoScale";
+	private static final String SWT_AUTOSCALE = "swt.autoScale";
+
+	/**
+	 * System property that controls the method for scaling images:
+	 * <ul>
+	 * <li>"nearest": nearest-neighbor interpolation, may look jagged</li>
+	 * <li>"smooth": smooth edges, may look blurry</li>
+	 * </ul>
+	 * The current default is to use "nearest", except on
+	 * GTK when the deviceZoom is not an integer multiple of 100%.
+	 * The smooth strategy currently doesn't work on Win32 and Cocoa, see
+	 * <a href="https://bugs.eclipse.org/493455">bug 493455</a>.
+	 */
+	private static final String SWT_AUTOSCALE_METHOD = "swt.autoScale.method";
 	static {
-		String value = System.getProperty (SWT_ENABLE_AUTOSCALE);
-		if (value != null && "false".equalsIgnoreCase (value))
-			autoScaleEnable = false;
+		String value = System.getProperty (SWT_AUTOSCALE_METHOD);
+		if (value != null) {
+			if (AutoScaleMethod.NEAREST.name().equalsIgnoreCase(value)) {
+				autoScaleMethod = autoScaleMethodSetting = AutoScaleMethod.NEAREST;
+			} else if (AutoScaleMethod.SMOOTH.name().equalsIgnoreCase(value)) {
+				autoScaleMethod = autoScaleMethodSetting = AutoScaleMethod.SMOOTH;
+			}
+		}
 	}
 
 /**
  * Auto-scale down ImageData
  */
-public static ImageData autoScaleDown (ImageData imageData) {
-	if (deviceZoom == 100 || !isAutoScaleEnable () || imageData == null) return imageData;
-	float scaleFactor = getScalingFactor ();
-	return imageData.scaledTo (Math.round ((float)imageData.width / scaleFactor), Math.round ((float)imageData.height / scaleFactor));
+public static ImageData autoScaleDown (Device device, final ImageData imageData) {
+	if (deviceZoom == 100 || imageData == null) return imageData;
+	float scaleFactor = 1.0f / getScalingFactor ();
+	return autoScaleImageData(device, imageData, scaleFactor);
 }
 
 public static int[] autoScaleDown(int[] pointArray) {
-	if (deviceZoom == 100 || !isAutoScaleEnable () || pointArray == null) return pointArray;
+	if (deviceZoom == 100 || pointArray == null) return pointArray;
 	float scaleFactor = getScalingFactor ();
 	int [] returnArray = new int[pointArray.length];
 	for (int i = 0; i < pointArray.length; i++) {
@@ -71,7 +104,7 @@
  * Auto-scale up float array dimensions.
  */
 public static float[] autoScaleDown (float size[]) {
-	if (deviceZoom == 100 || !isAutoScaleEnable () || size == null) return size;
+	if (deviceZoom == 100 || size == null) return size;
 	float scaleFactor = getScalingFactor ();
 	float scaledSize[] = new float[size.length];
 	for (int i = 0; i < scaledSize.length; i++) {
@@ -83,7 +116,7 @@
  * Auto-scale down int dimensions.
  */
 public static int autoScaleDown (int size) {
-	if (deviceZoom == 100 || !isAutoScaleEnable ()||size == SWT.DEFAULT) return size;
+	if (deviceZoom == 100 || size == SWT.DEFAULT) return size;
 	float scaleFactor = getScalingFactor ();
 	return Math.round (size / scaleFactor);
 }
@@ -91,7 +124,7 @@
  * Auto-scale down float dimensions.
  */
 public static float autoScaleDown (float size) {
-	if (deviceZoom == 100 || !isAutoScaleEnable ()||size == SWT.DEFAULT) return size;
+	if (deviceZoom == 100 || size == SWT.DEFAULT) return size;
 	float scaleFactor = getScalingFactor ();
 	return (size / scaleFactor);
 }
@@ -100,7 +133,7 @@
  * Returns a new scaled down Point.
  */
 public static Point autoScaleDown (Point point) {
-	if (deviceZoom == 100 || !isAutoScaleEnable () || point == null) return point;
+	if (deviceZoom == 100 || point == null) return point;
 	float scaleFactor = getScalingFactor ();
 	Point scaledPoint = new Point (0,0);
 	scaledPoint.x = Math.round (point.x / scaleFactor);
@@ -112,7 +145,7 @@
  * Returns a new scaled down Rectangle.
  */
 public static Rectangle autoScaleDown (Rectangle rect) {
-	if (deviceZoom == 100 || !isAutoScaleEnable () || rect == null) return rect;
+	if (deviceZoom == 100 || rect == null) return rect;
 	Rectangle scaledRect = new Rectangle (0,0,0,0);
 	Point scaledTopLeft = DPIUtil.autoScaleDown (new Point (rect.x, rect.y));
 	Point scaledBottomRight = DPIUtil.autoScaleDown (new Point (rect.x + rect.width, rect.y + rect.height));
@@ -127,17 +160,60 @@
 /**
  * Auto-scale image with ImageData
  */
-public static ImageData autoScaleImageData (ImageData imageData, int targetZoom, int currentZoom) {
-	if (!isAutoScaleEnable () || imageData == null || targetZoom == currentZoom) return imageData;
-	float scaleFactor = ((float) targetZoom)/((float) currentZoom);
-	return imageData.scaledTo (Math.round ((float)imageData.width * scaleFactor), Math.round ((float)imageData.height * scaleFactor));
+public static ImageData autoScaleImageData (Device device, final ImageData imageData, int targetZoom, int currentZoom) {
+	if (deviceZoom == 100 || imageData == null || targetZoom == currentZoom) return imageData;
+	float scaleFactor = (float) targetZoom / (float) currentZoom;
+	return autoScaleImageData(device, imageData, scaleFactor);
+}
+
+private static ImageData autoScaleImageData (Device device, final ImageData imageData, float scaleFactor) {
+	// Guards are already implemented in callers: if (deviceZoom == 100 || imageData == null || scaleFactor == 1.0f) return imageData;
+	int width = imageData.width;
+	int height = imageData.height;
+	int scaledWidth = Math.round ((float) width * scaleFactor);
+	int scaledHeight = Math.round ((float) height * scaleFactor);
+	switch (autoScaleMethod) {
+	case SMOOTH:
+		Image original = new Image (device, new ImageDataProvider () {
+			@Override
+			public ImageData getImageData (int zoom) {
+				return imageData;
+			}
+		});
+
+		/* Create a 24 bit image data with alpha channel */
+		final ImageData resultData = new ImageData (scaledWidth, scaledHeight, 24, new PaletteData (0xFF, 0xFF00, 0xFF0000));
+		resultData.alphaData = new byte [scaledWidth * scaledHeight];
+
+		Image resultImage = new Image (device, new ImageDataProvider () {
+			@Override
+			public ImageData getImageData (int zoom) {
+				return resultData;
+			}
+		});
+		GC gc = new GC (resultImage);
+		gc.setAntialias (SWT.ON);
+		gc.drawImage (original, 0, 0, DPIUtil.autoScaleDown (width), DPIUtil.autoScaleDown (height),
+				/* E.g. destWidth here is effectively DPIUtil.autoScaleDown (scaledWidth), but avoiding rounding errors.
+				 * Nevertheless, we still have some rounding errors due to the point-based API GC#drawImage(..).
+				 */
+				0, 0, Math.round (DPIUtil.autoScaleDown ((float) width * scaleFactor)), Math.round (DPIUtil.autoScaleDown ((float) height * scaleFactor)));
+		gc.dispose ();
+		original.dispose ();
+		ImageData result = resultImage.getImageDataAtCurrentZoom ();
+		resultImage.dispose ();
+		return result;
+	case NEAREST:
+	default:
+		return imageData.scaledTo (scaledWidth, scaledHeight);
+	}
 }
 
 /**
  * Returns a new rectangle as per the scaleFactor.
  */
 public static Rectangle autoScaleBounds (Rectangle rect, int targetZoom, int currentZoom) {
-	if (!isAutoScaleEnable () || rect == null || targetZoom == currentZoom) return rect;
+	if (deviceZoom == 100 || rect == null || targetZoom == currentZoom) return rect;
 	float scaleFactor = ((float)targetZoom) / (float)currentZoom;
 	Rectangle returnRect = new Rectangle (0,0,0,0);
 	returnRect.x = Math.round (rect.x * scaleFactor);
@@ -150,14 +226,14 @@
 /**
  * Auto-scale up ImageData
  */
-public static ImageData autoScaleUp (ImageData imageData) {
-	if (deviceZoom == 100 || !isAutoScaleEnable () || imageData == null) return imageData;
+public static ImageData autoScaleUp (Device device, final ImageData imageData) {
+	if (deviceZoom == 100 || imageData == null) return imageData;
 	float scaleFactor = getScalingFactor ();
-	return imageData.scaledTo (Math.round ((float)imageData.width * scaleFactor), Math.round ((float)imageData.height * scaleFactor));
+	return autoScaleImageData(device, imageData, scaleFactor);
 }
 
 public static int[] autoScaleUp(int[] pointArray) {
-	if (deviceZoom == 100 || !isAutoScaleEnable () || pointArray == null) return pointArray;
+	if (deviceZoom == 100 || pointArray == null) return pointArray;
 	float scaleFactor = getScalingFactor ();
 	int [] returnArray = new int[pointArray.length];
 	for (int i = 0; i < pointArray.length; i++) {
@@ -170,13 +246,13 @@
  * Auto-scale up int dimensions.
  */
 public static int autoScaleUp (int size) {
-	if (deviceZoom == 100 || !isAutoScaleEnable ()||size == SWT.DEFAULT) return size;
+	if (deviceZoom == 100 || size == SWT.DEFAULT) return size;
 	float scaleFactor = getScalingFactor ();
 	return Math.round (size * scaleFactor);
 }
 
 public static float autoScaleUp(float size) {
-	if (deviceZoom == 100 || !isAutoScaleEnable ()||size == SWT.DEFAULT) return size;
+	if (deviceZoom == 100 || size == SWT.DEFAULT) return size;
 	float scaleFactor = getScalingFactor ();
 	return (size * scaleFactor);
 }
@@ -185,7 +261,7 @@
  * Returns a new scaled up Point.
  */
 public static Point autoScaleUp (Point point) {
-	if (deviceZoom == 100 || !isAutoScaleEnable () || point == null) return point;
+	if (deviceZoom == 100 || point == null) return point;
 	float scaleFactor = getScalingFactor ();
 	Point scaledPoint = new Point (0,0);
 	scaledPoint.x = Math.round (point.x * scaleFactor);
@@ -197,7 +273,7 @@
  * Returns a new scaled up Rectangle.
  */
 public static Rectangle autoScaleUp (Rectangle rect) {
-	if (deviceZoom == 100 || !isAutoScaleEnable () || rect == null) return rect;
+	if (deviceZoom == 100 || rect == null) return rect;
 	Rectangle scaledRect = new Rectangle (0,0,0,0);
 	Point scaledTopLeft = DPIUtil.autoScaleUp (new Point (rect.x, rect.y));
 	Point scaledBottomRight = DPIUtil.autoScaleUp (new Point (rect.x + rect.width, rect.y + rect.height));
@@ -208,39 +284,24 @@
 	scaledRect.height = scaledBottomRight.y - scaledTopLeft.y;
 	return scaledRect;
 }
-public static boolean isAutoScaleEnable () {
-	return autoScaleEnable;
-}
 
 /**
  * Returns Scaling factor from the display
  * @return float scaling factor
  */
 private static float getScalingFactor () {
-	float scalingFactor = 1;
-	if (isAutoScaleEnable ()) {
-		scalingFactor = getDeviceZoom ()/100f;
-	}
-	return scalingFactor;
+	return deviceZoom / 100f;
 }
 
 /**
- * Compute the zoom value based on the scaleFactor value.
- *
- * @return zoom
- */
-public static int mapSFToZoom (float scaleFactor) {
-	return mapDPIToZoom ((int) (scaleFactor * DPI_ZOOM_100));
-}
-/**
  * Compute the zoom value based on the DPI value.
  *
  * @return zoom
  */
 public static int mapDPIToZoom (int dpi) {
-	double zoom = (double)dpi * 100 / DPI_ZOOM_100; //convert to percentage
-	int roundedZoom = (int) (Math.round (zoom / MIN_ZOOM_INTERVAL) * MIN_ZOOM_INTERVAL); //rounding to MIN_ZOOM_INTERVAL steps
-	return Math.max(100, roundedZoom); //We are setting the minimum zoom value as 100%. below that it causing too many problems
+	double zoom = (double) dpi * 100 / DPI_ZOOM_100;
+	int roundedZoom = (int) Math.round (zoom);
+	return roundedZoom;
 }
 /**
  * Gets Image data at specified zoom level, if image is missing then
@@ -272,34 +333,59 @@
 	return filename;
 }
 
-/**
- * @return the deviceZoom
- */
 public static int getDeviceZoom() {
-	return isAutoScaleEnable () ? deviceZoom : 100;
+	return deviceZoom;
 }
 
-/**
- * @param deviceZoom the deviceZoom to set
- */
-public static void setDeviceZoom(int deviceZoom) {
+public static void setDeviceZoom (int nativeDeviceZoom) {
+	int deviceZoom = 0;
+	String value = System.getProperty (SWT_AUTOSCALE);
+ 	if (value != null) {
+		if ("false".equalsIgnoreCase (value)) {
+			deviceZoom = 100;
+		} else if ("quarter".equalsIgnoreCase (value)) {
+			deviceZoom = (int) (Math.round (nativeDeviceZoom / 25f) * 25);
+		} else if ("exact".equalsIgnoreCase (value)) {
+			deviceZoom = nativeDeviceZoom;
+		} else {
+			try {
+				int zoom = Integer.parseInt (value);
+				deviceZoom = Math.max (Math.min (zoom, 1600), 25);
+			} catch (NumberFormatException e) {
+				// unsupported value, use default
+			}
+		}
+	}
+ 	if (deviceZoom == 0) { // || "integer".equalsIgnoreCase (value)
+		deviceZoom = Math.max ((nativeDeviceZoom + 25) / 100 * 100, 100);
+	}
+
 	DPIUtil.deviceZoom = deviceZoom;
 	System.setProperty("org.eclipse.swt.internal.deviceZoom", Integer.toString(deviceZoom));
+	if (deviceZoom != 100 && autoScaleMethodSetting == AutoScaleMethod.AUTO) {
+		if (deviceZoom / 100 * 100 == deviceZoom || !"gtk".equals(SWT.getPlatform())) {
+			autoScaleMethod = AutoScaleMethod.NEAREST;
+		} else {
+			autoScaleMethod = AutoScaleMethod.SMOOTH;
+		}
+	}
 }
 
 /**
  * AutoScale ImageDataProvider.
  */
 public static final class AutoScaleImageDataProvider implements ImageDataProvider {
+	Device device;
 	ImageData imageData;
 	int currentZoom;
-	public AutoScaleImageDataProvider(ImageData data, int zoom){
+	public AutoScaleImageDataProvider(Device device, ImageData data, int zoom){
+		this.device = device;
 		this.imageData = data;
 		this.currentZoom = zoom;
 	}
 	@Override
 	public ImageData getImageData(int zoom) {
-		return DPIUtil.autoScaleImageData(imageData, zoom, currentZoom);
+		return DPIUtil.autoScaleImageData(device, imageData, zoom, currentZoom);
 	}
 }
 }
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java
index c593caa..25befb4 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java
@@ -580,7 +580,7 @@
 	super(device);
 	if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
 	currentDeviceZoom = DPIUtil.getDeviceZoom();
-	data = DPIUtil.autoScaleUp (data);
+	data = DPIUtil.autoScaleUp (device, data);
 	init(data);
 	init();
 }
@@ -623,8 +623,8 @@
 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
 	}
 	currentDeviceZoom = DPIUtil.getDeviceZoom();
-	source = DPIUtil.autoScaleUp (source);
-	mask = DPIUtil.autoScaleUp (mask);
+	source = DPIUtil.autoScaleUp (device, source);
+	mask = DPIUtil.autoScaleUp (device, mask);
 	mask = ImageData.convertMask (mask);
 	ImageData image = new ImageData(source.width, source.height, source.depth, source.palette, source.scanlinePad, source.data);
 	image.maskPad = mask.scanlinePad;
@@ -690,7 +690,7 @@
 	super(device);
 	ImageData data = new ImageData(stream);
 	currentDeviceZoom = DPIUtil.getDeviceZoom();
-	data = DPIUtil.autoScaleUp (data);
+	data = DPIUtil.autoScaleUp (device, data);
 	init(data);
 	init();
 }
@@ -733,7 +733,7 @@
 
 	ImageData data = new ImageData(filename);
 	currentDeviceZoom = DPIUtil.getDeviceZoom();
-	data = DPIUtil.autoScaleUp (data);
+	data = DPIUtil.autoScaleUp (device, data);
 	init(data);
 	init();
 }
@@ -781,7 +781,7 @@
 		}
 	} else {
 		ImageData imageData = new ImageData (filename);
-		ImageData resizedData = DPIUtil.autoScaleUp (imageData);
+		ImageData resizedData = DPIUtil.autoScaleUp (device, imageData);
 		init(resizedData);
 	}
 	init ();
@@ -825,7 +825,7 @@
 	if (found[0]) {
 		init (data);
 	} else {
-		ImageData resizedData = DPIUtil.autoScaleUp (data);
+		ImageData resizedData = DPIUtil.autoScaleUp (device, data);
 		init (resizedData);
 	}
 	init ();
@@ -860,7 +860,7 @@
 				/* Release current native resources */
 				destroy ();
 				ImageData imageData = new ImageData (filename);
-				ImageData resizedData = DPIUtil.autoScaleUp (imageData);
+				ImageData resizedData = DPIUtil.autoScaleUp (device, imageData);
 				init(resizedData);
 				init ();
 				refreshed = true;
@@ -883,7 +883,7 @@
 			if (!found[0]) {
 				/* Release current native resources */
 				destroy ();
-				ImageData resizedData = DPIUtil.autoScaleImageData(data, deviceZoomLevel, 100);
+				ImageData resizedData = DPIUtil.autoScaleImageData(device, data, deviceZoomLevel, 100);
 				init(resizedData);
 				init();
 				refreshed = true;
@@ -895,7 +895,7 @@
 		if (deviceZoomLevel != currentDeviceZoom) {
 			ImageData data = getImageDataAtCurrentZoom();
 			destroy ();
-			ImageData resizedData = DPIUtil.autoScaleImageData(data, deviceZoomLevel, currentDeviceZoom);
+			ImageData resizedData = DPIUtil.autoScaleImageData(device, data, deviceZoomLevel, currentDeviceZoom);
 			init(resizedData);
 			init();
 			refreshed = true;
@@ -1520,7 +1520,7 @@
  */
 ImageData getImageData (int zoom) {
 	if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
-	return DPIUtil.autoScaleImageData (getImageDataAtCurrentZoom (), zoom, currentDeviceZoom);
+	return DPIUtil.autoScaleImageData (device, getImageDataAtCurrentZoom (), zoom, currentDeviceZoom);
 }
 
 /**
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java
index ad91150..2c3f18a 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java
@@ -927,8 +927,8 @@
  * </ul>
  */
 public void drawImage (Image image, int x, int y) {
-	x = (x != SWT.DEFAULT ? DPIUtil.autoScaleUp(x) : x);
-	y = (y != SWT.DEFAULT ? DPIUtil.autoScaleUp(y) : y);
+	x = DPIUtil.autoScaleUp(x);
+	y = DPIUtil.autoScaleUp(y);
 	drawImageInPixels(image, x, y);
 }
 
@@ -972,18 +972,6 @@
  * </ul>
  */
 public void drawImage (Image image, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight) {
-	srcX = (srcX != SWT.DEFAULT ? DPIUtil.autoScaleUp(srcX) : srcX);
-	srcY = (srcY != SWT.DEFAULT ? DPIUtil.autoScaleUp(srcY) : srcY);
-	srcWidth = (srcWidth != SWT.DEFAULT ? DPIUtil.autoScaleUp(srcWidth) : srcWidth);
-	srcHeight = (srcHeight != SWT.DEFAULT ? DPIUtil.autoScaleUp(srcHeight) : srcHeight);
-	destX = (destX != SWT.DEFAULT ? DPIUtil.autoScaleUp(destX) : destX);
-	destY = (destY != SWT.DEFAULT ? DPIUtil.autoScaleUp(destY) : destY);
-	destWidth = (destWidth != SWT.DEFAULT ? DPIUtil.autoScaleUp(destWidth) : destWidth);
-	destHeight = (destHeight != SWT.DEFAULT ? DPIUtil.autoScaleUp(destHeight) : destHeight);
-	drawImageInPixels(image, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight);
-}
-
-void drawImageInPixels(Image image, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight) {
 	if (handle == 0) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
 	if (srcWidth == 0 || srcHeight == 0 || destWidth == 0 || destHeight == 0) return;
 	if (srcX < 0 || srcY < 0 || srcWidth < 0 || srcHeight < 0 || destWidth < 0 || destHeight < 0) {
@@ -991,7 +979,28 @@
 	}
 	if (image == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
 	if (image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
-	drawImage(image, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, false);
+
+	Rectangle src = DPIUtil.autoScaleUp(new Rectangle(srcX, srcY, srcWidth, srcHeight));
+	Rectangle dest = DPIUtil.autoScaleUp(new Rectangle(destX, destY, destWidth, destHeight));
+	int deviceZoom = DPIUtil.getDeviceZoom();
+	if (deviceZoom != 100) {
+		/*
+		 * This is a HACK! Due to rounding errors at fractional scale factors,
+		 * the coordinates may be slightly off. The workaround is to restrict
+		 * coordinates to the allowed bounds.
+		 */
+		Rectangle b = image.getBoundsInPixels();
+		int errX = src.x + src.width - b.width;
+		int errY = src.y + src.height - b.height;
+		if (errX != 0 || errY != 0) {
+			if (errX <= deviceZoom / 100 && errY <= deviceZoom / 100) {
+				src.intersect(b);
+			} else {
+				SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+			}
+		}
+	}
+	drawImage(image, src.x, src.y, src.width, src.height, dest.x, dest.y, dest.width, dest.height, false);
 }
 
 void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple) {
@@ -1213,18 +1222,7 @@
 		srcHeight = destHeight = imgHeight;
 	} else {
 		if (srcX + srcWidth > imgWidth || srcY + srcHeight > imgHeight) {
-			/*
-			 * This is a HACK ! Due to auto-scale of dimensions at high DPI
-			 * display(specifically 150% zoom), rounding error of 1 pixel
-			 * gets introduced in srcX . Below check detects this particular
-			 * scenario and adjusts the dimension for 1 pixel grace value.
-			 */
-			if (DPIUtil.isAutoScaleEnable() && DPIUtil.getDeviceZoom() > 100 && ((srcX + srcWidth - imgWidth) == 1)) {
-				srcX--;
-			}
-			else {
-				SWT.error(SWT.ERROR_INVALID_ARGUMENT);
-			}
+			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
 		}
 		simple = srcX == 0 && srcY == 0 &&
 			srcWidth == destWidth && destWidth == imgWidth &&
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
index 1fa7ee9..f6b6817 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
@@ -495,7 +495,7 @@
 	super(device);
 	if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
 	currentDeviceZoom = DPIUtil.getDeviceZoom ();
-	data = DPIUtil.autoScaleUp (data);
+	data = DPIUtil.autoScaleUp (device, data);
 	init(data);
 	init();
 }
@@ -538,8 +538,8 @@
 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
 	}
 	currentDeviceZoom = DPIUtil.getDeviceZoom ();
-	source = DPIUtil.autoScaleUp(source);
-	mask = DPIUtil.autoScaleUp(mask);
+	source = DPIUtil.autoScaleUp(device, source);
+	mask = DPIUtil.autoScaleUp(device, mask);
 	mask = ImageData.convertMask(mask);
 	init(this.device, this, source, mask);
 	init();
@@ -601,7 +601,7 @@
 public Image (Device device, InputStream stream) {
 	super(device);
 	currentDeviceZoom = DPIUtil.getDeviceZoom ();
-	ImageData data = DPIUtil.autoScaleUp(new ImageData(stream));
+	ImageData data = DPIUtil.autoScaleUp(device, new ImageData(stream));
 	init(data);
 	init();
 }
@@ -642,7 +642,7 @@
 	super(device);
 	if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
 	currentDeviceZoom = DPIUtil.getDeviceZoom ();
-	ImageData data = DPIUtil.autoScaleUp(new ImageData(filename));
+	ImageData data = DPIUtil.autoScaleUp(device, new ImageData(filename));
 	init(data);
 	init();
 }
@@ -686,7 +686,7 @@
 		initNative (fileName);
 		if (this.handle == 0) init(new ImageData (fileName));
 	} else {
-		ImageData resizedData = DPIUtil.autoScaleUp (new ImageData (fileName));
+		ImageData resizedData = DPIUtil.autoScaleUp (device, new ImageData (fileName));
 		init(resizedData);
 	}
 	init();
@@ -730,7 +730,7 @@
 	if (found[0]) {
 		init(data);
 	} else {
-		ImageData resizedData = DPIUtil.autoScaleUp(data);
+		ImageData resizedData = DPIUtil.autoScaleUp(device, data);
 		init (resizedData);
 	}
 	init();
@@ -760,7 +760,7 @@
 			if (!found[0]) {
 				/* Release current native resources */
 				destroy ();
-				ImageData resizedData = DPIUtil.autoScaleUp (new ImageData (filename));
+				ImageData resizedData = DPIUtil.autoScaleUp (device, new ImageData (filename));
 				init(resizedData);
 				init ();
 				refreshed = true;
@@ -782,7 +782,7 @@
 			if (!found[0]) {
 				/* Release current native resources */
 				destroy ();
-				ImageData resizedData = DPIUtil.autoScaleUp (data);
+				ImageData resizedData = DPIUtil.autoScaleUp (device, data);
 				init(resizedData);
 				init();
 				refreshed = true;
@@ -793,7 +793,7 @@
 		if (deviceZoomLevel != currentDeviceZoom) {
 			ImageData data = getImageDataAtCurrentZoom();
 			destroy ();
-			ImageData resizedData = DPIUtil.autoScaleImageData(data, deviceZoomLevel, currentDeviceZoom);
+			ImageData resizedData = DPIUtil.autoScaleImageData(device, data, deviceZoomLevel, currentDeviceZoom);
 			init(resizedData);
 			init();
 			refreshed = true;
@@ -1470,7 +1470,7 @@
 }
 
 ImageData getImageData (int zoom) {
-	return DPIUtil.autoScaleImageData(this.getImageDataAtCurrentZoom(), zoom, currentDeviceZoom);
+	return DPIUtil.autoScaleImageData(device, this.getImageDataAtCurrentZoom(), zoom, currentDeviceZoom);
 }
 
 /**
diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Display.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Display.java
index cdf4c0c..a8d1dcd 100644
--- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Display.java
+++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Display.java
@@ -48,7 +48,6 @@
 @Test
 public void test_Constructor() {
 	Display disp = new Display();
-	System.out.println("org.eclipse.swt.internal.DPIUtil.isAutoScaleEnable(): " + DPIUtil.isAutoScaleEnable());
 	System.out.println("org.eclipse.swt.internal.DPIUtil.getDeviceZoom(): " + DPIUtil.getDeviceZoom());
 	disp.dispose();
 	if (SwtTestUtil.isGTK) {