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

SVG support based on SVG Salamander and Batik #337

Open
mgarin opened this Issue Sep 30, 2015 · 5 comments

Comments

2 participants
@mgarin
Owner

mgarin commented Sep 30, 2015

Provide additional tools to simplify SVG graphics usage within components.

Here is a set of planned features:

  • Convenient implementation of Swing Icon for the SVG graphics

  • Easy way to retrieve BufferedImage of specific size from loaded SVG image

  • SVG support based on SVG Salamander free open-source library

  • SVG support based on Apache Batik free open-source free library

  • Separate svg module - would contain base API for providing SVG graphics loading support and won't be useful without a second module containing the actual implementation

  • Separate svg-salamander module - would contain SVG Salamander implementation

  • Separate svg-batik module - would contain Apache Batik implementation

Since this will certainly involve additional 3rd-party library or libraries - it should be placed into separate module. It will work perfectly along with other v1.3.0 module changes: #336

@mgarin mgarin self-assigned this Sep 30, 2015

@mgarin mgarin added this to the v1.30 milestone Sep 30, 2015

@mgarin mgarin referenced this issue Sep 30, 2015

Open

Modules, Dependencies and Versioning #336

1 of 11 tasks complete
@mgarin

This comment has been minimized.

Show comment
Hide comment
@mgarin

mgarin Jul 19, 2016

Owner

Some part of this enhancement request will be available with v1.2.9 release as SVG icons are quite convenient since they can be re-used for skins with different color schemes.

Owner

mgarin commented Jul 19, 2016

Some part of this enhancement request will be available with v1.2.9 release as SVG icons are quite convenient since they can be re-used for skins with different color schemes.

@mgarin

This comment has been minimized.

Show comment
Hide comment
@mgarin

mgarin Nov 24, 2017

Owner

I have updated the main issue text with more information on planned stuff.

At this point it is mostly about adding Apache Batik support and separating all implementations from the API I've got in the IconManager. That should help keeping code clean of any SVG library-related mess and to allow developers to choose which library they want SVG graphics to be loaded and modified with.

I should generally say that I'd prefer using SVG Salamander as it is lightweight and has a really convenient API, but I've encountered quite a few issues with multiple SVG files and sometimes it is hard to say what exactly goes wrong.

On the other hand Apache Batik has extensive support for almost all SVG features (which you would probably never use anyway, but still) and does succeed at loading almost all SVG files. It also has slightly higher performance at painting more complex images. But the amount of dependencies it drags along is atrocious and it is the reason why I didn't go for it initially.

Overall - there is no best solution, but there are options and I want to allow WebLaF users to have those options (and maybe more in the future as well?) instead of hardcoding a single solution I prefer.

Owner

mgarin commented Nov 24, 2017

I have updated the main issue text with more information on planned stuff.

At this point it is mostly about adding Apache Batik support and separating all implementations from the API I've got in the IconManager. That should help keeping code clean of any SVG library-related mess and to allow developers to choose which library they want SVG graphics to be loaded and modified with.

I should generally say that I'd prefer using SVG Salamander as it is lightweight and has a really convenient API, but I've encountered quite a few issues with multiple SVG files and sometimes it is hard to say what exactly goes wrong.

On the other hand Apache Batik has extensive support for almost all SVG features (which you would probably never use anyway, but still) and does succeed at loading almost all SVG files. It also has slightly higher performance at painting more complex images. But the amount of dependencies it drags along is atrocious and it is the reason why I didn't go for it initially.

Overall - there is no best solution, but there are options and I want to allow WebLaF users to have those options (and maybe more in the future as well?) instead of hardcoding a single solution I prefer.

@mgarin mgarin changed the title from Additional tools to work with SVG to SVG support based on SVG Salamander and Batik Nov 24, 2017

@Sciss

This comment has been minimized.

Show comment
Hide comment
@Sciss

Sciss Nov 25, 2017

Contributor

As a side note, I once dragged Batik as a dependency in a project, and it was a nightmare; it transitively depends on zillions of other libraries which are not even consistently compatible to each other; I also found Batik's Java2D rendering extremely ugly, but perhaps I wasn't investing enough energy to understand how to tune it. For me, that project was a no-go, a good example of a failed humongous Java project that is just a big pile of spaghetti.

Contributor

Sciss commented Nov 25, 2017

As a side note, I once dragged Batik as a dependency in a project, and it was a nightmare; it transitively depends on zillions of other libraries which are not even consistently compatible to each other; I also found Batik's Java2D rendering extremely ugly, but perhaps I wasn't investing enough energy to understand how to tune it. For me, that project was a no-go, a good example of a failed humongous Java project that is just a big pile of spaghetti.

@mgarin

This comment has been minimized.

Show comment
Hide comment
@mgarin

mgarin Nov 26, 2017

Owner

As a side note, I once dragged Batik as a dependency in a project, and it was a nightmare; it transitively depends on zillions of other libraries which are not even consistently compatible to each other;

Couldn't agree more. If not the dependencies I would've gone with Batik from the beginning.

I also found Batik's Java2D rendering extremely ugly, but perhaps I wasn't investing enough energy to understand how to tune it

I didn't find a good way to use Batik first time I tried it either and I don't consider their JSVGCanvas or JSVGComponent as a good solution at all - those are basically inconvenient/useless on practice.

After a bit more research though I found a way to do everything I did with SVG Salamander using Batik. Here is a short example of how to create WebLaF's SvgIcon analogue:

public class BatikSvgIcon implements Icon
{
    private final SVGDocument svgDocument;
    private final GraphicsNode svgIcon;

    public BatikSvgIcon ( URL url ) throws Exception
    {
        final String xmlParser = XMLResourceDescriptor.getXMLParserClassName ();
        final SAXSVGDocumentFactory df = new SAXSVGDocumentFactory ( xmlParser );
        svgDocument = df.createSVGDocument ( url.toString () );

        final UserAgent userAgent = new UserAgentAdapter ();
        final DocumentLoader loader = new DocumentLoader ( userAgent );
        final BridgeContext ctx = new BridgeContext ( userAgent, loader );
        ctx.setDynamicState ( BridgeContext.DYNAMIC );
        final GVTBuilder builder = new GVTBuilder ();
        svgIcon = builder.build ( ctx, svgDocument );
    }

    public void paintIcon ( Component c, Graphics g, int x, int y )
    {
        paintSvgIcon ( ( Graphics2D ) g, x, y );
    }

    private void paintSvgIcon ( Graphics2D g, int x, int y )
    {
        g.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
        AffineTransform transform = new AffineTransform ( 1.0, 0.0, 0.0, 1.0, x, y );
        svgIcon.setTransform ( transform );
        svgIcon.paint ( g );
    }

    public int getIconWidth ()
    {
        return ( int ) svgIcon.getGeometryBounds ().getWidth ();
    }

    public int getIconHeight ()
    {
        return ( int ) svgIcon.getGeometryBounds ().getHeight ();
    }
}

You can also modify SVG structure through svgDocument in a similar way you can do it using SVG Salamander and changes are reflected upon the next paint operation.

Batik's API is not too bad if you get into it, I guess it just became overly complicated after all the features that been added there.

And well, there is one good reason to choose Batik over other options - performance. Painting operations mostly take twice less time than with other libraries I've tried. Not like its a huge difference when we're talking about 4ms vs 2ms, but unfortunately it all adds up and slows down Swing UI due to its single-thread nature.

Owner

mgarin commented Nov 26, 2017

As a side note, I once dragged Batik as a dependency in a project, and it was a nightmare; it transitively depends on zillions of other libraries which are not even consistently compatible to each other;

Couldn't agree more. If not the dependencies I would've gone with Batik from the beginning.

I also found Batik's Java2D rendering extremely ugly, but perhaps I wasn't investing enough energy to understand how to tune it

I didn't find a good way to use Batik first time I tried it either and I don't consider their JSVGCanvas or JSVGComponent as a good solution at all - those are basically inconvenient/useless on practice.

After a bit more research though I found a way to do everything I did with SVG Salamander using Batik. Here is a short example of how to create WebLaF's SvgIcon analogue:

public class BatikSvgIcon implements Icon
{
    private final SVGDocument svgDocument;
    private final GraphicsNode svgIcon;

    public BatikSvgIcon ( URL url ) throws Exception
    {
        final String xmlParser = XMLResourceDescriptor.getXMLParserClassName ();
        final SAXSVGDocumentFactory df = new SAXSVGDocumentFactory ( xmlParser );
        svgDocument = df.createSVGDocument ( url.toString () );

        final UserAgent userAgent = new UserAgentAdapter ();
        final DocumentLoader loader = new DocumentLoader ( userAgent );
        final BridgeContext ctx = new BridgeContext ( userAgent, loader );
        ctx.setDynamicState ( BridgeContext.DYNAMIC );
        final GVTBuilder builder = new GVTBuilder ();
        svgIcon = builder.build ( ctx, svgDocument );
    }

    public void paintIcon ( Component c, Graphics g, int x, int y )
    {
        paintSvgIcon ( ( Graphics2D ) g, x, y );
    }

    private void paintSvgIcon ( Graphics2D g, int x, int y )
    {
        g.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
        AffineTransform transform = new AffineTransform ( 1.0, 0.0, 0.0, 1.0, x, y );
        svgIcon.setTransform ( transform );
        svgIcon.paint ( g );
    }

    public int getIconWidth ()
    {
        return ( int ) svgIcon.getGeometryBounds ().getWidth ();
    }

    public int getIconHeight ()
    {
        return ( int ) svgIcon.getGeometryBounds ().getHeight ();
    }
}

You can also modify SVG structure through svgDocument in a similar way you can do it using SVG Salamander and changes are reflected upon the next paint operation.

Batik's API is not too bad if you get into it, I guess it just became overly complicated after all the features that been added there.

And well, there is one good reason to choose Batik over other options - performance. Painting operations mostly take twice less time than with other libraries I've tried. Not like its a huge difference when we're talking about 4ms vs 2ms, but unfortunately it all adds up and slows down Swing UI due to its single-thread nature.

@mgarin

This comment has been minimized.

Show comment
Hide comment
@mgarin

mgarin Nov 26, 2017

Owner

In either case, I was thinking to add Batik implementation in a separate module, so you can always throw that one away safely if you don't want to drag all the dependencies and use SVG Salamander implementation (or even write your own if needed based on WebLaF's API).

Owner

mgarin commented Nov 26, 2017

In either case, I was thinking to add Batik implementation in a separate module, so you can always throw that one away safely if you don't want to drag all the dependencies and use SVG Salamander implementation (or even write your own if needed based on WebLaF's API).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment