From aaa430904e29bb95da91aeba564e2f5e2477c050 Mon Sep 17 00:00:00 2001 From: "Klare, Heiko" Date: Thu, 12 Feb 2026 11:02:12 +0100 Subject: [PATCH] Avoid attempt to draw images with target width/height of 0 Recently, the ability to scale images for the desired target size of a drawImage operation executed on a GC and the incorporation of a Transform potentially applied to that GC have been implemented. With this, the target size for an image to be drawn can become 0 and the request of a handle of that size can fail, in particular when trying to rasterize an SVG at that size. This change adapts the implementation of the handle retrieval for a desired size in the Image implementation so that the minimum size of the provided handle is 1 in both of the dimensions. The draw operation in the GC will then take care of potentially not drawing anything at all when the actual intended size is really 0. It also adds a test case for the specific case of requesting a handle at size 0 and also adds a test for the general correctness of image handle retrieval when a transform is applied to the GC. Fixes https://github.com/eclipse-platform/eclipse.platform.swt/issues/3078 --- .../cocoa/org/eclipse/swt/graphics/Image.java | 2 +- .../gtk/org/eclipse/swt/graphics/Image.java | 2 +- .../win32/org/eclipse/swt/graphics/Image.java | 4 +- .../Test_org_eclipse_swt_graphics_GC.java | 49 +++++++++++++++++++ 4 files changed, 53 insertions(+), 4 deletions(-) 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 efc5d4f3b1b..8caa1fc49de 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 @@ -1878,7 +1878,7 @@ 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); + Optional imageAtSize = cachedImageAtSize.refresh(Math.max(1, destWidth), Math.max(1, destHeight)); imageAtBestFittingSizeConsumer.accept(imageAtSize.orElse(this)); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java index 8e08a3cd97c..f9bffc90c9e 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java @@ -1025,7 +1025,7 @@ private Optional loadImageDataAtExactSize(int targetWidth, int target } void executeOnImageAtSize(Consumer imageAtBestFittingSizeConsumer, int destWidth, int destHeight) { - Optional imageAtSize = cachedImageAtSize.refresh(destWidth, destHeight); + Optional imageAtSize = cachedImageAtSize.refresh(Math.max(1, destWidth), Math.max(1, destHeight)); imageAtBestFittingSizeConsumer.accept(imageAtSize.orElse(this)); } 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 7abf910b88e..55c984bf2a5 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 @@ -941,8 +941,8 @@ ImageHandle getHandle (int targetZoom, int nativeZoom) { return imageHandleManager.getOrCreate(targetZoom, () -> imageProvider.newImageHandle(zoomContext)); } -void executeOnImageHandleAtBestFittingSize(Consumer handleAtSizeConsumer, int widthHint, int heightHint) { - ImageHandle imageHandle = lastRequestedHandle.refresh(widthHint, heightHint); +void executeOnImageHandleAtBestFittingSize(Consumer handleAtSizeConsumer, int destWidth, int destHeight) { + ImageHandle imageHandle = lastRequestedHandle.refresh(Math.max(1, destWidth), Math.max(1, destHeight)); handleAtSizeConsumer.accept(imageHandle); } 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 b47a182bac2..563f4cbecb9 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 @@ -27,6 +27,8 @@ import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import java.io.IOException; +import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -48,12 +50,15 @@ import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.Transform; import org.eclipse.swt.internal.DPIUtil; +import org.eclipse.swt.tests.graphics.ImageDataTestHelper; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; 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.OS; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -394,6 +399,50 @@ public void test_drawImageLorg_eclipse_swt_graphics_ImageIIII() { images.dispose(); } +@Test +@EnabledOnOs(OS.WINDOWS) +public void test_drawImageLorg_eclipse_swt_graphics_ImageIIII_withTransform() throws IOException { + Image image = null; + try (InputStream is = getClass().getResourceAsStream("collapseall.svg")) { + image = new Image(Display.getDefault(), is); + } + Transform transform = new Transform(display, 2, 0, 0, 2, 0, 0); + gc.setTransform(transform); + gc.drawImage(image, 0, 0, 2, 2); + ImageData resultImageDataWithTransform = getImageDataFromGC(gc, 0, 0, 4, 4); + + gc.setTransform(null); + gc.drawImage(image, 10, 10, 4, 4); + ImageData resultImageDataWithoutTransform = getImageDataFromGC(gc, 10, 10, 4, 4); + + ImageDataTestHelper.assertImageDataEqual(resultImageDataWithoutTransform, resultImageDataWithoutTransform, resultImageDataWithTransform); + + image.dispose(); + transform.dispose(); +} + +private ImageData getImageDataFromGC(GC gc, int x, int y, int width, int height) { + Image extractionImage = new Image(display, width, height); + gc.copyArea(extractionImage, x, y); + ImageData resultImageData = extractionImage.getImageData(); + extractionImage.dispose(); + return resultImageData; +} + +public void test_drawImageLorg_eclipse_swt_graphics_ImageIIII_withTransform_zeroTargetSize() throws IOException { + Image image = null; + try (InputStream is = getClass().getResourceAsStream("collapseall.svg")) { + image = new Image(Display.getDefault(), is); + } + Transform transform = new Transform(display, 0.1f, 0, 0, 0.1f, 0, 0); + gc.setTransform(transform); + // The destination size will become 0, but no exception should be thrown. + gc.drawImage(image, 0, 0, 2, 2); + + image.dispose(); + transform.dispose(); +} + @Test public void test_drawImageLorg_eclipse_swt_graphics_ImageIIII_ImageDataProvider() { int width = 32;