Skip to content

Commit 22f378e

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 22f378e

File tree

2 files changed

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

2 files changed

+55
-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: 46 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
@@ -361,7 +362,7 @@ public Image(Device device, ImageData data) {
361362
if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
362363
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
363364
int deviceZoom = getZoom();
364-
data = DPIUtil.scaleImageData(device, new ElementAtZoom<>(data, 100), deviceZoom);
365+
data = scaleImageData(device, data, deviceZoom, 100);
365366
init(data, deviceZoom);
366367
init();
367368
this.device.registerResourceWithZoomSupport(this);
@@ -405,8 +406,8 @@ public Image(Device device, ImageData source, ImageData mask) {
405406
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
406407
}
407408
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
408-
source = DPIUtil.autoScaleUp(device, source);
409-
mask = DPIUtil.autoScaleUp(device, mask);
409+
source = scaleImageData(device, source, getZoom(), 100);
410+
mask = scaleImageData(device, mask, getZoom(), 100);
410411
mask = ImageData.convertMask(mask);
411412
initIconHandle(this.device, source, mask, getZoom());
412413
init();
@@ -470,7 +471,8 @@ public Image (Device device, InputStream stream) {
470471
super(device);
471472
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
472473
int deviceZoom = getZoom();
473-
ImageData data = DPIUtil.scaleImageData(device, ImageDataLoader.load(stream, FileFormat.DEFAULT_ZOOM, deviceZoom), deviceZoom);
474+
ElementAtZoom<ImageData> imageCandidate = ImageDataLoader.load(stream, FileFormat.DEFAULT_ZOOM, deviceZoom);
475+
ImageData data = scaleImageData(device, imageCandidate.element(), deviceZoom, imageCandidate.zoom());
474476
init(data, deviceZoom);
475477
init();
476478
this.device.registerResourceWithZoomSupport(this);
@@ -513,7 +515,8 @@ public Image (Device device, String filename) {
513515
if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
514516
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
515517
int deviceZoom = getZoom();
516-
ImageData data = DPIUtil.scaleImageData(device, ImageDataLoader.load(filename, FileFormat.DEFAULT_ZOOM, deviceZoom), deviceZoom);
518+
ElementAtZoom<ImageData> imageCandidate = ImageDataLoader.load(filename, FileFormat.DEFAULT_ZOOM, deviceZoom);
519+
ImageData data = scaleImageData(device, imageCandidate.element(), deviceZoom, imageCandidate.zoom());
517520
init(data, deviceZoom);
518521
init();
519522
this.device.registerResourceWithZoomSupport(this);
@@ -1237,7 +1240,7 @@ private ImageData getScaledImageData (int zoom) {
12371240
}
12381241
TreeSet<Integer> availableZooms = new TreeSet<>(zoomLevelToImageHandle.keySet());
12391242
int closestZoom = Optional.ofNullable(availableZooms.higher(zoom)).orElse(availableZooms.lower(zoom));
1240-
return DPIUtil.scaleImageData (device, getImageMetadata(closestZoom).getImageData(), zoom, closestZoom);
1243+
return scaleImageData(device, getImageMetadata(closestZoom).getImageData(), zoom, closestZoom);
12411244
}
12421245

12431246

@@ -1850,6 +1853,40 @@ private void setBackground(Color color, long handle) {
18501853
device.internal_dispose_GC(hDC, null);
18511854
}
18521855

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

20602097
private ImageData scaleIfNecessary(ElementAtZoom<ImageData> imageDataAtZoom, int zoom) {
20612098
if (imageDataAtZoom.zoom() != zoom) {
2062-
return DPIUtil.scaleImageData(device, imageDataAtZoom, zoom);
2099+
return scaleImageData(device, imageDataAtZoom.element(), zoom, imageDataAtZoom.zoom());
20632100
} else {
20642101
return imageDataAtZoom.element();
20652102
}
@@ -2308,13 +2345,13 @@ protected Rectangle getBounds(int zoom) {
23082345
@Override
23092346
ImageData getImageData(int zoom) {
23102347
ElementAtZoom<ImageData> data = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
2311-
return DPIUtil.scaleImageData (device, data.element(), zoom, data.zoom());
2348+
return scaleImageData(device, data.element(), zoom, data.zoom());
23122349
}
23132350

23142351
@Override
23152352
ImageHandle getImageMetadata(int zoom) {
23162353
ElementAtZoom<ImageData> imageCandidate = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
2317-
ImageData resizedData = DPIUtil.scaleImageData (device, imageCandidate.element(), zoom, imageCandidate.zoom());
2354+
ImageData resizedData = scaleImageData(device, imageCandidate.element(), zoom, imageCandidate.zoom());
23182355
ImageData newData = adaptImageDataIfDisabledOrGray(resizedData);
23192356
init(newData, zoom);
23202357
return zoomLevelToImageHandle.get(zoom);

0 commit comments

Comments
 (0)