diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java index e9fd414034..9eb543f311 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java @@ -4461,6 +4461,15 @@ private long identity() { return Gdip.Matrix_new(1, 0, 0, 1, 0, 0); } +private void initWithImageHandle(Drawable drawable, GCData data, long hDC, ImageHandle imageHandle) { + Image image = data.image; + data.image = null; + init(drawable, data, hDC); + data.hNullBitmap = OS.SelectObject(hDC, imageHandle.getHandle()); + image.memGC = this; + data.image = image; +} + private void init(Drawable drawable, GCData data, long hDC) { int foreground = data.foreground; if (foreground != -1) { @@ -5935,12 +5944,12 @@ Point textExtentInPixels(String string, int flags) { return new Point(rect.right, rect.bottom); } -void refreshFor(Drawable drawable) { +void refreshFor(Drawable drawable, ImageHandle imageHandle) { if (drawable == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); destroy(); GCData newData = new GCData(); originalData.copyTo(newData); - createGcHandle(drawable, newData); + createGcHandle(drawable, newData, imageHandle); } /** @@ -6059,10 +6068,10 @@ private void removePreviousOperationIfSupercededBy(Operation operation) { } } -private void createGcHandle(Drawable drawable, GCData newData) { +private void createGcHandle(Drawable drawable, GCData newData, ImageHandle imageHandle) { long newHandle = drawable.internal_new_GC(newData); if (newHandle == 0) SWT.error(SWT.ERROR_NO_HANDLES); - init(drawable, newData, newHandle); + initWithImageHandle(drawable, newData, newHandle, imageHandle); for (Operation operation : operations) { operation.apply(); } 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 a580d24c3c..e01de576d6 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 @@ -132,17 +132,95 @@ public final class Image extends Resource implements Drawable { */ static final int DEFAULT_SCANLINE_PAD = 4; - private Map zoomLevelToImageHandle = new HashMap<>(); - private List> onDisposeListeners; + private final ImageHandleManager imageHandleManager = new ImageHandleManager(); + + private class ImageHandleManager { + private Map zoomLevelToImageHandle = new HashMap<>(); + + ImageHandle getImageHandle(int zoom) { + final ImageHandle imageHandle = zoomLevelToImageHandle.get(zoom); + if (imageHandle == null) { + return null; + } + return imageHandle; + } + + + ImageHandle getHandleOrCreate(int zoom, Supplier creator) { + if (zoom == -1) { + return null; + } + + ImageHandle imageHandle = getImageHandle(zoom); + if (imageHandle != null) { + return imageHandle; + } + + synchronized (zoomLevelToImageHandle) { + imageHandle = getImageHandle(zoom); + if (imageHandle == null) { + imageHandle = creator.get(); + zoomLevelToImageHandle.put(zoom, imageHandle); + } + return imageHandle; + } + } + + T runSynchronized(Supplier supplier) { + synchronized (zoomLevelToImageHandle) { + return supplier.get(); + } + } + + boolean hasImageHandle(ImageHandle imageHandle) { + return zoomLevelToImageHandle.values().contains(imageHandle); + } + + boolean hasZoom(int zoom) { + return zoomLevelToImageHandle.containsKey(zoom); + } + + boolean hasNoHandle() { + return zoomLevelToImageHandle.isEmpty(); + } + + List getAllImageHandles() { + return zoomLevelToImageHandle.values().stream().toList(); + } + + Set getAllZooms() { + return zoomLevelToImageHandle.keySet(); + } + + void destroyHandles(Predicate filter) { + Iterator> it = zoomLevelToImageHandle.entrySet().iterator(); + while (it.hasNext()) { + Entry zoomToHandle = it.next(); + if (filter.test(zoomToHandle.getKey())) { + ImageHandle imageHandle = zoomToHandle.getValue(); + it.remove(); + zoomLevelToImageHandle.remove(imageHandle.zoom, imageHandle); + imageHandle.destroy(); + } + } + } + + @Override + public String toString() { + return zoomLevelToImageHandle.toString(); + + } + } + private class HandleAtSize { private ImageHandle handleContainer = null; private int requestedWidth = -1; private int requestedHeight = -1; public void destroy() { - if (handleContainer != null && !zoomLevelToImageHandle.containsValue(handleContainer)) { + if (handleContainer != null && !imageHandleManager.hasImageHandle(handleContainer)) { handleContainer.destroy(); } handleContainer = null; @@ -184,7 +262,7 @@ private ImageHandle getOrCreateImageHandleAtClosestSize(int widthHint, int heigh int imageZoomForWidth = 100 * widthHint / bounds.width; int imageZoomForHeight = 100 * heightHint / bounds.height; int imageZoom = DPIUtil.getZoomForAutoscaleProperty(Math.max(imageZoomForWidth, imageZoomForHeight)); - ImageHandle bestFittingHandle = zoomLevelToImageHandle.get(imageZoom); + ImageHandle bestFittingHandle = imageHandleManager.getImageHandle(imageZoom); if (bestFittingHandle == null) { ImageData bestFittingImageData = imageProvider.loadImageData(imageZoom).element(); ImageData adaptedData = adaptImageDataIfDisabledOrGray(bestFittingImageData); @@ -296,7 +374,7 @@ public Image(Device device, Image srcImage, int flag) { case SWT.IMAGE_COPY: { switch (type) { case SWT.BITMAP: - for (ImageHandle imageHandle : srcImage.zoomLevelToImageHandle.values()) { + for (ImageHandle imageHandle : srcImage.imageHandleManager.getAllImageHandles()) { Rectangle rect = imageHandle.getBounds(); long srcImageHandle = imageHandle.handle; /* Get the HDC for the device */ @@ -308,7 +386,11 @@ public Image(Device device, Image srcImage, int flag) { long hOldSrc = OS.SelectObject(hdcSource, srcImageHandle); BITMAP bm = new BITMAP(); OS.GetObject(srcImageHandle, BITMAP.sizeof, bm); - imageMetadata = new ImageHandle(OS.CreateCompatibleBitmap(hdcSource, rect.width, bm.bmBits != 0 ? -rect.height : rect.height), imageHandle.zoom, imageHandle.transparentPixel); + imageMetadata = imageHandleManager.getHandleOrCreate(imageHandle.zoom, + () -> new ImageHandle( + OS.CreateCompatibleBitmap(hdcSource, rect.width, + bm.bmBits != 0 ? -rect.height : rect.height), + imageHandle.zoom, imageHandle.transparentPixel)); if (imageMetadata.handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); long hOldDest = OS.SelectObject(hdcDest, imageMetadata.handle); OS.BitBlt(hdcDest, 0, 0, rect.width, rect.height, hdcSource, 0, 0, OS.SRCCOPY); @@ -322,9 +404,12 @@ public Image(Device device, Image srcImage, int flag) { } break; case SWT.ICON: - for (ImageHandle imageHandle : srcImage.zoomLevelToImageHandle.values()) { + for (ImageHandle imageHandle : srcImage.imageHandleManager.getAllImageHandles()) { Rectangle rect = imageHandle.getBounds(); - imageMetadata = new ImageHandle(OS.CopyImage(imageHandle.handle, OS.IMAGE_ICON, rect.width, rect.height, 0), imageHandle.zoom, imageHandle.transparentPixel); + imageMetadata = imageHandleManager.getHandleOrCreate(imageHandle.zoom, + () -> new ImageHandle( + OS.CopyImage(imageHandle.handle, OS.IMAGE_ICON, rect.width, rect.height, 0), + imageHandle.zoom, imageHandle.transparentPixel)); if (imageMetadata.handle == 0) SWT.error(SWT.ERROR_NO_HANDLES); } break; @@ -334,20 +419,20 @@ public Image(Device device, Image srcImage, int flag) { break; } case SWT.IMAGE_DISABLE: { - for (ImageHandle imageHandle : srcImage.zoomLevelToImageHandle.values()) { + for (ImageHandle imageHandle : srcImage.imageHandleManager.getAllImageHandles()) { Rectangle rect = imageHandle.getBounds(); ImageData data = srcImage.getImageData(imageHandle.zoom); ImageData newData = applyDisableImageData(data, rect.height, rect.width); - init (newData, imageHandle.zoom); + imageHandleManager.getHandleOrCreate(imageHandle.zoom, () -> init (newData, imageHandle.zoom)); } break; } case SWT.IMAGE_GRAY: { - for (ImageHandle imageHandle : srcImage.zoomLevelToImageHandle.values()) { + for (ImageHandle imageHandle : srcImage.imageHandleManager.getAllImageHandles()) { Rectangle rect = imageHandle.getBounds(); ImageData data = srcImage.getImageData(imageHandle.zoom); ImageData newData = applyGrayImageData(data, rect.height, rect.width); - init (newData, imageHandle.zoom); + imageHandleManager.getHandleOrCreate(imageHandle.zoom, () -> init (newData, imageHandle.zoom)); } break; } @@ -840,10 +925,7 @@ private ImageData applyGrayImageData(ImageData data, int pHeight, int pWidth) { private ImageHandle getImageMetadata(ZoomContext zoomContext) { int targetZoom = zoomContext.targetZoom(); - if (zoomLevelToImageHandle.get(targetZoom) != null) { - return zoomLevelToImageHandle.get(targetZoom); - } - return imageProvider.newImageHandle(zoomContext); + return imageHandleManager.getHandleOrCreate(targetZoom, () -> imageProvider.newImageHandle(zoomContext)); } @@ -1114,24 +1196,12 @@ void destroy () { private void destroyHandles() { lastRequestedHandle.destroy(); - destroyHandles(__ -> true); + imageHandleManager.destroyHandles(__ -> true); } @Override void destroyHandlesExcept(Set zoomLevels) { - destroyHandles(zoom -> !zoomLevels.contains(zoom) && !this.imageProvider.getPreservedZoomLevels().contains(zoom)); -} - -private void destroyHandles(Predicate filter) { - Iterator> it = zoomLevelToImageHandle.entrySet().iterator(); - while (it.hasNext()) { - Entry zoomToHandle = it.next(); - if (filter.test(zoomToHandle.getKey())) { - ImageHandle imageHandle = zoomToHandle.getValue(); - it.remove(); - imageHandle.destroy(); - } - } + imageHandleManager.destroyHandles(zoom -> !zoomLevels.contains(zoom) && !this.imageProvider.getPreservedZoomLevels().contains(zoom)); } /** @@ -1246,8 +1316,8 @@ public Rectangle getBounds() { Rectangle getBounds(int zoom) { if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); - if (zoomLevelToImageHandle.containsKey(zoom)) { - ImageHandle imageMetadata = zoomLevelToImageHandle.get(zoom); + if (imageHandleManager.hasZoom(zoom)) { + ImageHandle imageMetadata = imageHandleManager.getImageHandle(zoom); Rectangle rectangle = new Rectangle(0, 0, imageMetadata.width, imageMetadata.height); return Win32DPIUtils.scaleBounds(rectangle, zoom, imageMetadata.zoom); } @@ -1325,12 +1395,19 @@ public ImageData getImageData() { */ public ImageData getImageData (int zoom) { if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); - if (zoomLevelToImageHandle.containsKey(zoom)) { - return zoomLevelToImageHandle.get(zoom).getImageData(); + ImageHandle imageHandle = imageHandleManager.getImageHandle(zoom); + if (imageHandle != null) { + return imageHandle.getImageData(); } - return this.imageProvider.newImageData(zoom); -} + return imageHandleManager.runSynchronized(() -> { + ImageHandle obtainedImageHandle = imageHandleManager.getImageHandle(zoom); + if (obtainedImageHandle != null) { + return obtainedImageHandle.getImageData(); + } + return this.imageProvider.newImageData(zoom); + }); +} /** * Returns an ImageData based on the receiver. @@ -1654,15 +1731,6 @@ else if (i.alphaData != null) { } } -private void setImageMetadataForHandle(ImageHandle imageMetadata, int zoom) { - if (zoom == -1) - return; - if (zoomLevelToImageHandle.containsKey(zoom)) { - SWT.error(SWT.ERROR_ITEM_NOT_ADDED); - } - zoomLevelToImageHandle.put(zoom, imageMetadata); -} - private ImageHandle initIconHandle(Device device, ImageData source, ImageData mask, Integer zoom) { ImageData imageData = applyMask(source, mask); HandleForImageDataContainer imageDataHandle = init(device, imageData); @@ -1845,8 +1913,8 @@ private void checkImageTypeForValidCustomDrawing(int zoom) { if (imageProvider instanceof ImageDataProviderWrapper || imageProvider instanceof ImageFileNameProviderWrapper) { String message = "***WARNING: Image initialized with ImageDataProvider or ImageFileNameProvider is not supposed to be modified."; System.err.println(message + " " + replacementInfo); - } else if (!zoomLevelToImageHandle.isEmpty() - && (zoomLevelToImageHandle.size() != 1 || !zoomLevelToImageHandle.containsKey(zoom))) { + } else if (!imageHandleManager.hasNoHandle() + && (imageHandleManager.getAllImageHandles().size() != 1 || !imageHandleManager.hasZoom(zoom))) { String message = "***WARNING: Images with handles created for multiple zooms should not be modified. "; System.err.println(message + " " + replacementInfo); } @@ -1926,7 +1994,7 @@ public void setBackground(Color color) { if (color == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); backgroundColor = color.getRGB(); - zoomLevelToImageHandle.values().forEach(imageHandle -> imageHandle.setBackground(backgroundColor)); + imageHandleManager.getAllImageHandles().forEach(imageHandle -> imageHandle.setBackground(backgroundColor)); } /** @@ -1938,11 +2006,11 @@ public void setBackground(Color color) { @Override public String toString () { if (isDisposed()) return "Image {*DISPOSED*}"; - return "Image {" + zoomLevelToImageHandle + "}"; + return "Image {" + imageHandleManager + "}"; } T applyUsingAnyHandle(Function function) { - if (zoomLevelToImageHandle.isEmpty()) { + if (imageHandleManager.hasNoHandle()) { ImageHandle temporaryHandle = this.imageProvider.newImageHandle(new ZoomContext(DPIUtil.getDeviceZoom(), DPIUtil.getNativeDeviceZoom())); try { return function.apply(temporaryHandle); @@ -1950,7 +2018,7 @@ T applyUsingAnyHandle(Function function) { temporaryHandle.destroy(); } } - return function.apply(zoomLevelToImageHandle.values().iterator().next()); + return function.apply(imageHandleManager.getAllImageHandles().get(0)); } /** @@ -2017,7 +2085,7 @@ ImageData getScaledImageData (int zoom) { } ElementAtZoom getClosestAvailableImageData(int zoom) { - TreeSet availableZooms = new TreeSet<>(zoomLevelToImageHandle.keySet()); + TreeSet availableZooms = new TreeSet<>(imageHandleManager.getAllZooms()); int closestZoom = Optional.ofNullable(availableZooms.higher(zoom)).orElse(availableZooms.lower(zoom)); return new ElementAtZoom<>(getImageMetadata(new ZoomContext(closestZoom)).getImageData(), closestZoom); } @@ -2054,7 +2122,7 @@ private class ExistingImageHandleProviderWrapper extends AbstractImageProviderWr public ExistingImageHandleProviderWrapper(long handle, int zoomForHandle) { this.handle = handle; this.zoomForHandle = zoomForHandle; - ImageHandle imageHandle = new ImageHandle(handle, zoomForHandle, -1); + ImageHandle imageHandle = imageHandleManager.getHandleOrCreate(zoomForHandle, () -> new ImageHandle(handle, zoomForHandle, -1)); ImageData baseData = imageHandle.getImageData(); this.width = DPIUtil.pixelToPoint(baseData.width, zoomForHandle); @@ -2271,7 +2339,7 @@ protected Rectangle getBounds(int zoom) { @Override ImageData newImageData(int zoom) { - if (zoomLevelToImageHandle.isEmpty()) { + if (imageHandleManager.hasNoHandle()) { return createBaseHandle(zoom).getImageData(); } // if a GC is initialized with an Image (memGC != null), the image data must not be resized, because it would @@ -2290,17 +2358,18 @@ protected ElementAtZoom loadImageData(int zoom) { @Override protected ImageHandle newImageHandle(ZoomContext zoomContext) { int targetZoom = zoomContext.targetZoom(); - if (zoomLevelToImageHandle.isEmpty()) { + if (imageHandleManager.hasNoHandle()) { return createBaseHandle(targetZoom); } if (memGC != null) { if (memGC.getZoom() != targetZoom) { GC currentGC = memGC; memGC = null; - createHandle(targetZoom); - currentGC.refreshFor(new DrawableWrapper(Image.this, zoomContext)); + ImageHandle imageHandle = createHandle(targetZoom); + currentGC.refreshFor(new DrawableWrapper(Image.this, zoomContext), imageHandle); + return imageHandle; } - return zoomLevelToImageHandle.get(targetZoom); + return imageHandleManager.getImageHandle(targetZoom); } return super.newImageHandle(zoomContext); } @@ -2312,7 +2381,6 @@ private ImageHandle createBaseHandle(int zoom) { private ImageHandle createHandle(int zoom) { long handle = initHandle(zoom); ImageHandle imageHandle = new ImageHandle(handle, zoom, -1); - zoomLevelToImageHandle.put(zoom, imageHandle); return imageHandle; } @@ -2446,8 +2514,8 @@ protected ElementAtZoom loadImageData(int zoom) { // Load at file zoom (native or via loader) and rescale ImageHandle nativeInitializedImage; - if (zoomLevelToImageHandle.containsKey(fileForZoom.zoom())) { - nativeInitializedImage = zoomLevelToImageHandle.get(fileForZoom.zoom()); + if (imageHandleManager.hasZoom(fileForZoom.zoom())) { + nativeInitializedImage = imageHandleManager.getImageHandle(fileForZoom.zoom()); } else { nativeInitializedImage = initNative(fileForZoom.element(), fileForZoom.zoom()); } @@ -2456,7 +2524,9 @@ protected ElementAtZoom loadImageData(int zoom) { imageDataAtZoom = ImageDataLoader.loadByZoom(fileForZoom.element(), fileForZoom.zoom(), zoom); } else { imageDataAtZoom = new ElementAtZoom<>(nativeInitializedImage.getImageData(), fileForZoom.zoom()); - nativeInitializedImage.destroy(); + if (!imageHandleManager.getAllImageHandles().contains(nativeInitializedImage)) { + nativeInitializedImage.destroy(); + } } return imageDataAtZoom; } @@ -2602,8 +2672,7 @@ ImageHandle initNative(String filename, int zoom) { img.transparentPixel = transparentPixel; img.alphaData = alphaData; - init(img, zoom); - imageMetadata = zoomLevelToImageHandle.get(zoom); + imageMetadata = init(img, zoom); handle = imageMetadata.handle; } Gdip.Bitmap_UnlockBits(bitmap, lockedBitmapData); @@ -2835,7 +2904,6 @@ class ImageHandle { if (backgroundColor != null) { setBackground(backgroundColor); } - setImageMetadataForHandle(this, zoom); } long getHandle() { @@ -3183,7 +3251,6 @@ private boolean isDisposed() { } private void destroy() { - zoomLevelToImageHandle.remove(zoom, this); if (type == SWT.ICON) { OS.DestroyIcon (handle); } else {