💩

JavaFXでSkiaを使った描写をしたい!【Skija】

2023/11/24に公開

JavaFXでSkiaを使った描写をしたい!

というわけでまずは適当に画像を表示するプログラムを、IntelliJのJavaFXテンプレートから作成しました。
この記事もMarkdownチートシート見ながら書いてるくらいの初心者なので、変なところあっても許してください。
今の時点ではSkiaは使っていません。

HelloApplication.java
package com.example.skijafx;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 500, 300);
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}
hello-view.fxml
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.BorderPane?>

<BorderPane xmlns:fx="http://javafx.com/fxml"
            fx:controller="com.example.skijafx.HelloController">
    <top>
        <Button text="Hello!"  onAction="#onHelloButtonClick"/>
    </top>
    <center>
        <Label fx:id="welcomeText" text="pls click button"/>
    </center>
    <bottom>
        <ImageView fx:id="image" fitWidth="300" fitHeight="300" />
    </bottom>

</BorderPane>
HelloController.java
package com.example.skijafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;

import java.net.URL;
import java.util.ResourceBundle;

public class HelloController implements Initializable {
    @FXML
    private ImageView image;
    @FXML
    private Label welcomeText;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Image testImage = new Image("C:\\test.bmp");
        image.setImage(testImage);
    }


    @FXML
    protected void onHelloButtonClick() {
        welcomeText.setText("Welcome to JavaFX Application!");
    }


}

実行結果(画像は昔学校課題で作成したゲームの素材なのでお気になさらず)

こっからどうするんだ?

なんもわからん。
とりあえずライブラリは「Skija」使うか。
jetbrainsのリポジトリもありますが更新続いてるのはこちらのようですね。
https://github.com/HumbleUI/Skija/

適当に
「JavaFX Skija」とかでぐぐるとこれにたどり着きました。
https://stackoverflow.com/questions/67870815/how-to-use-skija-for-javafx
fxml使わずにコードでViewを記述してるけど参考になりそう?
というわけで以下は上記リンク先回答者様のmipa様、NM Naufaldo様の投稿を参考にさせていただきます。

とりあえず試す(記事書きながら試しているので動くのかわからん)

ライブラリ追加

build.gradle
dependencies {
    //これを追記
    implementation("io.github.humbleui.skija:skija-windows:0.96.0")
}

とりあえずControllerに書いていきます。
MVC?うるせえ!

終わった
なぜかSkijaの型が見つかりません。

importもうまくいっていない

再起動したりWindowsDefenderのスキャン対象外に追加みたいなダイアログ押してたらなんか出たぞ
「なんとか~ディレクティブを追加」押すぞ!
うおお!エラーが消えた!
どうやらIntelliJのテンプレートで生成されてたmodule-info.javaに

module-info.java
requires io.github.humbleui.skija.windows;

が追記されたようです。

できた!!!


適当に円を描写してみました。
いじったのはHelloControllerのみです。

HelloController.java
package com.example.skijafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;

import io.github.humbleui.skija.*;

import java.io.ByteArrayInputStream;
import java.net.URL;
import java.util.ResourceBundle;


public class HelloController implements Initializable {
    @FXML
    private ImageView image;
    @FXML
    private Label welcomeText;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Surface surface = Surface.makeRasterN32Premul(300, 300);
        Canvas canvas = surface.getCanvas();
        Paint paint = new Paint();
        canvas.drawCircle(50,50,50,paint);
        //Image型が2つあるので注意
        io.github.humbleui.skija.Image skiaImage = surface.makeImageSnapshot();
        Data data = skiaImage.encodeToData(EncodedImageFormat.PNG);
        byte[] pngBytes = data.getBytes();
        ByteArrayInputStream inputStream = new ByteArrayInputStream(pngBytes);

        javafx.scene.image.Image imageFX = new javafx.scene.image.Image(inputStream);
        image.setImage(imageFX);
    }


    @FXML
    protected void onHelloButtonClick() {
        welcomeText.setText("Welcome to JavaFX Application!");
    }


}

怖いのはImage型がJavaFXのとSkijaのものがごっちゃになりそうなことくらいであとは結構簡単にできました。
よくわからんけどsurfaceのインスタンスを別クラスで生成してコントローラーに渡せばいい感じに制御できるんですかね。
あと今回は一度描写したら終わりだから良いですが、60fpsで描写とかできるのでしょうか?
多分メモリに画像データ書き込んでいってるので厳しそう。実は将来的にそれをやりたかったので個人的な目標はもしや達成できない?
パソコン捨てるか...

JavaFXで図形を60fpsでレンダリングできそうなやつに心当たりあればコメントで教えてください!

あとがき

デスクトップアプリで図形を60fpsでレンダリングしたく、同じことをAvaloniaという別フレームワークで試そうとしていました。
私はC#が今のところ一番好きな言語で、.NETでクロスプラットフォームでデスクトップアプリ作れるAvaloniaはすごく魅力的だったのですが残念ながらうまくいかず...。
JavaFXでSkia使う日本語情報が見つからなかったので電子ゴミ書きました。誰かのお役に立てれば幸いです。
Zennへの投稿は初めてなのでなにかおかしなところがあってもご容赦ください(でもコメントで教えてくれると嬉しいです)。

Discussion