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 202e9a7e1f1..c4cfebd141c 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
@@ -198,9 +198,9 @@ void checkGC(int mask) {
Pattern pattern = data.foregroundPattern;
if (pattern != null) {
if(data.alpha == 0xFF) {
- brush = pattern.handle;
+ brush = pattern.getHandle(getZoom());
} else {
- brush = data.gdipFgPatternBrushAlpha != 0 ? Gdip.Brush_Clone(data.gdipFgPatternBrushAlpha) : createAlphaTextureBrush(pattern.handle, data.alpha);
+ brush = data.gdipFgPatternBrushAlpha != 0 ? Gdip.Brush_Clone(data.gdipFgPatternBrushAlpha) : createAlphaTextureBrush(pattern.getHandle(getZoom()), data.alpha);
data.gdipFgPatternBrushAlpha = brush;
}
if ((data.style & SWT.MIRRORED) != 0) {
@@ -289,9 +289,9 @@ void checkGC(int mask) {
Pattern pattern = data.backgroundPattern;
if (pattern != null) {
if(data.alpha == 0xFF) {
- data.gdipBrush = pattern.handle;
+ data.gdipBrush = pattern.getHandle(getZoom());
} else {
- long brush = data.gdipBgPatternBrushAlpha != 0 ? Gdip.Brush_Clone(data.gdipBgPatternBrushAlpha) : createAlphaTextureBrush(pattern.handle, data.alpha);
+ long brush = data.gdipBgPatternBrushAlpha != 0 ? Gdip.Brush_Clone(data.gdipBgPatternBrushAlpha) : createAlphaTextureBrush(pattern.getHandle(getZoom()), data.alpha);
data.gdipBrush = data.gdipBgBrush /*= data.gdipBgPatternBrushAlpha */ = brush;
}
if ((data.style & SWT.MIRRORED) != 0) {
@@ -3440,7 +3440,7 @@ public void getClipping (Region region) {
}
long getFgBrush() {
- return data.foregroundPattern != null ? data.foregroundPattern.handle : data.gdipFgBrush;
+ return data.foregroundPattern != null ? data.foregroundPattern.getHandle(getZoom()) : data.gdipFgBrush;
}
/**
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 38a9eb8629a..1465e35103f 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
@@ -13,6 +13,8 @@
*******************************************************************************/
package org.eclipse.swt.graphics;
+import java.util.*;
+
import org.eclipse.swt.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.gdip.*;
@@ -39,22 +41,18 @@
*/
public class Pattern extends Resource {
- /**
- * the OS resource for the Pattern
- * (Warning: This field is platform dependent)
- *
- * IMPORTANT: This field is not part of the SWT
- * public API. It is marked public only so that it can be shared
- * within the packages provided by SWT. It is not available on all
- * platforms and should never be accessed from application code.
- *
- *
- * @noreference This field is not intended to be referenced by clients.
- */
- public long handle;
+ private int initialZoom;
private Runnable bitmapDestructor;
+ // These are the possible fields with which a pattern can be initialized from the appropriate constructors.
+ private Image image;
+ private float baseX1, baseY1, baseX2, baseY2;
+ private Color color1, color2;
+ private int alpha1, alpha2;
+
+ private final Map zoomLevelToHandle = new HashMap<>();
+
/**
* Constructs a new Pattern given an image. Drawing with the resulting
* pattern will cause the image to be tiled over the resulting area.
@@ -88,22 +86,9 @@ public Pattern(Device device, Image image) {
if (image == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
this.device.checkGDIP();
- long[] gdipImage = image.createGdipImage();
- long img = gdipImage[0];
- int width = Gdip.Image_GetWidth(img);
- int height = Gdip.Image_GetHeight(img);
- handle = Gdip.TextureBrush_new(img, Gdip.WrapModeTile, 0, 0, width, height);
- bitmapDestructor = () -> {
- Gdip.Bitmap_delete(img);
- if (gdipImage[1] != 0) {
- long hHeap = OS.GetProcessHeap ();
- OS.HeapFree(hHeap, 0, gdipImage[1]);
- }
- };
- if (handle == 0) {
- bitmapDestructor.run();
- SWT.error(SWT.ERROR_NO_HANDLES);
- }
+ this.image = image;
+ initialZoom = DPIUtil.getDeviceZoom();
+ setImageHandle(image, initialZoom);
init();
}
@@ -187,10 +172,35 @@ public Pattern(Device device, float x1, float y1, float x2, float y2, Color colo
*/
public Pattern(Device device, float x1, float y1, float x2, float y2, Color color1, int alpha1, Color color2, int alpha2) {
super(device);
- x1 = DPIUtil.autoScaleUp(x1);
- y1 = DPIUtil.autoScaleUp(y1);
- x2 = DPIUtil.autoScaleUp(x2);
- y2 = DPIUtil.autoScaleUp(y2);
+ this.baseX1 = x1;
+ this.baseX2 = x2;
+ this.baseY1 = y1;
+ this.baseY2 = y2;
+ this.color1 = color1;
+ this.color2 = color2;
+ this.alpha1 = alpha1;
+ this.alpha2 = alpha2;
+ initialZoom = DPIUtil.getDeviceZoom();
+ initializeSize(initialZoom);
+}
+
+long getHandle(int zoom) {
+ if (!this.zoomLevelToHandle.containsKey(zoom)) {
+ if (isImagePattern()) {
+ setImageHandle(image, zoom);
+ } else {
+ initializeSize(zoom);
+ }
+ }
+ return this.zoomLevelToHandle.get(zoom);
+}
+
+private void initializeSize(int zoom) {
+ long handle;
+ float x1 = DPIUtil.autoScaleUp(this.baseX1, zoom);
+ float y1 = DPIUtil.autoScaleUp(this.baseY1, zoom);
+ float x2 = DPIUtil.autoScaleUp(this.baseX2, zoom);
+ float y2 = DPIUtil.autoScaleUp(this.baseY2, zoom);
if (color1 == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (color1.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
if (color2 == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
@@ -221,27 +231,51 @@ public Pattern(Device device, float x1, float y1, float x2, float y2, Color colo
Gdip.LinearGradientBrush_SetInterpolationColors(handle, new int [] {foreColor, midColor, backColor}, new float[]{0, 0.5f, 1}, 3);
}
}
+ this.zoomLevelToHandle.put(zoom, handle);
init();
}
+void setImageHandle(Image image, int zoom) {
+ long[] gdipImage = image.createGdipImage(zoom);
+ long img = gdipImage[0];
+ int width = Gdip.Image_GetWidth(img);
+ int height = Gdip.Image_GetHeight(img);
+ long handle = Gdip.TextureBrush_new(img, Gdip.WrapModeTile, 0, 0, width, height);
+ bitmapDestructor = () -> {
+ Gdip.Bitmap_delete(img);
+ if (gdipImage[1] != 0) {
+ long hHeap = OS.GetProcessHeap ();
+ OS.HeapFree(hHeap, 0, gdipImage[1]);
+ }
+ };
+ if (handle == 0) {
+ bitmapDestructor.run();
+ SWT.error(SWT.ERROR_NO_HANDLES);
+ } else {
+ zoomLevelToHandle.put(zoom, handle);
+ }
+}
+
@Override
void destroy() {
- 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;
+ 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;
+ }
}
- handle = 0;
+ zoomLevelToHandle.clear();
if (bitmapDestructor != null) {
bitmapDestructor.run();
bitmapDestructor = null;
@@ -260,7 +294,7 @@ void destroy() {
*/
@Override
public boolean isDisposed() {
- return handle == 0;
+ return zoomLevelToHandle.isEmpty();
}
/**
@@ -272,7 +306,11 @@ public boolean isDisposed() {
@Override
public String toString() {
if (isDisposed()) return "Pattern {*DISPOSED*}";
- return "Pattern {" + handle + "}";
+ return "Pattern {" + zoomLevelToHandle + "}";
+}
+
+private boolean isImagePattern() {
+ return image != null;
}
}
diff --git a/tests/org.eclipse.swt.tests.win32/ManualTests/org/eclipse/swt/widgets/PatternWin32ManualTest.java b/tests/org.eclipse.swt.tests.win32/ManualTests/org/eclipse/swt/widgets/PatternWin32ManualTest.java
new file mode 100644
index 00000000000..371e918f412
--- /dev/null
+++ b/tests/org.eclipse.swt.tests.win32/ManualTests/org/eclipse/swt/widgets/PatternWin32ManualTest.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Yatta Solutions
+ *
+ * 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 Solutions - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Pattern;
+import org.eclipse.swt.internal.DPIUtil;
+
+/*
+ * This Snippet tests a pattern at multiple zoom levels.
+ *
+ * It is difficult to test the pattern at multiple zoom level automatically since we also need
+ * to test the visual behavior. It is important to make sure that the pattern looks the same
+ * regardless of its size as per the zoom level. On the execution, 2 shells are
+ * opened at 2 different zoom levels. The pattern is a gradient of 2 colors. On both the
+ * shells, the pattern should be uniformly drawn and the size difference of both the patterns
+ * should be clearly visible without any visual difference except for the size. The size
+ * difference should be equal to the scalingFactor in the snippet.
+ *
+ */
+public class PatternWin32ManualTest {
+ private static Display display = Display.getDefault();
+
+ public static void main (String [] args) {
+ int zoom = DPIUtil.getDeviceZoom();
+ int scalingFactor = 3;
+ int scaledZoom = zoom * scalingFactor;
+ int width = 400;
+ int height = 300;
+ final Pattern pat = new Pattern(display, 0, 0, width, height, new Color(null, 200, 200, 200), 0, new Color(null, 255, 0, 0), 255);
+
+ Shell shell1 = createShellWithPattern(width, height, pat, zoom);
+ Shell shell2 = createShellWithPattern(width, height, pat, scaledZoom);
+
+ while (!shell1.isDisposed() || !shell2.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ pat.dispose();
+ shell1.dispose();
+ shell2.dispose();
+ }
+
+ private static Shell createShellWithPattern(int width, int height, final Pattern pat, int nativeZoom) {
+ Shell shell = new Shell(display);
+ shell.nativeZoom = nativeZoom;
+ shell.setText("Unscaled shell");
+ shell.setSize(width, height);
+ shell.addPaintListener(e -> {
+ e.gc.setBackground(new Color(null, 100, 200, 0));
+ e.gc.fillRectangle(0, 0, shell.getBounds().width, shell.getBounds().height);
+ e.gc.setBackground(new Color(null, 255, 0, 0));
+ e.gc.setBackgroundPattern(pat);
+ e.gc.fillRectangle(0, 0, shell.getBounds().width, shell.getBounds().height);
+ });
+ shell.open();
+ return shell;
+ }
+}