Skip to content

Commit ee7e0e7

Browse files
committed
Improve Program icons scaling for win32 #62
This commit implements scaling of program icons by retrieving the system ImageList at various zoom levels and finding the ImageList with size of icons closest to the zoom that is required and obtaining the scaled imageData from the retrieved icon. contributes to #62 and #127
1 parent 857ac0b commit ee7e0e7

File tree

9 files changed

+116
-18
lines changed

9 files changed

+116
-18
lines changed

bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/com.c

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2022 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -870,6 +870,24 @@ JNIEXPORT jlong JNICALL COM_NATIVE(SHCreateMemStream)
870870
}
871871
#endif
872872

873+
#ifndef NO_SHGetImageList
874+
JNIEXPORT jlong JNICALL COM_NATIVE(SHGetImageList)
875+
(JNIEnv *env, jclass that, jint arg0, jobject arg1, jlongArray arg2)
876+
{
877+
GUID _arg1, *lparg1=NULL;
878+
jlong *lparg2=NULL;
879+
jlong rc = 0;
880+
COM_NATIVE_ENTER(env, that, SHGetImageList_FUNC);
881+
if (arg1) if ((lparg1 = getGUIDFields(env, arg1, &_arg1)) == NULL) goto fail;
882+
if (arg2) if ((lparg2 = (*env)->GetPrimitiveArrayCritical(env, arg2, NULL)) == NULL) goto fail;
883+
rc = (jlong)SHGetImageList(arg0, lparg1, (VOID **)lparg2);
884+
fail:
885+
if (arg2 && lparg2) (*env)->ReleasePrimitiveArrayCritical(env, arg2, lparg2, 0);
886+
COM_NATIVE_EXIT(env, that, SHGetImageList_FUNC);
887+
return rc;
888+
}
889+
#endif
890+
873891
#ifndef NO_STGMEDIUM_1sizeof
874892
JNIEXPORT jint JNICALL COM_NATIVE(STGMEDIUM_1sizeof)
875893
(JNIEnv *env, jclass that)

bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/com_stats.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2023 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -83,6 +83,7 @@ typedef enum {
8383
RevokeDragDrop_FUNC,
8484
SHCreateItemFromParsingName_FUNC,
8585
SHCreateMemStream_FUNC,
86+
SHGetImageList_FUNC,
8687
STGMEDIUM_1sizeof_FUNC,
8788
StgCreateDocfile_FUNC,
8889
StgIsStorageFile_FUNC,

bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/os.c

+12
Original file line numberDiff line numberDiff line change
@@ -3981,6 +3981,18 @@ JNIEXPORT void JNICALL OS_NATIVE(ImageList_1EndDrag)
39813981
}
39823982
#endif
39833983

3984+
#ifndef NO_ImageList_1GetIcon
3985+
JNIEXPORT jlong JNICALL OS_NATIVE(ImageList_1GetIcon)
3986+
(JNIEnv *env, jclass that, jlong arg0, jint arg1, jint arg2)
3987+
{
3988+
jlong rc = 0;
3989+
OS_NATIVE_ENTER(env, that, ImageList_1GetIcon_FUNC);
3990+
rc = (jlong)ImageList_GetIcon((HIMAGELIST)arg0, arg1, (UINT)arg2);
3991+
OS_NATIVE_EXIT(env, that, ImageList_1GetIcon_FUNC);
3992+
return rc;
3993+
}
3994+
#endif
3995+
39843996
#ifndef NO_ImageList_1GetIconSize
39853997
JNIEXPORT jboolean JNICALL OS_NATIVE(ImageList_1GetIconSize)
39863998
(JNIEnv *env, jclass that, jlong arg0, jintArray arg1, jintArray arg2)

bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/os_stats.h

+1
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ typedef enum {
310310
ImageList_1DragMove_FUNC,
311311
ImageList_1DragShowNolock_FUNC,
312312
ImageList_1EndDrag_FUNC,
313+
ImageList_1GetIcon_FUNC,
313314
ImageList_1GetIconSize_FUNC,
314315
ImageList_1GetImageCount_FUNC,
315316
ImageList_1Remove_FUNC,

bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/os_structs.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2022 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0

bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/os_structs.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2021 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public class COM extends OS {
107107
public static final GUID IIDIAccessibleImage = IIDFromString("{FE5ABB3D-615E-4f7b-909F-5F0EDA9E8DDE}"); //$NON-NLS-1$
108108
public static final GUID IIDIAccessibleApplication = IIDFromString("{D49DED83-5B25-43F4-9B95-93B44595979E}"); //$NON-NLS-1$
109109
public static final GUID IIDIAccessibleContext = IIDFromString("{77A123E4-5794-44e0-B8BF-DE600C9D29BD}"); //$NON-NLS-1$
110-
110+
public static final GUID IID_IImageList = IIDFromString("{46EB5926-582E-4017-9FDF-E8998DAA0950}");
111111
/** Constants */
112112
public static final int CF_TEXT = 1;
113113
public static final int CF_BITMAP = 2;
@@ -465,6 +465,11 @@ private static GUID IIDFromString(String lpsz) {
465465
* @param ppv cast=(void **)
466466
*/
467467
public static final native int SHCreateItemFromParsingName (char [] pszName, long pbc, GUID riid, long [] ppv);
468+
/**
469+
* @param riid flags=no_out
470+
* @param ppvObj cast=(VOID **),flags=no_in critical
471+
*/
472+
public static final native long SHGetImageList (int iImageList, GUID riid, long[] ppvObj);
468473
/**
469474
* @param pwcsName cast=(const WCHAR *),flags=no_out
470475
* @param ppstgOpen cast=(IStorage **)

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

+11
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@ public class OS extends C {
661661
public static final int ILC_COLOR8 = 0x8;
662662
public static final int ILC_MASK = 0x1;
663663
public static final int ILC_MIRROR = 0x2000;
664+
public static final int ILD_TRANSPARENT = 0x00000001;
664665
public static final int IMAGE_ICON = 0x1;
665666
public static final int IME_CMODE_FULLSHAPE = 0x8;
666667
public static final int IME_CMODE_KATAKANA = 0x2;
@@ -1209,9 +1210,14 @@ public class OS extends C {
12091210
public static final int SET_FEATURE_ON_PROCESS = 0x2;
12101211
public static final int SHADEBLENDCAPS = 120;
12111212
public static final int SHGFI_ICON = 0x000000100;
1213+
public static final int SHGFI_ICONLOCATION = 0x000001000;
12121214
public static final int SHGFI_SMALLICON= 0x1;
12131215
public static final int SHGFI_LARGEICON= 0x0;
12141216
public static final int SHGFI_USEFILEATTRIBUTES = 0x000000010;
1217+
public static final int SHIL_LARGE = 0x0;
1218+
public static final int SHIL_SMALL = 0x1;
1219+
public static final int SHIL_EXTRALARGE = 0x2;
1220+
public static final int SHIL_SYSSMALL = 0x3;
12151221
public static final int SIGDN_FILESYSPATH = 0x80058000;
12161222
public static final int SIF_ALL = 0x17;
12171223
public static final int SIF_DISABLENOSCROLL = 0x8;
@@ -4458,6 +4464,11 @@ public static int HRESULT_FROM_WIN32(int x) {
44584464
public static final native boolean ShowScrollBar (long hWnd, int wBar, boolean bShow);
44594465
/** @param hWnd cast=(HWND) */
44604466
public static final native boolean ShowWindow (long hWnd, int nCmdShow);
4467+
/**
4468+
* @param himl cast=(HIMAGELIST)
4469+
* @param flags cast=(UINT)
4470+
*/
4471+
public static final native long ImageList_GetIcon (long himl, int i, int flags);
44614472
/**
44624473
* @param hdc cast=(HDC)
44634474
* @param lpdi flags=no_out

bundles/org.eclipse.swt/Eclipse SWT Program/win32/org/eclipse/swt/program/Program.java

+63-13
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import org.eclipse.swt.*;
2121
import org.eclipse.swt.graphics.*;
2222
import org.eclipse.swt.internal.*;
23+
import org.eclipse.swt.internal.ole.win32.*;
2324
import org.eclipse.swt.internal.win32.*;
25+
import org.eclipse.swt.widgets.*;
2426

2527
/**
2628
* Instances of this class represent programs and
@@ -381,26 +383,20 @@ public ImageData getImageData () {
381383
public ImageData getImageData (int zoom) {
382384
// OS.SHGetFileInfo is System DPI-aware, hence it retrieves the icon with zoom
383385
// of primary monitor at the application startup
384-
int initialNativeZoom = getPrimaryMonitorZoomAtStartup();
385386
if (extension != null) {
386387
SHFILEINFO shfi = new SHFILEINFO ();
387-
int flags = OS.SHGFI_ICON | OS.SHGFI_USEFILEATTRIBUTES;
388-
boolean useLargeIcon = 100 * zoom / initialNativeZoom >= 200;
389-
if(useLargeIcon) {
390-
flags |= OS.SHGFI_LARGEICON;
391-
initialNativeZoom *= 2;
392-
} else {
393-
flags |= OS.SHGFI_SMALLICON;
394-
}
388+
int flags = OS.SHGFI_USEFILEATTRIBUTES | OS.SHGFI_ICONLOCATION;
395389
TCHAR pszPath = new TCHAR (0, extension, true);
396390
OS.SHGetFileInfo (pszPath.chars, OS.FILE_ATTRIBUTE_NORMAL, shfi, SHFILEINFO.sizeof, flags);
397-
if (shfi.hIcon != 0) {
398-
Image image = Image.win32_new (null, SWT.ICON, shfi.hIcon, initialNativeZoom);
399-
ImageData imageData = image.getImageData (zoom);
400-
image.dispose ();
391+
if (shfi.iIcon >= 0) {
392+
Image icon = getImageForZoom(zoom, shfi.iIcon);
393+
ImageData imageData = icon.getImageData(zoom);
394+
icon.dispose();
401395
return imageData;
402396
}
403397
}
398+
399+
int initialNativeZoom = getPrimaryMonitorZoomAtStartup();
404400
int nIconIndex = 0;
405401
String fileName = iconName;
406402
int index = iconName.indexOf (',');
@@ -434,6 +430,60 @@ private int getPrimaryMonitorZoomAtStartup() {
434430
return DPIUtil.mapDPIToZoom(dpi);
435431
}
436432

433+
private NavigableMap<Integer, Long> getImageListinAllAvailableSizesMappedWithZoom() {
434+
TreeMap<Integer, Long> zoomToHImageList = new TreeMap<>();
435+
int primaryMonitorZoomAtStartup = getPrimaryMonitorZoomAtStartup();
436+
// SHIL_SYSSMALL is the size of icon at startup zoom of the primary monitor. The
437+
// sizes can differ as specified by OS.GetSystemMetrics.
438+
long hImageListAtPrimaryMonitorZoomAtStartup = getImageListForSize(OS.SHIL_SYSSMALL);
439+
int sizeAtPrimaryMonitorZoomAtStartup = getIconSizeOfImageList(hImageListAtPrimaryMonitorZoomAtStartup);
440+
zoomToHImageList.put(primaryMonitorZoomAtStartup, hImageListAtPrimaryMonitorZoomAtStartup);
441+
// SHIL_* (SMALL, LARGE and EXTRALARGE) are typically 1x, 2x, 3x relative to the
442+
// primary monitor zoom at startup, e.g., 16px, 32px and 48px
443+
// respectively.
444+
int[] allSizes = new int[] { OS.SHIL_SMALL, OS.SHIL_LARGE, OS.SHIL_EXTRALARGE };
445+
for (int size : allSizes) {
446+
long hImageList = getImageListForSize(size);
447+
int iconSize = getIconSizeOfImageList(hImageList);
448+
int zoom = (iconSize / sizeAtPrimaryMonitorZoomAtStartup) * primaryMonitorZoomAtStartup;
449+
if (!zoomToHImageList.containsKey(zoom)) {
450+
zoomToHImageList.put(zoom, hImageList);
451+
} else {
452+
OS.ImageList_Destroy(hImageList);
453+
}
454+
}
455+
return zoomToHImageList;
456+
}
457+
458+
private int getIconSizeOfImageList(long hImageList) {
459+
int [] cx = new int [1];
460+
int [] cy = new int [1];
461+
OS.ImageList_GetIconSize(hImageList, cx, cy);
462+
return cx[0];
463+
}
464+
465+
private Image getImageForZoom(int zoom, int index) {
466+
NavigableMap<Integer, Long> zoomToHImageList = getImageListinAllAvailableSizesMappedWithZoom();
467+
int closestZoomAvailable = getClosestKey(zoomToHImageList, zoom);
468+
long hIcon = OS.ImageList_GetIcon(zoomToHImageList.get(closestZoomAvailable), index, OS.ILD_TRANSPARENT);
469+
zoomToHImageList.values().forEach(handle -> OS.ImageList_Destroy(handle));
470+
return Image.win32_new(Display.getCurrent(), SWT.ICON, hIcon, closestZoomAvailable);
471+
}
472+
473+
private long getImageListForSize(int size) {
474+
long [] ppv = new long [1];
475+
COM.SHGetImageList(size, COM.IID_IImageList, ppv);
476+
return ppv[0];
477+
}
478+
479+
private static int getClosestKey(NavigableMap<Integer, Long> map, int x) {
480+
Integer floorKey = map.floorKey(x);
481+
Integer ceilingKey = map.ceilingKey(x);
482+
if (floorKey == null) return ceilingKey;
483+
if (ceilingKey == null) return floorKey;
484+
return (Math.abs(x - floorKey) <= Math.abs(x - ceilingKey)) ? floorKey : ceilingKey;
485+
}
486+
437487
/**
438488
* Returns the receiver's name. This is as short and
439489
* descriptive a name as possible for the program. If

0 commit comments

Comments
 (0)