From f5e534a8b3b85e70e8ac5c79f9d0ebc55b2cf04c Mon Sep 17 00:00:00 2001 From: Amartya Parijat Date: Mon, 30 Sep 2024 15:07:42 +0200 Subject: [PATCH] Destroy unused handles of resources This Contribution destroys those handles for the resources which were created for zoom levels which no monitor has anymore. The process is triggered on a DPI_CHANGED event. contributes to #62 and #127 --- .../org/eclipse/swt/graphics/Resource.java | 12 +++++ .../org/eclipse/swt/graphics/Device.java | 27 +++++++++++ .../win32/org/eclipse/swt/graphics/Image.java | 36 ++++++++++++-- .../win32/org/eclipse/swt/graphics/Path.java | 15 ++++++ .../org/eclipse/swt/graphics/Pattern.java | 48 +++++++++++++------ .../org/eclipse/swt/graphics/Region.java | 13 +++++ .../org/eclipse/swt/graphics/Transform.java | 13 +++++ .../org/eclipse/swt/widgets/Control.java | 1 + 8 files changed, 145 insertions(+), 20 deletions(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Resource.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Resource.java index cb47f9ddb39..5d61855544b 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Resource.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Resource.java @@ -13,6 +13,7 @@ *******************************************************************************/ package org.eclipse.swt.graphics; +import java.util.*; import java.util.function.*; import org.eclipse.swt.*; @@ -123,6 +124,17 @@ public Resource() { void destroy() { } +/** + * Destroy all handles of the resource which are not necessary for the given + * zoom levels. This method is supposed to be overridden by sub-classes that + * retain handles for different zoom levels. + * + * @param zoomLevels The zoom levels for which the handles are supposed to be + * retained. + */ +void destroyHandlesExcept(Set zoomLevels) { +} + /** * Disposes of the operating system resources associated with * this resource. Applications must dispose of all resources diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Device.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Device.java index f43b6bbc582..e136a87d87b 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Device.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Device.java @@ -14,10 +14,14 @@ package org.eclipse.swt.graphics; +import java.util.*; +import java.util.concurrent.*; + import org.eclipse.swt.*; import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.gdip.*; import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.widgets.*; /** * This class is the abstract superclass of all device objects, @@ -55,6 +59,7 @@ public abstract class Device implements Drawable { String[] loadedFonts; volatile boolean disposed; + private Set resourcesWithZoomSupport = ConcurrentHashMap.newKeySet(); /* * TEMPORARY CODE. When a graphics object is @@ -914,6 +919,8 @@ protected void release () { Gdip.GdiplusShutdown (gdipToken[0]); } SWTFontProvider.disposeFontRegistry(this); + resourcesWithZoomSupport.clear(); + resourcesWithZoomSupport = null; gdipToken = null; scripts = null; logFonts = null; @@ -947,4 +954,24 @@ public void setWarnings (boolean warnings) { protected int getDeviceZoom () { return DPIUtil.mapDPIToZoom ( _getDPIx ()); } + +void registerResourceWithZoomSupport(Resource resource) { + resourcesWithZoomSupport.add(resource); +} + +/** + * Destroys the handles of all the resources in the resource tracker by + * identifying the zoom levels which is not valid for any monitor + * + * @noreference This method is not intended to be referenced by clients. + */ +public static void win32_destroyUnusedHandles(Display display) { + Set availableZoomLevels = new HashSet<>(); + for (Monitor monitor : display.getMonitors()) { + availableZoomLevels.add(DPIUtil.getZoomForAutoscaleProperty(monitor.getZoom())); + } + for (Resource resource: ((Device) display).resourcesWithZoomSupport) { + resource.destroyHandlesExcept(availableZoomLevels); + } +} } 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 afed0488e7e..7d57b456a2c 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 @@ -164,6 +164,7 @@ public final class Image extends Resource implements Drawable { Image (Device device) { super(device); initialNativeZoom = DPIUtil.getNativeDeviceZoom(); + this.device.registerResourceWithZoomSupport(this); } /** @@ -209,6 +210,7 @@ public Image(Device device, int width, int height) { height = DPIUtil.scaleUp (height, zoom); init(width, height); init(); + this.device.registerResourceWithZoomSupport(this); } /** @@ -311,6 +313,7 @@ public Image(Device device, Image srcImage, int flag) { SWT.error(SWT.ERROR_INVALID_ARGUMENT); } init(); + this.device.registerResourceWithZoomSupport(this); } /** @@ -355,6 +358,7 @@ public Image(Device device, Rectangle bounds) { bounds = DPIUtil.scaleUp (bounds, getZoom()); init(bounds.width, bounds.height); init(); + this.device.registerResourceWithZoomSupport(this); } /** @@ -388,6 +392,7 @@ public Image(Device device, ImageData data) { data = DPIUtil.autoScaleUp(device, this.dataAtBaseZoom); init(data, getZoom()); init(); + this.device.registerResourceWithZoomSupport(this); } /** @@ -434,6 +439,7 @@ public Image(Device device, ImageData source, ImageData mask) { mask = ImageData.convertMask(mask); init(this.device, this, source, mask, getZoom()); init(); + this.device.registerResourceWithZoomSupport(this); } /** @@ -496,6 +502,7 @@ public Image (Device device, InputStream stream) { ImageData data = DPIUtil.autoScaleUp(device, this.dataAtBaseZoom); init(data, getZoom()); init(); + this.device.registerResourceWithZoomSupport(this); } /** @@ -538,6 +545,7 @@ public Image (Device device, String filename) { ImageData data = DPIUtil.autoScaleUp(device, this.dataAtBaseZoom); init(data, getZoom()); init(); + this.device.registerResourceWithZoomSupport(this); } /** @@ -586,6 +594,7 @@ public Image(Device device, ImageFileNameProvider imageFileNameProvider) { init(resizedData, getZoom()); } init(); + this.device.registerResourceWithZoomSupport(this); } /** @@ -625,6 +634,7 @@ public Image(Device device, ImageDataProvider imageDataProvider) { ImageData resizedData = DPIUtil.scaleImageData(device, data.element(), getZoom(), data.zoom()); init (resizedData, getZoom()); init(); + this.device.registerResourceWithZoomSupport(this); } private ImageData adaptImageDataIfDisabledOrGray(ImageData data) { @@ -1186,16 +1196,32 @@ void destroy () { private void destroyHandle () { for (Long handle : zoomLevelToHandle.values()) { - if (type == SWT.ICON) { - OS.DestroyIcon (handle); - } else { - OS.DeleteObject (handle); - } + destroyHandle(handle); } zoomLevelToHandle.clear(); handle = 0; } +@Override +void destroyHandlesExcept(Set zoomLevels) { + zoomLevelToHandle.entrySet().removeIf(entry -> { + final Integer zoom = entry.getKey(); + if (!zoomLevels.contains(zoom) && zoom != DPIUtil.getZoomForAutoscaleProperty(initialNativeZoom)) { + destroyHandle(entry.getValue()); + return true; + } + return false; + }); +} + +private void destroyHandle(long handle) { + if (type == SWT.ICON) { + OS.DestroyIcon (handle); + } else { + OS.DeleteObject (handle); + } +} + /** * Compares the argument to the receiver, and returns true * if they represent the same object using a class diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Path.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Path.java index 54d392f09a6..7949d08a685 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Path.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Path.java @@ -86,6 +86,7 @@ private Path(Device device, int zoom) { if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); zoomLevelToHandle.put(initialZoom, handle); init(); + this.device.registerResourceWithZoomSupport(this); } /** @@ -133,6 +134,7 @@ public Path (Device device, Path path, float flatness) { initialZoom = path.initialZoom; zoomLevelToHandle.put(initialZoom, handle); init(); + this.device.registerResourceWithZoomSupport(this); } /** @@ -172,6 +174,7 @@ private Path(Device device, PathData data, int zoom) { this(device, zoom); if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); init(data); + this.device.registerResourceWithZoomSupport(this); } /** @@ -438,6 +441,18 @@ void destroy() { zoomLevelToHandle.clear(); } +@Override +void destroyHandlesExcept(Set zoomLevels) { + zoomLevelToHandle.entrySet().removeIf(entry -> { + final Integer zoom = entry.getKey(); + if (!zoomLevels.contains(zoom) && zoom != initialZoom) { + Gdip.GraphicsPath_delete(entry.getValue()); + return true; + } + return false; + }); +} + /** * Replaces the first four elements in the parameter with values that * describe the smallest rectangle that will completely contain the diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Pattern.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Pattern.java index 02a065f325f..36e9d97bef8 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Pattern.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Pattern.java @@ -90,6 +90,7 @@ public Pattern(Device device, Image image) { initialZoom = DPIUtil.getDeviceZoom(); setImageHandle(image, initialZoom); init(); + this.device.registerResourceWithZoomSupport(this); } /** @@ -183,6 +184,7 @@ public Pattern(Device device, float x1, float y1, float x2, float y2, Color colo this.image = null; initialZoom = DPIUtil.getDeviceZoom(); initializeSize(initialZoom); + this.device.registerResourceWithZoomSupport(this); } long getHandle(int zoom) { @@ -260,21 +262,7 @@ void setImageHandle(Image image, int zoom) { @Override void destroy() { for (long handle: zoomLevelToHandle.values()) { - int type = Gdip.Brush_GetType(handle); - switch (type) { - case Gdip.BrushTypeSolidColor: - Gdip.SolidBrush_delete(handle); - break; - case Gdip.BrushTypeHatchFill: - Gdip.HatchBrush_delete(handle); - break; - case Gdip.BrushTypeLinearGradient: - Gdip.LinearGradientBrush_delete(handle); - break; - case Gdip.BrushTypeTextureFill: - Gdip.TextureBrush_delete(handle); - break; - } + destroyHandle(handle); } zoomLevelToHandle.clear(); if (bitmapDestructor != null) { @@ -283,6 +271,36 @@ void destroy() { } } +@Override +void destroyHandlesExcept(Set zoomLevels) { + zoomLevelToHandle.entrySet().removeIf(entry -> { + final Integer zoom = entry.getKey(); + if (!zoomLevels.contains(zoom) && zoom != initialZoom) { + destroyHandle(entry.getValue()); + return true; + } + return false; + }); +} + +private void destroyHandle(long handle) { + int type = Gdip.Brush_GetType(handle); + switch (type) { + case Gdip.BrushTypeSolidColor: + Gdip.SolidBrush_delete(handle); + break; + case Gdip.BrushTypeHatchFill: + Gdip.HatchBrush_delete(handle); + break; + case Gdip.BrushTypeLinearGradient: + Gdip.LinearGradientBrush_delete(handle); + break; + case Gdip.BrushTypeTextureFill: + Gdip.TextureBrush_delete(handle); + break; + } +} + /** * Returns true if the Pattern has been disposed, * and false otherwise. diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Region.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Region.java index 6c726998146..5219f385e52 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Region.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Region.java @@ -84,6 +84,7 @@ public Region (Device device) { zoomToHandle.put(initialZoom, handle); if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); init(); + this.device.registerResourceWithZoomSupport(this); } /** @@ -227,6 +228,18 @@ void destroy () { operations.clear(); } +@Override +void destroyHandlesExcept(Set zoomLevels) { + zoomToHandle.entrySet().removeIf(entry -> { + final Integer zoom = entry.getKey(); + if (!zoomLevels.contains(zoom) && zoom != initialZoom) { + OS.DeleteObject(entry.getValue()); + return true; + } + return false; + }); +} + /** * Compares the argument to the receiver, and returns true * if they represent the same object using a class diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Transform.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Transform.java index 058974d7a0f..c7174509209 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Transform.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Transform.java @@ -145,6 +145,7 @@ public Transform (Device device, float m11, float m12, float m21, float m22, flo if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); zoomLevelToHandle.put(initialZoom, handle); init(); + this.device.registerResourceWithZoomSupport(this); } static float[] checkTransform(float[] elements) { @@ -159,6 +160,18 @@ void destroy() { zoomLevelToHandle.clear(); } +@Override +void destroyHandlesExcept(Set zoomLevels) { + zoomLevelToHandle.entrySet().removeIf(entry -> { + final Integer zoom = entry.getKey(); + if (!zoomLevels.contains(zoom) && zoom != initialZoom) { + Gdip.Matrix_delete(entry.getValue()); + return true; + } + return false; + }); +} + /** * Fills the parameter with the values of the transformation matrix * that the receiver represents, in the order {m11, m12, m21, m22, dx, dy}. diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java index 76a60820c97..7478dabfb27 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java @@ -4899,6 +4899,7 @@ LRESULT WM_DPICHANGED (long wParam, long lParam) { // Map DPI to Zoom and compare int newNativeZoom = DPIUtil.mapDPIToZoom (OS.HIWORD (wParam)); if (getDisplay().isRescalingAtRuntime()) { + Device.win32_destroyUnusedHandles(getDisplay()); int oldNativeZoom = nativeZoom; if (newNativeZoom != oldNativeZoom) { DPIUtil.setDeviceZoom (newNativeZoom);