From 559f445021360228e592005885fcb75767f56bb5 Mon Sep 17 00:00:00 2001 From: Heiko Klare Date: Mon, 13 Oct 2025 17:47:10 +0200 Subject: [PATCH] [MacOS] Support loading and drawing images at desired size With the support of SVGs and the addition of the ImageDataAtSizeProvider, it is possible for many Image instances to provide a handle that represents the image at sharply rendered specific size in addition to the existing support of providing an image at different zooms of a base size. This change adds according support to the MacOS implementations of GC and Image. The GC#drawImage() method accepting only destination position and size now makes use of enhanced Image capabilities to load the image at the desired size if possible instead of just using the best fitting zoomed handle. --- .../cocoa/org/eclipse/swt/graphics/GC.java | 4 +- .../cocoa/org/eclipse/swt/graphics/Image.java | 70 +++++++++++++++++++ .../Test_org_eclipse_swt_graphics_GC.java | 6 +- 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GC.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GC.java index 31d122d55c..aa4818e9c1 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GC.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GC.java @@ -1238,7 +1238,9 @@ public void drawImage(Image image, int destX, int destY, int destWidth, int dest if (image.isDisposed()) { SWT.error(SWT.ERROR_INVALID_ARGUMENT); } - drawImage(image, 0, 0, 0, 0, destX, destY, destWidth, destHeight, false); + image.executeOnImageAtSizeBestFittingSize(imageAtSize -> { + drawImage(imageAtSize, 0, 0, imageAtSize.width, imageAtSize.height, destX, destY, destWidth, destHeight, false); + }, destWidth, destHeight); } void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java index 29eb789f6d..981e943a08 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java @@ -14,6 +14,7 @@ package org.eclipse.swt.graphics; +import static org.eclipse.swt.internal.DPIUtil.pointToPixel; import static org.eclipse.swt.internal.image.ImageColorTransformer.DEFAULT_DISABLED_IMAGE_TRANSFORMER; import java.io.*; @@ -1177,6 +1178,7 @@ private NSBitmapImageRep createRepresentation(ImageData imageData, AlphaInfo alp @Override void destroy() { + cachedImageAtSize.destroy(); if (memGC != null) memGC.dispose(); handle.release(); handle = null; @@ -1837,5 +1839,73 @@ public static void drawAtSize(GC gc, ImageData imageData, int width, int height) }); } +void executeOnImageAtSizeBestFittingSize(Consumer imageAtBestFittingSizeConsumer, int destWidth, int destHeight) { + Optional imageAtSize = cachedImageAtSize.refresh(destWidth, destHeight); + imageAtBestFittingSizeConsumer.accept(imageAtSize.orElse(this)); +} + +private CachedImageAtSize cachedImageAtSize = new CachedImageAtSize(); + +private class CachedImageAtSize { + private Image image; + + public void destroy() { + if (image != null) { + image.dispose(); + image = null; + } + } + + private Optional refresh(int destWidth, int destHeight) { + int scaledWidth = pointToPixel(destWidth, DPIUtil.getDeviceZoom()); + int scaledHeight = pointToPixel(destHeight, DPIUtil.getDeviceZoom()); + if (isReusable(scaledWidth, scaledHeight)) { + return Optional.of(image); + } else { + destroy(); + Optional imageAtSize = loadImageAtSize(scaledWidth, scaledHeight); + image = imageAtSize.orElse(null); + return imageAtSize; + } + } + + private boolean isReusable(int width, int height) { + return image != null && image.height == height && image.width == width; + } + + private Optional loadImageAtSize(int destWidth, int destHeight) { + Optional imageData = loadImageDataAtExactSize(destWidth, destHeight); + if (imageData.isEmpty()) { + return Optional.empty(); + } + Image image = new Image(device, imageData.get()); + if (styleFlag != SWT.IMAGE_COPY) { + NSBitmapImageRep representation = image.getRepresentation(100); + image.createRepFromSourceAndApplyFlag(representation, destWidth, destHeight, styleFlag); + image.handle.removeRepresentation(representation); + } + return Optional.of(image); + } + + private Optional loadImageDataAtExactSize(int targetWidth, int targetHeight) { + if (imageDataProvider instanceof ImageDataAtSizeProvider imageDataAtSizeProvider) { + ImageData imageData = imageDataAtSizeProvider.getImageData(targetWidth, targetHeight); + if (imageData == null) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, + " ImageDataAtSizeProvider returned null for width=" + targetWidth + ", height=" + targetHeight); + } + return Optional.of(imageData); + } + if (imageFileNameProvider != null) { + String fileName = DPIUtil.validateAndGetImagePathAtZoom(imageFileNameProvider, 100).element(); + if (ImageDataLoader.isDynamicallySizable(fileName)) { + ImageData imageDataAtSize = ImageDataLoader.loadBySize(fileName, targetWidth, targetHeight); + return Optional.of(imageDataAtSize); + } + } + return Optional.empty(); + } +} + } diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java index 6400e28771..2e40ee0034 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java @@ -54,7 +54,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -442,7 +442,7 @@ public void test_drawImageLorg_eclipse_swt_graphics_ImageIIII_ImageDataProvider( } @Test -@EnabledOnOs(value = OS.WINDOWS) +@DisabledOnOs(value = OS.LINUX) public void test_drawImageLorg_eclipse_swt_graphics_ImageIIII_ImageDataAtSizeProvider_invalid() { ImageDataAtSizeProvider provider = new ImageDataAtSizeProvider() { @Override @@ -469,7 +469,7 @@ public ImageData getImageData(int width, int height) { @ParameterizedTest @ValueSource(ints = {SWT.IMAGE_COPY, SWT.IMAGE_DISABLE, SWT.IMAGE_GRAY, -1}) -@EnabledOnOs(value = OS.WINDOWS) +@DisabledOnOs(value = OS.LINUX) public void test_drawImageLorg_eclipse_swt_graphics_ImageIIII_ImageDataAtSizeProvider(int styleFlag) { int width = 50; int height = 70;