Skip to content

Commit 2bcfb45

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 2bcfb45

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,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: 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
@@ -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, 100, deviceZoom, data);
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, 100, getZoom(), source);
410+
mask = scaleImageData(device, 100, getZoom(), mask);
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.zoom(), deviceZoom, imageCandidate.element());
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.zoom(), deviceZoom, imageCandidate.element());
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, closestZoom, zoom, getImageMetadata(closestZoom).getImageData());
12411244
}
12421245

12431246

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

1856+
private ImageData scaleImageData(Device device, int currentZoom, int targetZoom, ImageData imageData) {
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+
} else {
1867+
return imageData.scaledTo (scaledWidth, scaledHeight);
1868+
}
1869+
}
1870+
1871+
private ImageData scaleToUsingSmoothScaling(int width, int height, ImageData imageData) {
1872+
Image original = new Image (Display.getCurrent(), (ImageDataProvider) zoom -> imageData);
1873+
/* Create a 24 bit image data with alpha channel */
1874+
final ImageData resultData = new ImageData (width, height, 24, new PaletteData (0xFF, 0xFF00, 0xFF0000));
1875+
resultData.alphaData = new byte [width * height];
1876+
Image resultImage = new Image (Display.getCurrent(), (ImageDataProvider) zoom -> resultData);
1877+
GC gc = new GC (resultImage);
1878+
gc.setAntialias (SWT.ON);
1879+
gc.drawImage (original, 0, 0, imageData.width, imageData.height,
1880+
/* E.g. destWidth here is effectively DPIUtil.autoScaleDown (scaledWidth), but avoiding rounding errors.
1881+
* Nevertheless, we still have some rounding errors due to the point-based API GC#drawImage(..).
1882+
*/
1883+
0, 0, width, height, false);
1884+
gc.dispose ();
1885+
original.dispose ();
1886+
ImageData result = resultImage.getImageData (resultImage.getZoom());
1887+
resultImage.dispose ();
1888+
return result;
1889+
}
1890+
18531891
private int getZoom() {
18541892
return DPIUtil.getZoomForAutoscaleProperty(initialNativeZoom);
18551893
}
@@ -2059,7 +2097,7 @@ ImageData getImageData(int zoom) {
20592097

20602098
private ImageData scaleIfNecessary(ElementAtZoom<ImageData> imageDataAtZoom, int zoom) {
20612099
if (imageDataAtZoom.zoom() != zoom) {
2062-
return DPIUtil.scaleImageData(device, imageDataAtZoom, zoom);
2100+
return scaleImageData(device, imageDataAtZoom.zoom(), zoom, imageDataAtZoom.element());
20632101
} else {
20642102
return imageDataAtZoom.element();
20652103
}
@@ -2308,13 +2346,13 @@ protected Rectangle getBounds(int zoom) {
23082346
@Override
23092347
ImageData getImageData(int zoom) {
23102348
ElementAtZoom<ImageData> data = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
2311-
return DPIUtil.scaleImageData (device, data.element(), zoom, data.zoom());
2349+
return scaleImageData(device, data.zoom(), zoom, data.element());
23122350
}
23132351

23142352
@Override
23152353
ImageHandle getImageMetadata(int zoom) {
23162354
ElementAtZoom<ImageData> imageCandidate = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
2317-
ImageData resizedData = DPIUtil.scaleImageData (device, imageCandidate.element(), zoom, imageCandidate.zoom());
2355+
ImageData resizedData = scaleImageData(device, imageCandidate.zoom(), zoom, imageCandidate.element());
23182356
ImageData newData = adaptImageDataIfDisabledOrGray(resizedData);
23192357
init(newData, zoom);
23202358
return zoomLevelToImageHandle.get(zoom);

0 commit comments

Comments
 (0)