Replace svgSalamander with jsvg

This commit is contained in:
Jannis Weis
2023-05-29 16:30:12 +02:00
parent b391465fbf
commit a40b837634
5 changed files with 100 additions and 84 deletions

View File

@@ -23,7 +23,7 @@ plugins {
dependencies { dependencies {
implementation( project( ":flatlaf-core" ) ) implementation( project( ":flatlaf-core" ) )
implementation( libs.svgSalamander ) implementation( libs.jsvg )
} }
flatlafModuleInfo { flatlafModuleInfo {

View File

@@ -30,6 +30,7 @@ import java.awt.image.RGBImageFilter;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
@@ -49,9 +50,9 @@ import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.MultiResolutionImageSupport; import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import com.formdev.flatlaf.util.SoftCache; import com.formdev.flatlaf.util.SoftCache;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
import com.kitfox.svg.SVGDiagram; import com.github.weisj.jsvg.SVGDocument;
import com.kitfox.svg.SVGException; import com.github.weisj.jsvg.geometry.size.FloatSize;
import com.kitfox.svg.SVGUniverse; import com.github.weisj.jsvg.parser.SVGLoader;
/** /**
* An icon that loads and paints SVG. * An icon that loads and paints SVG.
@@ -62,11 +63,9 @@ public class FlatSVGIcon
extends ImageIcon extends ImageIcon
implements DisabledIconProvider implements DisabledIconProvider
{ {
// cache that uses soft references for values, which allows freeing SVG diagrams if no longer used // cache that uses soft references for values, which allows freeing SVG documents if no longer used
private static final SoftCache<URI, SVGDiagram> svgCache = new SoftCache<>(); private static final SoftCache<URI, SVGDocument> svgCache = new SoftCache<>();
private static final SVGLoader svgLoader = new SVGLoader();
// use own SVG universe so that it can not be cleared from anywhere
private static final SVGUniverse svgUniverse = new SVGUniverse();
private static int streamNumber; private static int streamNumber;
private final String name; private final String name;
@@ -79,7 +78,7 @@ public class FlatSVGIcon
private ColorFilter colorFilter; private ColorFilter colorFilter;
private SVGDiagram diagram; private SVGDocument document;
private boolean dark; private boolean dark;
private boolean loadFailed; private boolean loadFailed;
@@ -270,7 +269,7 @@ public class FlatSVGIcon
this( null, -1, -1, 1, false, null, loadFromStream( in ) ); this( null, -1, -1, 1, false, null, loadFromStream( in ) );
// since the input stream is already loaded and parsed, // since the input stream is already loaded and parsed,
// get diagram here and remove it from cache // get the document here and remove it from cache
update(); update();
synchronized( FlatSVGIcon.class ) { synchronized( FlatSVGIcon.class ) {
svgCache.remove( uri ); svgCache.remove( uri );
@@ -279,7 +278,12 @@ public class FlatSVGIcon
private static synchronized URI loadFromStream( InputStream in ) throws IOException { private static synchronized URI loadFromStream( InputStream in ) throws IOException {
try( InputStream in2 = in ) { try( InputStream in2 = in ) {
return svgUniverse.loadSVG( in2, "/flatlaf-stream-" + streamNumber++ ); SVGDocument document = svgLoader.load( in2 );
URI dummyUri = new URI( "inputStreamSVG", "/flatlaf-stream-" + streamNumber++, null );
svgCache.put( dummyUri, document );
return dummyUri;
} catch( URISyntaxException e ) {
throw new IllegalStateException( e );
} }
} }
@@ -293,11 +297,13 @@ public class FlatSVGIcon
public FlatSVGIcon( FlatSVGIcon icon ) { public FlatSVGIcon( FlatSVGIcon icon ) {
this( icon.name, icon.width, icon.height, icon.scale, icon.disabled, icon.classLoader, icon.uri ); this( icon.name, icon.width, icon.height, icon.scale, icon.disabled, icon.classLoader, icon.uri );
colorFilter = icon.colorFilter; colorFilter = icon.colorFilter;
diagram = icon.diagram; document = icon.document;
dark = icon.dark; dark = icon.dark;
} }
protected FlatSVGIcon( String name, int width, int height, float scale, boolean disabled, ClassLoader classLoader, URI uri ) { protected FlatSVGIcon( String name, int width, int height, float scale, boolean disabled, ClassLoader classLoader,
URI uri )
{
this.name = name; this.name = name;
this.width = width; this.width = width;
this.height = height; this.height = height;
@@ -385,7 +391,7 @@ public class FlatSVGIcon
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader, uri ); FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader, uri );
icon.colorFilter = colorFilter; icon.colorFilter = colorFilter;
icon.diagram = diagram; icon.document = document;
icon.dark = dark; icon.dark = dark;
return icon; return icon;
} }
@@ -404,7 +410,7 @@ public class FlatSVGIcon
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader, uri ); FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader, uri );
icon.colorFilter = colorFilter; icon.colorFilter = colorFilter;
icon.diagram = diagram; icon.document = document;
icon.dark = dark; icon.dark = dark;
return icon; return icon;
} }
@@ -423,7 +429,7 @@ public class FlatSVGIcon
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, true, classLoader, uri ); FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, true, classLoader, uri );
icon.colorFilter = colorFilter; icon.colorFilter = colorFilter;
icon.diagram = diagram; icon.document = document;
icon.dark = dark; icon.dark = dark;
return icon; return icon;
} }
@@ -462,18 +468,19 @@ public class FlatSVGIcon
if( loadFailed ) if( loadFailed )
return; return;
if( dark == isDarkLaf() && diagram != null ) if( dark == isDarkLaf() && document != null )
return; return;
dark = isDarkLaf(); dark = isDarkLaf();
// SVGs already loaded via url or input stream can not have light/dark variants // SVGs already loaded via url or input stream can not have light/dark variants
if( uri != null && diagram != null ) if( uri != null && document != null )
return; return;
URI uri = this.uri; URI uri = this.uri;
URL url = null;
if( uri == null ) { if( uri == null ) {
URL url = getIconURL( name, dark ); url = getIconURL( name, dark );
if( url == null && dark ) if( url == null && dark )
url = getIconURL( name, false ); url = getIconURL( name, false );
@@ -486,29 +493,35 @@ public class FlatSVGIcon
uri = url2uri( url ); uri = url2uri( url );
} }
diagram = loadSVG( uri ); if( url == null ) {
loadFailed = (diagram == null); url = uri2url( uri );
} }
static synchronized SVGDiagram loadSVG( URI uri ) { document = loadSVG( uri, url );
loadFailed = (document == null);
}
/*
* The uri and url parameters should always match each other in the sense that they represent the same
* location. We pass both as most places
*/
static synchronized SVGDocument loadSVG( URI uri, URL url ) {
// get from our cache // get from our cache
SVGDiagram diagram = svgCache.get( uri ); SVGDocument document = svgCache.get( uri );
if( diagram != null ) if( document != null )
return diagram; return document;
// load/get SVG diagram // load/get SVG document
diagram = svgUniverse.getDiagram( uri ); document = svgLoader.load( url );
if( diagram == null ) { if( document == null ) {
LoggingFacade.INSTANCE.logSevere( "FlatSVGIcon: failed to load '" + uri + "'", null ); LoggingFacade.INSTANCE.logSevere( "FlatSVGIcon: failed to load '" + uri + "'", null );
return null; return null;
} }
// add to our (soft) cache and remove from SVGUniverse (hard) cache svgCache.put( uri, document );
svgCache.put( uri, diagram );
svgUniverse.removeDocument( uri );
return diagram; return document;
} }
private URL getIconURL( String name, boolean dark ) { private URL getIconURL( String name, boolean dark ) {
@@ -528,7 +541,7 @@ public class FlatSVGIcon
*/ */
public boolean hasFound() { public boolean hasFound() {
update(); update();
return diagram != null; return document != null;
} }
/** /**
@@ -540,7 +553,7 @@ public class FlatSVGIcon
return scaleSize( width ); return scaleSize( width );
update(); update();
return scaleSize( (diagram != null) ? Math.round( diagram.getWidth() ) : 16 ); return scaleSize( (document != null) ? Math.round( document.size().width ) : 16 );
} }
/** /**
@@ -552,7 +565,7 @@ public class FlatSVGIcon
return scaleSize( height ); return scaleSize( height );
update(); update();
return scaleSize( (diagram != null) ? Math.round( diagram.getHeight() ) : 16 ); return scaleSize( (document != null) ? Math.round( document.size().height ) : 16 );
} }
private int scaleSize( int size ) { private int scaleSize( int size ) {
@@ -597,7 +610,7 @@ public class FlatSVGIcon
} }
private void paintSvg( Graphics2D g, int x, int y ) { private void paintSvg( Graphics2D g, int x, int y ) {
if( diagram == null ) { if( document == null ) {
paintSvgError( g, x, y ); paintSvgError( g, x, y );
return; return;
} }
@@ -607,19 +620,18 @@ public class FlatSVGIcon
UIScale.scaleGraphics( g ); UIScale.scaleGraphics( g );
if( width > 0 || height > 0 ) { if( width > 0 || height > 0 ) {
double sx = (width > 0) ? width / diagram.getWidth() : 1; FloatSize svgSize = document.size();
double sy = (height > 0) ? height / diagram.getHeight() : 1; double sx = (width > 0) ? width / svgSize.width : 1;
double sy = (height > 0) ? height / svgSize.height : 1;
if( sx != 1 || sy != 1 ) if( sx != 1 || sy != 1 )
g.scale( sx, sy ); g.scale( sx, sy );
} }
if( scale != 1 ) if( scale != 1 )
g.scale( scale, scale ); g.scale( scale, scale );
diagram.setIgnoringClipHeuristic( true );
try { try {
diagram.render( g ); document.render( null, g );
} catch( SVGException ex ) { } catch( Exception ex ) {
paintSvgError( g, 0, 0 ); paintSvgError( g, 0, 0 );
} }
} }
@@ -670,6 +682,14 @@ public class FlatSVGIcon
} }
} }
static URL uri2url( URI uri ) {
try {
return uri.toURL();
} catch( MalformedURLException ex ) {
throw new IllegalArgumentException( ex );
}
}
private static Boolean darkLaf; private static Boolean darkLaf;
/** /**

View File

@@ -28,8 +28,8 @@ import java.util.List;
import javax.swing.JWindow; import javax.swing.JWindow;
import com.formdev.flatlaf.util.MultiResolutionImageSupport; import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.kitfox.svg.SVGDiagram; import com.github.weisj.jsvg.SVGDocument;
import com.kitfox.svg.SVGException; import com.github.weisj.jsvg.geometry.size.FloatSize;
/** /**
* Utility methods for SVG. * Utility methods for SVG.
@@ -83,7 +83,7 @@ public class FlatSVGUtils
* @since 2 * @since 2
*/ */
public static List<Image> createWindowIconImages( URL svgUrl ) { public static List<Image> createWindowIconImages( URL svgUrl ) {
SVGDiagram diagram = loadSVG( svgUrl ); SVGDocument document = loadSVG( svgUrl );
if( SystemInfo.isWindows && MultiResolutionImageSupport.isAvailable() ) { if( SystemInfo.isWindows && MultiResolutionImageSupport.isAvailable() ) {
// use a multi-resolution image that creates images on demand for requested sizes // use a multi-resolution image that creates images on demand for requested sizes
@@ -102,17 +102,17 @@ public class FlatSVGUtils
new Dimension( 48, 48 ), // 300% new Dimension( 48, 48 ), // 300%
new Dimension( 64, 64 ), // 400% new Dimension( 64, 64 ), // 400%
}, dim -> { }, dim -> {
return svg2image( diagram, dim.width, dim.height ); return svg2image( document, dim.width, dim.height );
} ) ); } ) );
} else { } else {
return Arrays.asList( return Arrays.asList(
svg2image( diagram, 16, 16 ), // 100% svg2image( document, 16, 16 ), // 100%
svg2image( diagram, 20, 20 ), // 125% svg2image( document, 20, 20 ), // 125%
svg2image( diagram, 24, 24 ), // 150% svg2image( document, 24, 24 ), // 150%
svg2image( diagram, 28, 28 ), // 175% svg2image( document, 28, 28 ), // 175%
svg2image( diagram, 32, 32 ), // 200% svg2image( document, 32, 32 ), // 200%
svg2image( diagram, 48, 48 ), // 300% svg2image( document, 48, 48 ), // 300%
svg2image( diagram, 64, 64 ) // 400% svg2image( document, 64, 64 ) // 400%
); );
} }
} }
@@ -180,23 +180,23 @@ public class FlatSVGUtils
* @since 2 * @since 2
*/ */
public static BufferedImage svg2image( URL svgUrl, float scaleFactor ) { public static BufferedImage svg2image( URL svgUrl, float scaleFactor ) {
SVGDiagram diagram = loadSVG( svgUrl ); SVGDocument document = loadSVG( svgUrl );
int width = (int) (diagram.getWidth() * scaleFactor); FloatSize size = document.size();
int height = (int) (diagram.getHeight() * scaleFactor); int width = (int) (size.width * scaleFactor);
return svg2image( diagram, width, height ); int height = (int) (size.height * scaleFactor);
return svg2image( document, width, height );
} }
/** /**
* Creates a buffered image and renders the given SVGDiagram into it. * Creates a buffered image and renders the given SVGDocument into it.
* *
* @param diagram the SVG diagram * @param document the SVG document
* @param width the width of the image * @param width the width of the image
* @param height the height of the image * @param height the height of the image
* @return the image * @return the image
* @throws RuntimeException if failed to render SVG file * @throws RuntimeException if failed to render SVG file
*/ */
public static BufferedImage svg2image( SVGDiagram diagram, int width, int height ) { public static BufferedImage svg2image( SVGDocument document, int width, int height ) {
try {
BufferedImage image = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB ); BufferedImage image = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB );
Graphics2D g = image.createGraphics(); Graphics2D g = image.createGraphics();
@@ -204,29 +204,24 @@ public class FlatSVGUtils
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR ); g.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR );
double sx = width / diagram.getWidth(); FloatSize size = document.size();
double sy = height / diagram.getHeight(); double sx = width / size.width;
double sy = height / size.height;
if( sx != 1 || sy != 1 ) if( sx != 1 || sy != 1 )
g.scale( sx, sy ); g.scale( sx, sy );
diagram.setIgnoringClipHeuristic( true ); document.render( null, g );
diagram.render( g );
} finally { } finally {
g.dispose(); g.dispose();
} }
return image; return image;
} catch( SVGException ex ) {
throw new RuntimeException( ex );
}
} }
private static URL getResource( String svgName ) { private static URL getResource( String svgName ) {
return FlatSVGUtils.class.getResource( svgName ); return FlatSVGUtils.class.getResource( svgName );
} }
private static SVGDiagram loadSVG( URL url ) { private static SVGDocument loadSVG( URL url ) {
return FlatSVGIcon.loadSVG( FlatSVGIcon.url2uri( url ) ); return FlatSVGIcon.loadSVG( FlatSVGIcon.url2uri( url ), url );
} }
} }

View File

@@ -20,7 +20,7 @@
module com.formdev.flatlaf.extras { module com.formdev.flatlaf.extras {
requires java.desktop; requires java.desktop;
requires java.prefs; requires java.prefs;
requires static com.kitfox.svg; // optional at runtime requires static com.github.weisj.jsvg; // optional at runtime
requires com.formdev.flatlaf; requires com.formdev.flatlaf;
exports com.formdev.flatlaf.extras; exports com.formdev.flatlaf.extras;

View File

@@ -25,6 +25,7 @@ sigtest = "org.netbeans.tools:sigtest-maven-plugin:1.7"
# flatlaf-extras # flatlaf-extras
svgSalamander = "com.formdev:svgSalamander:1.1.3" svgSalamander = "com.formdev:svgSalamander:1.1.3"
jsvg = "com.github.weisj:jsvg:1.0.0"
# flatlaf-jide-oss # flatlaf-jide-oss
jide-oss = "com.formdev:jide-oss:3.7.12" jide-oss = "com.formdev:jide-oss:3.7.12"