RENDERING_HINTS = Map.of(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON, //
+ KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY, //
+ KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY, //
+ KEY_DITHERING, VALUE_DITHER_DISABLE, //
+ KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON, //
+ KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC, //
+ KEY_RENDERING, VALUE_RENDER_QUALITY, //
+ KEY_STROKE_CONTROL, VALUE_STROKE_PURE, //
+ KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON //
+ );
+
+ @Override
+ public ImageData rasterizeSVG(InputStream stream, float scalingFactor) throws IOException {
+ if (stream == null) {
+ throw new IllegalArgumentException("InputStream cannot be null");
+ }
+ if(svgLoader == null) {
+ svgLoader = new SVGLoader();
+ }
+ return rasterize(stream, scalingFactor);
+ }
+
+ @Override
+ public ImageData rasterizeDisabledSVG(InputStream stream, float scalingFactor) throws IOException {
+ if(svgLoader == null) {
+ svgLoader = new SVGLoader();
+ }
+ InputStream disabledStream = applyDisabledLook(stream);
+ return rasterize(disabledStream, scalingFactor);
+ }
+
+ @Override
+ public ImageData rasterizeGraySVG(InputStream stream, float scalingFactor) throws IOException {
+ if(svgLoader == null) {
+ svgLoader = new SVGLoader();
+ }
+ InputStream disabledStream = applyGrayLook(stream);
+ return rasterize(disabledStream, scalingFactor);
+ }
+
+ private ImageData rasterize(InputStream stream, float scalingFactor) throws IOException {
+ SVGDocument svgDocument = null;
+ stream.mark(Integer.MAX_VALUE);
+ InputStream nonClosingStream = new FilterInputStream(stream) {
+ @Override
+ public void close() throws IOException {
+ // Do nothing to prevent closing the underlying stream
+ }
+ };
+ svgDocument = svgLoader.load(nonClosingStream, null, LoaderContext.createDefault());
+ stream.reset();
+ if (svgDocument != null) {
+ FloatSize size = svgDocument.size();
+ double originalWidth = size.getWidth();
+ double originalHeight = size.getHeight();
+ int scaledWidth = (int) Math.round(originalWidth * scalingFactor);
+ int scaledHeight = (int) Math.round(originalHeight * scalingFactor);
+ BufferedImage image = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = image.createGraphics();
+ g.setRenderingHints(RENDERING_HINTS);
+ g.scale(scalingFactor, scalingFactor);
+ svgDocument.render(null, g);
+ g.dispose();
+ return convertToSWT(image);
+ }
+ return null;
+ }
+
+ private static InputStream applyDisabledLook(InputStream svgInputStream) throws IOException {
+ Document svgDocument = parseSVG(svgInputStream);
+ addDisabledFilter(svgDocument);
+ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+ writeSVG(svgDocument, outputStream);
+ return new ByteArrayInputStream(outputStream.toByteArray());
+ }
+ }
+
+ private static InputStream applyGrayLook(InputStream svgInputStream) throws IOException {
+ Document svgDocument = parseSVG(svgInputStream);
+ addGrayFilter(svgDocument);
+ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+ writeSVG(svgDocument, outputStream);
+ return new ByteArrayInputStream(outputStream.toByteArray());
+ }
+ }
+
+ private static Document parseSVG(InputStream inputStream) throws IOException {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder;
+ try {
+ builder = factory.newDocumentBuilder();
+ return builder.parse(inputStream);
+ } catch (SAXException | IOException | ParserConfigurationException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ private static void addDisabledFilter(Document document) {
+ addFilter(document, 0.64f, 0.4f);
+ }
+
+ private static void addGrayFilter(Document document) {
+ addFilter(document, 0.64f, 0.1f);
+ }
+
+ private static void addFilter(Document document, float slope, float intercept) {
+ Element defs = (Element) document.getElementsByTagName("defs").item(0);
+ if (defs == null) {
+ defs = document.createElement("defs");
+ document.getDocumentElement().appendChild(defs);
+ }
+
+ Element filter = document.createElement("filter");
+ filter.setAttribute("id", "customizedLook");
+
+ Element colorMatrix = document.createElement("feColorMatrix");
+ colorMatrix.setAttribute("type", "saturate");
+ colorMatrix.setAttribute("values", "0");
+ filter.appendChild(colorMatrix);
+
+ Element componentTransfer = document.createElement("feComponentTransfer");
+ for (String channel : new String[] { "R", "G", "B" }) {
+ Element func = document.createElement("feFunc" + channel);
+ func.setAttribute("type", "linear");
+ func.setAttribute("slope", Float.toString(slope));
+ func.setAttribute("intercept", Float.toString(intercept));
+ componentTransfer.appendChild(func);
+ }
+ filter.appendChild(componentTransfer);
+ defs.appendChild(filter);
+ document.getDocumentElement().setAttribute("filter", "url(#customizedLook)");
+ }
+
+ private static void writeSVG(Document document, OutputStream outputStream) throws IOException {
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer;
+ try {
+ transformer = transformerFactory.newTransformer();
+ transformer.transform(new DOMSource(document), new StreamResult(outputStream));
+ } catch (TransformerException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ private ImageData convertToSWT(BufferedImage bufferedImage) {
+ if (bufferedImage.getColorModel() instanceof DirectColorModel) {
+ DirectColorModel colorModel = (DirectColorModel)bufferedImage.getColorModel();
+ PaletteData palette = new PaletteData(
+ colorModel.getRedMask(),
+ colorModel.getGreenMask(),
+ colorModel.getBlueMask());
+ ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(),
+ colorModel.getPixelSize(), palette);
+ for (int y = 0; y < data.height; y++) {
+ for (int x = 0; x < data.width; x++) {
+ int rgb = bufferedImage.getRGB(x, y);
+ int pixel = palette.getPixel(new RGB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF));
+ data.setPixel(x, y, pixel);
+ if (colorModel.hasAlpha()) {
+ data.setAlpha(x, y, (rgb >> 24) & 0xFF);
+ }
+ }
+ }
+ return data;
+ }
+ else if (bufferedImage.getColorModel() instanceof IndexColorModel) {
+ IndexColorModel colorModel = (IndexColorModel)bufferedImage.getColorModel();
+ int size = colorModel.getMapSize();
+ byte[] reds = new byte[size];
+ byte[] greens = new byte[size];
+ byte[] blues = new byte[size];
+ colorModel.getReds(reds);
+ colorModel.getGreens(greens);
+ colorModel.getBlues(blues);
+ RGB[] rgbs = new RGB[size];
+ for (int i = 0; i < rgbs.length; i++) {
+ rgbs[i] = new RGB(reds[i] & 0xFF, greens[i] & 0xFF, blues[i] & 0xFF);
+ }
+ PaletteData palette = new PaletteData(rgbs);
+ ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(),
+ colorModel.getPixelSize(), palette);
+ data.transparentPixel = colorModel.getTransparentPixel();
+ WritableRaster raster = bufferedImage.getRaster();
+ int[] pixelArray = new int[1];
+ for (int y = 0; y < data.height; y++) {
+ for (int x = 0; x < data.width; x++) {
+ raster.getPixel(x, y, pixelArray);
+ data.setPixel(x, y, pixelArray[0]);
+ }
+ }
+ return data;
+ }
+ else if (bufferedImage.getColorModel() instanceof ComponentColorModel) {
+ ComponentColorModel colorModel = (ComponentColorModel)bufferedImage.getColorModel();
+ //ASSUMES: 3 BYTE BGR IMAGE TYPE
+ PaletteData palette = new PaletteData(0x0000FF, 0x00FF00,0xFF0000);
+ ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(),
+ colorModel.getPixelSize(), palette);
+ //This is valid because we are using a 3-byte Data model with no transparent pixels
+ data.transparentPixel = -1;
+ WritableRaster raster = bufferedImage.getRaster();
+ int[] pixelArray = new int[3];
+ for (int y = 0; y < data.height; y++) {
+ for (int x = 0; x < data.width; x++) {
+ raster.getPixel(x, y, pixelArray);
+ int pixel = palette.getPixel(new RGB(pixelArray[0], pixelArray[1], pixelArray[2]));
+ data.setPixel(x, y, pixel);
+ }
+ }
+ return data;
+ }
+ return null;
+ }
+
+ public boolean isSVGFile(InputStream stream) throws IOException {
+ if (stream == null) {
+ throw new IllegalArgumentException("InputStream cannot be null");
+ }
+ stream.mark(Integer.MAX_VALUE);
+ try {
+ int firstByte = stream.read();
+ return firstByte == '<';
+ } finally {
+ stream.reset();
+ }
+ }
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/ImageLoader.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/ImageLoader.java
index 5b063ab2e78..f414eadf7c0 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/ImageLoader.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/ImageLoader.java
@@ -14,9 +14,12 @@
package org.eclipse.swt.graphics;
+import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
+import javax.imageio.ImageIO;
+
import org.eclipse.swt.*;
import org.eclipse.swt.internal.image.*;
@@ -149,10 +152,72 @@ void reset() {
*
*/
public ImageData[] load(InputStream stream) {
+ return loadDefault(stream);
+}
+
+/**
+ * Loads an array of ImageData
objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of ImageData
objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException
+ * - ERROR_NULL_ARGUMENT - if the stream is null
+ *
+ * @exception SWTException
+ * - ERROR_IO - if an IO error occurs while reading from the stream
+ * - ERROR_INVALID_IMAGE - if the image stream contains invalid data
+ * - ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format
+ *
+ *
+ * @since 4.0
+ */
+public ImageData[] load(InputStream stream, int zoom, int flag) {
+ if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ reset();
+ if (!stream.markSupported()) {
+ stream = new BufferedInputStream(stream);
+ }
+ ImageData rasterizedData = null;
+ SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+ if (rasterizer != null && zoom != 0) {
+ try {
+ if (rasterizer.isSVGFile(stream)) {
+ float scalingFactor = zoom / 100.0f;
+ switch(flag) {
+ case SWT.IMAGE_DISABLE:
+ rasterizedData = rasterizer.rasterizeDisabledSVG(stream, scalingFactor);
+ break;
+ case SWT.IMAGE_GRAY:
+ rasterizedData = rasterizer.rasterizeGraySVG(stream, scalingFactor);
+ break;
+ case SWT.IMAGE_COPY:
+ rasterizedData = rasterizer.rasterizeSVG(stream, scalingFactor);
+ break;
+ }
+ if (rasterizedData != null) {
+ data = new ImageData[]{rasterizedData};
+ return data;
+ }
+ }
+ } catch (IOException e) {
+ //ignore.
+ }
+ }
+ return loadDefault(stream);
+}
+
+private ImageData[] loadDefault(InputStream stream) {
if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
reset();
data = FileFormat.load(stream, this);
- return data;
+ return data;
}
/**
@@ -175,18 +240,43 @@ public ImageData[] load(InputStream stream) {
*/
public ImageData[] load(String filename) {
if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
- InputStream stream = null;
- try {
- stream = new FileInputStream(filename);
- return load(stream);
+ try (InputStream stream = new FileInputStream(filename)) {
+ return loadDefault(stream);
+ } catch (IOException e) {
+ SWT.error(SWT.ERROR_IO, e);
+ }
+ return null;
+}
+
+/**
+ * Loads an array of ImageData
objects from the
+ * file with the specified name. If the filename is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG. Throws an error if either
+ * an error occurs while loading the images, or if the images are
+ * not of a supported type. Returns the loaded image data array.
+ *
+ * @param filename the name of the file to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of ImageData
objects loaded from the specified file
+ *
+ * @exception IllegalArgumentException
+ * - ERROR_NULL_ARGUMENT - if the file name is null
+ *
+ * @exception SWTException
+ * - ERROR_IO - if an IO error occurs while reading from the file
+ * - ERROR_INVALID_IMAGE - if the image file contains invalid data
+ * - ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format
+ *
+ *
+ * @since 4.0
+ */
+public ImageData[] load(String filename, int zoom, int flag) {
+ if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ try (InputStream stream = new FileInputStream(filename)) {
+ return load(stream, zoom, flag);
} catch (IOException e) {
SWT.error(SWT.ERROR_IO, e);
- } finally {
- try {
- if (stream != null) stream.close();
- } catch (IOException e) {
- // Ignore error
- }
}
return null;
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageData.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageData.java
index c268e5caaed..842c978c63a 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageData.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageData.java
@@ -331,7 +331,39 @@ scanlinePad, checkData(data), 0, null,
* @see ImageLoader#load(InputStream)
*/
public ImageData(InputStream stream) {
- ImageData[] data = ImageDataLoader.load(stream);
+ this(stream, 0, SWT.IMAGE_COPY);
+}
+
+/**
+ * Constructs an ImageData
loaded from the specified
+ * input stream. Throws an error if an error occurs while loading
+ * the image, or if the image has an unsupported type. Application
+ * code is still responsible for closing the input stream.
+ *
+ * This constructor is provided for convenience when loading a single
+ * image only. If the stream contains multiple images, only the first
+ * one will be loaded. To load multiple images, use
+ * ImageLoader.load()
.
+ *
+ *
+ * @param stream the input stream to load the image from (must not be null)
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ *
+ * @exception IllegalArgumentException
+ * - ERROR_NULL_ARGUMENT - if the stream is null
+ *
+ * @exception SWTException
+ * - ERROR_IO - if an IO error occurs while reading from the stream
+ * - ERROR_INVALID_IMAGE - if the image stream contains invalid data
+ * - ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format
+ *
+ *
+ * @see ImageLoader#load(InputStream)
+ * @since 4.0
+ */
+public ImageData(InputStream stream, int zoom, int flag) {
+ ImageData[] data = ImageDataLoader.load(stream, zoom, flag);
if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
ImageData i = data[0];
setAllFields(
@@ -377,7 +409,36 @@ public ImageData(InputStream stream) {
*
*/
public ImageData(String filename) {
- ImageData[] data = ImageDataLoader.load(filename);
+ this(filename, 0, SWT.IMAGE_COPY);
+}
+
+/**
+ * Constructs an ImageData
loaded from a file with the
+ * specified name. Throws an error if an error occurs loading the
+ * image, or if the image has an unsupported type.
+ *
+ * This constructor is provided for convenience when loading a single
+ * image only. If the file contains multiple images, only the first
+ * one will be loaded. To load multiple images, use
+ * ImageLoader.load()
.
+ *
+ *
+ * @param filename the name of the file to load the image from (must not be null)
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @exception IllegalArgumentException
+ * - ERROR_NULL_ARGUMENT - if the file name is null
+ *
+ * @exception SWTException
+ * - ERROR_IO - if an IO error occurs while reading from the file
+ * - ERROR_INVALID_IMAGE - if the image file contains invalid data
+ * - ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format
+ *
+ *
+ * @since 4.0
+ */
+public ImageData(String filename, int zoom, int flag) {
+ ImageData[] data = ImageDataLoader.load(filename, zoom, flag);
if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
ImageData i = data[0];
setAllFields(
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataLoader.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataLoader.java
index b1fe23d2472..ebf1979e8f4 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataLoader.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataLoader.java
@@ -25,8 +25,16 @@ public static ImageData[] load(InputStream stream) {
return new ImageLoader().load(stream);
}
+ public static ImageData[] load(InputStream stream, int zoom, int flag) {
+ return new ImageLoader().load(stream, zoom, flag);
+ }
+
public static ImageData[] load(String filename) {
return new ImageLoader().load(filename);
}
+ public static ImageData[] load(String filename, int zoom, int flag) {
+ return new ImageLoader().load(filename, zoom, flag);
+ }
+
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataProvider.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataProvider.java
index bfa0ec70a73..bf1c5a9f62b 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataProvider.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageDataProvider.java
@@ -25,7 +25,6 @@
*/
public interface ImageDataProvider {
-
/**
* Returns the image data for the given zoom level.
*
@@ -43,4 +42,17 @@ public interface ImageDataProvider {
*/
ImageData getImageData (int zoom);
+ /**
+ * @since 4.0
+ */
+ default ImageData getCustomizedImageData(int zoom, int flag) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @since 4.0
+ */
+ default boolean supportsRasterizationFlag(int flag) {
+ return false;
+ }
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizer.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizer.java
new file mode 100644
index 00000000000..4774a2f2f0a
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizer.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH 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: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.io.*;
+
+/**
+ * Defines the interface for an SVG rasterizer, responsible for converting SVG
+ * data into rasterized images.
+ *
+ * @since 4.0
+ */
+public interface SVGRasterizer {
+
+ /**
+ * Rasterizes an SVG image from the provided byte array, using the specified
+ * zoom factor.
+ *
+ * @param stream the SVG image as an {@link InputStream}.
+ * @param scalingFactor the scaling ratio e.g. 2.0 for doubled size.
+ * @return the {@link ImageData} for the rasterized image, or
+ * {@code null} if the input is not a valid SVG file or cannot be
+ * processed.
+ * @throws IOException if an error occurs while reading the SVG data.
+ */
+ public ImageData rasterizeSVG(InputStream stream, float scalingFactor) throws IOException;
+
+ public ImageData rasterizeDisabledSVG(InputStream stream, float scalingFactor) throws IOException;
+
+ public ImageData rasterizeGraySVG(InputStream stream, float scalingFactor) throws IOException;
+
+ /**
+ * Determines whether the given {@link InputStream} contains a SVG file.
+ *
+ * @param stream the input stream to check.
+ * @return {@code true} if the input stream contains SVG content; {@code false}
+ * otherwise.
+ * @throws IOException if an error occurs while reading the stream.
+ * @throws IllegalArgumentException if the input stream is {@code null}.
+ */
+ public boolean isSVGFile(InputStream stream) throws IOException;
+ //TODO: use optional above instead?
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizerRegistry.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizerRegistry.java
new file mode 100644
index 00000000000..e088d1ccdd0
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/SVGRasterizerRegistry.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Vector Informatik GmbH 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: Michael Bangas (Vector Informatik GmbH) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import java.util.*;
+
+/**
+ * A registry for managing the instance of an {@link SVGRasterizer} implementation.
+ * This allows for the registration and retrieval of a single rasterizer instance.
+ *
+ * @since 4.0
+ */
+class SVGRasterizerRegistry {
+
+ /**
+ * The instance of the registered {@link SVGRasterizer}.
+ */
+ private static final SVGRasterizer RASTERIZER = ServiceLoader.load(SVGRasterizer.class).findFirst().orElse(null);
+
+ /**
+ * Retrieves the currently registered {@link SVGRasterizer} implementation.
+ *
+ * @return the registered {@link SVGRasterizer}, or {@code null} if no implementation
+ * has been registered.
+ */
+ public static SVGRasterizer getRasterizer() {
+ return RASTERIZER;
+ }
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java
index 89b97cd86e6..c7d74c0c52a 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java
@@ -15,10 +15,13 @@
package org.eclipse.swt.graphics;
+import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
import java.util.List;
+import javax.imageio.ImageIO;
+
import org.eclipse.swt.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.gtk.*;
@@ -159,11 +162,72 @@ void reset() {
*
*/
public ImageData[] load(InputStream stream) {
+ return loadDefault(stream);
+}
+
+/**
+ * Loads an array of ImageData
objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of ImageData
objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException
+ * - ERROR_NULL_ARGUMENT - if the stream is null
+ *
+ * @exception SWTException
+ * - ERROR_IO - if an IO error occurs while reading from the stream
+ * - ERROR_INVALID_IMAGE - if the image stream contains invalid data
+ * - ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format
+ *
+ *
+ * @since 4.0
+ */
+public ImageData[] load(InputStream stream, int zoom, int flag) {
+ if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ reset();
+ if (!stream.markSupported()) {
+ stream = new BufferedInputStream(stream);
+ }
+ ImageData rasterizedData = null;
+ SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+ if (rasterizer != null && zoom != 0) {
+ try {
+ if (rasterizer.isSVGFile(stream)) {
+ float scalingFactor = zoom / 100.0f;
+ switch(flag) {
+ case SWT.IMAGE_DISABLE:
+ rasterizedData = rasterizer.rasterizeDisabledSVG(stream, scalingFactor);
+ break;
+ case SWT.IMAGE_GRAY:
+ rasterizedData = rasterizer.rasterizeGraySVG(stream, scalingFactor);
+ break;
+ case SWT.IMAGE_COPY:
+ rasterizedData = rasterizer.rasterizeSVG(stream, scalingFactor);
+ break;
+ }
+ if (rasterizedData != null) {
+ data = new ImageData[]{rasterizedData};
+ return data;
+ }
+ }
+ } catch (IOException e) {
+ //ignore.
+ }
+ }
+ return loadDefault(stream);
+}
+
+private ImageData[] loadDefault(InputStream stream) {
if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
reset();
- ImageData [] imgDataArray = getImageDataArrayFromStream(stream);
- data = imgDataArray;
- return imgDataArray;
+ data = getImageDataArrayFromStream(stream);
+ return data;
}
/**
@@ -294,18 +358,43 @@ boolean isInterlacedPNG(byte [] imageAsByteArray) {
*/
public ImageData[] load(String filename) {
if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
- InputStream stream = null;
- try {
- stream = new FileInputStream(filename);
- return load(stream);
+ try (InputStream stream = new FileInputStream(filename)) {
+ return loadDefault(stream);
+ } catch (IOException e) {
+ SWT.error(SWT.ERROR_IO, e);
+ }
+ return null;
+}
+
+/**
+ * Loads an array of ImageData
objects from the
+ * file with the specified name. If the filename is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG. Throws an error if either
+ * an error occurs while loading the images, or if the images are
+ * not of a supported type. Returns the loaded image data array.
+ *
+ * @param filename the name of the file to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of ImageData
objects loaded from the specified file
+ *
+ * @exception IllegalArgumentException
+ * - ERROR_NULL_ARGUMENT - if the file name is null
+ *
+ * @exception SWTException
+ * - ERROR_IO - if an IO error occurs while reading from the file
+ * - ERROR_INVALID_IMAGE - if the image file contains invalid data
+ * - ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format
+ *
+ *
+ * @since 4.0
+ */
+public ImageData[] load(String filename, int zoom, int flag) {
+ if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ try (InputStream stream = new FileInputStream(filename)) {
+ return load(stream, zoom, flag);
} catch (IOException e) {
SWT.error(SWT.ERROR_IO, e);
- } finally {
- try {
- if (stream != null) stream.close();
- } catch (IOException e) {
- // Ignore error
- }
}
return null;
}
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 c4013a30a08..80ca08147c4 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
@@ -235,6 +235,14 @@ public Image(Device device, Image srcImage, int flag) {
long srcImageHandle = win32_getHandle(srcImage, getZoom());
switch (flag) {
case SWT.IMAGE_COPY: {
+ ImageData data = null;
+ Image newImage = createWithSVG(device, flag);
+ if(newImage != null) {
+ data = newImage.getImageData(newImage.getZoom());
+ init(data, getZoom());
+ newImage.dispose();
+ break;
+ }
switch (type) {
case SWT.BITMAP:
/* Get the HDC for the device */
@@ -270,13 +278,29 @@ public Image(Device device, Image srcImage, int flag) {
break;
}
case SWT.IMAGE_DISABLE: {
- ImageData data = srcImage.getImageData(srcImage.getZoom());
+ ImageData data = null;
+ Image disabledImage = createWithSVG(device, flag);
+ if(disabledImage != null) {
+ data = disabledImage.getImageData(disabledImage.getZoom());
+ init(data, getZoom());
+ disabledImage.dispose();
+ break;
+ }
+ data = srcImage.getImageData(srcImage.getZoom());
ImageData newData = applyDisableImageData(data, rect.height, rect.width);
init (newData, getZoom());
break;
}
case SWT.IMAGE_GRAY: {
- ImageData data = srcImage.getImageData(srcImage.getZoom());
+ ImageData data = null;
+ Image grayImage = createWithSVG(device, flag);
+ if(grayImage != null) {
+ data = grayImage.getImageData(grayImage.getZoom());
+ init(data, getZoom());
+ grayImage.dispose();
+ break;
+ }
+ data = srcImage.getImageData(srcImage.getZoom());
ImageData newData = applyGrayImageData(data, rect.height, rect.width);
init (newData, getZoom());
break;
@@ -288,6 +312,26 @@ public Image(Device device, Image srcImage, int flag) {
this.device.registerResourceWithZoomSupport(this);
}
+private Image createWithSVG(Device device, int flag) {
+ Image customizedImage = null;
+ if (imageFileNameProvider != null) {
+ ElementAtZoom fileName = DPIUtil.validateAndGetImagePathAtZoom (imageFileNameProvider, getZoom());
+ try (InputStream stream = new BufferedInputStream(new FileInputStream(fileName.element()))){
+ SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+ if(rasterizer != null && rasterizer.isSVGFile(stream)) {
+ customizedImage = new Image(device, imageFileNameProvider, flag);
+ }
+ } catch (IOException e) {
+ SWT.error(SWT.ERROR_IO, e);
+ }
+ } else if (imageDataProvider != null) {
+ if(imageDataProvider.supportsRasterizationFlag(flag)) {
+ customizedImage = new Image(device, imageDataProvider, flag);
+ }
+ }
+ return customizedImage;
+}
+
/**
* Constructs an empty instance of this class with the
* width and height of the specified rectangle. The result
@@ -468,7 +512,7 @@ public Image(Device device, ImageData source, ImageData mask) {
public Image (Device device, InputStream stream) {
super(device);
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
- ImageData data = DPIUtil.autoScaleUp(device, new ElementAtZoom<>(new ImageData (stream), 100));
+ ImageData data = DPIUtil.autoScaleUp(device, new ElementAtZoom<>(new ImageData (stream, getZoom(), SWT.IMAGE_COPY), 100));
init(data, getZoom());
init();
this.device.registerResourceWithZoomSupport(this);
@@ -510,7 +554,7 @@ public Image (Device device, String filename) {
super(device);
if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
- ImageData data = DPIUtil.autoScaleUp(device, new ElementAtZoom<>(new ImageData (filename), 100));
+ ImageData data = DPIUtil.autoScaleUp(device, new ElementAtZoom<>(new ImageData (filename, getZoom(), SWT.IMAGE_COPY), 100));
init(data, getZoom());
init();
this.device.registerResourceWithZoomSupport(this);
@@ -546,6 +590,13 @@ public Image (Device device, String filename) {
* @since 3.104
*/
public Image(Device device, ImageFileNameProvider imageFileNameProvider) {
+ this(device, imageFileNameProvider, SWT.IMAGE_COPY);
+}
+
+/**
+ * @since 4.0
+ */
+public Image(Device device, ImageFileNameProvider imageFileNameProvider, int flag) {
super(device);
this.imageFileNameProvider = imageFileNameProvider;
initialNativeZoom = DPIUtil.getNativeDeviceZoom();
@@ -553,10 +604,10 @@ public Image(Device device, ImageFileNameProvider imageFileNameProvider) {
if (fileName.zoom() == getZoom()) {
ImageHandle imageMetadata = initNative (fileName.element(), getZoom());
if (imageMetadata == null) {
- init(new ImageData (fileName.element()), getZoom());
+ init(new ImageData (fileName.element(), getZoom(), flag), getZoom());
}
} else {
- ImageData resizedData = DPIUtil.autoScaleImageData (device, new ImageData (fileName.element()), fileName.zoom());
+ ImageData resizedData = DPIUtil.autoScaleImageData (device, new ImageData (fileName.element(), getZoom(), flag), fileName.zoom());
init(resizedData, getZoom());
}
init();
@@ -603,6 +654,16 @@ public Image(Device device, ImageDataProvider imageDataProvider) {
this.device.registerResourceWithZoomSupport(this);
}
+private Image(Device device, ImageDataProvider imageDataProvider, int flag) {
+ super(device);
+ this.imageDataProvider = imageDataProvider;
+ initialNativeZoom = DPIUtil.getNativeDeviceZoom();
+ ImageData data = imageDataProvider.getCustomizedImageData(getZoom(), flag);
+ init (data, getZoom());
+ init();
+ this.device.registerResourceWithZoomSupport(this);
+}
+
private ImageData adaptImageDataIfDisabledOrGray(ImageData data) {
ImageData returnImageData = null;
switch (this.styleFlag) {
@@ -753,7 +814,7 @@ private ImageHandle getImageMetadata(int zoom) {
if (imageFileNameProvider != null) {
ElementAtZoom imageCandidate = DPIUtil.validateAndGetImagePathAtZoom (imageFileNameProvider, zoom);
- ImageData imageData = new ImageData (imageCandidate.element());
+ ImageData imageData = new ImageData (imageCandidate.element(), zoom, SWT.IMAGE_COPY);
if (imageCandidate.zoom() == zoom) {
/* Release current native resources */
ImageHandle imageMetadata = initNative(imageCandidate.element(), zoom);
@@ -1389,7 +1450,7 @@ public ImageData getImageData (int zoom) {
return DPIUtil.scaleImageData (device, data.element(), zoom, data.zoom());
} else if (imageFileNameProvider != null) {
ElementAtZoom fileName = DPIUtil.validateAndGetImagePathAtZoom (imageFileNameProvider, zoom);
- return DPIUtil.scaleImageData (device, new ImageData (fileName.element()), zoom, fileName.zoom());
+ return DPIUtil.scaleImageData (device, new ImageData (fileName.element(), zoom, SWT.IMAGE_COPY), zoom, fileName.zoom());
}
// if a GC is initialized with an Image (memGC != null), the image data must not be resized, because it would
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
index a8e4c2f684c..b1b8780b419 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
@@ -149,6 +149,68 @@ void reset() {
*
*/
public ImageData[] load(InputStream stream) {
+ return loadDefault(stream);
+}
+
+/**
+ * Loads an array of ImageData
objects from the
+ * specified input stream. If the stream is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG.
+ * Throws an error if either an error occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of ImageData
objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException
+ * - ERROR_NULL_ARGUMENT - if the stream is null
+ *
+ * @exception SWTException
+ * - ERROR_IO - if an IO error occurs while reading from the stream
+ * - ERROR_INVALID_IMAGE - if the image stream contains invalid data
+ * - ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format
+ *
+ *
+ * @since 4.0
+ */
+public ImageData[] load(InputStream stream, int zoom, int flag) {
+ if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ reset();
+ if (!stream.markSupported()) {
+ stream = new BufferedInputStream(stream);
+ }
+ ImageData rasterizedData = null;
+ SVGRasterizer rasterizer = SVGRasterizerRegistry.getRasterizer();
+ if (rasterizer != null && zoom != 0) {
+ try {
+ if (rasterizer.isSVGFile(stream)) {
+ float scalingFactor = zoom / 100.0f;
+ switch(flag) {
+ case SWT.IMAGE_DISABLE:
+ rasterizedData = rasterizer.rasterizeDisabledSVG(stream, scalingFactor);
+ break;
+ case SWT.IMAGE_GRAY:
+ rasterizedData = rasterizer.rasterizeGraySVG(stream, scalingFactor);
+ break;
+ case SWT.IMAGE_COPY:
+ rasterizedData = rasterizer.rasterizeSVG(stream, scalingFactor);
+ break;
+ }
+ if (rasterizedData != null) {
+ data = new ImageData[]{rasterizedData};
+ return data;
+ }
+ }
+ } catch (IOException e) {
+ //ignore.
+ }
+ }
+ return loadDefault(stream);
+}
+
+private ImageData[] loadDefault(InputStream stream) {
if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
reset();
data = FileFormat.load(stream, this);
@@ -176,7 +238,40 @@ public ImageData[] load(InputStream stream) {
public ImageData[] load(String filename) {
if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
try (InputStream stream = new FileInputStream(filename)) {
- return load(stream);
+ return loadDefault(stream);
+ } catch (IOException e) {
+ SWT.error(SWT.ERROR_IO, e);
+ }
+ return null;
+}
+
+/**
+ * Loads an array of ImageData
objects from the
+ * file with the specified name. If the filename is a SVG File and zoom is not 0,
+ * this method will try to rasterize the SVG. Throws an error if either
+ * an error occurs while loading the images, or if the images are
+ * not of a supported type. Returns the loaded image data array.
+ *
+ * @param filename the name of the file to load the images from
+ * @param zoom the zoom factor to apply when rasterizing a SVG.
+ * A value of 0 means that the standard method for loading should be used.
+ * @return an array of ImageData
objects loaded from the specified file
+ *
+ * @exception IllegalArgumentException
+ * - ERROR_NULL_ARGUMENT - if the file name is null
+ *
+ * @exception SWTException
+ * - ERROR_IO - if an IO error occurs while reading from the file
+ * - ERROR_INVALID_IMAGE - if the image file contains invalid data
+ * - ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format
+ *
+ *
+ * @since 4.0
+ */
+public ImageData[] load(String filename, int zoom, int flag) {
+ if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ try (InputStream stream = new FileInputStream(filename)) {
+ return load(stream, zoom, flag);
} catch (IOException e) {
SWT.error(SWT.ERROR_IO, e);
}