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 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 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 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 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 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 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);