diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java
index 4d29c190884..7e340642da0 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java
@@ -17,6 +17,7 @@
import org.eclipse.swt.accessibility.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.*;
import org.eclipse.swt.widgets.*;
/**
@@ -717,26 +718,25 @@ public Rectangle computeTrim (int x, int y, int width, int height) {
}
return trim;
}
+
Image createButtonImage(Display display, int button) {
- return new Image(display, (ImageDataProvider) zoom -> {
- GC tempGC = new GC (CTabFolder.this);
- Point size = renderer.computeSize(button, SWT.NONE, tempGC, SWT.DEFAULT, SWT.DEFAULT);
- tempGC.dispose();
-
- Rectangle trim = renderer.computeTrim(button, SWT.NONE, 0, 0, 0, 0);
- Image image = new Image (display, size.x - trim.width, size.y - trim.height);
- GC gc = new GC (image);
- Color transColor = renderer.parent.getBackground();
- gc.setBackground(transColor);
- gc.fillRectangle(image.getBounds());
- renderer.draw(button, SWT.NONE, new Rectangle(trim.x, trim.y, size.x, size.y), gc);
- gc.dispose ();
-
- final ImageData imageData = image.getImageData (zoom);
- imageData.transparentPixel = imageData.palette.getPixel(transColor.getRGB());
- image.dispose();
- return imageData;
- });
+ final GC tempGC = new GC (CTabFolder.this);
+ final Point size = renderer.computeSize(button, SWT.NONE, tempGC, SWT.DEFAULT, SWT.DEFAULT);
+ tempGC.dispose();
+
+ final Rectangle trim = renderer.computeTrim(button, SWT.NONE, 0, 0, 0, 0);
+ final Point imageSize = new Point(size.x - trim.width, size.y - trim.height);
+ Color transColor = renderer.parent.getBackground();
+ final ImageGcDrawer imageGcDrawer = new TransparencyColorImageGcDrawer(transColor) {
+ @Override
+ public void drawOn(GC gc, int imageWidth, int imageHeight) {
+ Rectangle imageBounds = new Rectangle(0, 0, imageWidth, imageHeight);
+ gc.setBackground(transColor);
+ gc.fillRectangle(imageBounds);
+ renderer.draw(button, SWT.NONE, imageBounds, gc);
+ }
+ };
+ return new Image(display, imageGcDrawer, imageSize.x, imageSize.y);
}
private void notifyItemCountChange() {
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 f20ab4fc55c..97d16b8891f 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
@@ -15,6 +15,7 @@
import java.io.*;
+import java.util.*;
import org.eclipse.swt.*;
import org.eclipse.swt.internal.*;
@@ -135,6 +136,11 @@ public final class Image extends Resource implements Drawable {
*/
private ImageDataProvider imageDataProvider;
+ /**
+ * ImageGcDrawer to provide a callback to draw on a GC for various zoom levels
+ */
+ private ImageGcDrawer imageGcDrawer;
+
/**
* Style flag used to differentiate normal, gray-scale and disabled images based
* on image data providers. Without this, a normal and a disabled image of the
@@ -384,8 +390,9 @@ public Image(Device device, Image srcImage, int flag) {
imageFileNameProvider = srcImage.imageFileNameProvider;
imageDataProvider = srcImage.imageDataProvider;
+ imageGcDrawer = srcImage.imageGcDrawer;
this.styleFlag = srcImage.styleFlag | flag;
- if (imageFileNameProvider != null || imageDataProvider != null) {
+ if (imageFileNameProvider != null || imageDataProvider != null ||srcImage.imageGcDrawer != null) {
/* If source image has 200% representation then create the 200% representation for the new image & apply flag */
NSBitmapImageRep rep200 = srcImage.getRepresentation (200);
if (rep200 != null) createRepFromSourceAndApplyFlag(rep200, srcWidth * 2, srcHeight * 2, flag);
@@ -843,6 +850,54 @@ public Image(Device device, ImageDataProvider imageDataProvider) {
}
}
+/**
+ * The provided ImageGcDrawer will be called on demand whenever a new variant of the
+ * Image for an additional zoom is required. Depending on the OS specific implementation
+ * these calls will be done during the instantiation or later when a new variant is
+ * requested.
+ *
+ * @param device the device on which to create the image
+ * @param imageGcDrawer the ImageGcDrawer object to be called when a new image variant
+ * for another zoom is required.
+ * @param width the width of the new image in points
+ * @param height the height of the new image in points
+ *
+ * @exception IllegalArgumentException
+ * - ERROR_NULL_ARGUMENT - if device is null and there is no current device
+ * - ERROR_NULL_ARGUMENT - if the ImageGcDrawer is null
+ *
+ * @since 3.129
+ */
+public Image(Device device, ImageGcDrawer imageGcDrawer, int width, int height) {
+ super(device);
+ if (imageGcDrawer == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ this.imageGcDrawer = imageGcDrawer;
+ ImageData data = drawWithImageGcDrawer(imageGcDrawer, width, height, 100);
+ if (data == null) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ NSAutoreleasePool pool = null;
+ if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
+ try {
+ init (data);
+ init ();
+ } finally {
+ if (pool != null) pool.release();
+ }
+}
+
+private ImageData drawWithImageGcDrawer(ImageGcDrawer imageGcDrawer, int width, int height, int zoom) {
+ Image image = new Image(device, width, height);
+ GC gc = new GC(image);
+ try {
+ imageGcDrawer.drawOn(gc, width, height);
+ ImageData imageData = image.getImageData(zoom);
+ imageGcDrawer.postProcess(imageData);
+ return imageData;
+ } finally {
+ gc.dispose();
+ image.dispose();
+ }
+}
+
private AlphaInfo _getAlphaInfoAtCurrentZoom (NSBitmapImageRep rep) {
int deviceZoom = DPIUtil.getDeviceZoom();
if (deviceZoom != 100 && (imageFileNameProvider != null || imageDataProvider != null)) {
@@ -1121,6 +1176,9 @@ public boolean equals (Object object) {
return styleFlag == image.styleFlag && imageDataProvider.equals (image.imageDataProvider);
} else if (imageFileNameProvider != null && image.imageFileNameProvider != null) {
return styleFlag == image.styleFlag && imageFileNameProvider.equals (image.imageFileNameProvider);
+ } else if (imageGcDrawer != null && image.imageGcDrawer != null) {
+ return styleFlag == image.styleFlag && imageGcDrawer.equals(image.imageGcDrawer) && width == image.width
+ && height == image.height;
} else {
return handle == image.handle;
}
@@ -1357,6 +1415,8 @@ public int hashCode () {
return imageDataProvider.hashCode();
} else if (imageFileNameProvider != null) {
return imageFileNameProvider.hashCode();
+ } else if (imageGcDrawer != null) {
+ return Objects.hash(imageGcDrawer, height, width);
} else {
return handle != null ? (int)handle.id : 0;
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageGcDrawer.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageGcDrawer.java
new file mode 100644
index 00000000000..0400a0c2fd4
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageGcDrawer.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Yatta and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Yatta - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+/**
+ * Interface to provide a callback mechanism to draw on different GC instances
+ * depending on the zoom the image will be used for. A common use case is when
+ * the application is moved from a low DPI monitor to a high DPI monitor. This
+ * provides API which will be called by SWT during the image rendering.
+ *
+ * This interface needs to be implemented by client code to provide logic that
+ * draws on the empty GC on demand.
+ *
+ * @since 3.129
+ */
+public interface ImageGcDrawer {
+
+ /**
+ * Draws an image on a GC for a requested zoom level.
+ *
+ * @param gc The GC will draw on the underlying Image and is configured
+ * for the targeted zoom
+ * @param imageWidth The width of the image in points to draw on
+ * @param imageHeight The height of the image in points to draw on
+ */
+ void drawOn(GC gc, int imageWidth, int imageHeight);
+
+ /**
+ * Executes post processing on ImageData. This method will always be called
+ * after drawOn
and contain the resulting ImageData.
+ *
+ * @param imageData The resulting ImageData after drawOn
was called
+ */
+ default void postProcess(ImageData imageData) {
+ }
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/TransparencyColorImageGcDrawer.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/TransparencyColorImageGcDrawer.java
new file mode 100644
index 00000000000..e7ee43332e9
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/TransparencyColorImageGcDrawer.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Yatta and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Yatta - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal;
+
+import org.eclipse.swt.graphics.*;
+
+public abstract class TransparencyColorImageGcDrawer implements ImageGcDrawer {
+
+ private final Color transparencyColor;
+
+ public TransparencyColorImageGcDrawer(Color transparencyColor) {
+ this.transparencyColor = transparencyColor;
+ }
+
+ @Override
+ public void postProcess(ImageData imageData) {
+ imageData.transparentPixel = imageData.palette.getPixel(transparencyColor.getRGB());
+ }
+
+}
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 2b48e4b34a5..b51af22f7eb 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
@@ -15,6 +15,7 @@
import java.io.*;
+import java.util.*;
import org.eclipse.swt.*;
import org.eclipse.swt.internal.*;
@@ -151,6 +152,11 @@ public final class Image extends Resource implements Drawable {
*/
private ImageDataProvider imageDataProvider;
+ /**
+ * ImageGcDrawer to provide a callback to draw on a GC for various zoom levels
+ */
+ private ImageGcDrawer imageGcDrawer;
+
/**
* Style flag used to differentiate normal, gray-scale and disabled images based
* on image data providers. Without this, a normal and a disabled image of the
@@ -263,6 +269,7 @@ public Image(Device device, Image srcImage, int flag) {
this.type = srcImage.type;
this.imageDataProvider = srcImage.imageDataProvider;
this.imageFileNameProvider = srcImage.imageFileNameProvider;
+ this.imageGcDrawer = srcImage.imageGcDrawer;
this.styleFlag = srcImage.styleFlag | flag;
this.currentDeviceZoom = srcImage.currentDeviceZoom;
@@ -661,6 +668,36 @@ public Image(Device device, ImageDataProvider imageDataProvider) {
init ();
}
+/**
+ * The provided ImageGcDrawer will be called on demand whenever a new variant of the
+ * Image for an additional zoom is required. Depending on the OS specific implementation
+ * these calls will be done during the instantiation or later when a new variant is
+ * requested.
+ *
+ * @param device the device on which to create the image
+ * @param imageGcDrawer the ImageGcDrawer object to be called when a new image variant
+ * for another zoom is required.
+ * @param width the width of the new image in points
+ * @param height the height of the new image in points
+ *
+ * @exception IllegalArgumentException
+ * - ERROR_NULL_ARGUMENT - if device is null and there is no current device
+ * - ERROR_NULL_ARGUMENT - if the ImageGcDrawer is null
+ *
+ * @since 3.129
+ */
+public Image(Device device, ImageGcDrawer imageGcDrawer, int width, int height) {
+ super(device);
+ if (imageGcDrawer == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ this.imageGcDrawer = imageGcDrawer;
+ currentDeviceZoom = DPIUtil.getDeviceZoom();
+ ImageData imageData = drawWithImageGcDrawer(width, height, currentDeviceZoom);
+ init (imageData);
+ init ();
+}
+
/**
* Refreshes the image for the current device scale factor.
*
@@ -722,6 +759,17 @@ boolean refreshImageForZoom () {
refreshed = true;
currentDeviceZoom = deviceZoomLevel;
}
+ } else if (imageGcDrawer != null) {
+ int deviceZoomLevel = deviceZoom;
+ if (deviceZoomLevel != currentDeviceZoom) {
+ ImageData data = drawWithImageGcDrawer(width, height, deviceZoomLevel);
+ /* Release current native resources */
+ destroy ();
+ init(data);
+ init();
+ refreshed = true;
+ currentDeviceZoom = deviceZoomLevel;
+ }
} else {
if (!DPIUtil.useCairoAutoScale()) {
int deviceZoomLevel = deviceZoom;
@@ -904,6 +952,9 @@ public boolean equals (Object object) {
return (styleFlag == image.styleFlag) && imageDataProvider.equals (image.imageDataProvider);
} else if (imageFileNameProvider != null && image.imageFileNameProvider != null) {
return (styleFlag == image.styleFlag) && imageFileNameProvider.equals (image.imageFileNameProvider);
+ } else if (imageGcDrawer != null && image.imageGcDrawer != null) {
+ return styleFlag == image.styleFlag && imageGcDrawer.equals(image.imageGcDrawer) && width == image.width
+ && height == image.height;
} else {
return surface == image.surface;
}
@@ -1110,11 +1161,27 @@ public ImageData getImageData (int zoom) {
} else if (imageFileNameProvider != null) {
ElementAtZoom fileName = DPIUtil.validateAndGetImagePathAtZoom (imageFileNameProvider, zoom);
return DPIUtil.scaleImageData (device, new ImageData (fileName.element()), zoom, fileName.zoom());
+ } else if (imageGcDrawer != null) {
+ return drawWithImageGcDrawer(width, height, zoom);
} else {
return DPIUtil.scaleImageData (device, getImageDataAtCurrentZoom (), zoom, currentDeviceZoom);
}
}
+private ImageData drawWithImageGcDrawer(int width, int height, int zoom) {
+ Image image = new Image(device, width, height);
+ GC gc = new GC(image);
+ try {
+ imageGcDrawer.drawOn(gc, width, height);
+ ImageData imageData = image.getImageData(zoom);
+ imageGcDrawer.postProcess(imageData);
+ return imageData;
+ } finally {
+ gc.dispose();
+ image.dispose();
+ }
+}
+
/**
* Invokes platform specific functionality to allocate a new image.
*
@@ -1179,6 +1246,8 @@ public int hashCode () {
return imageDataProvider.hashCode();
} else if (imageFileNameProvider != null) {
return imageFileNameProvider.hashCode();
+ } else if (imageGcDrawer != null) {
+ return Objects.hash(imageGcDrawer, width, height);
} else {
return (int)surface;
}
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 ddd2aaeaee3..c27378b60da 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
@@ -172,8 +172,13 @@ private Image (Device device, int nativeZoom) {
* @see #dispose()
*/
public Image(Device device, int width, int height) {
+ this(device, width, height, DPIUtil.getNativeDeviceZoom());
+}
+
+
+private Image(Device device, int width, int height, int nativeZoom) {
super(device);
- initialNativeZoom = DPIUtil.getNativeDeviceZoom();
+ initialNativeZoom = nativeZoom;
final int zoom = getZoom();
width = DPIUtil.scaleUp (width, zoom);
height = DPIUtil.scaleUp (height, zoom);
@@ -602,6 +607,31 @@ public Image(Device device, ImageDataProvider imageDataProvider) {
this.device.registerResourceWithZoomSupport(this);
}
+/**
+ * The provided ImageGcDrawer will be called on demand whenever a new variant of the
+ * Image for an additional zoom is required. Depending on the OS-specific implementation
+ * these calls will be done during the instantiation or later when a new variant is
+ * requested.
+ *
+ * @param device the device on which to create the image
+ * @param imageGcDrawer the ImageGcDrawer object to be called when a new image variant
+ * for another zoom is required.
+ * @param width the width of the new image in points
+ * @param height the height of the new image in points
+ *
+ * @exception IllegalArgumentException
+ * - ERROR_NULL_ARGUMENT - if device is null and there is no current device
+ * - ERROR_NULL_ARGUMENT - if the ImageGcDrawer is null
+ *
+ * @since 3.129
+ */
+public Image(Device device, ImageGcDrawer imageGcDrawer, int width, int height) {
+ super(device);
+ this.imageProvider = new ImageGcDrawerWrapper(imageGcDrawer, width, height);
+ initialNativeZoom = DPIUtil.getNativeDeviceZoom();
+ init();
+}
+
private ImageData adaptImageDataIfDisabledOrGray(ImageData data) {
ImageData returnImageData = null;
switch (this.styleFlag) {
@@ -1140,6 +1170,9 @@ ImageHandle initNative(String filename, int zoom) {
void destroy () {
device.deregisterResourceWithZoomSupport(this);
if (memGC != null) memGC.dispose();
+ if (this.imageProvider != null) {
+ this.imageProvider.destroy();
+ }
destroyHandle();
memGC = null;
}
@@ -1282,14 +1315,17 @@ public Rectangle getBounds() {
Rectangle getBounds(int zoom) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
- ImageHandle imageMetadata;
if (zoomLevelToImageHandle.containsKey(zoom)) {
- imageMetadata = zoomLevelToImageHandle.get(zoom);
+ ImageHandle imageMetadata = zoomLevelToImageHandle.get(zoom);
+ Rectangle rectangle = new Rectangle(0, 0, imageMetadata.width, imageMetadata.height);
+ return DPIUtil.scaleBounds(rectangle, zoom, imageMetadata.zoom);
+ } else if (this.imageProvider != null) {
+ return this.imageProvider.getBounds(zoom);
} else {
- imageMetadata = zoomLevelToImageHandle.values().iterator().next();
+ ImageHandle imageMetadata = zoomLevelToImageHandle.values().iterator().next();
+ Rectangle rectangle = new Rectangle(0, 0, imageMetadata.width, imageMetadata.height);
+ return DPIUtil.scaleBounds(rectangle, zoom, imageMetadata.zoom);
}
- Rectangle rectangle = new Rectangle(0, 0, imageMetadata.width, imageMetadata.height);
- return DPIUtil.scaleBounds(rectangle, zoom, imageMetadata.zoom);
}
/**
@@ -1932,6 +1968,9 @@ public void internal_dispose_GC (long hDC, GCData data) {
*/
@Override
public boolean isDisposed() {
+ if (this.imageProvider != null) {
+ return this.imageProvider.isDisposed();
+ }
return zoomLevelToImageHandle.isEmpty();
}
@@ -2043,9 +2082,11 @@ public static Image win32_new(Device device, int type, long handle, int nativeZo
private abstract class AbstractImageProviderWrapper {
abstract Object getProvider();
+ protected abstract Rectangle getBounds(int zoom);
abstract ImageData getImageData(int zoom);
abstract ImageHandle getImageMetadata(int zoom);
abstract AbstractImageProviderWrapper createCopy(Image image);
+ abstract boolean isDisposed();
protected void checkProvider(Object provider, Class> expectedClass) {
if (provider == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
@@ -2062,6 +2103,9 @@ public boolean equals(Object otherProvider) {
return otherProvider instanceof AbstractImageProviderWrapper aip //
&& getProvider().equals(aip.getProvider());
}
+
+ protected void destroy() {
+ }
}
private class ImageFileNameProviderWrapper extends AbstractImageProviderWrapper {
@@ -2076,6 +2120,13 @@ private class ImageFileNameProviderWrapper extends AbstractImageProviderWrapper
this.provider = provider;
}
+ @Override
+ protected Rectangle getBounds(int zoom) {
+ ImageHandle imageHandle = zoomLevelToImageHandle.values().iterator().next();
+ Rectangle rectangle = new Rectangle(0, 0, imageHandle.width, imageHandle.height);
+ return DPIUtil.scaleBounds(rectangle, zoom, imageHandle.zoom);
+ }
+
@Override
ImageData getImageData(int zoom) {
ElementAtZoom fileName = DPIUtil.validateAndGetImagePathAtZoom (provider, zoom);
@@ -2099,6 +2150,11 @@ ImageHandle getImageMetadata(int zoom) {
return zoomLevelToImageHandle.get(zoom);
}
+ @Override
+ boolean isDisposed() {
+ return zoomLevelToImageHandle.isEmpty();
+ }
+
@Override
Object getProvider() {
return provider;
@@ -2127,6 +2183,13 @@ private class ImageDataProviderWrapper extends AbstractImageProviderWrapper {
this.provider = provider;
}
+ @Override
+ protected Rectangle getBounds(int zoom) {
+ ElementAtZoom data = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
+ Rectangle rectangle = new Rectangle(0, 0, data.element().width, data.element().height);
+ return DPIUtil.scaleBounds(rectangle, zoom, data.zoom());
+ }
+
@Override
ImageData getImageData(int zoom) {
ElementAtZoom data = DPIUtil.validateAndGetImageDataAtZoom (provider, zoom);
@@ -2143,6 +2206,11 @@ ImageHandle getImageMetadata(int zoom) {
return zoomLevelToImageHandle.get(zoom);
}
+ @Override
+ boolean isDisposed() {
+ return zoomLevelToImageHandle.isEmpty();
+ }
+
@Override
Object getProvider() {
return provider;
@@ -2154,6 +2222,81 @@ ImageDataProviderWrapper createCopy(Image image) {
}
}
+private class ImageGcDrawerWrapper extends AbstractImageProviderWrapper {
+ private ImageGcDrawer drawer;
+ private int width;
+ private int height;
+ private boolean isDestroyed;
+
+ public ImageGcDrawerWrapper(ImageGcDrawer imageGcDrawer, int width, int height) {
+ checkProvider(imageGcDrawer, ImageGcDrawer.class);
+ this.drawer = imageGcDrawer;
+ this.width = width;
+ this.height = height;
+ }
+
+ @Override
+ protected Rectangle getBounds(int zoom) {
+ Rectangle rectangle = new Rectangle(0, 0, width, height);
+ return DPIUtil.scaleBounds(rectangle, zoom, 100);
+ }
+
+ @Override
+ ImageData getImageData(int zoom) {
+ return getImageMetadata(zoom).getImageData();
+ }
+
+ @Override
+ ImageHandle getImageMetadata(int zoom) {
+ initialNativeZoom = zoom;
+ Image image = new Image(device, width, height, zoom);
+ GC gc = new GC(image);
+ try {
+ gc.data.nativeZoom = zoom;
+ drawer.drawOn(gc, width, height);
+ ImageData imageData = image.getImageMetadata(zoom).getImageData();
+ drawer.postProcess(imageData);
+ ImageData newData = adaptImageDataIfDisabledOrGray(imageData);
+ init(newData, zoom);
+ } finally {
+ gc.dispose();
+ image.dispose();
+ }
+ return zoomLevelToImageHandle.get(zoom);
+ }
+
+ @Override
+ protected void destroy() {
+ isDestroyed = true;
+ }
+
+ @Override
+ boolean isDisposed() {
+ return isDestroyed;
+ }
+
+ @Override
+ Object getProvider() {
+ return drawer;
+ }
+
+ @Override
+ ImageGcDrawerWrapper createCopy(Image image) {
+ return image.new ImageGcDrawerWrapper(drawer, width, height);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getProvider().hashCode(), width, height);
+ }
+
+ @Override
+ public boolean equals(Object otherProvider) {
+ return otherProvider instanceof ImageGcDrawerWrapper aip && getProvider().equals(aip.getProvider())
+ && width == aip.width && height == aip.height;
+ }
+}
+
private class ImageHandle {
private final long handle;
private final int zoom;
diff --git a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet367.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet367.java
index d808c867fdd..4133afb1516 100644
--- a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet367.java
+++ b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet367.java
@@ -60,6 +60,10 @@ public static void main (String [] args) {
return null;
}
};
+ final ImageGcDrawer imageGcDrawer = gc -> {
+ gc.drawRectangle(1, 1, 18, 18);
+ gc.drawLine(3, 3, 17, 17);
+ };
final Display display = new Display ();
final Shell shell = new Shell (display);
@@ -98,6 +102,10 @@ public static void main (String [] args) {
new Label (shell, SWT.NONE).setImage (new Image (display, imageDataProvider));
new Button(shell, SWT.NONE).setImage (new Image (display, imageDataProvider));
+ new Label (shell, SWT.NONE).setText ("ImageGcDrawer:");
+ new Label (shell, SWT.NONE).setImage (new Image (display, imageGcDrawer, 20, 20));
+ new Button(shell, SWT.NONE).setImage (new Image (display, imageGcDrawer, 20, 20));
+
createSeparator(shell);
new Label (shell, SWT.NONE).setText ("1. Canvas\n(PaintListener)");
diff --git a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet382.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet382.java
index 8a1b5273577..ff40443deea 100644
--- a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet382.java
+++ b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet382.java
@@ -63,6 +63,14 @@ public static void main (String [] args) {
};
final Display display = new Display ();
+
+ final ImageGcDrawer imageGcDrawer = gc -> {
+ gc.setBackground(display.getSystemColor(SWT.COLOR_RED));
+ gc.fillRectangle(0, 0, 16, 16);
+ gc.setForeground(display.getSystemColor(SWT.COLOR_YELLOW));
+ gc.drawRectangle(4, 4, 8, 8);
+ };
+
final Shell shell = new Shell (display);
shell.setText("Snippet382");
shell.setLayout (new GridLayout (3, false));
@@ -84,6 +92,10 @@ public void handleEvent(Event e) {
final Image disabledImageWithData = new Image (display,imageWithData, SWT.IMAGE_DISABLE);
final Image greyImageWithData = new Image (display,imageWithData, SWT.IMAGE_GRAY);
+ final Image imageWithGcDrawer = new Image (display, imageGcDrawer, 16, 16);
+ final Image disabledImageWithGcDrawer = new Image (display, imageWithGcDrawer, SWT.IMAGE_DISABLE);
+ final Image greyImageWithGcDrawer = new Image (display, imageWithGcDrawer, SWT.IMAGE_GRAY);
+
try {
drawImages(mainGC, gcData, "Normal",40, imageWithFileNameProvider);
drawImages(mainGC, gcData, "Disabled",80, disabledImageWithFileNameProvider);
@@ -96,6 +108,10 @@ public void handleEvent(Event e) {
drawImages(mainGC, gcData, "Normal",280, imageWithDataProvider);
drawImages(mainGC, gcData, "Disabled",320, disabledImageWithData);
drawImages(mainGC, gcData, "Greyed",360, greyImageWithData);
+
+ drawImages(mainGC, gcData, "Normal", 400, imageWithGcDrawer);
+ drawImages(mainGC, gcData, "Disabled", 440, disabledImageWithGcDrawer);
+ drawImages(mainGC, gcData, "Greyed", 480, greyImageWithGcDrawer);
} finally {
mainGC.dispose ();
}
@@ -114,7 +130,7 @@ private void drawImages(GC mainGC, GCData gcData, String text, int y, final Imag
};
shell.addListener(SWT.Paint, l);
- shell.setSize(400, 500);
+ shell.setSize(400, 550);
shell.open ();
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ()) display.sleep ();
diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Image.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Image.java
index 7e21ea728af..a9fbb03aef0 100644
--- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Image.java
+++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Image.java
@@ -37,6 +37,7 @@
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageDataProvider;
import org.eclipse.swt.graphics.ImageFileNameProvider;
+import org.eclipse.swt.graphics.ImageGcDrawer;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
@@ -97,6 +98,7 @@ public class Test_org_eclipse_swt_graphics_Image {
}
return new ImageData(getPath(fileName));
};
+ImageGcDrawer imageGcDrawer = (gc, width, height) -> {};
@Before
public void setUp() {
@@ -607,6 +609,23 @@ public void test_ConstructorLorg_eclipse_swt_graphics_Device_ImageDataProvider()
image.dispose();
}
+@Test
+public void test_ConstructorLorg_eclipse_swt_graphics_Device_ImageGcDrawer() {
+ // Null provider
+ ImageGcDrawer drawer = null;
+ try {
+ Image image = new Image(display, drawer, 20, 20);
+ image.dispose();
+ fail("No exception thrown for ImageGcDrawer == null");
+ } catch (IllegalArgumentException e) {
+ assertSWTProblem("Incorrect exception thrown for ImageGcDrawer == null", SWT.ERROR_NULL_ARGUMENT, e);
+ }
+
+ // Valid provider
+ Image image = new Image(display, imageGcDrawer, 20, 20);
+ image.dispose();
+}
+
@Test
public void test_equalsLjava_lang_Object() {
Image image = null;
@@ -675,6 +694,22 @@ public void test_equalsLjava_lang_Object() {
image.dispose();
image1.dispose();
}
+
+ // ImageGcDrawer
+ try {
+ image = new Image(display, imageGcDrawer, 10, 10);
+ image1 = image;
+
+ assertFalse(image.equals(null));
+
+ assertTrue(image.equals(image1));
+
+ image1 = new Image(display, imageGcDrawer, 10, 10);
+ assertTrue(image.equals(image1));
+ } finally {
+ image.dispose();
+ image1.dispose();
+ }
}
@Test
@@ -760,6 +795,13 @@ public void test_getBoundsInPixels() {
bounds = image.getBounds();
image.dispose();
assertEquals(":d: Image.getBoundsInPixels method doesn't return bounds in Pixel values.", boundsInPixels, DPIUtil.autoScaleUp(bounds));
+
+ // create image with ImageGcDrawer
+ image = new Image(display, imageGcDrawer, bounds.width, bounds.height);
+ boundsInPixels = image.getBoundsInPixels();
+ bounds = image.getBounds();
+ image.dispose();
+ assertEquals("Image.getBoundsInPixels method doesn't return bounds in Pixel values for ImageGcDrawer.", boundsInPixels, DPIUtil.autoScaleUp(bounds));
}
@SuppressWarnings("deprecation")
@@ -968,6 +1010,16 @@ public void test_hashCode() {
image.dispose();
image1.dispose();
}
+
+ // ImageGcDrawer
+ try {
+ image = new Image(display, imageGcDrawer, 10, 10);
+ image1 = new Image(display, imageGcDrawer, 10, 10);
+ assertEquals(image1.hashCode(), image.hashCode());
+ } finally {
+ image.dispose();
+ image1.dispose();
+ }
}
@Test