Repository: eclipse-platform/eclipse.platform.swt
Pull request #1913: Smooth Scaling Rounding error fix for win32 #62
Author: login: amartya4256
Created: 2025-03-18T13:31:52Z | Merged: 2025-03-21T10:42:59Z

This PR contributes to fixing the implementation of Smooth scaling of the ImageData to get rid of the rounding errors because of multiple scale ups and downs with fractional scale factor. The commit replicates and modifies the DPIUtil::autoScaleImageData method implementation in Image class to adapt the same.

contributes to
https://github.com/eclipse-platform/eclipse.platform.swt/issues/62 and https://github.com/eclipse-platform/eclipse.platform.swt/issues/127


Diff:
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 40dd39211c6..3cc772fb956 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	
@@ -292,7 +292,7 @@ private static ImageData autoScaleImageData (Device device, final ImageData imag
 	int height = imageData.height;
 	int scaledWidth = Math.round (width * scaleFactor);
 	int scaledHeight = Math.round (height * scaleFactor);
-	boolean useSmoothScaling = autoScaleMethod == AutoScaleMethod.SMOOTH && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
+	boolean useSmoothScaling = isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
 	if (useSmoothScaling) {
 		Image original = new Image (device, (ImageDataProvider) zoom -> imageData);
 		/* Create a 24 bit image data with alpha channel */
@@ -316,6 +316,10 @@ private static ImageData autoScaleImageData (Device device, final ImageData imag
 	}
 }
 
+public static boolean isSmoothScalingEnabled() {
+	return autoScaleMethod == AutoScaleMethod.SMOOTH;
+}
+
 /**
  * Returns a new rectangle as per the scaleFactor.
  */
@@ -628,6 +632,10 @@ public static boolean useCairoAutoScale() {
 }
 
 public static int getZoomForAutoscaleProperty (int nativeDeviceZoom) {
+	return getZoomForAutoscaleProperty(nativeDeviceZoom, autoScaleValue);
+}
+
+private static int getZoomForAutoscaleProperty (int nativeDeviceZoom, String autoScaleValue) {
 	int zoom = 0;
 	if (autoScaleValue != null) {
 		if ("false".equalsIgnoreCase (autoScaleValue)) {
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 34c4ea9bbe0..e4454867a34 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	
@@ -361,7 +361,7 @@ public Image(Device device, ImageData data) {
 	if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
 	initialNativeZoom = DPIUtil.getNativeDeviceZoom();
 	int deviceZoom = getZoom();
-	data = DPIUtil.scaleImageData(device, new ElementAtZoom<>(data, 100), deviceZoom);
+	data = scaleImageData(data, deviceZoom, 100);
 	init(data, deviceZoom);
 	init();
 	this.device.registerResourceWithZoomSupport(this);
@@ -405,8 +405,8 @@ public Image(Device device, ImageData source, ImageData mask) {
 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
 	}
 	initialNativeZoom = DPIUtil.getNativeDeviceZoom();
-	source = DPIUtil.autoScaleUp(device, source);
-	mask = DPIUtil.autoScaleUp(device, mask);
+	source = scaleImageData(source, getZoom(), 100);
+	mask = scaleImageData(mask, getZoom(), 100);
 	mask = ImageData.convertMask(mask);
 	initIconHandle(this.device, source, mask, getZoom());
 	init();
@@ -470,7 +470,8 @@ public Image (Device device, InputStream stream) {
 	super(device);
 	initialNativeZoom = DPIUtil.getNativeDeviceZoom();
 	int deviceZoom = getZoom();
-	ImageData data = DPIUtil.scaleImageData(device, ImageDataLoader.load(stream, FileFormat.DEFAULT_ZOOM, deviceZoom), deviceZoom);
+	ElementAtZoom<ImageData> imageCandidate = ImageDataLoader.load(stream, FileFormat.DEFAULT_ZOOM, deviceZoom);
+	ImageData data = scaleImageData(imageCandidate.element(), deviceZoom, imageCandidate.zoom());
 	init(data, deviceZoom);
 	init();
 	this.device.registerResourceWithZoomSupport(this);
@@ -513,7 +514,8 @@ public Image (Device device, String filename) {
 	if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
 	initialNativeZoom = DPIUtil.getNativeDeviceZoom();
 	int deviceZoom = getZoom();
-	ImageData data = DPIUtil.scaleImageData(device, ImageDataLoader.load(filename, FileFormat.DEFAULT_ZOOM, deviceZoom), deviceZoom);
+	ElementAtZoom<ImageData> imageCandidate = ImageDataLoader.load(filename, FileFormat.DEFAULT_ZOOM, deviceZoom);
+	ImageData data = scaleImageData(imageCandidate.element(), deviceZoom, imageCandidate.zoom());
 	init(data, deviceZoom);
 	init();
 	this.device.registerResourceWithZoomSupport(this);
@@ -1237,7 +1239,7 @@ private ImageData getScaledImageData (int zoom) {
 	}
 	TreeSet<Integer> availableZooms = new TreeSet<>(zoomLevelToImageHandle.keySet());
 	int closestZoom = Optional.ofNullable(availableZooms.higher(zoom)).orElse(availableZooms.lower(zoom));
-	return DPIUtil.scaleImageData (device, getImageMetadata(closestZoom).getImageData(), zoom, closestZoom);
+	return scaleImageData(getImageMetadata(closestZoom).getImageData(), zoom, closestZoom);
 }
 
 
@@ -1850,6 +1852,40 @@ private void setBackground(Color color, long handle) {
 	device.internal_dispose_GC(hDC, null);
 }
 
+private ImageData scaleImageData(final ImageData imageData, int targetZoom, int currentZoom) {
+	if (imageData == null || targetZoom == currentZoom || (device != null && !device.isAutoScalable())) return imageData;
+	float scaleFactor = (float) targetZoom / (float) currentZoom;
+	int width = imageData.width;
+	int height = imageData.height;
+	int scaledWidth = Math.round (width * scaleFactor);
+	int scaledHeight = Math.round (height * scaleFactor);
+	boolean useSmoothScaling = DPIUtil.isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
+	if (useSmoothScaling) {
+		return scaleToUsingSmoothScaling(scaledWidth, scaledHeight, imageData);
+	}
+	return imageData.scaledTo (scaledWidth, scaledHeight);
+}
+
+private ImageData scaleToUsingSmoothScaling(int width, int height, ImageData imageData) {
+	Image original = new Image (device, (ImageDataProvider) zoom -> imageData);
+	/* Create a 24 bit image data with alpha channel */
+	final ImageData resultData = new ImageData (width, height, 24, new PaletteData (0xFF, 0xFF00, 0xFF0000));
+	resultData.alphaData = new byte [width * height];
+	Image resultImage = new Image (device, (ImageDataProvider) zoom -> resultData);
+	GC gc = new GC (resultImage);
+	gc.setAntialias (SWT.ON);
+	gc.drawImage (original, 0, 0, imageData.width, imageData.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, width, height, false);
+	gc.dispose ();
+	original.dispose ();
+	ImageData result = resultImage.getImageData (resultImage.getZoom());
+	resultImage.dispose ();
+	return result;
+}
+
 private int getZoom() {
 	return DPIUtil.getZoomForAutoscaleProperty(initialNativeZoom);
 }
@@ -2059,7 +2095,7 @@ ImageData getImageData(int zoom) {
 
 	private ImageData scaleIfNecessary(ElementAtZoom<ImageData> imageDataAtZoom, int zoom) {
 		if (imageDataAtZoom.zoom() != zoom) {
-			return DPIUtil.scaleImageData(device, imageDataAtZoom, zoom);
+			return scaleImageData(imageDataAtZoom.element(), zoom, imageDataAtZoom.zoom());
 		} else {
 			return imageDataAtZoom.element();
 		}
@@ -2308,13 +2344,13 @@ protected Rectangle getBounds(int zoom) {
 	@Override
 	ImageData getImageData(int zoom) {
 		ElementAtZoom<ImageData> data = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
-		return DPIUtil.scaleImageData (device, data.element(), zoom, data.zoom());
+		return scaleImageData(data.element(), zoom, data.zoom());
 	}
 
 	@Override
 	ImageHandle getImageMetadata(int zoom) {
 		ElementAtZoom<ImageData> imageCandidate = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
-		ImageData resizedData = DPIUtil.scaleImageData (device, imageCandidate.element(), zoom, imageCandidate.zoom());
+		ImageData resizedData = scaleImageData(imageCandidate.element(), zoom, imageCandidate.zoom());
 		ImageData newData = adaptImageDataIfDisabledOrGray(resizedData);
 		init(newData, zoom);
 		return zoomLevelToImageHandle.get(zoom);


Reviews:
user: login: fedejeanne
body: We briefly discussed this in the call but I'd like to point it out here again: wouldn't it be better to fix the smooth scaling for all OSes instead of duplicating the logic in `Image` (which makes it Windows-specific)? 

Did you try fixing it for all OSes already? Did something break?

If it's just a matter of risk management, we can simply try and fix it in `DPIUtil` and test in Linux and Mac too.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-19T10:47:28Z
user: login: amartya4256
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T09:57:36Z
user: login: amartya4256
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T09:59:47Z
user: login: amartya4256
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T09:59:53Z
user: login: amartya4256
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T10:00:22Z
user: login: amartya4256
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T10:00:38Z
user: login: amartya4256
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T10:01:39Z

Review comments:
diff_hunk: @@ -1850,6 +1853,41 @@ private void setBackground(Color color, long handle) {
 	device.internal_dispose_GC(hDC, null);
 }
 
+private ImageData scaleImageData(Device device, int currentZoom, int targetZoom, ImageData imageData) {
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
user: login: fedejeanne
body: 1. Should be `static`
2. Why change the order of the parameters? It only adds more lines to review and also makes the new method a non-drop-in replacement.

```suggestion
private static ImageData scaleImageData(Device device, final ImageData imageData, int targetZoom, int currentZoom) {
```
created_at: 2025-03-19T10:40:02Z
author_association: MEMBER
diff_hunk: @@ -1850,6 +1853,41 @@ private void setBackground(Color color, long handle) {
 	device.internal_dispose_GC(hDC, null);
 }
 
+private ImageData scaleImageData(Device device, int currentZoom, int targetZoom, ImageData imageData) {
+	if (imageData == null || targetZoom == currentZoom || (device != null && !device.isAutoScalable())) return imageData;
+	float scaleFactor = (float) targetZoom / (float) currentZoom;
+	int width = imageData.width;
+	int height = imageData.height;
+	int scaledWidth = Math.round (width * scaleFactor);
+	int scaledHeight = Math.round (height * scaleFactor);
+	boolean useSmoothScaling = DPIUtil.isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
+	if (useSmoothScaling) {
+		return scaleToUsingSmoothScaling(scaledWidth, scaledHeight, imageData);
+	} else {
+		return imageData.scaledTo (scaledWidth, scaledHeight);
+	}
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
user: login: fedejeanne
body: ```suggestion
	}
	return imageData.scaledTo (scaledWidth, scaledHeight);
```
created_at: 2025-03-19T10:40:45Z
author_association: MEMBER
diff_hunk: @@ -1850,6 +1853,41 @@ private void setBackground(Color color, long handle) {
 	device.internal_dispose_GC(hDC, null);
 }
 
+private ImageData scaleImageData(Device device, int currentZoom, int targetZoom, ImageData imageData) {
+	if (imageData == null || targetZoom == currentZoom || (device != null && !device.isAutoScalable())) return imageData;
+	float scaleFactor = (float) targetZoom / (float) currentZoom;
+	int width = imageData.width;
+	int height = imageData.height;
+	int scaledWidth = Math.round (width * scaleFactor);
+	int scaledHeight = Math.round (height * scaleFactor);
+	boolean useSmoothScaling = DPIUtil.isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
+	if (useSmoothScaling) {
+		return scaleToUsingSmoothScaling(scaledWidth, scaledHeight, imageData);
+	} else {
+		return imageData.scaledTo (scaledWidth, scaledHeight);
+	}
+}
+
+private ImageData scaleToUsingSmoothScaling(int width, int height, ImageData imageData) {
+	Image original = new Image (Display.getCurrent(), (ImageDataProvider) zoom -> imageData);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
user: login: fedejeanne
body: Why `Display.getCurrent()` and not simply `device`, like it is in the original method in `DPIUtil`? Is there any use case where using `device` brings problems?
created_at: 2025-03-19T10:42:25Z
author_association: MEMBER
diff_hunk: @@ -1850,6 +1853,41 @@ private void setBackground(Color color, long handle) {
 	device.internal_dispose_GC(hDC, null);
 }
 
+private ImageData scaleImageData(Device device, int currentZoom, int targetZoom, ImageData imageData) {
+	if (imageData == null || targetZoom == currentZoom || (device != null && !device.isAutoScalable())) return imageData;
+	float scaleFactor = (float) targetZoom / (float) currentZoom;
+	int width = imageData.width;
+	int height = imageData.height;
+	int scaledWidth = Math.round (width * scaleFactor);
+	int scaledHeight = Math.round (height * scaleFactor);
+	boolean useSmoothScaling = DPIUtil.isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
+	if (useSmoothScaling) {
+		return scaleToUsingSmoothScaling(scaledWidth, scaledHeight, imageData);
+	} else {
+		return imageData.scaledTo (scaledWidth, scaledHeight);
+	}
+}
+
+private ImageData scaleToUsingSmoothScaling(int width, int height, ImageData imageData) {
+	Image original = new Image (Display.getCurrent(), (ImageDataProvider) zoom -> imageData);
+	/* Create a 24 bit image data with alpha channel */
+	final ImageData resultData = new ImageData (width, height, 24, new PaletteData (0xFF, 0xFF00, 0xFF0000));
+	resultData.alphaData = new byte [width * height];
+	Image resultImage = new Image (Display.getCurrent(), (ImageDataProvider) zoom -> resultData);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
user: login: fedejeanne
body: Same as a few lines above: why `Display.getCurrent()`... ?
created_at: 2025-03-19T10:42:47Z
author_association: MEMBER
diff_hunk: @@ -1850,6 +1853,41 @@ private void setBackground(Color color, long handle) {
 	device.internal_dispose_GC(hDC, null);
 }
 
+private ImageData scaleImageData(Device device, int currentZoom, int targetZoom, ImageData imageData) {
+	if (imageData == null || targetZoom == currentZoom || (device != null && !device.isAutoScalable())) return imageData;
+	float scaleFactor = (float) targetZoom / (float) currentZoom;
+	int width = imageData.width;
+	int height = imageData.height;
+	int scaledWidth = Math.round (width * scaleFactor);
+	int scaledHeight = Math.round (height * scaleFactor);
+	boolean useSmoothScaling = DPIUtil.isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
+	if (useSmoothScaling) {
+		return scaleToUsingSmoothScaling(scaledWidth, scaledHeight, imageData);
+	} else {
+		return imageData.scaledTo (scaledWidth, scaledHeight);
+	}
+}
+
+private ImageData scaleToUsingSmoothScaling(int width, int height, ImageData imageData) {
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
user: login: fedejeanne
body: ```suggestion
private static ImageData scaleToUsingSmoothScaling(int width, int height, ImageData imageData) {
```
created_at: 2025-03-19T10:43:45Z
author_association: MEMBER
diff_hunk: @@ -1850,6 +1853,41 @@ private void setBackground(Color color, long handle) {
 	device.internal_dispose_GC(hDC, null);
 }
 
+private ImageData scaleImageData(Device device, int currentZoom, int targetZoom, ImageData imageData) {
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
user: login: amartya4256
body: Done
created_at: 2025-03-21T09:57:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1850,6 +1853,41 @@ private void setBackground(Color color, long handle) {
 	device.internal_dispose_GC(hDC, null);
 }
 
+private ImageData scaleImageData(Device device, int currentZoom, int targetZoom, ImageData imageData) {
+	if (imageData == null || targetZoom == currentZoom || (device != null && !device.isAutoScalable())) return imageData;
+	float scaleFactor = (float) targetZoom / (float) currentZoom;
+	int width = imageData.width;
+	int height = imageData.height;
+	int scaledWidth = Math.round (width * scaleFactor);
+	int scaledHeight = Math.round (height * scaleFactor);
+	boolean useSmoothScaling = DPIUtil.isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
+	if (useSmoothScaling) {
+		return scaleToUsingSmoothScaling(scaledWidth, scaledHeight, imageData);
+	} else {
+		return imageData.scaledTo (scaledWidth, scaledHeight);
+	}
+}
+
+private ImageData scaleToUsingSmoothScaling(int width, int height, ImageData imageData) {
+	Image original = new Image (Display.getCurrent(), (ImageDataProvider) zoom -> imageData);
+	/* Create a 24 bit image data with alpha channel */
+	final ImageData resultData = new ImageData (width, height, 24, new PaletteData (0xFF, 0xFF00, 0xFF0000));
+	resultData.alphaData = new byte [width * height];
+	Image resultImage = new Image (Display.getCurrent(), (ImageDataProvider) zoom -> resultData);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
user: login: amartya4256
body: I forgot to change it back to device, my bad.
created_at: 2025-03-21T09:59:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1850,6 +1853,41 @@ private void setBackground(Color color, long handle) {
 	device.internal_dispose_GC(hDC, null);
 }
 
+private ImageData scaleImageData(Device device, int currentZoom, int targetZoom, ImageData imageData) {
+	if (imageData == null || targetZoom == currentZoom || (device != null && !device.isAutoScalable())) return imageData;
+	float scaleFactor = (float) targetZoom / (float) currentZoom;
+	int width = imageData.width;
+	int height = imageData.height;
+	int scaledWidth = Math.round (width * scaleFactor);
+	int scaledHeight = Math.round (height * scaleFactor);
+	boolean useSmoothScaling = DPIUtil.isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
+	if (useSmoothScaling) {
+		return scaleToUsingSmoothScaling(scaledWidth, scaledHeight, imageData);
+	} else {
+		return imageData.scaledTo (scaledWidth, scaledHeight);
+	}
+}
+
+private ImageData scaleToUsingSmoothScaling(int width, int height, ImageData imageData) {
+	Image original = new Image (Display.getCurrent(), (ImageDataProvider) zoom -> imageData);
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
user: login: amartya4256
body: Oops
created_at: 2025-03-21T09:59:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1850,6 +1853,41 @@ private void setBackground(Color color, long handle) {
 	device.internal_dispose_GC(hDC, null);
 }
 
+private ImageData scaleImageData(Device device, int currentZoom, int targetZoom, ImageData imageData) {
+	if (imageData == null || targetZoom == currentZoom || (device != null && !device.isAutoScalable())) return imageData;
+	float scaleFactor = (float) targetZoom / (float) currentZoom;
+	int width = imageData.width;
+	int height = imageData.height;
+	int scaledWidth = Math.round (width * scaleFactor);
+	int scaledHeight = Math.round (height * scaleFactor);
+	boolean useSmoothScaling = DPIUtil.isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
+	if (useSmoothScaling) {
+		return scaleToUsingSmoothScaling(scaledWidth, scaledHeight, imageData);
+	} else {
+		return imageData.scaledTo (scaledWidth, scaledHeight);
+	}
+}
+
+private ImageData scaleToUsingSmoothScaling(int width, int height, ImageData imageData) {
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
user: login: amartya4256
body: Can't make this static, we need the device of the image object in it

created_at: 2025-03-21T10:00:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1850,6 +1853,41 @@ private void setBackground(Color color, long handle) {
 	device.internal_dispose_GC(hDC, null);
 }
 
+private ImageData scaleImageData(Device device, int currentZoom, int targetZoom, ImageData imageData) {
+	if (imageData == null || targetZoom == currentZoom || (device != null && !device.isAutoScalable())) return imageData;
+	float scaleFactor = (float) targetZoom / (float) currentZoom;
+	int width = imageData.width;
+	int height = imageData.height;
+	int scaledWidth = Math.round (width * scaleFactor);
+	int scaledHeight = Math.round (height * scaleFactor);
+	boolean useSmoothScaling = DPIUtil.isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
+	if (useSmoothScaling) {
+		return scaleToUsingSmoothScaling(scaledWidth, scaledHeight, imageData);
+	} else {
+		return imageData.scaledTo (scaledWidth, scaledHeight);
+	}
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
user: login: amartya4256
body: Done
created_at: 2025-03-21T10:00:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1850,6 +1853,41 @@ private void setBackground(Color color, long handle) {
 	device.internal_dispose_GC(hDC, null);
 }
 
+private ImageData scaleImageData(Device device, int currentZoom, int targetZoom, ImageData imageData) {
path: bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java
user: login: amartya4256
body: But cannot make it static since we need some of the attributes of the image later in a method we call from this method.
created_at: 2025-03-21T10:01:39Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-18T13:43:54Z
body: ## Test Results
   533 files  ±0     533 suites  ±0   36m 35s ⏱️ + 6m 32s
 4 363 tests ±0   4 348 ✅ ±0   15 💤 ±0  0 ❌ ±0 
16 595 runs  ±0  16 485 ✅ ±0  110 💤 ±0  0 ❌ ±0 

Results for commit 6f78753b. ± Comparison against base commit b91e0121.

[test-results]:data:application/gzip;base64,H4sIALk93WcC/02NSQ7DIAxFrxKx7iJmMLSXiRhiCTVDRWBV9e4lUable9Z//jKKQ7+wV6OEeDRsKTHfMJRkc5ynKjg8VTX1mte7FCgO7Jbi/eakudw7fqqDc9SRjUM17Sn6lOa0m1SmNQuotjcrHllAaS63Z+EY3bMb36t+HseYKzAkbbQSTgSSHJ1xYF2w6AmM9pq3PQ+S0BH7/QFQJbnDEwEAAA==

:recycle: This comment has been updated with latest results.
author_association: CONTRIBUTOR
user: login: HeikoKlare
created_at: 2025-03-21T10:47:39Z
body: > We briefly discussed this in the call but I'd like to point it out here again: wouldn't it be better to fix the smooth scaling for all OSes instead of duplicating the logic in `Image` (which makes it Windows-specific)?

Can you please share why the solution was to duplicate functionality? We now have the (almost?) same implementation inside `Image` (on Windows) and inside `DPIUtil`. No one will know which one to use when, so I would either expect to have the fix done in `DPIUtil` or to move the implementation to the OS-specific `Image` classes if there needs to be a difference between them.
author_association: CONTRIBUTOR
user: login: amartya4256
created_at: 2025-03-21T11:38:41Z
body: We need to draw directly using pixel specific implementation and we cannot access that gc::drawImage usign pixels from DPIUtil. Moreover, DPIUtil uses intermediate scale ups ad downs so I moved it to the Image. Another idea will be to move the method to ImageData and call that from DPIUtil. But for that I need an API in ImageData for smooth scaling. (ImageData is in commons)
author_association: CONTRIBUTOR
user: login: HeikoKlare
created_at: 2025-03-21T11:47:38Z
body: But why is that specific to Windows? Shouldn't the functionality then be generally moved into the Image classes?
author_association: CONTRIBUTOR
user: login: amartya4256
created_at: 2025-03-21T13:39:08Z
body: It cannot be completely moved to Image because its not completely specific to Image but also Cursor and a few other places.
author_association: CONTRIBUTOR
user: login: amartya4256
created_at: 2025-03-21T13:40:12Z
body: Better option would be to move the smooth scaling block in the DPIUtil::autoScaleImageData to a new public method in ImageData class. There we can get rid of the rounding error.
author_association: CONTRIBUTOR
user: login: HeikoKlare
created_at: 2025-03-21T16:04:35Z
body: > Better option would be to move the smooth scaling block in the DPIUtil::autoScaleImageData to a new public method in ImageData class. There we can get rid of the rounding error.

Doing that in ImageData would be best anyway since there are several consumers using ImageData#scaledTo, which will produce bad results even if smooth scaling is enabled.
In any way, I think the duplication we have now is quite inacceptable. GC#drawImage contains a hack (explicitly marked as such via an according comment), which this change now works around via another hack. So we should at least factor out the common code into DPIUtil and pass the GC-drawing functionality to that common code as a lambda (or, of course, even better move the functionality into ImageData).
author_association: CONTRIBUTOR
user: login: amartya4256
created_at: 2025-03-21T16:37:26Z
body: Then we should revert the commit and I'll create another PR with a new impl.
author_association: CONTRIBUTOR