-
Notifications
You must be signed in to change notification settings - Fork 143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature Proposal: Rasterization of SVGs at Runtime for Eclipse Icons #1638
Open
Michael5601
wants to merge
8
commits into
eclipse-platform:master
Choose a base branch
from
Michael5601:IconScalingBackup
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+679
−35
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
0266fb9
Introduce SVG rasterizer logic
Michael5601 ffa17e4
Refactor names of SVGRasterizer and ISVGRasterizer
Michael5601 9648549
Remove SVGUtil and move utility methods into JSVGRasterizer
Michael5601 3bc3621
move SVG-File check out of JSVGRasterizer and adjust check
Michael5601 703913e
JSVGRasterizer now only accepts InputStream instead of byte array
Michael5601 1ccfe65
fix license info
Michael5601 b5365d8
Utilize mark and reset for stream handling
Michael5601 4aead69
update cocoa and gtk according to win32
Michael5601 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<classpath> | ||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/> | ||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> | ||
<classpathentry kind="src" path="src"/> | ||
<classpathentry kind="lib" path="libs/jsvg-1.6.1.jar"/> | ||
<classpathentry kind="output" path="bin"/> | ||
</classpath> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<projectDescription> | ||
<name>org.eclipse.swt.svg</name> | ||
<comment></comment> | ||
<projects> | ||
</projects> | ||
<buildSpec> | ||
<buildCommand> | ||
<name>org.eclipse.jdt.core.javabuilder</name> | ||
<arguments> | ||
</arguments> | ||
</buildCommand> | ||
<buildCommand> | ||
<name>org.eclipse.pde.ManifestBuilder</name> | ||
<arguments> | ||
</arguments> | ||
</buildCommand> | ||
<buildCommand> | ||
<name>org.eclipse.pde.SchemaBuilder</name> | ||
<arguments> | ||
</arguments> | ||
</buildCommand> | ||
</buildSpec> | ||
<natures> | ||
<nature>org.eclipse.pde.PluginNature</nature> | ||
<nature>org.eclipse.jdt.core.javanature</nature> | ||
</natures> | ||
</projectDescription> |
2 changes: 2 additions & 0 deletions
2
bundles/org.eclipse.swt.svg/.settings/org.eclipse.core.resources.prefs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
eclipse.preferences.version=1 | ||
encoding/<project>=UTF-8 |
9 changes: 9 additions & 0 deletions
9
bundles/org.eclipse.swt.svg/.settings/org.eclipse.jdt.core.prefs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
eclipse.preferences.version=1 | ||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 | ||
org.eclipse.jdt.core.compiler.compliance=17 | ||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error | ||
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled | ||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error | ||
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning | ||
org.eclipse.jdt.core.compiler.release=enabled | ||
org.eclipse.jdt.core.compiler.source=17 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,10 @@ | ||||||||||
Manifest-Version: 1.0 | ||||||||||
Bundle-ManifestVersion: 2 | ||||||||||
Bundle-Name: SvgPlugin | ||||||||||
Bundle-SymbolicName: org.eclipse.swt.svg | ||||||||||
Bundle-Version: 1.0.0.qualifier | ||||||||||
Automatic-Module-Name: org.eclipse.swt.svgPlugin | ||||||||||
Bundle-RequiredExecutionEnvironment: JavaSE-17 | ||||||||||
Export-Package: org.eclipse.swt.svg | ||||||||||
Import-Package: org.eclipse.swt.graphics | ||||||||||
Comment on lines
+8
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
Bundle-ClassPath: ., libs/jsvg-1.6.1.jar |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
source.. = src/ | ||
output.. = bin/ | ||
bin.includes = META-INF/,\ | ||
.,\ | ||
libs/jsvg-1.6.1.jar |
Binary file not shown.
176 changes: 176 additions & 0 deletions
176
bundles/org.eclipse.swt.svg/src/org/eclipse/swt/svg/JSVGRasterizer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
/******************************************************************************* | ||
* 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.svg; | ||
|
||
import static java.awt.RenderingHints.*; | ||
|
||
import java.awt.*; | ||
import java.awt.image.*; | ||
import java.io.*; | ||
import java.util.*; | ||
import org.eclipse.swt.graphics.SVGRasterizer; | ||
import org.eclipse.swt.graphics.ImageData; | ||
import org.eclipse.swt.graphics.PaletteData; | ||
import org.eclipse.swt.graphics.RGB; | ||
import org.eclipse.swt.graphics.SVGRasterizerRegistry; | ||
|
||
import com.github.weisj.jsvg.*; | ||
import com.github.weisj.jsvg.geometry.size.*; | ||
import com.github.weisj.jsvg.parser.*; | ||
|
||
/** | ||
* A rasterizer implementation for converting SVG data into rasterized images. | ||
* This class implements the {@code ISVGRasterizer} interface. | ||
* | ||
* @since 1.0.0 | ||
*/ | ||
public class JSVGRasterizer implements SVGRasterizer { | ||
|
||
private SVGLoader svgLoader; | ||
|
||
/** | ||
* Initializes the SVG rasterizer by registering an instance of this rasterizer | ||
* with the {@link SVGRasterizerRegistry}. | ||
*/ | ||
public static void intializeJSVGRasterizer() { | ||
SVGRasterizerRegistry.register(new JSVGRasterizer()); | ||
} | ||
|
||
private final static Map<Key, Object> 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"); | ||
} | ||
stream.mark(Integer.MAX_VALUE); | ||
if(svgLoader == null) { | ||
svgLoader = new SVGLoader(); | ||
} | ||
SVGDocument svgDocument = null; | ||
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 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(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes this project on o.e.swt.win32.win32.x86_64 and adds the latter to the classpath of the former, similar like how it's happening at runtime.
Of course this now only works for win32.x86_64. I also added other swt-fragments, but it looks like this is not optional and on a first sight I didn't found a way to make it optional. But it is possible to turn incomplete-classpath errors into warnings by adding
org.eclipse.jdt.core.incompleteClasspath=warning
to.settings/org.eclipse.jdt.core.prefs
.And to make it work in the build it is hopefully sufficient to add a pom.xml to the project where, depending on the running platform, an explicit dependency to the corresponding native fragment is added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to admit that I am not very experienced with this topic. It seems you have an idea how this could work with fragments if I read it right. This would be very nice. It would be great if we could have a call where you can explain this approach to me.