consumer, long wParam, long lParam);
+
+ void redrawTriggered();
+
+ void redrawTriggered(int x, int y, int width, int height, boolean all);
+
+ void scroll(int destX, int destY, int x, int y, int width, int height, boolean all);
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/canvasext/IExternalFontMetrics.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/canvasext/IExternalFontMetrics.java
new file mode 100644
index 00000000000..13431316131
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/canvasext/IExternalFontMetrics.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2025 SAP SE 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:
+ * SAP SE and others - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal.canvasext;
+
+public interface IExternalFontMetrics {
+
+ public int getAscent();
+
+ public double getAverageCharacterWidth();
+
+ @Deprecated
+ public int getAverageCharWidth();
+
+ public int getDescent();
+
+ public int getHeight();
+
+ public int getLeading();
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/canvasext/IExternalGC.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/canvasext/IExternalGC.java
new file mode 100644
index 00000000000..7b88a35f9a9
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/canvasext/IExternalGC.java
@@ -0,0 +1,197 @@
+/*******************************************************************************
+ * Copyright (c) 2025 SAP SE 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:
+ * SAP SE and others - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal.canvasext;
+
+import org.eclipse.swt.graphics.*;
+
+public interface IExternalGC {
+
+ Device getDevice();
+
+ void copyArea(Image image, int i, int j);
+
+ void dispose();
+
+ Point textExtent(String string);
+
+ void setBackground(Color color);
+
+ void setForeground(Color color);
+
+ void fillRectangle(Rectangle rect);
+
+ void drawImage(Image image, int x, int y);
+
+ void drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth,
+ int destHeight);
+
+ void drawLine(int x1, int y1, int x2, int y2);
+
+ Color getForeground();
+
+ void drawText(String string, int x, int y);
+
+ void drawText(String string, int x, int y, boolean isTransparent);
+
+ void drawText(String text, int x, int y, int flags);
+
+ void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle);
+
+ void drawFocus(int x, int y, int width, int height);
+
+ void drawOval(int x, int y, int width, int height);
+
+ void drawPath(Path path);
+
+ void drawPoint(int x, int y);
+
+ void drawPolygon(int[] pointArray);
+
+ void drawPolyline(int[] pointArray);
+
+ void drawRectangle(int x, int y, int width, int height);
+
+ void drawRectangle(Rectangle rect);
+
+ void drawRoundRectangle(int x, int y, int width, int height, int arcWidth, int arcHeight);
+
+ void drawString(String string, int x, int y);
+
+ void drawString(String string, int x, int y, boolean isTransparent);
+
+ void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle);
+
+ void fillGradientRectangle(int x, int y, int width, int height, boolean vertical);
+
+ void fillOval(int x, int y, int width, int height);
+
+ void fillPath(Path path);
+
+ void fillPolygon(int[] pointArray);
+
+ void fillRectangle(int x, int y, int width, int height);
+
+ Point textExtent(String string, int flags);
+
+ void fillRoundRectangle(int x, int y, int width, int height, int arcWidth, int arcHeight);
+
+ void setFont(Font font);
+
+ Font getFont();
+
+ void setAlpha(int alpha);
+
+ int getAlpha();
+
+ void setLineWidth(int i);
+
+ int getAntialias();
+
+ void setAntialias(int antialias);
+
+ void setAdvanced(boolean enable);
+
+ void setLineStyle(int lineStyle);
+
+ int getLineStyle();
+
+ int getLineWidth();
+
+ LineAttributes getLineAttributes();
+
+ void setClipping(int x, int y, int width, int height);
+
+ void setTransform(Transform transform);
+
+ Point stringExtent(String string);
+
+ int getLineCap();
+
+ Rectangle getClipping();
+
+ void copyArea(int srcX, int srcY, int width, int height, int destX, int destY);
+
+ void copyArea(int srcX, int srcY, int width, int height, int destX, int destY, boolean paint);
+
+ boolean isClipped();
+
+ int getFillRule();
+
+ void getClipping(Region region);
+
+ int getAdvanceWidth(char ch);
+
+ boolean getAdvanced();
+
+ int getCharWidth(char ch);
+
+ Color getBackground();
+
+ Pattern getBackgroundPattern();
+
+ Pattern getForegroundPattern();
+
+ GCData getGCData();
+
+ int getInterpolation();
+
+ int[] getLineDash();
+
+ int getLineJoin();
+
+ int getStyle();
+
+ int getTextAntialias();
+
+ void getTransform(Transform transform);
+
+ boolean getXORMode();
+
+ void setBackgroundPattern(Pattern pattern);
+
+ void setClipping(Path path);
+
+ void setClipping(Rectangle rect);
+
+ void setClipping(Region region);
+
+ void setFillRule(int rule);
+
+ void setInterpolation(int interpolation);
+
+ void setForegroundPattern(Pattern pattern);
+
+ void setLineAttributes(LineAttributes attributes);
+
+ void setLineCap(int cap);
+
+ void setLineDash(int[] dashes);
+
+ void setLineJoin(int join);
+
+ void setXORMode(boolean xor);
+
+ void setTextAntialias(int antialias);
+
+ boolean isDisposed();
+
+ void drawImage(Image image, int destX, int destY, int destWidth, int destHeight);
+
+ FontMetrics getFontMetrics();
+
+ Drawable getDrawable();
+
+ void textLayoutDraw(TextLayout textLayout, GC gc, int xInPoints, int yInPoints, int selectionStart,
+ int selectionEnd, Color selectionForeground, Color selectionBackground, int flags);
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/FontMetrics.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/FontMetrics.java
index 836325ca747..1fb0fc94108 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/FontMetrics.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/FontMetrics.java
@@ -23,7 +23,7 @@
* @see GC#getFontMetrics
* @see Sample code and further information
*/
-public final class FontMetrics {
+public sealed class FontMetrics permits FontMetricsExtension {
int ascentInPoints, descentInPoints, averageCharWidthInPoints;
FontMetrics() {
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/FontProperties.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/FontProperties.java
new file mode 100644
index 00000000000..63bc5a0a471
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/FontProperties.java
@@ -0,0 +1,57 @@
+package org.eclipse.swt.graphics;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.internal.gtk.*;
+
+/**
+ * @noreference This class is not intended to be referenced by clients.
+ */
+public class FontProperties {
+
+ public int lfHeight;
+ public int lfWidth = -1;
+ public int lfEscapement;
+ public int lfOrientation;
+ public int lfWeight = -1;
+ public byte lfItalic;
+ public byte lfUnderline;
+ public byte lfStrikeOut;
+ public String name;
+
+ private FontProperties() {
+
+ }
+
+ /**
+ * @noreference This class is not intended to be referenced by clients.
+ */
+ public static FontProperties getFontProperties(Font font) {
+ var fd = font.getFontData()[0];
+
+// float height = (float)OS.pango_font_description_get_size(font.handle) / OS.PANGO_SCALE;
+ var stretch = OS.pango_font_description_get_stretch(font.handle);
+// var variant = OS.pango_font_description_get_variant(font.handle);
+// var style = OS.pango_font_description_get_style(font.handle);
+ var weight = OS.pango_font_description_get_weight(font.handle);
+
+ var fp = new FontProperties();
+
+ fp.name = fd.getName();
+ fp.lfHeight = fd.getHeight();
+
+ if((fd.getStyle() & SWT.ITALIC) != 0 )
+ fp.lfItalic = 1;
+ fp.lfWeight = weight;
+
+ fp.lfWidth = stretch + 1;
+
+
+
+ return fp;
+
+ }
+
+
+
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/GC.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/GC.java
index 5d358be4b18..1802b6c17f5 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/GC.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/GC.java
@@ -60,7 +60,7 @@
* @see SWT Examples: GraphicsExample, PaintExample
* @see Sample code and further information
*/
-public final class GC extends Resource {
+public sealed class GC extends Resource permits GCExtension {
/**
* the handle to the OS device context
* (Warning: This field is platform dependent)
@@ -2602,6 +2602,10 @@ void init(Drawable drawable, GCData data, long gdkGC) {
if (cairoTransformationMatrix == null) cairoTransformationMatrix = new double[6];
Cairo.cairo_get_matrix(data.cairo, cairoTransformationMatrix);
clipping = getClipping();
+ if(this.drawable instanceof Image i) {
+ // it is likely that the GC modifies the image, so we increase the verion
+ i.increaseVersion();
+ }
}
void initCairo() {
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 cbd3348943f..4692f9359fd 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
@@ -173,6 +173,11 @@ public final class Image extends Resource implements Drawable {
*/
private int currentDeviceZoom = 100;
+ /**
+ * versions for an image cache
+ */
+ private int version;
+
Image(Device device) {
super(device);
currentDeviceZoom = DPIUtil.getDeviceZoom();
@@ -1678,4 +1683,20 @@ public static void drawAtSize(GC gc, ImageData imageData, int width, int height)
});
}
+void increaseVersion() {
+ if(this.version == Integer.MAX_VALUE) {
+ this.version = 0;
+ } else {
+ this.version++;
+ }
+}
+
+/**
+ * @noreference This field is not intended to be referenced by clients.
+ * @return The current version of the image, which is incremented each time the image data changes.
+ */
+public ImageVersion getImageVersion() {
+ return new ImageVersion(this.version);
+}
+
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Region.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Region.java
index 929e6435e14..b34e96c059c 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Region.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Region.java
@@ -46,6 +46,8 @@ public final class Region extends Resource {
*/
public long handle;
+ private final RegionLog log = new RegionLog();
+
/**
* Constructs a new empty region.
*
@@ -93,6 +95,14 @@ public Region(Device device) {
this.handle = handle;
}
+/**
+* @noreference This method is not intended to be referenced by clients.
+* @return a log characterizing the region
+*/
+public RegionLog getLog() {
+ return log;
+}
+
static long gdk_region_polygon(int[] pointArray, int npoints, int fill_rule) {
//TODO this does not perform well and could fail if the polygon is too big
int minX = pointArray[0], maxX = minX;
@@ -161,6 +171,7 @@ public void add (int[] pointArray) {
* with enough points for a polygon.
*/
if (pointArray.length < 6) return;
+ log.add(pointArray);
long polyRgn = gdk_region_polygon(pointArray, pointArray.length / 2, GDK.GDK_EVEN_ODD_RULE);
Cairo.cairo_region_union(handle, polyRgn);
Cairo.cairo_region_destroy(polyRgn);
@@ -207,6 +218,7 @@ public void add(Rectangle rect) {
public void add(int x, int y, int width, int height) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (width < 0 || height < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ log.add(new Rectangle(x, y, width, height));
cairo_rectangle_int_t rect = new cairo_rectangle_int_t();
rect.x = x;
rect.y = y;
@@ -234,6 +246,7 @@ public void add(Region region) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (region == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ log.add(region);
Cairo.cairo_region_union(handle, region.handle);
}
@@ -395,6 +408,7 @@ public void intersect(Rectangle rect) {
public void intersect(int x, int y, int width, int height) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (width < 0 || height < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ log.intersect(new Rectangle(x,y,width,height));
cairo_rectangle_int_t rect = new cairo_rectangle_int_t();
rect.x = x;
rect.y = y;
@@ -426,6 +440,7 @@ public void intersect(Region region) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (region == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ log.intersect(region);
Cairo.cairo_region_intersect(handle, region.handle);
}
@@ -532,6 +547,7 @@ public void subtract (int[] pointArray) {
* with enough points for a polygon.
*/
if (pointArray.length < 6) return;
+ log.subtract(pointArray);
long polyRgn = gdk_region_polygon(pointArray, pointArray.length / 2, GDK.GDK_EVEN_ODD_RULE);
Cairo.cairo_region_subtract(handle, polyRgn);
Cairo.cairo_region_destroy(polyRgn);
@@ -579,6 +595,7 @@ public void subtract(Rectangle rect) {
public void subtract(int x, int y, int width, int height) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (width < 0 || height < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ log.subtract(new Rectangle(x,y,width,height));
cairo_rectangle_int_t rect = new cairo_rectangle_int_t ();
rect.x = x;
rect.y = y;
@@ -610,6 +627,7 @@ public void subtract(Region region) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (region == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ log.subtract(region);
Cairo.cairo_region_subtract(handle, region.handle);
}
@@ -628,6 +646,7 @@ public void subtract(Region region) {
*/
public void translate (int x, int y) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
+ log.translate(new Point(x,y));
Cairo.cairo_region_translate (handle, x, y);
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/TextLayout.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/TextLayout.java
index 0cf16e2b58b..310d44d8ade 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/TextLayout.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/TextLayout.java
@@ -619,6 +619,12 @@ public void draw(GC gc, int x, int y, int selectionStart, int selectionEnd, Colo
}
void drawInPixels(GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground, int flags) {
checkLayout ();
+
+ if(gc instanceof GCExtension gcext) {
+ gcext.textLayoutDraw(this, gc, x, y, selectionStart, selectionEnd, selectionForeground, selectionBackground, flags);
+ return;
+ }
+
computeRuns();
if (gc == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (gc.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/canvasext/DpiScaler.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/canvasext/DpiScaler.java
new file mode 100644
index 00000000000..fbe5cffa8fd
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/canvasext/DpiScaler.java
@@ -0,0 +1,20 @@
+package org.eclipse.swt.internal.canvasext;
+
+import org.eclipse.swt.widgets.*;
+
+/**
+ *
+ * Provides utility methods for the canvas extension to scale values according
+ * to the current DPI settings of the OS. This is used internally to scale all
+ * values that are used for drawing and layout calculations.
+ */
+public class DpiScaler implements IDpiScaler {
+
+ public DpiScaler(Canvas canvas) {
+ }
+
+ public int getNativeZoom() {
+ return 100;
+ }
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/canvasext/FontProperties.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/canvasext/FontProperties.java
new file mode 100644
index 00000000000..e1f1d19942a
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/canvasext/FontProperties.java
@@ -0,0 +1,74 @@
+package org.eclipse.swt.internal.canvasext;
+
+import java.util.*;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.gtk.*;
+
+public class FontProperties {
+
+ public int lfHeight;
+ public int lfWidth = -1;
+ public int lfEscapement;
+ public int lfOrientation;
+ public int lfWeight = -1;
+ public byte lfItalic;
+ public byte lfUnderline;
+ public byte lfStrikeOut;
+ public String name;
+
+ private FontProperties() {
+
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(lfEscapement, lfHeight, lfItalic, lfOrientation, lfStrikeOut, lfUnderline, lfWeight,
+ lfWidth, name);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ FontProperties other = (FontProperties) obj;
+ return lfEscapement == other.lfEscapement && lfHeight == other.lfHeight && lfItalic == other.lfItalic
+ && lfOrientation == other.lfOrientation && lfStrikeOut == other.lfStrikeOut
+ && lfUnderline == other.lfUnderline && lfWeight == other.lfWeight && lfWidth == other.lfWidth
+ && Objects.equals(name, other.name);
+ }
+
+ public static FontProperties getFontProperties(Font font) {
+ var fd = font.getFontData()[0];
+
+// float height = (float)OS.pango_font_description_get_size(font.handle) / OS.PANGO_SCALE;
+ var stretch = OS.pango_font_description_get_stretch(font.handle);
+// var variant = OS.pango_font_description_get_variant(font.handle);
+// var style = OS.pango_font_description_get_style(font.handle);
+ var weight = OS.pango_font_description_get_weight(font.handle);
+
+ var fp = new FontProperties();
+
+ fp.name = fd.getName();
+ fp.lfHeight = fd.getHeight();
+
+ if((fd.getStyle() & SWT.ITALIC) != 0 )
+ fp.lfItalic = 1;
+ fp.lfWeight = weight;
+
+ fp.lfWidth = stretch + 1;
+
+
+
+ return fp;
+
+ }
+
+
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Canvas.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Canvas.java
index 250818d83b6..fcd72d91b52 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Canvas.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Canvas.java
@@ -17,6 +17,7 @@
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.cairo.*;
+import org.eclipse.swt.internal.canvasext.*;
import org.eclipse.swt.internal.gtk.*;
/**
@@ -45,6 +46,7 @@ public class Canvas extends Composite {
Caret caret;
IME ime;
boolean blink, drawFlag;
+ private IExternalCanvasHandler externalCanvasHandler;
Canvas () {}
@@ -76,6 +78,28 @@ public class Canvas extends Composite {
*/
public Canvas (Composite parent, int style) {
super (parent, checkStyle (style));
+ if( ExternalCanvasHandler.isActive(this,style))
+ externalCanvasHandler = ExternalCanvasHandler.createHandler(this);
+}
+
+@Override
+public void redraw () {
+
+ if(externalCanvasHandler != null) {
+ externalCanvasHandler.redrawTriggered();
+ }
+ super.redraw();
+}
+
+@Override
+public void redraw (int x, int y, int width, int height, boolean all) {
+ if(externalCanvasHandler != null) {
+ externalCanvasHandler.redrawTriggered(x,y,width,height,all);
+ super.redraw();
+ }
+ else {
+ super.redraw(x,y,width,height,all);
+ }
}
/**
@@ -170,6 +194,12 @@ long gtk_commit (long imcontext, long text) {
@Override
long gtk_draw (long widget, long cairo) {
if ((state & OBSCURED) != 0) return 0;
+
+ if(externalCanvasHandler != null) {
+ externalCanvasHandler.paint((e)-> sendEvent(SWT.Paint, e),widget, cairo);
+ return 0;
+ }
+
long result = super.gtk_draw (widget, cairo);
drawCaretInFocus(cairo);
return result;
@@ -331,6 +361,10 @@ void reskinChildren (int flags) {
*
*/
public void scroll (int destX, int destY, int x, int y, int width, int height, boolean all) {
+ if(this.externalCanvasHandler != null) {
+ this.externalCanvasHandler.scroll(destX, destY, x, y, width, height, all);
+ return;
+ }
checkWidget();
if (width <= 0 || height <= 0) return;
/*
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/FontMetrics.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/FontMetrics.java
index 457d3d8f029..01cfe312799 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/FontMetrics.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/FontMetrics.java
@@ -27,7 +27,7 @@
* @see GC#getFontMetrics
* @see Sample code and further information
*/
-public final class FontMetrics {
+public sealed class FontMetrics permits FontMetricsExtension {
/**
* On Windows, handle is a Win32 TEXTMETRIC struct
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 85a110bb40d..c59eb0026dd 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
@@ -63,7 +63,7 @@
* @see SWT Examples: GraphicsExample, PaintExample
* @see Sample code and further information
*/
-public final class GC extends Resource {
+public sealed class GC extends Resource permits GCExtension {
/**
* the handle to the OS device context
@@ -4555,6 +4555,11 @@ private void init(Drawable drawable, GCData data, long hDC, ImageHandle imageHan
this.drawable = drawable;
this.data = data;
handle = hDC;
+
+ if(this.drawable instanceof Image i) {
+ // it is likely that the GC modifies the image, so we increase the verion
+ i.increaseVersion();
+ }
}
private static int extractZoom(long hDC) {
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 eed02b72093..7816a358911 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
@@ -261,6 +261,8 @@ private InternalImageHandle getOrCreateImageHandleAtClosestSize(int widthHint, i
private final HandleAtSize lastRequestedHandle = new HandleAtSize();
+ private int version;
+
private Image (Device device, int type, long handle, int nativeZoom) {
super(device);
this.type = type;
@@ -3321,4 +3323,20 @@ void destroy() {
}
}
+
+ void increaseVersion() {
+ if(this.version == Integer.MAX_VALUE) {
+ this.version = 0;
+ } else {
+ this.version++;
+ }
+ }
+
+ /**
+ * @noreference This field is not intended to be referenced by clients.
+ * @return The current version of the image, which is incremented each time the image data changes.
+ */
+ public ImageVersion getImageVersion() {
+ return new ImageVersion(this.version);
+ }
}
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 5afccb2f3b2..198cdccb36c 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
@@ -219,6 +219,14 @@ void destroyHandlesExcept(Set zoomLevels) {
});
}
+/**
+ * Package-protected for internal use by fragments.
+ * @return the properties of this pattern, which can be used to create a copy of the pattern with the same properties as this pattern
+ */
+PatternProperties getProperties() {
+ return new PatternProperties(image, baseX1, baseY1, baseX2, baseY2, color1, alpha1, color2, alpha2);
+}
+
Pattern copy() {
if (image != null) {
return new Pattern(device, image);
@@ -364,4 +372,4 @@ protected void destroy() {
}
}
}
-}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Region.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Region.java
index 8aa648f3787..15fb1b06c15 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Region.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Region.java
@@ -37,6 +37,8 @@
*/
public final class Region extends Resource {
+ private final RegionLog log = new RegionLog();
+
private Map zoomToHandle = new HashMap<>();
private List operations = new ArrayList<>();
@@ -104,6 +106,7 @@ public Region (Device device) {
public void add (int[] pointArray) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (pointArray == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ log.add(pointArray);
final Operation operation = new OperationWithArray(Operation::add, Arrays.copyOf(pointArray, pointArray.length));
storeAndApplyOperationForAllHandles(operation);
}
@@ -125,6 +128,7 @@ public void add (int[] pointArray) {
public void add (Rectangle rect) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (rect == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ log.add(rect);
final Operation operation = new OperationWithRectangle(Operation::add, new Rectangle(rect.x, rect.y, rect.width, rect.height));
storeAndApplyOperationForAllHandles(operation);
}
@@ -149,6 +153,7 @@ public void add (Rectangle rect) {
*/
public void add (int x, int y, int width, int height) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
+ log.add(new Rectangle(x,y,width,height));
final Operation operation = new OperationWithRectangle(Operation::add, new Rectangle(x, y, width, height));
storeAndApplyOperationForAllHandles(operation);
}
@@ -172,6 +177,7 @@ public void add (Region region) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (region == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ log.add(region);
if (!region.operations.isEmpty()) {
adoptTemporaryHandleZoomHint(region);
final Operation operation = new OperationWithRegion(Operation::add, region.operations);
@@ -185,6 +191,14 @@ private void adoptTemporaryHandleZoomHint(Region region) {
}
}
+/**
+* @return a log characterizing the region
+*/
+RegionLog getLog() {
+ return log;
+}
+
+
/**
* Returns true if the point specified by the
* arguments is inside the area specified by the receiver,
@@ -332,6 +346,7 @@ public int hashCode () {
public void intersect (Rectangle rect) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (rect == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ log.intersect(rect);
final Operation operation = new OperationWithRectangle(Operation::intersect, new Rectangle(rect.x, rect.y, rect.width, rect.height));
storeAndApplyOperationForAllHandles(operation);
}
@@ -356,6 +371,7 @@ public void intersect (Rectangle rect) {
*/
public void intersect (int x, int y, int width, int height) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
+ log.intersect(new Rectangle(x,y,width,height));
final Operation operation = new OperationWithRectangle(Operation::intersect, new Rectangle(x, y, width, height));
storeAndApplyOperationForAllHandles(operation);
}
@@ -381,6 +397,7 @@ public void intersect (Region region) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (region == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ log.intersect(region);
if (!region.operations.isEmpty()) {
adoptTemporaryHandleZoomHint(region);
final Operation operation = new OperationWithRegion(Operation::intersect, region.operations);
@@ -511,6 +528,7 @@ void set(Function handleForZoomSupplier, int contextZoom) {
public void subtract (int[] pointArray) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (pointArray == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ log.subtract(pointArray);
final Operation operation = new OperationWithArray(Operation::subtract, Arrays.copyOf(pointArray, pointArray.length));
storeAndApplyOperationForAllHandles(operation);
}
@@ -534,6 +552,7 @@ public void subtract (int[] pointArray) {
public void subtract (Rectangle rect) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (rect == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ log.subtract(rect);
final Operation operation = new OperationWithRectangle(Operation::subtract, new Rectangle(rect.x, rect.y, rect.width, rect.height));
storeAndApplyOperationForAllHandles(operation);
}
@@ -558,6 +577,7 @@ public void subtract (Rectangle rect) {
*/
public void subtract (int x, int y, int width, int height) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
+ log.subtract(new Rectangle(x,y,width,height));
final Operation operation = new OperationWithRectangle(Operation::subtract, new Rectangle(x, y, width, height));
storeAndApplyOperationForAllHandles(operation);
}
@@ -583,6 +603,7 @@ public void subtract (Region region) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (region == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ log.subtract(region);
if (!region.operations.isEmpty()) {
adoptTemporaryHandleZoomHint(region);
final Operation operation = new OperationWithRegion(Operation::subtract, region.operations);
@@ -605,6 +626,7 @@ public void subtract (Region region) {
*/
public void translate (int x, int y) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
+ log.translate(new Point(x,y));
final Operation operation = new OperationWithPoint(Operation::translate, new Point(x, y));
storeAndApplyOperationForAllHandles(operation);
}
@@ -627,6 +649,7 @@ public void translate (int x, int y) {
public void translate (Point pt) {
if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
if (pt == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ log.translate(pt);
final Operation operation = new OperationWithPoint(Operation::translate, new Point(pt.x, pt.y));
storeAndApplyOperationForAllHandles(operation);
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java
index 0dd41a0f02b..5b32dd4ab67 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java
@@ -706,6 +706,9 @@ long createGdipBrush(Color color, int alpha) {
public void draw (GC gc, int x, int y) {
checkLayout();
drawInPixels(gc, x, y);
+
+
+
}
/**
@@ -788,6 +791,12 @@ void drawInPixels (GC gc, int xInPoints, int yInPoints) {
}
void drawInPixels (GC gc, int xInPoints, int yInPoints, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground, int flags) {
+
+ if(gc instanceof GCExtension gcext) {
+ gcext.textLayoutDraw(this, gc, xInPoints, yInPoints, selectionStart, selectionEnd, selectionForeground, selectionBackground, flags);
+ return;
+ }
+
computeRuns(gc);
if (gc == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (gc.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
@@ -4029,4 +4038,5 @@ int untranslateOffset(int offset) {
public void setDefaultTabWidth(int tabLength) {
// unused in win32
}
+
}
\ No newline at end of file
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/canvasext/DpiScaler.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/canvasext/DpiScaler.java
new file mode 100644
index 00000000000..7681a9d2913
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/canvasext/DpiScaler.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2025 SAP SE 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:
+ * SAP SE and others - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal.canvasext;
+
+import org.eclipse.swt.widgets.*;
+
+/**
+ *
+ * Provides utility methods for the canvas extension to scale values according
+ * to the current DPI settings of the OS. This is used internally to scale all
+ * values that are used for drawing and layout calculations.
+ */
+public class DpiScaler implements IDpiScaler {
+
+ private Canvas canvas;
+
+ public DpiScaler(Canvas canvas) {
+ this.canvas = canvas;
+ }
+
+ @Override
+ public int getNativeZoom() {
+ return canvas.nativeZoom;
+ }
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/canvasext/FontProperties.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/canvasext/FontProperties.java
new file mode 100644
index 00000000000..c32ccb3015b
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/canvasext/FontProperties.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2025 SAP SE 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:
+ * SAP SE and others - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.internal.canvasext;
+
+import java.util.*;
+
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Provides extended information about a font, including its stretch factor and other properties.
+ */
+public class FontProperties {
+
+ // Standard OpenType stretch values
+ private static final Map STRETCH_MAP = new HashMap<>();
+ private static final Set REGION_KEYS = new HashSet<>();
+
+ static {
+ STRETCH_MAP.put("UltraCondensed", 1); // 50.0%
+ STRETCH_MAP.put("ExtraCondensed", 2); // 62.5%
+ STRETCH_MAP.put("Condensed", 3); // 75.0%
+ STRETCH_MAP.put("SemiCondensed", 4); // 87.5%
+ STRETCH_MAP.put("Normal", 5); // 100.0%
+ STRETCH_MAP.put("Medium", 5);
+ STRETCH_MAP.put("SemiExpanded", 6); // 112.5%
+ STRETCH_MAP.put("Expanded", 7); // 125.0%
+ STRETCH_MAP.put("ExtraExpanded", 8); // 150.0%
+ STRETCH_MAP.put("UltraExpanded", 9); // 200.0%
+
+
+ REGION_KEYS.add(" Greek");
+ REGION_KEYS.add(" TUR");
+ REGION_KEYS.add(" Baltic");
+ REGION_KEYS.add(" CE");
+ REGION_KEYS.add(" CYR");
+ REGION_KEYS.add(" Transparent");
+ }
+
+
+
+
+
+ public int lfHeight;
+ public int lfWidth;
+ public int lfEscapement;
+ public int lfOrientation;
+ public int lfWeight;
+ public byte lfItalic;
+ public byte lfUnderline;
+ public byte lfStrikeOut;
+ public String name;
+
+ private FontProperties() {
+
+ }
+
+ private static FontProperties getFontProperties(FontData fd) {
+ var fp = new FontProperties();
+ var d = fd.data;
+
+ String name = fd.getName();
+
+ for(String local : REGION_KEYS) {
+ if(name.endsWith(local)) {
+ name = name.substring(0, name.length() - local.length());
+ break;
+ }
+
+ }
+
+ var fontName = analyzeManual(name);
+
+ fp.name = fontName.baseName;
+ fp.lfHeight = fd.getHeight();
+ fp.lfItalic = d.lfItalic;
+ fp.lfEscapement = d.lfEscapement;
+ fp.lfOrientation = d.lfOrientation;
+ fp.lfStrikeOut = d.lfStrikeOut;
+ fp.lfUnderline = d.lfUnderline;
+ if(d.lfWeight == 0)
+ fp.lfWeight = 400; // Normal weight
+ else
+ fp.lfWeight = d.lfWeight;
+ var stretch = STRETCH_MAP.get(fontName.detectedStretch);
+ if(stretch != null)
+ fp.lfWidth = stretch;
+
+ return fp;
+ }
+
+ public static FontProperties getFontProperties(Font font) {
+ return getFontProperties(font.getFontData()[0]);
+ }
+
+ private static FontName analyzeManual(String fullDescription) {
+ // known stretch keywords
+ String[] stretchKeywords = { "UltraCondensed", "ExtraCondensed", "Condensed", "SemiCondensed", "SemiExpanded",
+ "Expanded" };
+
+ String baseName = fullDescription;
+ String detectedStretch = null;
+
+ for (String keyword : stretchKeywords) {
+ if (fullDescription.contains(keyword)) {
+ detectedStretch = keyword;
+ baseName = fullDescription;
+ break;
+ }
+ }
+
+ return new FontName(baseName, detectedStretch);
+ }
+
+ private record FontName(String baseName, String detectedStretch) {
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(lfEscapement, lfHeight, lfItalic, lfOrientation, lfStrikeOut, lfUnderline, lfWeight,
+ lfWidth, name);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ FontProperties other = (FontProperties) obj;
+ return lfEscapement == other.lfEscapement && lfHeight == other.lfHeight && lfItalic == other.lfItalic
+ && lfOrientation == other.lfOrientation && lfStrikeOut == other.lfStrikeOut
+ && lfUnderline == other.lfUnderline && lfWeight == other.lfWeight && lfWidth == other.lfWidth
+ && Objects.equals(name, other.name);
+ }
+
+
+
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Canvas.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Canvas.java
index cf4df92bc54..09397611df4 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Canvas.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Canvas.java
@@ -17,6 +17,7 @@
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.*;
+import org.eclipse.swt.internal.canvasext.*;
import org.eclipse.swt.internal.win32.*;
/**
@@ -44,6 +45,7 @@
public class Canvas extends Composite {
Caret caret;
IME ime;
+ private IExternalCanvasHandler externalCanvasHandler;
/**
* Prevents uninitialized instances from being created outside the package.
@@ -79,6 +81,50 @@ public class Canvas extends Composite {
*/
public Canvas (Composite parent, int style) {
super (parent, style);
+ if( ExternalCanvasHandler.isActive(this,style))
+ externalCanvasHandler = ExternalCanvasHandler.createHandler(this);
+}
+
+@Override
+public void redraw () {
+
+ if(externalCanvasHandler != null) {
+ externalCanvasHandler.redrawTriggered();
+ }
+ super.redraw();
+}
+
+@Override
+public void redraw (int x, int y, int width, int height, boolean all) {
+
+ if(externalCanvasHandler != null) {
+ externalCanvasHandler.redrawTriggered( x, y, width, height, all);
+ super.redraw();
+ }
+ else
+ super.redraw( x, y, width, height, all);
+}
+
+@Override
+LRESULT WM_PAINT (long wParam, long lParam) {
+ if(externalCanvasHandler != null)
+ {
+
+ if ((state & DISPOSE_SENT) != 0) return LRESULT.ZERO;
+
+ var result = externalCanvasHandler.paint(e -> sendEvent(SWT.Paint,e),wParam, lParam);
+ if(result instanceof Integer i)
+ return new LRESULT(i);
+
+ if(result instanceof LRESULT r) {
+ return r;
+ }
+ return null;
+
+ }
+
+ return super.WM_PAINT(wParam, lParam);
+
}
/**
@@ -196,7 +242,12 @@ void reskinChildren (int flags) {
*
*/
public void scroll (int destX, int destY, int x, int y, int width, int height, boolean all) {
+ if(this.externalCanvasHandler != null) {
+ this.externalCanvasHandler.scroll(destX, destY, x, y, width, height, all);
+ return;
+ }
checkWidget ();
+
int zoom = getAutoscalingZoom();
destX = DPIUtil.pointToPixel(destX, zoom);
destY = DPIUtil.pointToPixel(destY, zoom);
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/GraphicsExample.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/GraphicsExample.java
index 67477fe2a21..58d30f9b422 100644
--- a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/GraphicsExample.java
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/GraphicsExample.java
@@ -70,7 +70,7 @@ public class GraphicsExample {
Sash hSash, vSash;
Canvas canvas;
Composite tabControlPanel;
- ToolItem backItem, dbItem; // background, double buffer items
+ ToolItem backItem, dbItem,skiaItem; // background, double buffer, skia items
Menu backMenu; // background menu item
List resources; // stores resources that will be disposed
@@ -205,6 +205,7 @@ void createControls(final Composite parent) {
void createCanvas(Composite parent) {
int style = SWT.NO_BACKGROUND;
if (dbItem.getSelection()) style |= SWT.DOUBLE_BUFFERED;
+ if(skiaItem.getSelection()) style |= SWT.SKIA;
canvas = new Canvas(parent, style);
canvas.addListener(SWT.Paint, event -> {
GC gc = event.gc;
@@ -233,7 +234,11 @@ void createCanvas(Composite parent) {
}
void recreateCanvas() {
- if (dbItem.getSelection() == ((canvas.getStyle() & SWT.DOUBLE_BUFFERED) != 0)) return;
+ boolean recreate = false;
+ if (dbItem.getSelection() != ((canvas.getStyle() & SWT.DOUBLE_BUFFERED) != 0)) recreate |= true;
+ if (skiaItem.getSelection() != ((canvas.getStyle() & SWT.SKIA) != 0)) recreate |= true;
+ if(recreate == false) return;
+
Object data = canvas.getLayoutData();
if (canvas != null) canvas.dispose();
createCanvas(parent);
@@ -312,6 +317,14 @@ void createToolBar(final Composite parent) {
dbItem.setText(getResourceString("DoubleBuffer")); //$NON-NLS-1$
dbItem.setImage(loadImage(display, "db.gif")); //$NON-NLS-1$
dbItem.addListener(SWT.Selection, event -> setDoubleBuffered(dbItem.getSelection()));
+
+ // skia tool item
+ skiaItem = new ToolItem(toolBar, SWT.CHECK);
+ skiaItem.setText(getResourceString("Skia")); //$NON-NLS-1$
+ skiaItem.setImage(loadImage(display, "db.gif")); //$NON-NLS-1$
+ skiaItem.setSelection(true);
+ skiaItem.addListener(SWT.Selection, event -> setSkia(skiaItem.getSelection()));
+
}
/**
@@ -595,6 +608,14 @@ public void setDoubleBuffered(boolean doubleBuffered) {
recreateCanvas();
}
+/**
+ * Sets whether the canvas is double buffered or not.
+ */
+public void setSkia(boolean skia) {
+ skiaItem.setSelection(skia);
+ recreateCanvas();
+}
+
/**
* Grabs input focus.
*/
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/LineStyleTab.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/LineStyleTab.java
index a14f67a6654..f9057fdc8b9 100644
--- a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/LineStyleTab.java
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/graphics/LineStyleTab.java
@@ -116,6 +116,9 @@ public void createControlPanel(Composite parent) {
public void paint(GC gc, int width, int height) {
Device device = gc.getDevice();
+ int sections = 7;
+ int textPositions = 14;
+
Pattern pattern = null;
if (lineColor.getBgColor1() != null) {
gc.setForeground(lineColor.getBgColor1());
@@ -128,15 +131,18 @@ public void paint(GC gc, int width, int height) {
gc.setLineWidth(lineWidthSpinner.getSelection());
// draw lines with caps
- gc.drawLine(3*width/16, 1*height/6, 13*width/16, 1*height/6);
+ gc.drawLine(3*width/16, 1*height/sections, 13*width/16, 1*height/sections);
gc.setLineStyle(SWT.LINE_DASH);
- gc.drawLine(3*width/16, 2*height/6, 13*width/16, 2*height/6);
+ gc.drawLine(3*width/16, 2*height/sections, 13*width/16, 2*height/sections);
gc.setLineStyle(SWT.LINE_DOT);
- gc.drawLine(3*width/16, 3*height/6, 13*width/16, 3*height/6);
+ gc.drawLine(3*width/16, 3*height/sections, 13*width/16, 3*height/sections);
gc.setLineStyle(SWT.LINE_DASHDOT);
- gc.drawLine(3*width/16, 4*height/6, 13*width/16, 4*height/6);
+ gc.drawLine(3*width/16, 4*height/sections, 13*width/16, 4*height/sections);
gc.setLineStyle(SWT.LINE_DASHDOTDOT);
- gc.drawLine(3*width/16, 5*height/6, 13*width/16, 5*height/6);
+ gc.drawLine(3*width/16, 5*height/sections, 13*width/16, 5*height/sections);
+ gc.setLineDash(new int[] {25,20,15,10,5});
+ gc.drawLine(3*width/16, 6*height/sections, 13*width/16, 6*height/sections);
+
// draw labels
Font font = new Font(device, getPlatformFont(), 20, SWT.NORMAL);
@@ -146,19 +152,22 @@ public void paint(GC gc, int width, int height) {
String text = GraphicsExample.getResourceString("Solid"); //$NON-NLS-1$
Point size = gc.stringExtent(text);
- gc.drawString(text, (width-size.x)/2, 1*height/12, true);
+ gc.drawString(text, (width-size.x)/2, 1*height/textPositions, true);
text = GraphicsExample.getResourceString("Dash"); //$NON-NLS-1$
size = gc.stringExtent(text);
- gc.drawString(text, (width-size.x)/2, 3*height/12, true);
+ gc.drawString(text, (width-size.x)/2, 3*height/textPositions, true);
text = GraphicsExample.getResourceString("Dot"); //$NON-NLS-1$
size = gc.stringExtent(text);
- gc.drawString(text, (width-size.x)/2, 5*height/12, true);
+ gc.drawString(text, (width-size.x)/2, 5*height/textPositions, true);
text = GraphicsExample.getResourceString("DashDot"); //$NON-NLS-1$
size = gc.stringExtent(text);
- gc.drawString(text, (width-size.x)/2, 7*height/12, true);
+ gc.drawString(text, (width-size.x)/2, 7*height/textPositions, true);
text = GraphicsExample.getResourceString("DashDotDot"); //$NON-NLS-1$
size = gc.stringExtent(text);
- gc.drawString(text, (width-size.x)/2, 9*height/12, true);
+ gc.drawString(text, (width-size.x)/2, 9*height/textPositions, true);
+ text = GraphicsExample.getResourceString("CustomLineStyle {25,20,15,10,5}"); //$NON-NLS-1$
+ size = gc.stringExtent(text);
+ gc.drawString(text, (width-size.x)/2, 11*height/textPositions , true);
font.dispose();
}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetAlpha.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetAlpha.java
new file mode 100644
index 00000000000..e61c5cfee7d
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetAlpha.java
@@ -0,0 +1,63 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGBA;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetAlpha {
+
+ final static boolean USE_SKIA = true; // Set to true to use Skia rendering, false for default SWT rendering
+
+ public static void main(String[] args) {
+ final Display display = new Display();
+ final Shell shell = new Shell(display);
+ shell.setText("Snippet Alpha");
+
+ var style = SWT.DOUBLE_BUFFERED | (USE_SKIA ? SWT.SKIA : SWT.NONE);
+
+ final Canvas canvas = new Canvas(shell,style);
+ canvas.setSize(400, 400);
+ shell.setSize(420, 440);
+
+ canvas.addListener(SWT.Paint, SnippetAlpha::onPaint);
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ display.dispose();
+ }
+
+ private static void onPaint(Event e) {
+
+ e.gc.setAdvanced(false);
+ e.gc.setBackground(e.display.getSystemColor(SWT.COLOR_RED));
+ e.gc.fillRectangle(0, 0, 100, 100);
+
+ // on windows and linux alpha in colors is not supported at all
+ Color c = new Color(e.display, new RGBA(0, 0, 255, 50));
+ e.gc.setBackground(c);
+ e.gc.fillRectangle(200, 0, 100, 100);
+ c.dispose();
+
+ // on windows and linux alpha colors is not supported at all
+
+ e.gc.setAlpha(100);
+ e.gc.setBackground(e.display.getSystemColor(SWT.COLOR_GREEN));
+ e.gc.fillRectangle(0, 200, 100, 100);
+ c.dispose();
+
+
+ e.gc.setLineWidth(10);
+ e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_YELLOW));
+ e.gc.drawLine(0, 0, 500, 500);
+
+
+
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetAnimatedProgress.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetAnimatedProgress.java
new file mode 100644
index 00000000000..1d0e12e4f2c
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetAnimatedProgress.java
@@ -0,0 +1,49 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.AnimatedProgress;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Example usage of AnimatedProgress (deprecated).
+ */
+public class SnippetAnimatedProgress {
+
+ final static boolean USE_SKIA = true; // Set to true to use Skia rendering, false for default SWT rendering
+
+
+ @SuppressWarnings("deprecation")
+ public static void main(String[] args) {
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setText("AnimatedProgress Example");
+ shell.setLayout(new GridLayout(1, false));
+
+ Composite composite = new Composite(shell, SWT.NONE);
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ composite.setLayout(new GridLayout(1, false));
+
+ AnimatedProgress progress = new AnimatedProgress(composite, SWT.HORIZONTAL | SWT.BORDER | (USE_SKIA ? SWT.SKIA : SWT.NONE));
+ progress.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ Button startButton = new Button(composite, SWT.PUSH);
+ startButton.setText("Start Animation");
+ startButton.addListener(SWT.Selection, e -> progress.start());
+
+ Button stopButton = new Button(composite, SWT.PUSH);
+ stopButton.setText("Stop Animation");
+ stopButton.addListener(SWT.Selection, e -> progress.stop());
+
+ shell.setSize(300, 120);
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) display.sleep();
+ }
+ display.dispose();
+ }
+}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetBalls.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetBalls.java
new file mode 100644
index 00000000000..0bff43ada6c
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetBalls.java
@@ -0,0 +1,204 @@
+package org.eclipse.swt.examples.skia;
+
+import java.util.LinkedList;
+
+/*
+ * Fill a shape with a predefined pattern
+ *
+ * For a list of all SWT example snippets see
+ * http://www.eclipse.org/swt/snippets/
+ *
+ * @since 3.1
+ */
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Path;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.Transform;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetBalls {
+
+ final static boolean USE_SKIA = true;
+ final static int BALL_COUNT = 3;
+
+ static int skiaStyle = USE_SKIA ? SWT.SKIA : SWT.NONE;
+ final static int WIDTH = 100;
+ final static int HEIGHT = 100;
+ static final int TIMER = 30;
+
+ static Composite parent;
+ static Canvas canvas;
+ static boolean animate = true;
+
+ public static void main(String[] args) {
+
+ Display display = new Display();
+ bc = new BallCollection[BALL_COUNT];
+
+ Shell shell = new Shell(display);
+ shell.setText("SnippetBalls");
+ shell.setLayout(new FillLayout());
+ parent = shell;
+ Canvas c = new Canvas(shell, SWT.DOUBLE_BUFFERED | skiaStyle);
+ canvas = c;
+
+ startAnimationTimer();
+ c.setBackground(display.getSystemColor(SWT.COLOR_GREEN));
+ c.addListener(SWT.Paint, event -> {
+ Rectangle r = ((Composite) event.widget).getClientArea();
+ GC gc1 = event.gc;
+
+ paint(gc1, r.width, r.height);
+
+ });
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+ animate = false;
+
+ display.dispose();
+ }
+
+ static BallCollection[] bc;
+
+ /**
+ * This inner class serves as a container for the data needed to display a
+ * collection of balls.
+ */
+ static class BallCollection {
+ float x, y; // position of ball
+ float incX, incY; // values by which to move the ball
+ int ball_size; // size (diameter) of the ball
+ int capacity; // number of balls in the collection
+ LinkedList prevx, prevy; // collection of previous x and y positions
+ // of ball
+ Color[] colors; // colors used for this ball collection
+
+ public BallCollection(float x, float y, float incX, float incY, int ball_size, int capacity, Color[] colors) {
+ this.x = x;
+ this.y = y;
+ this.incX = incX;
+ this.incY = incY;
+ this.ball_size = ball_size;
+ this.capacity = capacity;
+ prevx = new LinkedList<>();
+ prevy = new LinkedList<>();
+ this.colors = colors;
+ }
+ }
+
+ /**
+ * Starts the animation if the animate flag is set.
+ */
+ static void startAnimationTimer() {
+ final Display display = parent.getDisplay();
+ display.timerExec(TIMER, new Runnable() {
+ @Override
+ public void run() {
+ if (canvas.isDisposed())
+ return;
+ int timeout = TIMER;
+ if (animate) {
+ Rectangle rect = canvas.getClientArea();
+ next(rect.width, rect.height);
+ canvas.redraw();
+ canvas.update();
+ display.timerExec(timeout, this);
+ }
+ }
+ });
+ }
+
+ public int getInitialAnimationTime() {
+ return 10;
+ }
+
+ public static void next(int width, int height) {
+ for (int i = 0; i < bc.length; i++) {
+ if (bc[i] == null)
+ return;
+ if (bc[i].prevx.isEmpty()) {
+ bc[i].prevx.addLast(Float.valueOf(bc[i].x));
+ bc[i].prevy.addLast(Float.valueOf(bc[i].y));
+ } else if (bc[i].prevx.size() == bc[i].capacity) {
+ bc[i].prevx.removeFirst();
+ bc[i].prevy.removeFirst();
+ }
+
+ bc[i].x += bc[i].incX;
+ bc[i].y += bc[i].incY;
+
+ float random = (float) Math.random();
+
+ // right
+ if (bc[i].x + bc[i].ball_size > width) {
+ bc[i].x = width - bc[i].ball_size;
+ bc[i].incX = random * -width / 16 - 1;
+ }
+ // left
+ if (bc[i].x < 0) {
+ bc[i].x = 0;
+ bc[i].incX = random * width / 16 + 1;
+ }
+ // bottom
+ if (bc[i].y + bc[i].ball_size > height) {
+ bc[i].y = (height - bc[i].ball_size) - 2;
+ bc[i].incY = random * -height / 16 - 1;
+ }
+ // top
+ if (bc[i].y < 0) {
+ bc[i].y = 0;
+ bc[i].incY = random * height / 16 + 1;
+ }
+ bc[i].prevx.addLast(Float.valueOf(bc[i].x));
+ bc[i].prevy.addLast(Float.valueOf(bc[i].y));
+ }
+ }
+
+ public static void paint(GC gc, int width, int height) {
+ Device device = gc.getDevice();
+ gc.setBackground(device.getSystemColor(SWT.COLOR_WHITE));
+ gc.fillRectangle(0, 0, width, height);
+
+ if (bc[0] == null) {
+
+ for (int i = 0; i < bc.length; i++) {
+ bc[i] = new BallCollection((float) (Math.random() * width), (float) (Math.random() * height),
+ (float) (Math.random() * 10 - 5), (float) (Math.random() * 10 - 5), (int) (Math.random() * 30 + 20),
+ 50, new Color[] { device.getSystemColor(SWT.COLOR_GREEN) });
+ }
+
+// bc[0] = new BallCollection((float) (Math.random() * width), (float) (Math.random() * height),
+// (float) (Math.random() * 10 - 5), (float) (Math.random() * 10 - 5), (int) (Math.random() * 30 + 20),
+// 50, new Color[] { device.getSystemColor(SWT.COLOR_GREEN) });
+
+ }
+
+ for (BallCollection ballCollection : bc) {
+ for (int i = 0; i < ballCollection.prevx.size(); i++) {
+ Transform transform = new Transform(device);
+ transform.translate(ballCollection.prevx.get(ballCollection.prevx.size() - (i + 1)).floatValue(),
+ ballCollection.prevy.get(ballCollection.prevy.size() - (i + 1)).floatValue());
+ gc.setTransform(transform);
+ transform.dispose();
+
+ Path path = new Path(device);
+ path.addArc(0, 0, ballCollection.ball_size, ballCollection.ball_size, 0, 360);
+ gc.setAlpha(255 - i * (255 / ballCollection.capacity));
+ gc.setBackground(ballCollection.colors[0]);
+ gc.fillPath(path);
+ gc.drawPath(path);
+ path.dispose();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetBarChart.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetBarChart.java
new file mode 100644
index 00000000000..451b0c09b1f
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetBarChart.java
@@ -0,0 +1,41 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Example usage of BarChart.
+ */
+public class SnippetBarChart {
+ private static final boolean USE_SKIA = true;
+
+ public static void main(String[] args) {
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setText("BarChart Example");
+ shell.setLayout(new GridLayout(1, false));
+
+ org.eclipse.swt.examples.accessibility.BarChart chart = new org.eclipse.swt.examples.accessibility.BarChart(shell, SWT.BORDER | (USE_SKIA ? SWT.SKIA : SWT.NONE));
+ chart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ chart.setTitle("Sample Bar Chart");
+ chart.setColor(SWT.COLOR_GREEN);
+ chart.setValueMin(0);
+ chart.setValueMax(10);
+ chart.setValueIncrement(1);
+ chart.addData("A", 2);
+ chart.addData("B", 7);
+ chart.addData("C", 5);
+ chart.addData("D", 9);
+ chart.addData("E", 4);
+
+ shell.setSize(400, 250);
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) display.sleep();
+ }
+ display.dispose();
+ }
+}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCLabel.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCLabel.java
new file mode 100644
index 00000000000..659368a350c
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCLabel.java
@@ -0,0 +1,48 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * Example usage of CLabel.
+ */
+public class SnippetCLabel {
+ public static final boolean USE_SKIA = true;
+
+ public static void main(String[] args) {
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setText("CLabel Example");
+ shell.setLayout(new GridLayout(1, false));
+
+ // Simple CLabel with text
+ CLabel label1 = new CLabel(shell, (USE_SKIA ? SWT.SKIA : 0) | SWT.NONE);
+ label1.setText("This is a CLabel with text.");
+ label1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ // CLabel with image and text
+ Image image = display.getSystemImage(SWT.ICON_INFORMATION);
+ CLabel label2 = new CLabel(shell, (USE_SKIA ? SWT.SKIA : 0) | SWT.NONE);
+ label2.setText("CLabel with image and text.");
+ label2.setImage(image);
+ label2.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ // CLabel with alignment
+ CLabel label3 = new CLabel(shell, (USE_SKIA ? SWT.SKIA : 0) | SWT.NONE);
+ label3.setText("Right aligned CLabel.");
+ label3.setAlignment(SWT.RIGHT);
+ label3.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ shell.setSize(350, 150);
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) display.sleep();
+ }
+ display.dispose();
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasAdvancedCompare.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasAdvancedCompare.java
new file mode 100644
index 00000000000..afc1c1155c4
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasAdvancedCompare.java
@@ -0,0 +1,445 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Path;
+import org.eclipse.swt.graphics.Pattern;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.Region;
+import org.eclipse.swt.graphics.Transform;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetCanvasAdvancedCompare {
+
+ // working variable for the snippet
+ private static boolean skiaCurrentlyActive;
+
+ public static void main(String[] args) {
+ final Display display = new Display();
+ final Shell s = new Shell(display);
+ s.setLayout(new FillLayout());
+
+ final ScrolledComposite sc = new ScrolledComposite(s, SWT.V_SCROLL | SWT.H_SCROLL);
+ sc.setExpandHorizontal(true);
+ sc.setExpandVertical(true);
+
+ final Composite shell = new Composite(sc, SWT.NONE);
+ sc.setContent(shell);
+
+ shell.setLayout(new GridLayout(2, true));
+
+ text(shell, "Zeichne Text mit verschiedenen Farben");
+
+ coloredTextCanvas(shell);
+
+ resetCanvasConfiguration();
+
+ activateSkiaRaster();
+ coloredTextCanvas(shell);
+ resetCanvasConfiguration();
+
+ text(shell, "Zeichne Text AntiAlias-Modi");
+
+ coloredTextNoAACanvas(shell);
+
+ activateSkiaRaster();
+ coloredTextNoAACanvas(shell);
+ resetCanvasConfiguration();
+
+ text(shell, "Zeichne rechteckige Linien mit verschiedenen Strichstärken");
+
+ lineAttributesCanvas(shell);
+ activateSkiaRaster();
+ lineAttributesCanvas(shell);
+ resetCanvasConfiguration();
+
+ text(shell, " Zeichne mit einem Path");
+
+ pathCanvas(shell);
+
+ activateSkiaRaster();
+ pathCanvas(shell);
+ resetCanvasConfiguration();
+
+ text(shell, " XOR-Mode");
+
+ xorMode(shell);
+ activateSkiaRaster();
+ xorMode(shell);
+ resetCanvasConfiguration();
+
+ text(shell, " Geänderte Transformationsmatrix");
+
+ transformCanvas(shell);
+ activateSkiaRaster();
+ transformCanvas(shell);
+ resetCanvasConfiguration();
+
+ text(shell, " Benutze Region");
+
+ regionCanvas(shell);
+ activateSkiaRaster();
+ regionCanvas(shell);
+ resetCanvasConfiguration();
+
+ text(shell, " Zeichne mit einem Muster");
+
+ patternCanvas(shell);
+
+ activateSkiaRaster();
+ patternCanvas(shell);
+ resetCanvasConfiguration();
+
+ text(shell, " Anti-Aliasing für Grafik");
+
+ antiAliasCanvas(shell);
+ activateSkiaRaster();
+ antiAliasCanvas(shell);
+ resetCanvasConfiguration();
+
+ text(shell, " Zeichne Bilder mit verschiedenen Alpha-Werten");
+
+ alphaCanvas(shell);
+ activateSkiaRaster();
+ alphaCanvas(shell);
+ resetCanvasConfiguration();
+
+ text(shell, "Clipping Rectangle");
+
+ clipRectangleCanvas(shell);
+ activateSkiaRaster();
+ clipRectangleCanvas(shell);
+ resetCanvasConfiguration();
+
+ text(shell, "Clipping Region");
+
+ clipCanvasRegion(shell);
+ activateSkiaRaster();
+ clipCanvasRegion(shell);
+ resetCanvasConfiguration();
+
+ text(shell, "Clipping Path");
+
+ clipCanvasPath(shell);
+ activateSkiaRaster();
+ clipCanvasPath(shell);
+ resetCanvasConfiguration();
+
+ final var size = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ sc.setMinSize(size);
+
+ s.open();
+ while (!s.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+
+ display.dispose();
+ }
+
+ private static void resetCanvasConfiguration() {
+ skiaCurrentlyActive = false;
+ }
+
+ private static void activateSkiaRaster() {
+ skiaCurrentlyActive = true;
+ }
+
+ private static void coloredTextNoAACanvas(Composite shell) {
+ final Canvas coloredTextCanvas = createCanvas(shell);
+ coloredTextCanvas.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_YELLOW));
+ coloredTextCanvas.addPaintListener(e -> {
+ final GC gc = e.gc;
+
+ final boolean advanced = e.gc.getAdvanced();
+ final int textAA = e.gc.getTextAntialias();
+
+ final var f = shell.getDisplay().getSystemFont();
+
+ final var fd = f.getFontData()[0];
+
+ fd.height = 20;
+
+ final Font nf = new Font(shell.getDisplay(), new FontData[] { fd });
+
+ e.gc.setFont(nf);
+
+ e.gc.setAdvanced(true);
+ e.gc.setTextAntialias(SWT.OFF);
+
+ final Color blue = shell.getDisplay().getSystemColor(SWT.COLOR_BLUE);
+ gc.setForeground(blue);
+ gc.drawText("No AntiAlias Text", 10, 10);
+
+ final Color red = shell.getDisplay().getSystemColor(SWT.COLOR_RED);
+ gc.setForeground(red);
+ e.gc.setTextAntialias(SWT.ON);
+ gc.drawText("AntiAlias Text", 10, 70);
+
+ final Color black = shell.getDisplay().getSystemColor(SWT.COLOR_BLACK);
+ gc.setForeground(black);
+ e.gc.setTextAntialias(SWT.DEFAULT);
+ gc.drawText("Default Text", 10, 130);
+
+ gc.setAdvanced(advanced);
+ gc.setTextAntialias(textAA);
+
+ });
+
+ setGridData(coloredTextCanvas, 200);
+
+ }
+
+ private static void clipCanvasPath(Composite shell) {
+ final Canvas c = createCanvas(shell);
+ final var display = shell.getDisplay();
+
+ c.addPaintListener(e -> {
+ final var p = c.getSize();
+ final int width = p.x;
+ final int height = p.y;
+
+ final Path pa = new Path(display);
+ pa.addArc(0, 0, 100, 100, 90, 200);
+
+ e.gc.setClipping(pa);
+ e.gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));
+
+ e.gc.fillRectangle(0, 0, width, height);
+ });
+
+ setGridData(c, 200);
+
+ }
+
+ private static void clipCanvasRegion(Composite shell) {
+
+ final Canvas c = createCanvas(shell);
+ final var display = shell.getDisplay();
+
+ c.addPaintListener(e -> {
+ final var p = c.getSize();
+ final int width = p.x;
+ final int height = p.y;
+
+ final Region r = new Region(display);
+ r.add(new int[] { width / 2, 0, 0, height / 2, width / 2, height, width, height / 2 });
+ e.gc.setClipping(r);
+ e.gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));
+
+ e.gc.fillPolygon(new int[] { 0, 0, width, 0, width / 2, height });
+ });
+
+ setGridData(c, 200);
+
+ }
+
+ private static void clipRectangleCanvas(Composite shell) {
+ final Canvas c = createCanvas(shell);
+ final var display = shell.getDisplay();
+
+ c.addPaintListener(e -> {
+ final var p = c.getSize();
+ final int width = p.x;
+ final int height = p.y;
+ e.gc.setClipping(20, 20, width - 40, height - 40);
+ e.gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));
+
+ e.gc.fillPolygon(new int[] { 0, 0, width, 0, width / 2, height });
+ });
+
+ setGridData(c, 200);
+
+ }
+
+ private static void alphaCanvas(Composite shell) {
+ final Canvas alphaCanvas = createCanvas(shell);
+ final var display = shell.getDisplay();
+ final Image image = new Image(display, 100, 50);
+ final GC gcImage = new GC(image);
+ gcImage.setBackground(display.getSystemColor(SWT.COLOR_GREEN));
+ gcImage.fillRectangle(0, 0, 100, 50);
+ gcImage.dispose();
+
+ alphaCanvas.addPaintListener(e -> {
+ final GC gc = e.gc;
+ gc.setAlpha(128);
+ gc.drawImage(image, 10, 10);
+ });
+
+ setGridData(alphaCanvas);
+
+ }
+
+ private static void antiAliasCanvas(Composite shell) {
+ final Canvas antiAliasCanvas = createCanvas(shell);
+ antiAliasCanvas.addPaintListener(e -> {
+ final GC gc = e.gc;
+ gc.setAntialias(SWT.ON);
+ gc.drawOval(10, 10, 100, 50);
+ });
+
+ setGridData(antiAliasCanvas);
+
+ }
+
+ private static void patternCanvas(Composite shell) {
+
+ final Canvas patternCanvas = createCanvas(shell);
+ patternCanvas.addPaintListener(e -> {
+ final GC gc = e.gc;
+
+ final Pattern pattern = new Pattern(shell.getDisplay(), 10, 10, 100, 50, new Color(new RGB(255, 0, 0)), 100,
+ new Color(new RGB(0, 0, 255)), 255);
+ gc.setBackgroundPattern(pattern);
+ gc.fillRectangle(10, 10, 100, 50);
+ pattern.dispose();
+ });
+
+ setGridData(patternCanvas);
+
+ }
+
+ private static void regionCanvas(Composite shell) {
+ final Canvas regionCanvas = createCanvas(shell);
+ regionCanvas.addPaintListener(e -> {
+ final GC gc = e.gc;
+ final Region region = new Region();
+ region.add(new Rectangle(10, 10, 100, 50));
+ region.add(new Rectangle(60, 30, 100, 50));
+ gc.setClipping(region);
+ gc.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_GREEN));
+ gc.fillRectangle(0, 0, 200, 100);
+ region.dispose();
+ });
+
+ setGridData(regionCanvas);
+
+ }
+
+ private static void transformCanvas(Composite shell) {
+ final Canvas transformCanvas = createCanvas(shell);
+ transformCanvas.addPaintListener(e -> {
+ final GC gc = e.gc;
+ final Transform transform = new Transform(shell.getDisplay());
+ transform.translate(50, 50);
+ transform.rotate(45);
+ gc.setTransform(transform);
+ gc.drawRectangle(-25, -25, 100, 50);
+ transform.dispose();
+ });
+
+ setGridData(transformCanvas);
+
+ }
+
+ private static void xorMode(Composite shell) {
+
+ final var dis = shell.getDisplay();
+
+ final Canvas xorModeCanvas = createCanvas(shell);
+ xorModeCanvas.setBackground(dis.getSystemColor(SWT.COLOR_BLACK));
+ xorModeCanvas.addPaintListener(e -> {
+ final GC gc = e.gc;
+ gc.setXORMode(true);
+ gc.setForeground(shell.getDisplay().getSystemColor(SWT.COLOR_RED));
+ gc.drawRectangle(10, 10, 100, 50);
+ gc.drawRectangle(30, 30, 60, 30);
+ gc.setXORMode(false);
+ });
+
+ setGridData(xorModeCanvas);
+
+ }
+
+ private static Canvas createCanvas(Composite shell) {
+ int style = skiaCurrentlyActive ? SWT.SKIA : SWT.NONE;
+ return new Canvas(shell, SWT.BORDER | style);
+ }
+
+ private static void pathCanvas(Composite shell) {
+ final Canvas pathCanvas = createCanvas(shell);
+ pathCanvas.addPaintListener(e -> {
+ final GC gc = e.gc;
+ final Path path = new Path(shell.getDisplay());
+ path.moveTo(10, 10);
+ path.lineTo(110, 60);
+ path.lineTo(60, 110);
+ path.close();
+ gc.drawPath(path);
+ path.dispose();
+ });
+
+ setGridData(pathCanvas);
+
+ }
+
+ private static void setGridData(Control c) {
+ setGridData(c, 150);
+ }
+
+ private static void setGridData(Control c, int minHeight) {
+ final var g = new GridData();
+ g.grabExcessHorizontalSpace = true;
+ g.grabExcessVerticalSpace = true;
+ g.horizontalAlignment = GridData.FILL;
+ g.verticalAlignment = GridData.FILL;
+ g.minimumHeight = minHeight;
+ c.setLayoutData(g);
+ }
+
+ private static void lineAttributesCanvas(Composite shell) {
+ final Canvas lineAttributesCanvas = createCanvas(shell);
+ lineAttributesCanvas.addPaintListener(e -> {
+ final GC gc = e.gc;
+ gc.setLineWidth(5);
+ gc.setLineJoin(SWT.JOIN_ROUND);
+ gc.setLineCap(SWT.CAP_ROUND);
+ gc.drawRectangle(10, 10, 100, 50);
+ });
+
+ setGridData(lineAttributesCanvas);
+
+ }
+
+ private static void coloredTextCanvas(Composite shell) {
+ final Canvas coloredTextCanvas = createCanvas(shell);
+ coloredTextCanvas.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_YELLOW));
+ coloredTextCanvas.addPaintListener(e -> {
+ final GC gc = e.gc;
+ final Color blue = shell.getDisplay().getSystemColor(SWT.COLOR_BLUE);
+ gc.setForeground(blue);
+ gc.drawText("Blauer Text", 10, 10);
+ });
+
+ setGridData(coloredTextCanvas);
+
+ }
+
+ private static void text(Composite shell, String string) {
+ final var l = new Label(shell, SWT.BORDER);
+ l.setText(string);
+ final var g = new GridData();
+ g.horizontalSpan = 2;
+ g.grabExcessHorizontalSpace = true;
+
+ g.horizontalAlignment = GridData.FILL;
+ l.setLayoutData(g);
+
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasCaret.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasCaret.java
new file mode 100644
index 00000000000..27c52c4c124
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasCaret.java
@@ -0,0 +1,71 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Caret;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetCanvasCaret {
+ public static void main(String[] args) {
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setText("Canvas Caret Demo");
+ shell.setSize(400, 300);
+ shell.setLayout(new GridLayout(1, false)); // One column, vertical stack
+
+ // Create two canvases stacked vertically
+ Canvas canvas1 = new Canvas(shell, SWT.BORDER | SWT.SKIA);
+ canvas1.setLayoutData(new GridData(GridData.FILL_BOTH));
+ canvas1.setBackground(display.getSystemColor(SWT.COLOR_GREEN));
+ Canvas canvas2 = new Canvas(shell, SWT.BORDER);
+ canvas2.setLayoutData(new GridData(GridData.FILL_BOTH));
+ canvas2.setBackground(display.getSystemColor(SWT.COLOR_BLUE));
+
+ // Initialize carets for both canvases
+ Caret caret1 = new Caret(canvas1, SWT.NONE);
+ caret1.setSize(2, 30);
+ caret1.setVisible(true);
+ Caret caret2 = new Caret(canvas2, SWT.NONE);
+ caret2.setSize(2, 30);
+ caret2.setVisible(true);
+
+ // Timer logic for moving the carets
+ final int steps = 20; // Number of positions
+ Runnable moveCarets = new Runnable() {
+ int pos = 0;
+
+ @Override
+ public void run() {
+ if (canvas1.isDisposed() || caret1.isDisposed() || canvas2.isDisposed() || caret2.isDisposed())
+ return;
+
+ var ca = canvas2.getClientArea();
+ var width = ca.width;
+ int x = (int) ((1f * pos) / (1f * steps) * width);
+
+ if (pos < 10) {
+ caret1.setLocation(x, 15);
+ canvas1.setFocus();
+
+ } else {
+ caret2.setLocation(x, 15);
+ canvas2.setFocus();
+ }
+
+ pos = (pos + 1) % steps;
+ display.timerExec(1000, this);
+ }
+ };
+ display.timerExec(0, moveCarets);
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+ display.dispose();
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasCompare.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasCompare.java
new file mode 100644
index 00000000000..317870b5e19
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasCompare.java
@@ -0,0 +1,151 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Path;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetCanvasCompare {
+
+ public static void main(String[] args) {
+ final Display display = new Display();
+ final Shell shell = new Shell(display);
+ shell.setText("Snippet Canvas Compare");
+
+
+ configureCanvas(shell, new Canvas(shell, SWT.DOUBLE_BUFFERED| SWT.H_SCROLL | SWT.BORDER), 1, "SWT Canvas");
+ configureCanvas(shell, new Canvas(shell, SWT.DOUBLE_BUFFERED | SWT.H_SCROLL| SWT.SKIA | SWT.BORDER), 2, "SkiaGlCanvas");
+
+ shell.setSize(1500, 1000);
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ display.dispose();
+ }
+
+ private static void configureCanvas(final Shell shell, final Canvas canvas, int index, String title) {
+ canvas.setSize(100, 100);
+// canvas.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_YELLOW));
+ shell.addListener(SWT.Resize, e -> onResize(canvas, index));
+ canvas.addListener(SWT.Paint, e -> onPaint(e, title));
+ }
+
+ private static void onPaint(Event e, String title) {
+ final Display d = e.widget.getDisplay();
+ final GC gc = e.gc;
+
+ var bg = gc.getBackground();
+
+ gc.setBackground(d.getSystemColor(SWT.COLOR_YELLOW));
+ gc.fillRectangle(new Rectangle(-100, -100, 3000, 3000));
+
+ gc.setBackground(bg);
+
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_RED));
+ gc.drawRectangle(new Rectangle(0, 0, 100, 100));
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_BLUE));
+ gc.drawRectangle(new Rectangle(100, 0, 100, 100));
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_GREEN));
+ gc.drawRectangle(new Rectangle(0, 100, 100, 100));
+
+ final var img = d.getSystemImage(SWT.ICON_QUESTION);
+ gc.drawImage(img, 100, 100);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_RED));
+ gc.drawLine(200, 100, 100, 200);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_CYAN));
+ gc.drawFocus(200, 0, 100, 100);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_RED));
+ gc.drawArc(200, 100, 100, 100, 90, 200);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_BLACK));
+ gc.drawText(title, 100, 250);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_DARK_CYAN));
+ gc.drawOval(0, 300, 100, 50);
+
+ final Path p = new Path(d);
+ p.addRectangle(0, 400, 100, 100);
+ gc.setForeground(d.getSystemColor(SWT.COLOR_DARK_GREEN));
+ gc.drawPath(p);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_GRAY));
+ gc.drawRoundRectangle(0, 500, 100, 100, 50, 50);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_DARK_RED));
+ gc.drawPoint(5, 600);
+
+ final var bo = img.getBounds();
+ gc.drawImage(img, 0, 0, bo.width /2 , bo.height /2 , 20 , 600, bo.width * 2, bo.height *2 );
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_DARK_MAGENTA));
+ gc.drawText("Test\nTest2", 300, 0);
+ gc.drawText("Test2\nTest3", 300, 100, false);
+ gc.drawText("Transparent Text", 300,200, true);
+
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_DARK_GRAY));
+ gc.drawPolygon(new int[] {
+ 400,2, //
+ 500,100, //
+ 400,100, //
+ 500,2 //
+ });
+
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_DARK_YELLOW));
+ gc.drawPolyline(new int[] {
+ 400,50, //
+ 500,100, //
+ 600,200
+
+ });
+
+ gc.setBackground(d.getSystemColor(SWT.COLOR_DARK_YELLOW));
+ gc.fillArc(100, 300, 100, 100, 90, 200);
+
+ gc.setBackground(d.getSystemColor(SWT.COLOR_DARK_GREEN));
+ gc.fillGradientRectangle(100, 400, 100, 100, false);
+
+ gc.fillOval(100, 500, 100, 50);
+
+
+ final Path p2 = new Path(d);
+ p2.addRectangle(100, 600, 100, 100);
+ gc.fillPath(p2);
+
+ gc.fillPolygon(new int[] {
+ 100,700, //
+ 200,800, //
+ 100,800, //
+ 200,700 //
+ });
+
+ gc.fillRectangle(new Rectangle(100, 800, 100, 100));
+
+ gc.fillRoundRectangle(200, 800, 100, 100, 20, 20);
+ }
+
+ private static void onResize(Canvas c, int index) {
+ final var ca = c.getShell().getClientArea();
+ switch(index) {
+ case 1 -> c.setBounds(new Rectangle(0, 0, ca.width / 2, ca.height));
+ case 2 -> c.setBounds(new Rectangle(ca.width / 2, 0, ca.width / 2, ca.height));
+ default -> { /* nothing to do */ }
+ }
+ }
+
+}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasText.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasText.java
new file mode 100644
index 00000000000..173077ea460
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasText.java
@@ -0,0 +1,122 @@
+package org.eclipse.swt.examples.skia;
+
+import java.util.Random;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetCanvasText {
+ final static boolean useSkia = true;
+
+
+ final static int style = SWT.DOUBLE_BUFFERED | (useSkia ? SWT.SKIA : SWT.NONE);
+ final static int LETTERS_PER_LINE = 2000;
+ final static int LINES = 60;
+ final static int SHIFT_LEFT = 2000;
+
+ static String[] text = new String[LINES];
+
+ public static void main(String[] args) {
+ final Display display = new Display();
+ final Shell shell = new Shell(display);
+ shell.setText("Snippet Canvas");
+ // here you can switch between Canvas SkiaRasterCanvas and SkiaCanvas
+ final Canvas c = new Canvas(shell, style );
+
+ for (int j = 0; j < LINES; j++) {
+ text[j] = generateText(LETTERS_PER_LINE);
+ }
+
+ c.setSize(100, 100);
+
+ shell.addListener(SWT.Resize, e -> onResize(e, c));
+ c.addListener(SWT.Paint, SnippetCanvasText::onPaint);
+ c.addListener(SWT.Paint, SnippetCanvasText::onPaint2);
+
+ shell.setSize(1000, LETTERS_PER_LINE * 3 + 80);
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ display.dispose();
+ }
+
+ static long startTime = System.currentTimeMillis();
+ private static boolean printFrameRate = true;
+ private static int frames;
+ private static long lastFrame;
+ private static long framesToDraw;
+
+ private static void onPaint(Event e) {
+
+ final var s = ((Canvas) e.widget);
+
+ final Point size = s.getSize();
+
+ long currentPosTime = System.currentTimeMillis() - startTime;
+
+ currentPosTime = currentPosTime % 10000;
+
+ final double position = (double) currentPosTime / (double) 10000;
+
+ final int shift = (int) (position * size.x) - SHIFT_LEFT;
+ final int shiftDown = 20;
+
+
+ for(int j = 0 ; j < LINES; j++) {
+ e.gc.drawText(text[j], shift, shiftDown + 20 * j,true);
+ }
+ }
+
+ private static void onPaint2(Event e) {
+
+ final var s = ((Canvas) e.widget);
+
+ if (printFrameRate) {
+
+ if (System.currentTimeMillis() - lastFrame > 1000) {
+ framesToDraw = frames;
+
+ frames = 0;
+ lastFrame = System.currentTimeMillis();
+ }
+ frames++;
+
+ e.gc.drawText("Frames: " + framesToDraw, 10, 10,true);
+
+ }
+
+ s.redraw();
+
+ }
+
+ private static void onResize(Event e, Canvas c) {
+
+ final var ca = c.getShell().getClientArea();
+
+ c.setSize(ca.width, ca.height);
+
+ }
+ public static String generateText(int textLength) {
+ final int leftLimit = 97; // letter 'a'
+ final int rightLimit = 122; // letter 'z'
+ final Random random = new Random();
+
+ final String generatedString = random.ints(leftLimit, rightLimit + 1)
+ .limit(textLength)
+ .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
+ .toString();
+
+
+ return generatedString;
+ }
+
+
+}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasTextOCX26.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasTextOCX26.java
new file mode 100644
index 00000000000..3932f3ec2dc
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCanvasTextOCX26.java
@@ -0,0 +1,164 @@
+package org.eclipse.swt.examples.skia;
+
+import java.util.Random;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetCanvasTextOCX26 {
+ final static boolean useSkia = true;
+
+ final static int style = SWT.DOUBLE_BUFFERED | (useSkia ? SWT.SKIA : SWT.NONE);
+ final static int LETTERS_PER_LINE = 2000;
+ final static int LINES = 60;
+ final static int SHIFT_LEFT = 2000;
+
+ final static int LEFT_START = 50;
+ static String[] text = new String[LINES];
+
+ public static void main(String[] args) {
+ final Display display = new Display();
+ final Shell shell = new Shell(display);
+ shell.setText("Snippet Canvas");
+ // here you can switch between Canvas SkiaRasterCanvas and SkiaCanvas
+ final Canvas c = new Canvas(shell, style );
+ c.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
+ c.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
+
+ for (int j = 0; j < LINES; j++) {
+ text[j] = generateText(LETTERS_PER_LINE);
+ }
+
+ c.setSize(100, 100);
+
+ shell.addListener(SWT.Resize, e -> onResize(e, c));
+ c.addListener(SWT.Paint, SnippetCanvasTextOCX26::onPaint);
+ c.addListener(SWT.Paint, SnippetCanvasTextOCX26::onPaint2);
+
+ shell.setSize(1000, LETTERS_PER_LINE * 3 + 80);
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ display.dispose();
+ }
+
+ static long startTime = System.currentTimeMillis();
+ private static boolean printFrameRate = true;
+ private static int frames;
+ private static long lastFrame;
+ private static long framesToDraw;
+
+ private static void onPaint(Event e) {
+
+ final var s = ((Canvas) e.widget);
+
+ Font f = e.display.getSystemFont();
+
+ var fd = f.getFontData()[0];
+
+ fd.setHeight(200);
+
+ e.gc.setAlpha(200);
+ var fontBefore = e.gc.getFont();
+ var newFont = new Font(e.display, fd);
+ e.gc.setFont(newFont);
+ e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_GREEN));
+ e.gc.drawText("OCX 2026" ,LEFT_START,50,true );
+
+ var ocxSize = e.gc.textExtent("OCX 2026");
+
+ newFont.dispose();
+
+ fd.setHeight(50);
+ newFont = new Font(e.display, fd);
+ e.gc.setFont(newFont);
+
+ if(useSkia) {
+ e.gc.drawText("GPU Accelerated Drawing With Skia SWT" ,LEFT_START,50 + ocxSize.y + 5,true );
+ } else {
+ e.gc.drawText("CPU Drawing With Classical SWT" ,LEFT_START,50 + ocxSize.y + 5,true );
+ }
+ newFont.dispose();
+
+ e.gc.setFont(fontBefore);
+ e.gc.setAlpha(255);
+ e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_WHITE));
+
+ final var size = s.getSize();
+
+ long currentPosTime = System.currentTimeMillis() - startTime;
+
+ currentPosTime = currentPosTime % 10000;
+
+ final double position = (double) currentPosTime / (double) 10000;
+
+ final int shift = (int) (position * size.x) - SHIFT_LEFT;
+ final int shiftDown = 20;
+
+
+ for(int j = 0 ; j < LINES; j++) {
+ e.gc.drawText(text[j], shift, shiftDown + 20 * j,true);
+ }
+ }
+
+ private static void onPaint2(Event e) {
+
+ final var s = ((Canvas) e.widget);
+
+ if (printFrameRate) {
+
+ if (System.currentTimeMillis() - lastFrame > 1000) {
+ framesToDraw = frames;
+
+ frames = 0;
+ lastFrame = System.currentTimeMillis();
+ }
+ frames++;
+
+ var fontBefore = e.display.getSystemFont();
+ var fd = fontBefore.getFontData()[0];
+ fd.setHeight(50);
+ var newFont = new Font(e.display, fd);
+ e.gc.setFont(newFont);
+
+ e.gc.drawText("Frames: " + framesToDraw, 10, 10,true);
+
+ newFont.dispose();
+
+ }
+
+ s.redraw();
+
+ }
+
+ private static void onResize(Event e, Canvas c) {
+
+ final var ca = c.getShell().getClientArea();
+
+ c.setSize(ca.width, ca.height);
+
+ }
+ public static String generateText(int textLength) {
+ final int leftLimit = 97; // letter 'a'
+ final int rightLimit = 122; // letter 'z'
+ final Random random = new Random();
+
+ final String generatedString = random.ints(leftLimit, rightLimit + 1)
+ .limit(textLength)
+ .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
+ .toString();
+
+
+ return generatedString;
+ }
+
+
+}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetClippingRectangle.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetClippingRectangle.java
new file mode 100644
index 00000000000..e849e90b7d9
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetClippingRectangle.java
@@ -0,0 +1,155 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Path;
+import org.eclipse.swt.graphics.Region;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetClippingRectangle {
+
+
+ private static boolean skiaEnabled = true;
+ public static void main(String[] args) {
+ final Display display = new Display();
+ final Shell s = new Shell(display);
+ s.setLayout(new FillLayout());
+
+ final Composite shell = new Composite(s, SWT.FILL | SWT.V_SCROLL);
+
+ shell.setSize(1000, 2000); // Erhöhte Höhe für mehr Canvas
+ shell.setLayout(new GridLayout(2, true));
+
+ text(shell, "Clipping Rectangle");
+
+ resetCanvasConfiguration();
+ clipRectangleCanvas(shell);
+ activateSkiaRaster();
+ clipRectangleCanvas(shell);
+ resetCanvasConfiguration();
+
+ text(shell, "Clipping Region");
+
+ clipCanvasRegion(shell);
+ activateSkiaRaster();
+ clipCanvasRegion(shell);
+ resetCanvasConfiguration();
+
+ text(shell, "Clipping Path");
+
+ clipCanvasPath(shell);
+ activateSkiaRaster();
+ clipCanvasPath(shell);
+ resetCanvasConfiguration();
+
+
+ s.open();
+ while (!s.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+
+ display.dispose();
+ }
+
+ private static void clipCanvasPath(Composite shell) {
+ final Canvas c = createCanvas(shell);
+ final var display = shell.getDisplay();
+
+ c.addPaintListener(e -> {
+ final var p = c.getSize();
+ final int width = p.x;
+ final int height = p.y;
+
+ final Path r = new Path(display);
+
+ r.addArc(width / 4, height / 4, width / 2, height / 2, 0, 360);
+
+ e.gc.setClipping(r);
+ e.gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));
+
+ e.gc.fillRectangle(0, 0, width, height);
+ });
+
+ setGridData(c, 200);
+ }
+
+ private static void resetCanvasConfiguration() {
+ skiaEnabled = false;
+ }
+
+ private static void activateSkiaRaster() {
+ skiaEnabled = true;
+ }
+
+ private static void clipCanvasRegion(Composite shell) {
+ final Canvas c = createCanvas(shell);
+ final var display = shell.getDisplay();
+
+ c.addPaintListener(e -> {
+ final var p = c.getSize();
+ final int width = p.x;
+ final int height = p.y;
+
+ final Region r = new Region(display);
+ r.add(new int[] { width / 2, 0, 0, height / 2, width / 2, height, width, height / 2 });
+ e.gc.setClipping(r);
+ e.gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));
+
+ e.gc.fillPolygon(new int[] { 0, 0, width, 0, width / 2, height });
+ });
+
+ setGridData(c, 200);
+ }
+
+ private static void clipRectangleCanvas(Composite shell) {
+ final Canvas c = createCanvas(shell);
+ final var display = shell.getDisplay();
+
+ c.addPaintListener(e -> {
+ final var p = c.getSize();
+ final int width = p.x;
+ final int height = p.y;
+ e.gc.setClipping(20, 20, width /2, height /2 );
+ e.gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));
+
+ e.gc.fillPolygon(new int[] { 0, 0, width, 0, width / 2, height });
+ });
+
+ setGridData(c, 200);
+ }
+
+ private static Canvas createCanvas(Composite shell) {
+ int style = skiaEnabled ? SWT.SKIA : SWT.NONE;
+ return new Canvas(shell, SWT.BORDER | style);
+ }
+
+
+ private static void setGridData(Control c, int minHeight) {
+ final var g = new GridData();
+ g.grabExcessHorizontalSpace = true;
+ g.grabExcessVerticalSpace = true;
+ g.horizontalAlignment = GridData.FILL;
+ g.verticalAlignment = GridData.FILL;
+ g.minimumHeight = minHeight;
+ c.setLayoutData(g);
+ }
+
+ private static void text(Composite shell, String string) {
+ final var l = new Label(shell, SWT.BORDER);
+ l.setText(string);
+ final var g = new GridData();
+ g.horizontalSpan = 2;
+ g.grabExcessHorizontalSpace = true;
+ g.horizontalAlignment = GridData.FILL;
+ l.setLayoutData(g);
+ }
+}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCompareImages.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCompareImages.java
new file mode 100644
index 00000000000..bf62bdcdc0d
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCompareImages.java
@@ -0,0 +1,148 @@
+package org.eclipse.swt.examples.skia;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetCompareImages {
+
+ record ImageInfo(String name, Image image) {
+ }
+
+ private static final String IMAGES_PATH = "images";
+ private static final int IMAGE_SPACING = 10;
+ private final static ArrayList swtImages = new ArrayList<>();
+
+ public static void main(String[] args) {
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setText("Compare Images: GC vs Skija");
+ shell.setLayout(new GridLayout(2, true));
+
+ Composite leftComposite = new Composite(shell, SWT.BORDER);
+ leftComposite.setLayout(new FillLayout());
+ leftComposite.setLayoutData(new org.eclipse.swt.layout.GridData(SWT.FILL, SWT.FILL, true, true));
+ Canvas gcCanvas = new Canvas(leftComposite, SWT.V_SCROLL | SWT.DOUBLE_BUFFERED);
+ gcCanvas.setLayoutData(new org.eclipse.swt.layout.GridData(SWT.FILL, SWT.FILL, true, true));
+ ScrollBar gcScrollBar = gcCanvas.getVerticalBar();
+
+ Composite rightComposite = new Composite(shell, SWT.BORDER);
+ rightComposite.setLayout(new FillLayout());
+ rightComposite.setLayoutData(new org.eclipse.swt.layout.GridData(SWT.FILL, SWT.FILL, true, true));
+ Canvas skiaCanvas = new Canvas(rightComposite, SWT.V_SCROLL | SWT.SKIA);
+ skiaCanvas.setLayoutData(new org.eclipse.swt.layout.GridData(SWT.FILL, SWT.FILL, true, true));
+ ScrollBar skiaScrollBar = skiaCanvas.getVerticalBar();
+ // Load images
+
+ swtImages.add(new ImageInfo("Question Icon", Display.getDefault().getSystemImage(SWT.ICON_QUESTION)));
+ swtImages.add(new ImageInfo("Error Icon", Display.getDefault().getSystemImage(SWT.ICON_ERROR)));
+ swtImages.add(new ImageInfo("Info Icon", Display.getDefault().getSystemImage(SWT.ICON_INFORMATION)));
+
+ List imageFiles = new ArrayList<>();
+ // Use working directory reliably
+ File imagesDir = new File(System.getProperty("user.dir"), IMAGES_PATH);
+ if (imagesDir.exists() && imagesDir.isDirectory()) {
+ for (File f : imagesDir.listFiles()) {
+ String name = f.getName().toLowerCase();
+ if (name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".gif")
+ || name.endsWith(".bmp") || name.endsWith(".ico")) {
+
+ try {
+ Image img = new Image(display, f.getAbsolutePath());
+ swtImages.add(new ImageInfo(f.getAbsolutePath(), img));
+ imageFiles.add(f);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ // Calculate total height
+ int totalHeight = IMAGE_SPACING;
+ for (ImageInfo img : swtImages) {
+ Rectangle bounds = img.image().getBounds();
+ totalHeight += bounds.height + IMAGE_SPACING;
+ }
+
+ // Scroll logic
+ gcScrollBar.setMaximum(totalHeight);
+ gcScrollBar.setThumb(600); // Initial thumb size, will resize with shell
+ skiaScrollBar.setMaximum(totalHeight);
+ skiaScrollBar.setThumb(600); // Initial thumb size, will resize with shell
+
+ PaintListener gcPaintListener = new PaintListener() {
+ @Override
+ public void paintControl(PaintEvent e) {
+ int y = IMAGE_SPACING - gcScrollBar.getSelection();
+ drawImages(y, e.gc);
+ }
+ };
+ gcCanvas.addPaintListener(gcPaintListener);
+
+ PaintListener swtPaintListener = new PaintListener() {
+ @Override
+ public void paintControl(PaintEvent e) {
+ int y = IMAGE_SPACING - skiaScrollBar.getSelection();
+ drawImages(y, e.gc);
+ }
+ };
+
+ skiaCanvas.addPaintListener(swtPaintListener);
+
+ gcScrollBar.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ gcCanvas.redraw();
+ }
+ });
+
+ skiaScrollBar.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ skiaCanvas.redraw();
+ }
+ });
+
+ shell.setSize(1200, 700); // Initial size, but will resize
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+ for (ImageInfo img : swtImages)
+ img.image().dispose();
+ display.dispose();
+ }
+
+ private static void drawImages(int y, GC gc) {
+ gc.setInterpolation(SWT.HIGH);
+ for (ImageInfo img : swtImages) {
+ Rectangle bounds = img.image().getBounds();
+ try {
+ gc.drawImage(img.image(), 10, y);
+ } catch (Exception ex) {
+ System.err.println(" Exception drawing image: " + ex);
+ }
+ gc.drawText(img.name(), 10 + bounds.width + 30, y);
+ y += bounds.height + IMAGE_SPACING;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCopyArea.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCopyArea.java
new file mode 100644
index 00000000000..4d978994539
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetCopyArea.java
@@ -0,0 +1,55 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetCopyArea {
+
+ final static boolean useSkia = true;
+
+ public static void main(String[] args) {
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setText("Skija CopyArea paint=true Example");
+ shell.setSize(400, 300);
+
+ Canvas canvas = new Canvas(shell, useSkia ? SWT.SKIA : SWT.NONE);
+ canvas.setBounds(10, 10, 380, 280);
+
+ // Create an image and draw something on it
+ Image image = new Image(display, 100, 100);
+ GC gcImage = new GC(image);
+ gcImage.setBackground(display.getSystemColor(SWT.COLOR_YELLOW));
+ gcImage.fillRectangle(0, 0, 100, 100);
+ gcImage.setForeground(display.getSystemColor(SWT.COLOR_RED));
+ gcImage.drawLine(0, 0, 100, 100);
+ gcImage.dispose();
+
+ canvas.addPaintListener(e -> {
+
+ if(e.x == 20 && e.y == 20) {
+ return;
+ }
+
+ // Draw the original image
+ e.gc.drawImage(image, 20, 20);
+ // Use copyArea to move a part of the image
+ // Rectangle: srcX, srcY, width, height, destX, destY
+ e.gc.copyArea(20, 20, 50, 50, 120, 20, true); // paint=true
+ // Draw a border around the copied area
+ e.gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
+ e.gc.drawRectangle(120, 20, 50, 50);
+ });
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) display.sleep();
+ }
+ image.dispose();
+ display.dispose();
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetDrawImageThenChange.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetDrawImageThenChange.java
new file mode 100644
index 00000000000..056108ed9d7
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetDrawImageThenChange.java
@@ -0,0 +1,74 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetDrawImageThenChange {
+
+ private static final boolean USE_SKIA = true;
+
+ public static void main(String[] args) {
+
+ int canvasStyle = SWT.BORDER | (USE_SKIA ? SWT.SKIA : SWT.NONE);
+
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setText("Image Cache Test");
+ shell.setLayout(new RowLayout(SWT.HORIZONTAL));
+
+ int width = 120, height = 80;
+ // Erzeuge das Image
+ Image image = new Image(display, width, height);
+ GC gc = new GC(image);
+ gc.setBackground(display.getSystemColor(SWT.COLOR_YELLOW));
+ gc.fillRectangle(0, 0, width, height);
+ gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
+ gc.drawText("Original", 10, 30);
+ gc.dispose();
+
+ Button b = new Button(shell, SWT.PUSH);
+ b.setText("Change Image");
+ Canvas canvas1 = new Canvas(shell, canvasStyle);
+ canvas1.setSize(width, height);
+ canvas1.addPaintListener(e -> {
+ e.gc.drawImage(image, 0, 0);
+ });
+
+ Canvas canvas2 = new Canvas(shell, canvasStyle);
+ canvas2.setSize(width, height);
+ canvas2.addPaintListener(e -> {
+ e.gc.drawImage(image, 0, 0);
+ });
+
+ b.addListener(SWT.Selection, e -> {
+
+ // Ändere das Image mit neuem GC
+ GC gc2 = new GC(image);
+ gc2.setForeground(display.getSystemColor(SWT.COLOR_RED));
+ gc2.setLineWidth(4);
+ gc2.drawLine(0, 0, width, height);
+ gc2.drawText("Geändert", 10, 50);
+ gc2.dispose();
+ canvas1.redraw();
+ canvas2.redraw();
+
+ });
+
+ // Image-Größe
+
+ shell.pack();
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+ image.dispose();
+ display.dispose();
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetFillGradientRectangle.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetFillGradientRectangle.java
new file mode 100644
index 00000000000..9adcfeeb558
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetFillGradientRectangle.java
@@ -0,0 +1,41 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetFillGradientRectangle {
+
+ final static boolean USE_SKIA = true;
+
+ public static void main(String[] args) {
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setText("Skia FillGradientRectangle Example");
+ shell.setSize(400, 300);
+
+ Canvas canvas = new Canvas(shell, USE_SKIA ? SWT.SKIA : SWT.NONE);
+ canvas.setBounds(20, 20, 360, 240);
+
+ canvas.addListener(SWT.Paint, event -> {
+ GC gc = event.gc;
+ Color color1 = display.getSystemColor(SWT.COLOR_RED);
+ Color color2 = display.getSystemColor(SWT.COLOR_YELLOW);
+ gc.setForeground(color1);
+ gc.setBackground(color2);
+ gc.fillGradientRectangle(40, 40, 200, 100, true); // vertical gradient
+ gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
+ gc.setBackground(display.getSystemColor(SWT.COLOR_GREEN));
+ gc.fillGradientRectangle(40, 160, 200, 40, false); // horizontal gradient
+ });
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) display.sleep();
+ }
+ display.dispose();
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetGetClippings.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetGetClippings.java
new file mode 100644
index 00000000000..e493e6d0646
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetGetClippings.java
@@ -0,0 +1,100 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.Region;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetGetClippings {
+ static boolean useSkia = true;
+
+ public static void main(String[] args) {
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setText("GetClipping Example");
+ shell.setSize(800, 400);
+ shell.setLayout(null);
+
+ int style = SWT.DOUBLE_BUFFERED | SWT.BORDER | (useSkia ? SWT.SKIA : SWT.NONE);
+
+
+ // Rectangle clipping example
+ Canvas canvasRect = new Canvas(shell, style);
+ canvasRect.setBounds(20, 20, 350, 350);
+ canvasRect.addPaintListener(e -> {
+ GC gc = e.gc;
+ // Set rectangular clipping
+ Rectangle clipRect = new Rectangle(10, 10, 100, 100);
+
+ gc.setBackground(display.getSystemColor(SWT.COLOR_YELLOW));
+ gc.setClipping(clipRect);
+ gc.fillRectangle(0, 0, 120, 120);
+
+ int shift = 130;
+
+ // Draw something
+ // Get clipping rectangle
+ Rectangle receivedClip = gc.getClipping();
+ receivedClip.y += shift;
+
+ gc.setBackground(display.getSystemColor(SWT.COLOR_RED));
+ gc.setClipping(receivedClip);
+ gc.fillRectangle(receivedClip);
+ gc.drawText("getClipping()", 20, 60 +shift);
+
+ Region r = new Region();
+ gc.getClipping(r);
+
+ r.translate(0, shift);
+
+ gc.setBackground(display.getSystemColor(SWT.COLOR_GREEN));
+ gc.setClipping(r);
+ gc.fillRectangle(new Rectangle(0, 0, 1200, 1020));
+ gc.drawText("getClipping(Region)", 20, 60+ 2*shift);
+
+
+ });
+
+ // Region (circle) clipping example
+ Canvas canvasRegion = new Canvas(shell, style);
+ canvasRegion.setBounds(400, 20, 350, 350);
+ canvasRegion.addPaintListener(e -> {
+ GC gc = e.gc;
+ // Set rectangular clipping
+ Rectangle clipRect = new Rectangle(10, 10, 100, 100);
+ Region region = new Region();
+ region.add(clipRect);
+
+ gc.setBackground(display.getSystemColor(SWT.COLOR_YELLOW));
+ gc.setClipping(clipRect);
+ gc.fillRectangle(0, 0, 120, 120);
+
+ int shift = 130;
+
+ // Draw something
+ // Get clipping rectangle
+ Region ret = new Region();
+ gc.getClipping(ret);
+
+ gc.setBackground(display.getSystemColor(SWT.COLOR_RED));
+
+ ret.translate(0, 130);
+
+ gc.setClipping(ret);
+
+ gc.setBackground(display.getSystemColor(SWT.COLOR_GREEN));
+ gc.setClipping(ret);
+ gc.fillRectangle(new Rectangle(0, 0, 1200, 1020));
+ gc.drawText("Region Clipping", 20, 60+shift);
+ });
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) display.sleep();
+ }
+ display.dispose();
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetGifInterpolationLoader.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetGifInterpolationLoader.java
new file mode 100644
index 00000000000..514e75a389a
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetGifInterpolationLoader.java
@@ -0,0 +1,139 @@
+package org.eclipse.swt.examples.skia;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.Transform;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetGifInterpolationLoader {
+
+ final static boolean useSkia = true;
+
+ static Image loadImage(Device device, Class clazz, String string) {
+ try (InputStream stream = clazz.getResourceAsStream(string)) {
+ if (stream == null)
+ return null;
+ return new Image(device, stream);
+ } catch (SWTException ex) {
+ } catch (IOException ex) {
+ }
+ return null;
+ }
+
+ public static void main(String[] args) {
+
+ Display display = new Display();
+ final var image = SnippetGifInterpolationLoader.loadImage(display, SnippetGifInterpolationLoader.class, "home_nav.gif");
+
+ Shell s = new Shell(display);
+ s.setLayout(new FillLayout());
+
+ int style = useSkia ? (SWT.FILL | SWT.SKIA ) : SWT.FILL;
+
+ Canvas c = new Canvas(s, style );
+ c.setBackground(display.getSystemColor(SWT.COLOR_RED));
+ c.addPaintListener(new PaintListener() {
+
+ @Override
+ public void paintControl(PaintEvent e) {
+ e.gc.drawImage(image, 0, 0);
+ var gc = e.gc;
+ var width = c.getBounds().width;
+ var height = c.getBounds().height;
+ Rectangle bounds = image.getBounds();
+
+ String text;
+ Point size = gc.stringExtent("OriginalText");
+
+
+ float scaleX = 10f;
+ float scaleY = 10f;
+ var device = e.gc.getDevice();
+
+ Transform transform = new Transform(device);
+ transform.translate((width - (bounds.width * scaleX + 10) * 4) / 2, 25 + bounds.height + size.y +
+ (height - (25 + bounds.height + size.y + bounds.height*scaleY)) / 2);
+ transform.scale(scaleX, scaleY);
+
+ // --- draw strings ---
+ float[] point = new float[2];
+ text = "None"; //$NON-NLS-1$
+ size = gc.stringExtent(text);
+ point[0] = (scaleX*bounds.width + 5 - size.x)/(2*scaleX);
+ point[1] = bounds.height;
+ transform.transform(point);
+ gc.drawString(text, (int)point[0], (int)point[1], true);
+
+ text = "Low"; //$NON-NLS-1$
+ size = gc.stringExtent(text);
+ point[0] = (scaleX*bounds.width + 5 - size.x)/(2*scaleX) + bounds.width;
+ point[1] = bounds.height;
+ transform.transform(point);
+ gc.drawString(text, (int)point[0], (int)point[1], true);
+
+ text = "Default"; //$NON-NLS-1$
+ size = gc.stringExtent(text);
+ point[0] = (scaleX*bounds.width + 5 - size.x)/(2*scaleX) + 2*bounds.width;
+ point[1] = bounds.height;
+ transform.transform(point);
+ gc.drawString(text, (int)point[0], (int)point[1], true);
+
+ text = "High"; //$NON-NLS-1$
+ size = gc.stringExtent(text);
+ point[0] = (scaleX*bounds.width + 5 - size.x)/(2*scaleX) + 3*bounds.width;
+ point[1] = bounds.height;
+ transform.transform(point);
+ gc.drawString(text, (int)point[0], (int)point[1], true);
+
+ gc.setTransform(transform);
+ transform.dispose();
+
+ // --- draw images ---
+
+ // no interpolation
+ gc.setInterpolation(SWT.NONE);
+ gc.drawImage(image, 0, 0);
+
+ // low interpolation
+ gc.setInterpolation(SWT.LOW);
+ gc.drawImage(image, bounds.width, 0);
+
+ // default interpolation
+ gc.setInterpolation(SWT.DEFAULT);
+ gc.drawImage(image, 2*bounds.width, 0);
+
+ // high interpolation
+ gc.setInterpolation(SWT.HIGH);
+ gc.drawImage(image, 3*bounds.width, 0);
+
+ }
+ });
+
+
+ s.open();
+ s.setSize(500, 500);
+
+ while(!s.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+
+ display.dispose();
+
+
+
+ }
+
+}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetImageToPNG.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetImageToPNG.java
new file mode 100644
index 00000000000..657e7a3a52b
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetImageToPNG.java
@@ -0,0 +1,118 @@
+package org.eclipse.swt.examples.skia;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.widgets.Display;
+
+public class SnippetImageToPNG {
+
+ final static boolean DELETE_OUTPUT_DIR = true;
+
+ record ImageInfo(String name, Image image) {
+ }
+
+ record FormatInfo(String format, int type) {
+ }
+
+
+ private final static List formats = List.of(
+// new FormatInfo(".gif", SWT.IMAGE_GIF), // SWT does not support saving GIFs for these images
+// new FormatInfo(".ico", SWT.IMAGE_ICO), // SWT does not support saving ICOs for these images
+ new FormatInfo(".bmp", SWT.IMAGE_BMP),
+ new FormatInfo(".jpef", SWT.IMAGE_JPEG),
+ new FormatInfo(".png", SWT.IMAGE_PNG)
+ );
+
+
+ private static final String IMAGES_PATH = "images";
+ private final static ArrayList swtImages = new ArrayList<>();
+
+ public static void main(String[] args) {
+ Display display = new Display();
+
+ // Load images
+ swtImages.add(new ImageInfo("question", Display.getDefault().getSystemImage(SWT.ICON_QUESTION)));
+ swtImages.add(new ImageInfo("error", Display.getDefault().getSystemImage(SWT.ICON_ERROR)));
+ swtImages.add(new ImageInfo("info", Display.getDefault().getSystemImage(SWT.ICON_INFORMATION)));
+
+ List imageFiles = new ArrayList<>();
+ // Use working directory reliably
+ File imagesDir = new File(System.getProperty("user.dir"), IMAGES_PATH);
+ if (imagesDir.exists() && imagesDir.isDirectory()) {
+ for (File f : imagesDir.listFiles()) {
+ String name = f.getName().toLowerCase();
+ if (name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".gif")
+ || name.endsWith(".bmp") || name.endsWith(".ico")) {
+ try {
+ swtImages.add(new ImageInfo(name, new Image(display, f.getAbsolutePath())));
+ imageFiles.add(f);
+ } catch (Exception e) {
+ // skip invalid image
+ }
+ }
+ }
+ }
+
+ File f = new File("./out");
+
+ if (f.exists()) {
+ for (File oldFile : f.listFiles()) {
+ oldFile.delete();
+ }
+ }
+ f.mkdirs();
+
+ for (FormatInfo format : formats) {
+ System.out.println(format.format + " (" + format.type + "):");
+ int i = 0;
+ for (ImageInfo img : swtImages) {
+
+// printHasAlpha(img, "original image " + i);
+
+ ImageLoader loader = new ImageLoader();
+ loader.data = new ImageData[] { img.image.getImageData() };
+ String outPath = "./out/out" + i + format.format;
+ loader.save(outPath, format.type);
+
+ if (format.type == SWT.IMAGE_PNG) {
+ printHasAlpha(outPath);
+ }
+
+ i++;
+ }
+
+ }
+
+ if (DELETE_OUTPUT_DIR) {
+ for (File oldFile : f.listFiles()) {
+ oldFile.delete();
+ }
+ f.delete();
+
+ }
+ }
+
+// private static void printHasAlpha(ImageInfo img, String outPath) {
+// // Transparenzprüfung
+// ImageData pngData = img.image.getImageData();
+// boolean hasAlpha = pngData.alphaData != null || pngData.alpha != -1;
+// System.out.println("Path: " + img.name + " hasAlpha=" + hasAlpha + " alphaData="
+// + (pngData.alphaData != null ? pngData.alphaData.length : "null") + " alpha=" + pngData.alpha);
+//
+// }
+
+ private static void printHasAlpha(String outPath) {
+ // Transparenzprüfung
+ ImageData pngData = new ImageLoader().load(outPath)[0];
+ boolean hasAlpha = pngData.alphaData != null || pngData.alpha != -1;
+ System.out.println("Path: " + outPath + " hasAlpha=" + hasAlpha + " alphaData="
+ + (pngData.alphaData != null ? pngData.alphaData.length : "null") + " alpha=" + pngData.alpha);
+ }
+
+}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetPattern.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetPattern.java
new file mode 100644
index 00000000000..441a4c106ab
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetPattern.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2016 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ * SAP SE - skia support
+ *******************************************************************************/
+package org.eclipse.swt.examples.skia;
+
+/*
+ * Fill a shape with a predefined pattern
+ *
+ * For a list of all SWT example snippets see
+ * http://www.eclipse.org/swt/snippets/
+ *
+ * @since 3.1
+ */
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Pattern;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetPattern {
+
+ final static boolean USE_SKIA = true;
+
+ static int style = USE_SKIA ? SWT.SKIA : SWT.NONE;
+ final static int WIDTH = 100;
+ final static int HEIGHT = 100;
+
+public static void main(String[] args) {
+
+ Display display = new Display();
+ //define a pattern on an image
+ final Image image = new Image(display, WIDTH, HEIGHT);
+ Color blue = display.getSystemColor(SWT.COLOR_BLUE);
+ Color yellow = display.getSystemColor(SWT.COLOR_YELLOW);
+ Color white = display.getSystemColor(SWT.COLOR_WHITE);
+ GC gc = new GC(image);
+ gc.setBackground(white);
+ gc.setForeground(yellow);
+ gc.fillGradientRectangle(0, 0, 1000, 1000, true);
+ for (int i=-500; i<1000; i+=10) {
+ gc.setForeground(blue);
+ gc.drawLine(i, 0, 500 + i, 1000);
+ gc.drawLine(500 + i, 0, i, 1000);
+ }
+ gc.drawRectangle(5, 5, WIDTH - 11, HEIGHT -11);
+ gc.dispose();
+ final Pattern pattern;
+ try {
+ pattern = new Pattern(display, image);
+// pattern = new Pattern(display, 0, 0, 100, 100, blue, yellow);
+ } catch (SWTException e) {
+ //Advanced Graphics not supported.
+ //This new API requires the Cairo Vector engine on GTK and GDI+ on Windows.
+ System.out.println(e.getMessage());
+ display.dispose();
+ return;
+ }
+
+ Shell shell = new Shell(display);
+ shell.setText("SnippetPattern");
+ shell.setLayout(new FillLayout());
+ Canvas c = new Canvas(shell, SWT.DOUBLE_BUFFERED | style);
+ c.addListener(SWT.Paint, event -> {
+ Rectangle r = ((Composite)event.widget).getClientArea();
+ GC gc1 = event.gc;
+ gc1.setBackgroundPattern(pattern);
+ gc1.fillRectangle(5, 5, r.width - 10, r.height - 10);
+ });
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+ image.dispose();
+ pattern.dispose();
+ display.dispose();
+}
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetPattern2.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetPattern2.java
new file mode 100644
index 00000000000..ccecad6bbff
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetPattern2.java
@@ -0,0 +1,89 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Pattern;
+import org.eclipse.swt.graphics.RGBA;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetPattern2 {
+
+
+ public static void main(String[] args) {
+ final Display display = new Display();
+ final Shell s = new Shell(display);
+ s.setLayout(new FillLayout());
+
+ final Composite shell = new Composite(s, SWT.FILL | SWT.V_SCROLL);
+ final GridLayout gridLayout = new GridLayout(2, false);
+ shell.setLayout(gridLayout);
+
+ patternCanvas(shell);
+
+ activateSkiaRaster();
+ patternCanvas(shell);
+ resetCanvasConfiguration();
+
+ s.open();
+ while (!s.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+
+ display.dispose();
+ }
+
+ private static boolean skiaEnabled = false;
+ private static void resetCanvasConfiguration() {
+ skiaEnabled = false;
+ }
+
+ private static void activateSkiaRaster() {
+ skiaEnabled = true;
+ }
+
+ private static void patternCanvas(Composite shell) {
+
+ final Canvas patternCanvas = createCanvas(shell);
+ patternCanvas.addPaintListener(e -> {
+ final GC gc = e.gc;
+// in the RGBA color for the pattern the alpha value will be ignored, the alpha values passed to the Pattern constructor will be used
+ final Pattern pattern = new Pattern(shell.getDisplay(), 10, 10, 100, 50,
+ new Color(new RGBA(255, 0, 0, 50)), 100, new Color(new RGBA(0, 0, 255, 100)), 200);
+ gc.setBackgroundPattern(pattern);
+ gc.fillRectangle(10, 10, 100, 50);
+ pattern.dispose();
+ });
+
+ setGridData(patternCanvas);
+
+ }
+
+ private static Canvas createCanvas(Composite shell) {
+ int style = skiaEnabled ? SWT.SKIA : SWT.NONE;
+ return new Canvas(shell, SWT.BORDER | style);
+ }
+
+ private static void setGridData(Control c) {
+ setGridData(c, 150);
+ }
+
+ private static void setGridData(Control c, int minHeight) {
+ final var g = new GridData();
+ g.grabExcessHorizontalSpace = true;
+ g.grabExcessVerticalSpace = true;
+ g.horizontalAlignment = GridData.FILL;
+ g.verticalAlignment = GridData.FILL;
+ g.minimumHeight = minHeight;
+ c.setLayoutData(g);
+ }
+}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetRasterSkiaCanvas.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetRasterSkiaCanvas.java
new file mode 100644
index 00000000000..5182e79af9e
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetRasterSkiaCanvas.java
@@ -0,0 +1,144 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetRasterSkiaCanvas {
+
+ final static boolean useSkia = true;
+
+ final static int RECTANGLES_PER_LINE = 200;
+
+ static class RecDraw{
+
+ public RecDraw(int xPos, int yPos, Color c) {
+ super();
+ this.xPos = xPos;
+ this.yPos = yPos;
+ this.c = c;
+ }
+ int xPos ,yPos;
+ Color c;
+
+ }
+
+ static RecDraw[][] recDraws = new RecDraw[RECTANGLES_PER_LINE][RECTANGLES_PER_LINE];
+ private static int style = useSkia ? SWT.SKIA : SWT.NONE;
+ int width = 2;
+
+ public static void main(String[] args) {
+
+ final Display display = new Display();
+ final Shell shell = new Shell(display);
+ shell.setText("Snippet Canvas");
+ final Canvas c = new Canvas(shell, SWT.DOUBLE_BUFFERED | style);
+
+ for( int x = 0 ; x < RECTANGLES_PER_LINE ; x++ ) {
+ for(int y = 0 ; y < RECTANGLES_PER_LINE ; y++) {
+
+ recDraws[x][y] = new RecDraw( x*2,y*2,Display.getDefault().getSystemColor((x+y )% 16 ));
+
+ }
+ }
+
+ c.setSize(100, 100);
+
+ shell.addListener(SWT.Resize, e -> onResize(e, c));
+ c.addListener(SWT.Paint, SnippetRasterSkiaCanvas::onPaint);
+ c.addListener(SWT.Paint, SnippetRasterSkiaCanvas::onPaint2);
+
+ shell.setSize(1000, RECTANGLES_PER_LINE*3+80);
+
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ c.redraw();
+ }
+ }
+ display.dispose();
+ }
+
+
+ static long startTime = System.currentTimeMillis();
+ private static boolean printFrameRate = true;
+ private static int frames;
+ private static long lastFrame;
+ private static long framesToDraw;
+
+ private static void onPaint(Event e) {
+
+
+ final var s = ((Canvas) e.widget);
+
+ // surface.getCanvas().clear(0xFFFFFFFF);
+
+ final Point size = s.getSize();
+
+ long currentPosTime = System.currentTimeMillis() - startTime;
+
+ currentPosTime = currentPosTime % 10000;
+
+ final double position = (double) currentPosTime / (double) 10000;
+
+ final int shift = (int) (position * size.x);
+ final int shiftDown = 20;
+
+ for( int x = 0 ; x < RECTANGLES_PER_LINE ; x++ ) {
+ for(int y = 0 ; y < RECTANGLES_PER_LINE ; y++) {
+
+ final var rec = recDraws[x][y];
+ e.gc.setForeground(rec.c);
+ e.gc.drawRectangle(shift + rec.xPos ,shiftDown + rec.yPos, 2,2 );
+
+ }
+ }
+
+
+ // int colorAsRGB = 0xFF42FFF4;
+ // int colorRed = 0xFFFF0000;
+ // int colorGreen = 0xFF00FF00;
+ // int colorBlue = 0xFF0000FF;
+ //
+ // e.gc.setForeground(s.getDisplay().getSystemColor(SWT.COLOR_RED));
+
+
+ }
+
+ private static void onPaint2(Event e) {
+
+ if (printFrameRate) {
+
+ if (System.currentTimeMillis() - lastFrame > 1000) {
+ // System.out.println("Frames: " + frames);
+ framesToDraw = frames;
+
+
+ frames = 0;
+ lastFrame = System.currentTimeMillis();
+ }
+ frames++;
+
+ e.gc.drawText("Frames: " + framesToDraw, 10,10);
+
+ }
+
+
+ }
+
+
+
+ private static void onResize(Event e, Canvas c) {
+
+ final var ca = c.getShell().getClientArea();
+
+ c.setSize(ca.width, ca.height);
+
+ }
+
+}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetRegion.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetRegion.java
new file mode 100644
index 00000000000..2d317648b0c
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetRegion.java
@@ -0,0 +1,106 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.Region;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetRegion {
+
+ final static Region r = new Region();
+
+ public static void main(String[] args) {
+
+ for (int re = 0; re < 30; re++) {
+ r.add(new Rectangle(1 + 3 * re, 2 + 3 * re, 40, 40));
+ }
+ r.subtract(15, 15, 20, 20);
+
+ final Region inters = new Region();
+ inters.add(new Rectangle(20, 20, 60, 60));
+ r.intersect(inters);
+
+
+ r.translate(15, 15);
+
+ r.add(toArray(generateCirclePoints(10, 30, 0)));
+
+ final Display display = Display.getDefault();
+ final Shell shell = new Shell(display);
+ shell.setText("Snippet Canvas");
+ // here you can switch between Canvas SkiaRasterCanvas and SkiaCanvas
+
+ setupCanvas(shell, SWT.DOUBLE_BUFFERED | SWT.SKIA, new Point(0, 0));
+ setupCanvas(shell, SWT.DOUBLE_BUFFERED, new Point(100, 100));
+
+ shell.setSize(1000, 1000);
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ display.readAndDispatch();
+ }
+ display.dispose();
+
+ }
+
+ public static int[] toArray(int[][] points) {
+ final int l = points.length;
+ final int[] ret = new int[l *2];
+ for( int i = 0 ; i < l ; i++) {
+ ret[2*i] = points[i][0];
+ ret[2*i+1] = points[i][1];
+ }
+
+ return ret;
+
+ }
+
+
+ public static int[][] generateCirclePoints(int numPoints, int radius, int translate) {
+ final int[][] points = new int[numPoints][2];
+ final double angleIncrement = 2 * Math.PI / numPoints;
+
+ for (int i = 0; i < numPoints; i++) {
+ final double angle = i * angleIncrement;
+ final int x = (int) (radius * Math.cos(angle)) + translate;
+ final int y = (int) (radius * Math.sin(angle)) + translate;
+ points[i][0] = x;
+ points[i][1] = y;
+ }
+
+ return points;
+ }
+
+ private static void setupCanvas(Shell shell, int style, Point loc) {
+
+ final Canvas c1 = new Canvas(shell, style);
+ c1.setSize(100, 100);
+ c1.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_CYAN));
+ c1.addListener(SWT.Paint, SnippetRegion::onPaint);
+ c1.setLocation(loc);
+
+ }
+
+ private static void onPaint(Event e) {
+
+ // surface.getCanvas().clear(0xFFFFFFFF);
+
+ e.gc.setClipping(r);
+
+ e.gc.setBackground(e.display.getSystemColor(SWT.COLOR_RED));
+ e.gc.fillRectangle(0, 0, 100, 100);
+
+ // int colorAsRGB = 0xFF42FFF4;
+ // int colorRed = 0xFFFF0000;
+ // int colorGreen = 0xFF00FF00;
+ // int colorBlue = 0xFF0000FF;
+ //
+ // e.gc.setForeground(s.getDisplay().getSystemColor(SWT.COLOR_RED));
+
+ }
+
+}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetSkiaDirectDrawing.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetSkiaDirectDrawing.java
new file mode 100644
index 00000000000..8f47792f27b
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetSkiaDirectDrawing.java
@@ -0,0 +1,50 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetSkiaDirectDrawing {
+
+ public static void main(String[] arg) {
+
+
+ final Display d = new Display();
+ final Shell s = new Shell(d);
+ s.setLayout(new FillLayout());
+
+ final Canvas c = new Canvas(s, SWT.DOUBLE_BUFFERED | SWT.FILL | SWT.SKIA );
+
+ c.setBackground(d.getSystemColor(SWT.COLOR_RED));
+
+ s.addControlListener(new ControlListener() {
+
+ @Override
+ public void controlResized(ControlEvent e) {
+ c.setSize(s.getSize());
+
+ }
+
+ @Override
+ public void controlMoved(ControlEvent e) {
+
+ }
+ });
+
+ s.open();
+
+ while (!s.isDisposed()) {
+ d.readAndDispatch();
+ if(!s.isDisposed())
+ c.redraw();
+ }
+
+ d.close();
+
+ }
+
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetSkiaFonts.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetSkiaFonts.java
new file mode 100644
index 00000000000..99c23ac1a25
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetSkiaFonts.java
@@ -0,0 +1,112 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetSkiaFonts {
+
+ final static int stepSize = 60;
+
+ public static void main(String[] args) {
+ final Display display = new Display();
+ final Shell shell = new Shell(display, SWT.V_SCROLL | SWT.SHELL_TRIM);
+ shell.setText("Available Fonts");
+ shell.setSize(800, 600);
+ shell.setLayout(new GridLayout(2,true));
+
+ final Canvas canvas = new Canvas(shell, SWT.BORDER);
+ final Canvas skiaCanvas = new Canvas(shell, SWT.BORDER | SWT.SKIA);
+
+ setGridData(canvas, 300);
+ setGridData(skiaCanvas, 300);
+
+ final var pl = (PaintListener) event -> {
+ final GC gc = event.gc;
+ final FontData[] fontDataArrayScalable = display.getFontList(null, true);
+ final FontData[] fontDataArrayNotScalable = display.getFontList(null, false);
+ int y = 10 - shell.getVerticalBar().getSelection() ;
+
+ // Courier is a bitmap font on windows, skia does not support it...
+ final Font font1 = new Font(display, "Courier", 20, SWT.NONE);
+ y = drawFont(gc,font1,"Courier",y);
+
+
+ for (final FontData fd : fontDataArrayScalable) {
+
+ fd.setHeight(20);
+ if( y < -stepSize || y > canvas.getSize().y ) {
+ y += stepSize;
+ continue;
+ }
+
+ final Font font = new Font(display, fd);
+ y = drawFont(gc,font,"'" + fd.getName() +"'",y);
+
+ }
+
+ for (final FontData fd : fontDataArrayNotScalable) {
+
+ if( y < -stepSize || y > canvas.getSize().y ) {
+ y += stepSize;
+ continue;
+ }
+
+ fd.setHeight(20);
+ final Font font = new Font(display, fd);
+ y = drawFont(gc,font,"'" + fd.getName() +"'",y);
+ }
+
+ shell.getVerticalBar().setMaximum(y + shell.getVerticalBar().getSelection());
+ };
+
+ canvas.addPaintListener(pl );
+ skiaCanvas.addPaintListener(pl);
+
+
+ shell.getVerticalBar().addListener(SWT.Selection, e -> {
+
+ canvas.redraw();
+ skiaCanvas.redraw();
+
+ });
+
+
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ display.dispose();
+ }
+
+ private static int drawFont(GC gc, Font font1, String fontName1, int y) {
+
+ gc.setFont(font1);
+ gc.drawText(fontName1, 20, y);
+ font1.dispose(); // Frees system resources
+ y += stepSize;
+
+ return y;
+ }
+
+ private static void setGridData(Control c, int minHeight) {
+ final var g = new GridData();
+ g.grabExcessHorizontalSpace = true;
+ g.grabExcessVerticalSpace = true;
+ g.horizontalAlignment = GridData.FILL;
+ g.verticalAlignment = GridData.FILL;
+ g.minimumHeight = minHeight;
+ c.setLayoutData(g);
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetSkijaCanvas.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetSkijaCanvas.java
new file mode 100644
index 00000000000..315feb622e7
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetSkijaCanvas.java
@@ -0,0 +1,124 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetSkijaCanvas {
+ final static boolean useSkia = true;
+
+ static final int RECTANGLES_PER_LINE = 200;
+
+
+ static record RecDraw(int xPos, int yPos, Color c) {
+ }
+
+ static int style = SWT.FILL | SWT.DOUBLE_BUFFERED | (useSkia ? SWT.SKIA : SWT.NONE);
+ private static final RecDraw[][] recDraws = new RecDraw[RECTANGLES_PER_LINE][RECTANGLES_PER_LINE];
+ private static long minFrameRate = Long.MAX_VALUE;
+ private static long maxFrameRate = 0;
+ private static long sum = 0;
+ private static double count = 0.0;
+ private static double average = 0;
+
+ public static void main(String[] args) {
+ final Display display = new Display();
+ final Shell shell = new Shell(display);
+ shell.setText("Snippet 1");
+
+ shell.setLayout(new FillLayout());
+ final Canvas c = new Canvas(shell,style);
+
+
+ for (int x = 0; x < RECTANGLES_PER_LINE; x++) {
+ for (int y = 0; y < RECTANGLES_PER_LINE; y++) {
+ recDraws[x][y] = new RecDraw(x * 2, y * 2, Display.getDefault().getSystemColor((x + y) % 16));
+ }
+ }
+
+ c.setSize(100, 100);
+
+ shell.addListener(SWT.Resize, e -> onResize(e, c));
+ c.addListener(SWT.Paint, SnippetSkijaCanvas::onPaint);
+ c.addListener(SWT.Paint, SnippetSkijaCanvas::onPaint2);
+
+ shell.setSize(1000, RECTANGLES_PER_LINE * 3 + 80);
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ display.dispose();
+ }
+
+ static long startTime = System.currentTimeMillis();
+ private static boolean printFrameRate = true;
+ private static int frames;
+ private static long lastFrame;
+ private static long framesToDraw;
+
+ private static void onPaint(Event e) {
+ final var s = ((Canvas) e.widget);
+
+ final Point size = s.getSize();
+ long currentPosTime = System.currentTimeMillis() - startTime;
+ currentPosTime = currentPosTime % 10000;
+
+ final double position = (double) currentPosTime / (double) 10000;
+ final int shift = (int) (position * size.x);
+ final int shiftDown = 20;
+
+ for (int x = 0; x < RECTANGLES_PER_LINE; x++) {
+ for (int y = 0; y < RECTANGLES_PER_LINE; y++) {
+ final var rec = recDraws[x][y];
+ e.gc.setForeground(rec.c);
+ e.gc.drawRectangle(shift + rec.xPos, shiftDown + rec.yPos, 2, 2);
+
+ }
+ }
+ }
+
+ private static void onPaint2(Event e) {
+ final var s = ((Canvas) e.widget);
+
+ if (printFrameRate) {
+ if (System.currentTimeMillis() - lastFrame > 1000) {
+ framesToDraw = frames;
+ frames = 0;
+ lastFrame = System.currentTimeMillis();
+ if(framesToDraw != 0) {
+ minFrameRate = Math.min(minFrameRate, framesToDraw);
+ }
+ maxFrameRate = Math.max(maxFrameRate, framesToDraw);
+ sum += framesToDraw;
+ count++;
+ average = sum/count;
+ }
+ frames++;
+ e.gc.drawText("Frames: min: " + minFrameRate + ", max: " + maxFrameRate
+ + " cur: " + framesToDraw
+ + " avg: " + String.format("%.1f", average) , 10, 10);
+ }
+ s.redraw();
+
+ // Mac need an additional redraw call. Else the animation stops and it reacts on user input.
+ e.display.timerExec(10, () -> {
+ if(!s.isDisposed()) {
+ s.redraw();
+ }
+ });
+ }
+
+ private static void onResize(Event e, Canvas c) {
+ final var ca = c.getShell().getClientArea();
+ c.setSize(ca.width, ca.height);
+ }
+
+}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetText.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetText.java
new file mode 100644
index 00000000000..3b2393885e5
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetText.java
@@ -0,0 +1,77 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetText {
+
+ public static void main(String[] args) {
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setText("SWT vs Skija drawText Example");
+ shell.setSize(1100, 700);
+ shell.setLayout(new FillLayout(SWT.HORIZONTAL));
+
+ // Composite for two canvases side by side
+ org.eclipse.swt.widgets.Composite composite = shell;
+
+ // Left: Classic SWT Canvas with scrollbars
+ ScrolledComposite scrolledClassic = new ScrolledComposite(composite, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
+ Canvas canvasClassic = new Canvas(scrolledClassic, SWT.NONE);
+ scrolledClassic.setContent(canvasClassic);
+ scrolledClassic.setExpandHorizontal(true);
+ scrolledClassic.setExpandVertical(true);
+ canvasClassic.setSize(500, 1200);
+ scrolledClassic.setMinSize(canvasClassic.getSize());
+
+ // Right: Skija Canvas with scrollbars
+ ScrolledComposite scrolledSkija = new ScrolledComposite(composite, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
+ Canvas canvasSkija = new Canvas(scrolledSkija, SWT.SKIA);
+ scrolledSkija.setContent(canvasSkija);
+ scrolledSkija.setExpandHorizontal(true);
+ scrolledSkija.setExpandVertical(true);
+ canvasSkija.setSize(500, 1200);
+ scrolledSkija.setMinSize(canvasSkija.getSize());
+
+ String multiLine = "Short\nA much &longer \ttab line &of text\rCarriage &return\nMid length\nTiny\nThe last line is the longest of all lines in this example.\nAmpersand: &File\nBackspace: A\bB\nForm feed: A\fB";
+ Font font = new Font(display, "Arial", 15, SWT.NORMAL);
+ String displayText = multiLine.replace("\f", "\u240C");
+
+ // Paint logic for both canvases
+ java.util.function.Consumer paintLogic = gc -> {
+ gc.setFont(font);
+ gc.setBackground(display.getSystemColor(SWT.COLOR_YELLOW));
+ int y = 5;
+ gc.drawText("draw\ttab", 20, y , SWT.DRAW_TAB);
+ y +=30;
+ gc.drawText("draw\rdeleimiter\r\ncarriagereturn", 20, y , SWT.DRAW_DELIMITER);
+ y +=70;
+ gc.drawText("draw\rtab and draw deleimiter\r\ncarriagereturn", 20, y , SWT.DRAW_DELIMITER | SWT.DRAW_TAB);
+ y +=80;
+ gc.drawText("Not draw\rtab", 20, y , SWT.NONE);
+ y +=30;
+ gc.drawText(displayText, 20, y);
+ y +=300;
+ gc.drawText(displayText, 20, y, true);
+ y +=300;
+ // windows always draws win delimiter mode, even without flag...
+ gc.drawText("SWT.DRAW_MNEMONIC: "+displayText, 20, y, SWT.DRAW_MNEMONIC);
+ y+=300;
+ };
+
+ canvasClassic.addPaintListener(e -> paintLogic.accept(e.gc));
+ canvasSkija.addPaintListener(e -> paintLogic.accept(e.gc));
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) display.sleep();
+ }
+ font.dispose();
+ display.dispose();
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetTextExtent.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetTextExtent.java
new file mode 100644
index 00000000000..fbfb0345578
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetTextExtent.java
@@ -0,0 +1,82 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetTextExtent {
+ public static void main(String[] args) {
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setText("Text Extent Snippet");
+ shell.setLayout(new GridLayout(2, true));
+
+ // Create two canvases
+ for (int i = 0; i < 2; i++) {
+ int skiaStyle = (i == 0) ? SWT.NONE : SWT.SKIA;
+ Canvas canvas = new Canvas(shell, SWT.BORDER | skiaStyle);
+ canvas.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ canvas.addListener(SWT.Paint, new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ GC gc = event.gc;
+ Rectangle rect = canvas.getClientArea();
+ Font font = new Font(display, "Arial", 250, SWT.BOLD);
+ gc.setFont(font);
+ String text = "a";
+ // Use textExtent to get size
+ Point extent = gc.textExtent(text);
+ int x = rect.x + (rect.width - extent.x) / 2;
+ int y = rect.y + (rect.height - extent.y) / 2;
+
+ // Print font metrics for debugging
+ org.eclipse.swt.graphics.FontMetrics metrics = gc.getFontMetrics();
+ System.out.println((gc.getClass().getName().contains("GCExtension") ? "[SkiaGC]" : "[SWTGC]") +
+ " ascent=" + metrics.getAscent() +
+ ", descent=" + metrics.getDescent() +
+ ", leading=" + metrics.getLeading() +
+ ", height=" + metrics.getHeight() +
+ ", extentY=" + extent.y);
+
+
+ boolean red = false;
+ for (int i = 0; i < extent.y / 5; i = i + 5) {
+ if (red) {
+ gc.setBackground(display.getSystemColor(SWT.COLOR_RED));
+ red = false;
+ } else {
+ gc.setBackground(display.getSystemColor(SWT.COLOR_GREEN));
+ red = true;
+ }
+ gc.fillRectangle(x, y + i * 5, extent.x, 5);
+ }
+ gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
+ gc.drawRectangle(new Rectangle(x, y, extent.x, extent.y));
+ gc.drawString(text, x, y, true);
+ // Draw baseline indicator
+ int baselineY = y + metrics.getAscent();
+ gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
+ gc.drawLine(x, baselineY, x + extent.x, baselineY);
+ font.dispose();
+ }
+ });
+ }
+
+ shell.setSize(600, 300);
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+ display.dispose();
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetTextLayout.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetTextLayout.java
new file mode 100644
index 00000000000..e9093be8fdc
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetTextLayout.java
@@ -0,0 +1,51 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetTextLayout {
+
+ private static final boolean useSkia = false; // Set to false to use default TextLayout
+
+ public static void main(String[] args) {
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setText("TextLayout Example");
+ shell.setLayout(new FillLayout());
+
+ int style = useSkia ? SWT.DOUBLE_BUFFERED | SWT.SKIA : SWT.DOUBLE_BUFFERED;
+
+ Canvas canvas = new Canvas(shell, style);
+ canvas.addPaintListener(new PaintListener() {
+ @Override
+ public void paintControl(PaintEvent e) {
+ TextLayout layout = new TextLayout(display);
+ layout.setText("Hello, Skia TextLayout!\nMultiline and styled text.");
+ layout.setStyle(null, 0, 4); // Default style for 'Hello'
+ layout.setStyle(new org.eclipse.swt.graphics.TextStyle(
+ display.getSystemFont(), new Color(display, 0, 128, 255), null), 7, 15); // Style 'Skia Text'
+
+ e.gc.setBackground(display.getSystemColor(SWT.COLOR_GREEN));
+
+ e.gc.fillRectangle(e.x, e.y, e.width, e.height);
+
+ layout.draw(e.gc, 20, 20);
+ layout.dispose();
+ }
+ });
+
+ shell.setSize(400, 200);
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) display.sleep();
+ }
+ display.dispose();
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetTransformAndClipping.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetTransformAndClipping.java
new file mode 100644
index 00000000000..791307e2461
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetTransformAndClipping.java
@@ -0,0 +1,66 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Region;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetTransformAndClipping {
+
+ final static boolean USE_SKIA = true; // Set to true to use Skia rendering, false for default SWT rendering
+
+ public static void main(String[] args) {
+ final Display display = new Display();
+ final Shell shell = new Shell(display);
+ shell.setText("Snippet Transform");
+
+ var style = SWT.DOUBLE_BUFFERED | (USE_SKIA ? SWT.SKIA : SWT.NONE);
+
+ final Canvas canvas = new Canvas(shell,style);
+ canvas.setSize(400, 400);
+ shell.setSize(420, 440);
+
+ canvas.addListener(SWT.Paint, SnippetTransformAndClipping::onPaint);
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ display.dispose();
+ }
+
+ private static void onPaint(Event e) {
+
+ e.gc.setClipping(5, 5, 20, 20); // Set clipping region to canvas size
+ e.gc.setBackground(e.display.getSystemColor(SWT.COLOR_DARK_GRAY));
+ e.gc.fillRectangle(0, 0, 1000, 1000);
+
+ e.gc.setClipping((Region)null); // Remove clipping region
+
+ // Draw a blue rectangle without transform
+ e.gc.setBackground(e.display.getSystemColor(SWT.COLOR_BLUE));
+ e.gc.fillRectangle(50, 50, 100, 60);
+
+ // Apply a transform: translate and rotate
+ org.eclipse.swt.graphics.Transform transform = new org.eclipse.swt.graphics.Transform(e.display);
+ transform.rotate(30); // rotate 30 degrees
+ transform.translate(200, 200); // translate to (200, 200)
+ e.gc.setTransform(transform);
+
+
+ // Draw a red rectangle with transform
+ e.gc.setBackground(e.display.getSystemColor(SWT.COLOR_RED));
+ e.gc.fillRectangle(0, 0, 100, 60); // Centered at the origin after transform
+
+ e.gc.setClipping(50, 50, 20, 20); // Set clipping region to a small area
+ e.gc.setBackground(e.display.getSystemColor(SWT.COLOR_GREEN));
+ e.gc.fillRectangle(0, 0, 1000, 1000);
+
+ // Clean up
+ transform.dispose();
+ e.gc.setTransform(null); // Reset to default
+ }
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetTwoGlCanvasDrawing.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetTwoGlCanvasDrawing.java
new file mode 100644
index 00000000000..a7f7aa8146a
--- /dev/null
+++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/SnippetTwoGlCanvasDrawing.java
@@ -0,0 +1,140 @@
+package org.eclipse.swt.examples.skia;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Path;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Shell;
+
+public class SnippetTwoGlCanvasDrawing {
+
+ public static void main(String[] args) {
+ final Display display = new Display();
+ final Shell shell = new Shell(display);
+ shell.setText("Snippet Two Gl Canvas");
+
+ final Canvas c1 = new Canvas(shell, SWT.DOUBLE_BUFFERED | SWT.H_SCROLL | SWT.SKIA);
+ final Canvas c2 = new Canvas(shell, SWT.DOUBLE_BUFFERED | SWT.H_SCROLL | SWT.SKIA);
+
+ configureCanvas(shell, c1, 1, "SkiaGlCanvas");
+ configureCanvas(shell, c2, 2, "SkiaGlCanvas2");
+
+ shell.setSize(1500, 1000);
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+
+ }
+ }
+ display.dispose();
+ }
+
+ private static void configureCanvas(final Shell shell, final Canvas canvas, int index, String title) {
+ canvas.setSize(100, 100);
+ shell.addListener(SWT.Resize, e -> onResize(canvas, index));
+ canvas.addListener(SWT.Paint, e -> onPaint(e, title));
+ }
+
+ private static void onPaint(Event e, String title) {
+ final Display d = e.widget.getDisplay();
+ final GC gc = e.gc;
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_RED));
+ gc.drawRectangle(new Rectangle(0, 0, 100, 100));
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_BLUE));
+ gc.drawRectangle(new Rectangle(100, 0, 100, 100));
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_GREEN));
+ gc.drawRectangle(new Rectangle(0, 100, 100, 100));
+
+ final var img = d.getSystemImage(SWT.ICON_QUESTION);
+ gc.drawImage(img, 100, 100);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_RED));
+ gc.drawLine(200, 100, 100, 200);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_CYAN));
+ gc.drawFocus(200, 0, 100, 100);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_RED));
+ gc.drawArc(200, 100, 100, 100, 90, 200);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_BLACK));
+ gc.drawText(title, 100, 250);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_DARK_CYAN));
+ gc.drawOval(0, 300, 100, 50);
+
+ final Path p = new Path(d);
+ p.addRectangle(0, 400, 100, 100);
+ gc.setForeground(d.getSystemColor(SWT.COLOR_DARK_GREEN));
+ gc.drawPath(p);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_GRAY));
+ gc.drawRoundRectangle(0, 500, 100, 100, 50, 50);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_DARK_RED));
+ gc.drawPoint(5, 600);
+
+ final var bo = img.getBounds();
+ gc.drawImage(img, 0, 0, bo.width / 2, bo.height / 2, 20, 600, bo.width * 2, bo.height * 2);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_DARK_MAGENTA));
+ gc.drawText("Test\nTest2", 300, 0);
+ gc.drawText("Test2\nTest3", 300, 100, false);
+ gc.drawText("Transparent Text", 300, 200, true);
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_DARK_GRAY));
+ gc.drawPolygon(new int[] { 400, 2, //
+ 500, 100, //
+ 400, 100, //
+ 500, 2 //
+ });
+
+ gc.setForeground(d.getSystemColor(SWT.COLOR_DARK_YELLOW));
+ gc.drawPolyline(new int[] { 400, 50, //
+ 500, 100, //
+ 600, 200
+
+ });
+
+ gc.setBackground(d.getSystemColor(SWT.COLOR_DARK_YELLOW));
+ gc.fillArc(100, 300, 100, 100, 90, 200);
+
+ gc.setBackground(d.getSystemColor(SWT.COLOR_DARK_GREEN));
+ gc.fillGradientRectangle(100, 400, 100, 100, false);
+
+ gc.fillOval(100, 500, 100, 50);
+
+ final Path p2 = new Path(d);
+ p2.addRectangle(100, 600, 100, 100);
+ gc.fillPath(p2);
+
+ gc.fillPolygon(new int[] { 100, 700, //
+ 200, 800, //
+ 100, 800, //
+ 200, 700 //
+ });
+
+ gc.fillRectangle(new Rectangle(100, 800, 100, 100));
+
+ gc.fillRoundRectangle(200, 800, 100, 100, 20, 20);
+ }
+
+ private static void onResize(Canvas c, int index) {
+ final var ca = c.getShell().getClientArea();
+ switch (index) {
+ case 1 -> c.setBounds(new Rectangle(0, 0, ca.width / 2, ca.height));
+ case 2 -> c.setBounds(new Rectangle(ca.width / 2, 0, ca.width / 3, ca.height));
+ default -> {
+ /* nothing to do */ }
+ }
+ }
+
+}
diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/home_nav.gif b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/home_nav.gif
new file mode 100644
index 00000000000..4472e8ce5b3
Binary files /dev/null and b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/skia/home_nav.gif differ