Skip to content

Commit 6f78753

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 f69194a commit 6f78753

File tree

2 files changed

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

2 files changed

+54
-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,7 +292,7 @@ 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
Image original = new Image (device, (ImageDataProvider) zoom -> imageData);
298298
/* Create a 24 bit image data with alpha channel */
@@ -316,6 +316,10 @@ private static ImageData autoScaleImageData (Device device, final ImageData imag
316316
}
317317
}
318318

319+
public static boolean isSmoothScalingEnabled() {
320+
return autoScaleMethod == AutoScaleMethod.SMOOTH;
321+
}
322+
319323
/**
320324
* Returns a new rectangle as per the scaleFactor.
321325
*/
@@ -628,6 +632,10 @@ public static boolean useCairoAutoScale() {
628632
}
629633

630634
public static int getZoomForAutoscaleProperty (int nativeDeviceZoom) {
635+
return getZoomForAutoscaleProperty(nativeDeviceZoom, autoScaleValue);
636+
}
637+
638+
private static int getZoomForAutoscaleProperty (int nativeDeviceZoom, String autoScaleValue) {
631639
int zoom = 0;
632640
if (autoScaleValue != null) {
633641
if ("false".equalsIgnoreCase (autoScaleValue)) {

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

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ public Image(Device device, ImageData data) {
361361
if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
362362
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
363363
int deviceZoom = getZoom();
364-
data = DPIUtil.scaleImageData(device, new ElementAtZoom<>(data, 100), deviceZoom);
364+
data = scaleImageData(data, deviceZoom, 100);
365365
init(data, deviceZoom);
366366
init();
367367
this.device.registerResourceWithZoomSupport(this);
@@ -405,8 +405,8 @@ public Image(Device device, ImageData source, ImageData mask) {
405405
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
406406
}
407407
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
408-
source = DPIUtil.autoScaleUp(device, source);
409-
mask = DPIUtil.autoScaleUp(device, mask);
408+
source = scaleImageData(source, getZoom(), 100);
409+
mask = scaleImageData(mask, getZoom(), 100);
410410
mask = ImageData.convertMask(mask);
411411
initIconHandle(this.device, source, mask, getZoom());
412412
init();
@@ -470,7 +470,8 @@ public Image (Device device, InputStream stream) {
470470
super(device);
471471
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
472472
int deviceZoom = getZoom();
473-
ImageData data = DPIUtil.scaleImageData(device, ImageDataLoader.load(stream, FileFormat.DEFAULT_ZOOM, deviceZoom), deviceZoom);
473+
ElementAtZoom<ImageData> imageCandidate = ImageDataLoader.load(stream, FileFormat.DEFAULT_ZOOM, deviceZoom);
474+
ImageData data = scaleImageData(imageCandidate.element(), deviceZoom, imageCandidate.zoom());
474475
init(data, deviceZoom);
475476
init();
476477
this.device.registerResourceWithZoomSupport(this);
@@ -513,7 +514,8 @@ public Image (Device device, String filename) {
513514
if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
514515
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
515516
int deviceZoom = getZoom();
516-
ImageData data = DPIUtil.scaleImageData(device, ImageDataLoader.load(filename, FileFormat.DEFAULT_ZOOM, deviceZoom), deviceZoom);
517+
ElementAtZoom<ImageData> imageCandidate = ImageDataLoader.load(filename, FileFormat.DEFAULT_ZOOM, deviceZoom);
518+
ImageData data = scaleImageData(imageCandidate.element(), deviceZoom, imageCandidate.zoom());
517519
init(data, deviceZoom);
518520
init();
519521
this.device.registerResourceWithZoomSupport(this);
@@ -1237,7 +1239,7 @@ private ImageData getScaledImageData (int zoom) {
12371239
}
12381240
TreeSet<Integer> availableZooms = new TreeSet<>(zoomLevelToImageHandle.keySet());
12391241
int closestZoom = Optional.ofNullable(availableZooms.higher(zoom)).orElse(availableZooms.lower(zoom));
1240-
return DPIUtil.scaleImageData (device, getImageMetadata(closestZoom).getImageData(), zoom, closestZoom);
1242+
return scaleImageData(getImageMetadata(closestZoom).getImageData(), zoom, closestZoom);
12411243
}
12421244

12431245

@@ -1850,6 +1852,40 @@ private void setBackground(Color color, long handle) {
18501852
device.internal_dispose_GC(hDC, null);
18511853
}
18521854

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

20602096
private ImageData scaleIfNecessary(ElementAtZoom<ImageData> imageDataAtZoom, int zoom) {
20612097
if (imageDataAtZoom.zoom() != zoom) {
2062-
return DPIUtil.scaleImageData(device, imageDataAtZoom, zoom);
2098+
return scaleImageData(imageDataAtZoom.element(), zoom, imageDataAtZoom.zoom());
20632099
} else {
20642100
return imageDataAtZoom.element();
20652101
}
@@ -2308,13 +2344,13 @@ protected Rectangle getBounds(int zoom) {
23082344
@Override
23092345
ImageData getImageData(int zoom) {
23102346
ElementAtZoom<ImageData> data = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
2311-
return DPIUtil.scaleImageData (device, data.element(), zoom, data.zoom());
2347+
return scaleImageData(data.element(), zoom, data.zoom());
23122348
}
23132349

23142350
@Override
23152351
ImageHandle getImageMetadata(int zoom) {
23162352
ElementAtZoom<ImageData> imageCandidate = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
2317-
ImageData resizedData = DPIUtil.scaleImageData (device, imageCandidate.element(), zoom, imageCandidate.zoom());
2353+
ImageData resizedData = scaleImageData(imageCandidate.element(), zoom, imageCandidate.zoom());
23182354
ImageData newData = adaptImageDataIfDisabledOrGray(resizedData);
23192355
init(newData, zoom);
23202356
return zoomLevelToImageHandle.get(zoom);

0 commit comments

Comments
 (0)