0

How can I use Skija for JavaFX?

This is the code example from the repository

Surface surface = Surface.makeRasterN32Premul(100, 100);
Canvas canvas = surface.getCanvas();

Paint paint = new Paint();
paint.setColor(0xFFFF0000);
canvas.drawCircle(50, 50, 30, paint);

But how to convert this skija canvas object to a JavaFX node?

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
NM Naufaldo
  • 1,032
  • 1
  • 12
  • 30
  • 3
    The [Getting Started doc](https://github.com/JetBrains/skija/blob/master/docs/Getting%20Started.md) shows how to export that canvas to PNG format in a `byte[]`. So you could just use that `byte[]` to create a `ByteArrayInputStream` and pass it to a JavaFX `Image`, and display in an `ImageView`. – James_D Jun 07 '21 at 13:29

2 Answers2

3

This variant of the above code should do the conversion much faster because it completely avoids the encoding and decoding of the raw image data to PNG and back again. Note: This only works with recent JavaFX versions because it makes use of some newer features.

import org.jetbrains.skija.Canvas;
import org.jetbrains.skija.Image;
import org.jetbrains.skija.Paint;
import org.jetbrains.skija.Surface;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelBuffer;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class SkijaFX extends Application{
    public static void main(String[] args){
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception{
        Surface surface = Surface.makeRasterN32Premul(100, 100);
        Canvas canvas = surface.getCanvas();
        Paint paint = new Paint();
        canvas.drawCircle(50, 50, 30, paint);
        Image image = surface.makeImageSnapshot();
        
        ImageView imageView = new ImageView(new WritableImage(new PixelBuffer<>(image.getWidth(), image.getHeight(), image.peekPixels().asIntBuffer(), PixelFormat.getIntArgbPreInstance())));

        StackPane root = new StackPane(imageView);
        Scene scene = new Scene(root, 300, 300);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

Update: In this variant the colors are wrong. The native image representation of Skia does not seem to be IntArgbPre. (Needs some more investigation.) This problem is avoided in the original version via the PNG conversion.

mipa
  • 10,369
  • 2
  • 16
  • 35
2

This answer based on James_D comment to use byte[] as an input stream. Here is sample code

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.jetbrains.skija.*;

import java.io.ByteArrayInputStream;

public class SkijaFX extends Application{
    public static void main(String[] args){
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception{
        Surface surface = Surface.makeRasterN32Premul(100, 100);
        Canvas canvas = surface.getCanvas();
        Paint paint = new Paint();
        canvas.drawCircle(50, 50, 30, paint);
        Image image = surface.makeImageSnapshot();
        Data data = image.encodeToData(EncodedImageFormat.PNG);
        byte[] pngBytes = data.getBytes();
        ByteArrayInputStream inputStream = new ByteArrayInputStream(pngBytes);

        javafx.scene.image.Image imageFx = new javafx.scene.image.Image(inputStream);
        ImageView imageView = new ImageView(imageFx);

        StackPane root = new StackPane(imageView);
        Scene scene = new Scene(root, 300, 300);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

enter image description here

UPDATE:

As mipa suggested, if we use JavaFX 13 and above, instead of using ByteArrayInputStream, we can use WritableImage and PixelBuffer to create an ImageView

NM Naufaldo
  • 1,032
  • 1
  • 12
  • 30