Skip to content

Commit 1ffa8a7

Browse files
committed
MenuItem Image Rounding Error #62
This commit contributes to fixing the logic for scaling ImageData with Smooth scaling strategy. The previous implementation scales the bounds of image up and down several times which can lead to rounding error in case of scaling factor being a fractional value. With this implementation, the obtained imageData has no rounding error after ruling out those scale ups and downs and hence improves the menu item icons. contributes to #62 and #127
1 parent 1bc7548 commit 1ffa8a7

File tree

4 files changed

+70
-8
lines changed

4 files changed

+70
-8
lines changed

bundles/org.eclipse.swt/Eclipse SWT PI/win32/org/eclipse/swt/internal/win32/OS.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,7 @@ public class OS extends C {
12461246
public static final int SM_CYFOCUSBORDER = 84;
12471247
public static final int SM_CYHSCROLL = 0x3;
12481248
public static final int SM_CYMENU = 0xf;
1249+
public static final int SM_CYMENUCHECK = 72;
12491250
public static final int SM_CXMINTRACK = 34;
12501251
public static final int SM_CYMINTRACK = 35;
12511252
public static final int SM_CXMAXTRACK = 59;

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

Lines changed: 17 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
*/
@@ -631,7 +635,19 @@ public static boolean useCairoAutoScale() {
631635
return useCairoAutoScale;
632636
}
633637

638+
public static int getZoomForMenuItemImage(int nativeDeviceZoom) {
639+
String autoScaleValueForMenuItemImage = DPIUtil.autoScaleValue;
640+
if(autoScaleValueForMenuItemImage.equals("quarter") || autoScaleValueForMenuItemImage.equals("exact")) {
641+
autoScaleValueForMenuItemImage = "half";
642+
}
643+
return getZoomForAutoscaleProperty(nativeDeviceZoom, autoScaleValueForMenuItemImage);
644+
}
645+
634646
public static int getZoomForAutoscaleProperty (int nativeDeviceZoom) {
647+
return getZoomForAutoscaleProperty(nativeDeviceZoom, autoScaleValue);
648+
}
649+
650+
private static int getZoomForAutoscaleProperty (int nativeDeviceZoom, String autoScaleValue) {
635651
int zoom = 0;
636652
if (autoScaleValue != null) {
637653
if ("false".equalsIgnoreCase (autoScaleValue)) {

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

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.eclipse.swt.internal.DPIUtil.*;
2323
import org.eclipse.swt.internal.gdip.*;
2424
import org.eclipse.swt.internal.win32.*;
25+
import org.eclipse.swt.widgets.*;
2526

2627
/**
2728
* Instances of this class are graphics which have been prepared
@@ -1420,7 +1421,7 @@ public ImageData getImageData (int zoom) {
14201421
}
14211422
TreeSet<Integer> availableZooms = new TreeSet<>(zoomLevelToImageHandle.keySet());
14221423
int closestZoom = Optional.ofNullable(availableZooms.higher(zoom)).orElse(availableZooms.lower(zoom));
1423-
return DPIUtil.scaleImageData (device, getImageMetadata(closestZoom).getImageData(), zoom, closestZoom);
1424+
return scaleImageData(closestZoom, zoom, getImageMetadata(closestZoom).getImageData());
14241425
}
14251426

14261427
/**
@@ -2048,6 +2049,40 @@ private void setBackground(Color color, long handle) {
20482049
device.internal_dispose_GC(hDC, null);
20492050
}
20502051

2052+
private ImageData scaleImageData(int currentZoom, int targetZoom, ImageData imageData) {
2053+
float scaleFactor = (float) targetZoom / (float) currentZoom;
2054+
int width = imageData.width;
2055+
int height = imageData.height;
2056+
int scaledWidth = Math.round (width * scaleFactor);
2057+
int scaledHeight = Math.round (height * scaleFactor);
2058+
boolean useSmoothScaling = DPIUtil.isSmoothScalingEnabled() && imageData.getTransparencyType() != SWT.TRANSPARENCY_MASK;
2059+
if (useSmoothScaling) {
2060+
return scaleToUsingSmoothScaling(scaledWidth, scaledHeight, imageData);
2061+
} else {
2062+
return imageData.scaledTo (scaledWidth, scaledHeight);
2063+
}
2064+
}
2065+
2066+
private ImageData scaleToUsingSmoothScaling(int width, int height, ImageData imageData) {
2067+
Image original = new Image (Display.getCurrent(), (ImageDataProvider) zoom -> imageData);
2068+
/* Create a 24 bit image data with alpha channel */
2069+
final ImageData resultData = new ImageData (width, height, 24, new PaletteData (0xFF, 0xFF00, 0xFF0000));
2070+
resultData.alphaData = new byte [width * height];
2071+
Image resultImage = new Image (Display.getCurrent(), (ImageDataProvider) zoom -> resultData);
2072+
GC gc = new GC (resultImage);
2073+
gc.setAntialias (SWT.ON);
2074+
gc.drawImage (original, 0, 0, imageData.width, imageData.height,
2075+
/* E.g. destWidth here is effectively DPIUtil.autoScaleDown (scaledWidth), but avoiding rounding errors.
2076+
* Nevertheless, we still have some rounding errors due to the point-based API GC#drawImage(..).
2077+
*/
2078+
0, 0, width, height, false);
2079+
gc.dispose ();
2080+
original.dispose ();
2081+
ImageData result = resultImage.getImageData (resultImage.getZoom());
2082+
resultImage.dispose ();
2083+
return result;
2084+
}
2085+
20512086
private int getZoom() {
20522087
return DPIUtil.getZoomForAutoscaleProperty(initialNativeZoom);
20532088
}
@@ -2138,7 +2173,8 @@ protected Rectangle getBounds(int zoom) {
21382173
@Override
21392174
ImageData getImageData(int zoom) {
21402175
ElementAtZoom<String> fileName = DPIUtil.validateAndGetImagePathAtZoom (provider, zoom);
2141-
return DPIUtil.scaleImageData (device, new ImageData (fileName.element()), zoom, fileName.zoom());
2176+
ImageData imageData = new ImageData (fileName.element());
2177+
return scaleImageData(fileName.zoom(), zoom, imageData);
21422178
}
21432179

21442180
@Override
@@ -2151,7 +2187,7 @@ ImageHandle getImageMetadata(int zoom) {
21512187
if (imageMetadata == null) init(imageData, zoom);
21522188
init();
21532189
} else {
2154-
ImageData resizedData = DPIUtil.scaleImageData(device, imageData, zoom, imageCandidate.zoom());
2190+
ImageData resizedData = scaleImageData(imageCandidate.zoom(), zoom, imageData);
21552191
ImageData newData = adaptImageDataIfDisabledOrGray(resizedData);
21562192
init(newData, zoom);
21572193
}
@@ -2201,13 +2237,13 @@ protected Rectangle getBounds(int zoom) {
22012237
@Override
22022238
ImageData getImageData(int zoom) {
22032239
ElementAtZoom<ImageData> data = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
2204-
return DPIUtil.scaleImageData (device, data.element(), zoom, data.zoom());
2240+
return scaleImageData(data.zoom(), zoom, data.element());
22052241
}
22062242

22072243
@Override
22082244
ImageHandle getImageMetadata(int zoom) {
22092245
ElementAtZoom<ImageData> imageCandidate = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
2210-
ImageData resizedData = DPIUtil.scaleImageData (device, imageCandidate.element(), zoom, imageCandidate.zoom());
2246+
ImageData resizedData = scaleImageData(imageCandidate.zoom(), zoom, imageCandidate.element());
22112247
ImageData newData = adaptImageDataIfDisabledOrGray(resizedData);
22122248
init(newData, zoom);
22132249
init();

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/MenuItem.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -781,8 +781,7 @@ public void setImage (Image image) {
781781
info.hbmpItem = OS.HBMMENU_CALLBACK;
782782
} else {
783783
if (OS.IsAppThemed ()) {
784-
if (hBitmap != 0) OS.DeleteObject (hBitmap);
785-
info.hbmpItem = hBitmap = image != null ? Display.create32bitDIB (image, getZoom()) : 0;
784+
info.hbmpItem = hBitmap = getMenuItemIconBitmapHandle(image);
786785
} else {
787786
info.hbmpItem = image != null ? OS.HBMMENU_CALLBACK : 0;
788787
}
@@ -792,6 +791,16 @@ public void setImage (Image image) {
792791
parent.redraw ();
793792
}
794793

794+
private long getMenuItemIconBitmapHandle(Image image) {
795+
if(image == null) {
796+
return 0;
797+
}
798+
if (hBitmap != 0) OS.DeleteObject (hBitmap);
799+
int desiredSize = getSystemMetrics(OS.SM_CYMENUCHECK);
800+
int zoom = (int) (((double) desiredSize / image.getBounds().height) * 100);
801+
return Display.create32bitDIB (image, zoom);
802+
}
803+
795804
/**
796805
* Sets the receiver's pull down menu to the argument.
797806
* Only <code>CASCADE</code> menu items can have a

0 commit comments

Comments
 (0)