Skip to content

Commit dee6c01

Browse files
committed
Smooth Scaling Rounding error fix for win32 #62
This commit 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 #62 and #127
1 parent 2df298f commit dee6c01

File tree

2 files changed

+56
-10
lines changed
  • bundles/org.eclipse.swt/Eclipse SWT

2 files changed

+56
-10
lines changed

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,13 +292,17 @@ private static ImageData autoScaleImageData (Device device, final ImageData imag
292292
int height = imageData.height;
293293
int scaledWidth = Math.round (width * scaleFactor);
294294
int scaledHeight = Math.round (height * scaleFactor);
295-
boolean useSmoothScaling = autoScaleMethod == AutoScaleMethod.SMOOTH && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
295+
boolean useSmoothScaling = isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
296296
if (useSmoothScaling) {
297297
return imageData.scaleToUsingSmoothScaling(device, scaledWidth, scaledHeight);
298298
}
299299
return imageData.scaledTo (scaledWidth, scaledHeight);
300300
}
301301

302+
public static boolean isSmoothScalingEnabled() {
303+
return autoScaleMethod == AutoScaleMethod.SMOOTH;
304+
}
305+
302306
/**
303307
* Returns a new rectangle as per the scaleFactor.
304308
*/
@@ -611,6 +615,10 @@ public static boolean useCairoAutoScale() {
611615
}
612616

613617
public static int getZoomForAutoscaleProperty (int nativeDeviceZoom) {
618+
return getZoomForAutoscaleProperty(nativeDeviceZoom, autoScaleValue);
619+
}
620+
621+
private static int getZoomForAutoscaleProperty (int nativeDeviceZoom, String autoScaleValue) {
614622
int zoom = 0;
615623
if (autoScaleValue != null) {
616624
if ("false".equalsIgnoreCase (autoScaleValue)) {

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.eclipse.swt.internal.gdip.*;
2424
import org.eclipse.swt.internal.image.*;
2525
import org.eclipse.swt.internal.win32.*;
26+
import org.eclipse.swt.widgets.*;
2627

2728
/**
2829
* Instances of this class are graphics which have been prepared
@@ -372,7 +373,7 @@ public Image(Device device, ImageData data) {
372373
if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
373374
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
374375
int deviceZoom = getZoom();
375-
data = DPIUtil.scaleImageData(device, new ElementAtZoom<>(data, 100), deviceZoom);
376+
data = scaleImageData(device, 100, deviceZoom, data);
376377
init(data, deviceZoom);
377378
init();
378379
this.device.registerResourceWithZoomSupport(this);
@@ -416,8 +417,8 @@ public Image(Device device, ImageData source, ImageData mask) {
416417
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
417418
}
418419
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
419-
source = DPIUtil.autoScaleUp(device, source);
420-
mask = DPIUtil.autoScaleUp(device, mask);
420+
source = scaleImageData(device, 100, getZoom(), source);
421+
mask = scaleImageData(device, 100, getZoom(), mask);
421422
mask = ImageData.convertMask(mask);
422423
initIconHandle(this.device, source, mask, getZoom());
423424
init();
@@ -481,7 +482,8 @@ public Image (Device device, InputStream stream) {
481482
super(device);
482483
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
483484
int deviceZoom = getZoom();
484-
ImageData data = DPIUtil.scaleImageData(device, ImageDataLoader.load(stream, FileFormat.DEFAULT_ZOOM, deviceZoom), deviceZoom);
485+
ElementAtZoom<ImageData> imageCandidate = ImageDataLoader.load(stream, FileFormat.DEFAULT_ZOOM, deviceZoom);
486+
ImageData data = scaleImageData(device, imageCandidate.zoom(), deviceZoom, imageCandidate.element());
485487
init(data, deviceZoom);
486488
init();
487489
this.device.registerResourceWithZoomSupport(this);
@@ -524,7 +526,8 @@ public Image (Device device, String filename) {
524526
if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
525527
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
526528
int deviceZoom = getZoom();
527-
ImageData data = DPIUtil.scaleImageData(device, ImageDataLoader.load(filename, FileFormat.DEFAULT_ZOOM, deviceZoom), deviceZoom);
529+
ElementAtZoom<ImageData> imageCandidate = ImageDataLoader.load(filename, FileFormat.DEFAULT_ZOOM, deviceZoom);
530+
ImageData data = scaleImageData(device, imageCandidate.zoom(), deviceZoom, imageCandidate.element());
528531
init(data, deviceZoom);
529532
init();
530533
this.device.registerResourceWithZoomSupport(this);
@@ -1264,7 +1267,7 @@ private ImageData getScaledImageData (int zoom) {
12641267
}
12651268
TreeSet<Integer> availableZooms = new TreeSet<>(zoomLevelToImageHandle.keySet());
12661269
int closestZoom = Optional.ofNullable(availableZooms.higher(zoom)).orElse(availableZooms.lower(zoom));
1267-
return DPIUtil.scaleImageData (device, getImageMetadata(closestZoom).getImageData(), zoom, closestZoom);
1270+
return scaleImageData(device, closestZoom, zoom, getImageMetadata(closestZoom).getImageData());
12681271
}
12691272

12701273

@@ -1854,6 +1857,41 @@ public void setBackground(Color color) {
18541857
zoomLevelToImageHandle.values().forEach(imageHandle -> imageHandle.setBackground(backgroundColor));
18551858
}
18561859

1860+
private ImageData scaleImageData(Device device, int currentZoom, int targetZoom, ImageData imageData) {
1861+
if (imageData == null || targetZoom == currentZoom || (device != null && !device.isAutoScalable())) return imageData;
1862+
float scaleFactor = (float) targetZoom / (float) currentZoom;
1863+
int width = imageData.width;
1864+
int height = imageData.height;
1865+
int scaledWidth = Math.round (width * scaleFactor);
1866+
int scaledHeight = Math.round (height * scaleFactor);
1867+
boolean useSmoothScaling = DPIUtil.isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
1868+
if (useSmoothScaling) {
1869+
return scaleToUsingSmoothScaling(scaledWidth, scaledHeight, imageData);
1870+
} else {
1871+
return imageData.scaledTo (scaledWidth, scaledHeight);
1872+
}
1873+
}
1874+
1875+
private ImageData scaleToUsingSmoothScaling(int width, int height, ImageData imageData) {
1876+
Image original = new Image (Display.getCurrent(), (ImageDataProvider) zoom -> imageData);
1877+
/* Create a 24 bit image data with alpha channel */
1878+
final ImageData resultData = new ImageData (width, height, 24, new PaletteData (0xFF, 0xFF00, 0xFF0000));
1879+
resultData.alphaData = new byte [width * height];
1880+
Image resultImage = new Image (Display.getCurrent(), (ImageDataProvider) zoom -> resultData);
1881+
GC gc = new GC (resultImage);
1882+
gc.setAntialias (SWT.ON);
1883+
gc.drawImage (original, 0, 0, imageData.width, imageData.height,
1884+
/* E.g. destWidth here is effectively DPIUtil.autoScaleDown (scaledWidth), but avoiding rounding errors.
1885+
* Nevertheless, we still have some rounding errors due to the point-based API GC#drawImage(..).
1886+
*/
1887+
0, 0, width, height, false);
1888+
gc.dispose ();
1889+
original.dispose ();
1890+
ImageData result = resultImage.getImageData (resultImage.getZoom());
1891+
resultImage.dispose ();
1892+
return result;
1893+
}
1894+
18571895
private int getZoom() {
18581896
return DPIUtil.getZoomForAutoscaleProperty(initialNativeZoom);
18591897
}
@@ -2073,7 +2111,7 @@ ImageData getImageData(int zoom) {
20732111

20742112
private ImageData scaleIfNecessary(ElementAtZoom<ImageData> imageDataAtZoom, int zoom) {
20752113
if (imageDataAtZoom.zoom() != zoom) {
2076-
return DPIUtil.scaleImageData(device, imageDataAtZoom, zoom);
2114+
return scaleImageData(device, imageDataAtZoom.zoom(), zoom, imageDataAtZoom.element());
20772115
} else {
20782116
return imageDataAtZoom.element();
20792117
}
@@ -2298,13 +2336,13 @@ private class ImageDataProviderWrapper extends BaseImageProviderWrapper<ImageDat
22982336
@Override
22992337
ImageData getImageData(int zoom) {
23002338
ElementAtZoom<ImageData> data = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
2301-
return DPIUtil.scaleImageData (device, data.element(), zoom, data.zoom());
2339+
return scaleImageData(device, data.zoom(), zoom, data.element());
23022340
}
23032341

23042342
@Override
23052343
ImageHandle getImageMetadata(int zoom) {
23062344
ElementAtZoom<ImageData> imageCandidate = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
2307-
ImageData resizedData = DPIUtil.scaleImageData (device, imageCandidate.element(), zoom, imageCandidate.zoom());
2345+
ImageData resizedData = scaleImageData(device, imageCandidate.zoom(), zoom, imageCandidate.element());
23082346
ImageData newData = adaptImageDataIfDisabledOrGray(resizedData);
23092347
init(newData, zoom);
23102348
return zoomLevelToImageHandle.get(zoom);

0 commit comments

Comments
 (0)