weisJ / jsvg

Java SVG renderer
MIT License
127 stars 7 forks source link

Request for Direct SVG to JavaFX Image Conversion to Support Packaging with Gluon/GraalVM #54

Open leewyatt opened 11 months ago

leewyatt commented 11 months ago

Hello,

I am reaching out to suggest a feature for the JSVG library that I believe would be beneficial for JavaFX applications, particularly those that aim to achieve native packaging.

As you may know, in order to package a JavaFX application natively with tools such as Gluon or GraalVM, it is necessary to avoid using classes from the AWT package, as these are not supported. In the context of this project, this means avoiding the use of java.awt.image.BufferedImage.

Currently, when using JSVG in a JavaFX application, we have to convert BufferedImage to JavaFX's Image in order to display SVG icons. However, this approach prevents us from utilizing tools like Gluon/GraalVM for native packaging.

Given this, I would like to request the possibility of directly converting SVG files to JavaFX's Image. This should not be overly complex to implement, and it would greatly facilitate the use of SVG icons in JavaFX applications, without the aforementioned complications regarding native packaging.

You might consider adding a JavaFX module or creating a new branch specifically adapted for JavaFX.

Thank you for your wonderful work on JSVG; it is truly an excellent library, and your contributions are greatly appreciated.

Best regards,

weisJ commented 11 months ago

I can see that this would be a desirable feature for JavaFX applications. Though the implementation would be a huge undertaking. The usage of BufferedImages can't easily be replaced as they are tightly integrated into the rendering process especially for filters and masks. Moreover everything is rendered to a java.awt.Graphics2D object. A rewrite would need to happen through the entire rendering stack to allow for javafx.scene.canvas.GraphicsContext (I guess this is this the JavaFX equivalent. I don't have much experience with JavaFX).

To be honest I don't think that I have the resources at the moment to make it happen any time soon. Though I would be willing to guide through needed changes for a PR.

leewyatt commented 11 months ago

Thank you for your response. It seems that due to my limited knowledge on rendering, I've oversimplified the issue. I hope that some knowledgeable expert in this field would be willing to help resolve this problem.

weisJ commented 7 months ago

Are all classes from java.awt unsupported or is it only BufferedImage specifically? With the latest changes regarding support for shape computation I have moved forward with abstracting away the underlying render target. It would allow to make an implementation for JavaFX more feasible. One would have to implement com.github.weisj.jsvg.renderer.Output for it. Though <mask>, <filter> and soft clipping for <clipPath> still rely on BufferedImage, however SVGs without them could be rendered this way.

leewyatt commented 7 months ago

Thank you very much for your detailed response . It's greatly appreciated and speaks volumes of your dedication to the JSVG library.

Our objective is to avoid all java.awt classes to ensure the widest compatibility and performance optimization for JavaFX applications. This includes BufferedImage among others.

weisJ commented 7 months ago

Let me be a bit more explicit with may question:

Do you know if there is a certain subset of java.awt which causes incompatibility with native packaging? For example I don’t quite see why the Path related apis would cause problems. Are they supported?

leewyatt commented 7 months ago

Thank you for your insightful questions. Allow me to address them as follows:

  1. Regarding the Path API: In JavaFX, there is a similar class, javafx.scene.shape.Path, which allows for the drawing of various shapes. This class might serve as a direct alternative for drawing functionalities in a JavaFX environment. You can find more details about it in the JavaFX documentation: JavaFX Path Documentation.

  2. On the relationship between JavaFX native packaging and AWT: I came across a useful reference that might shed some light on this issue. A reply from José Pereda on Stack Overflow discusses the compatibility issues between Swing/JavaFX and native packaging with GraalVM, specifically regarding the 'No AWT in java.library.path' error. This discussion seems highly relevant to our conversation: Swing and JavaFX native image with GraalVM.

  3. Assistance and Testing: I am more than willing to assist in testing or provide any help within my capabilities. If there are specific scenarios or configurations you'd like me to explore, please let me know.

leewyatt commented 7 months ago

JavaFX indeed includes an SVGPath class, which is primarily geared towards supporting path data similar to the "path" element in SVG files. However, when it comes to SVG icons that incorporate other shapes like circles, lines, rectangles, ellipses, polygons, and text, manual drawing and coordinate calculation are required. Fortunately, JavaFX provides comprehensive APIs for drawing these shapes, which means that transforming these SVG elements into JavaFX equivalents is feasible but demands familiarity with SVG syntax and semantics.

For those well-versed in SVG, adapting SVG icons to JavaFX manually can be a detailed but rewarding process, as JavaFX's drawing capabilities are extensive. The process involves interpreting the SVG elements and attributes and then using JavaFX's graphical primitives to render them accordingly.

Regarding performance, utilizing the Canvas API in JavaFX might offer superior performance for dynamic or complex drawings because it allows for direct rendering onto a single graphical surface, which can be more efficient than managing multiple individual shape nodes in a scene graph for highly complex or frequently updated graphics.

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.SVGPath;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class AppMain extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {

        SVGPath svgPath = new SVGPath();
        // only supports path data in the form of a string
        svgPath.setContent("M 0 0 L 10 10 L 0 20 Z");
        svgPath.setFill(Color.CORAL);

        Circle circle = new Circle(15);
        circle.setFill(Color.AQUA);

        Polygon polygon = new Polygon(0, 0, 5, 5, 0, 10);
        polygon.setFill(Color.DEEPPINK);

        Ellipse ellipse = new Ellipse(15, 35);
        ellipse.setFill(Color.BLUEVIOLET);

        Text text = new Text("Hello");
        text.setFont(Font.font(20));
        text.setFill(Color.DARKORANGE);

        Canvas canvas = new Canvas(100, 100);
        canvas.getGraphicsContext2D().setFill(Color.LIGHTPINK);
        GraphicsContext g2d = canvas.getGraphicsContext2D();
        g2d.fillOval(0, 0, 100, 100);

        HBox group = new HBox(10, svgPath, circle, polygon, ellipse, text, canvas);
        group.setAlignment(Pos.CENTER);
        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(new javafx.scene.Scene(group, 520, 230));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}
leewyatt commented 7 months ago

I've attempted to create an RXSVGView, which can parse multiple paths from an SVG icon and fill them with colors extracted directly from the SVG. Essentially, it translates the multiple paths within an SVG icon into individual SVGPath objects and then places them into a Pane container. Fully supporting SVG icons seems to be a daunting task, involving extensive XML parsing, which is why I've limited the implementation to parsing only the paths. RXSVGView Skin source

weisJ commented 7 months ago

Thank you for the additional information. Something that would be very helpful would be a minimal example repository to showcase native packaging.

leewyatt commented 7 months ago

I found a comprehensive guide on the Gluon website that covers the process of packaging JavaFX applications for various platforms, including Windows, macOS, Linux, Android, and iOS. This guide explains how to use the GluonFX plugin with GraalVM and JavaFX to compile Java client applications and their dependencies into native code for direct execution on these platforms. For detailed steps and more information, you can visit the Gluon documentation page​.

I've been busy troubleshooting a rather tricky bug recently. If, after reviewing the documentation, you feel there's a need for a minimal demo,I will try to write one as soon as possible. However, please note that the minimal demo is not closely related to packaging; the main reference should still be the packaging process outlined in the Gluon documentation. If there's anything else you need from me, please let me know.