JavaFX のフォントレンダリング品質問題


はじめに

JavaFx font rendering low quality などで検索すると山ほどでてくる昔からの問題。


実例

以下のようなコード。

public class App extends Application {

    @Override
    public void start(Stage stage) {
        var vbox = new VBox(canvas1(), canvas2(), stackPaneText(), stackPaneLabel());
        vbox.setStyle("-fx-background-color: #000;");
        var scene = new Scene(vbox, 600, 200);
        scene.setFill(Color.TRANSPARENT);
        stage.setScene(scene);
        stage.setTitle("font-rendering-quality");
        stage.show();
    }

    private Canvas canvas1() {
        var canvas = new Canvas(600, 25);
        var gc = canvas.getGraphicsContext2D();
        gc.setFontSmoothingType(FontSmoothingType.LCD);
        gc.setFill(Color.WHITE);
        gc.fillText("Font Rendering Quality : フォントレンダリング品質(Canvas LCD)", 13, 20);
        return canvas;
    }

    private Canvas canvas2() {
        var canvas = new Canvas(600, 25);
        var gc = canvas.getGraphicsContext2D();
        gc.setFontSmoothingType(FontSmoothingType.GRAY);
        gc.setFill(Color.WHITE);
        gc.fillText("Font Rendering Quality : フォントレンダリング品質(Canvas GRAY)", 13, 20);
        return canvas;
    }

    private Pane stackPaneText() {
        var pane = new StackPane();
        Text text = new Text("Font Rendering Quality : フォントレンダリング品質(StackPane + Text)");
        text.setFill(Color.WHITE);
        StackPane.setAlignment(text, Pos.CENTER_LEFT);
        StackPane.setMargin(text, new Insets(10, 0, 0, 13));
        pane.getChildren().add(text);
        return pane;
    }

    private Pane stackPaneLabel() {
        var pane = new StackPane();
        Label label = new Label("Font Rendering Quality : フォントレンダリング品質(StackPane + Label)");
        label.setTextFill(Color.WHITE);
        label.setPadding(new Insets(10, 0, 0, 13));
        StackPane.setAlignment(label, Pos.CENTER_LEFT);
        pane.getChildren().add(label);
        return pane;
    }

    private Canvas canvasRecoat() {
        var canvas = new Canvas(600, 25);
        var gc = canvas.getGraphicsContext2D();
        gc.setFontSmoothingType(FontSmoothingType.LCD);
        gc.setFill(Color.WHITE);
        gc.fillText("Font Rendering Quality : フォントレンダリング品質(Canvas LCD 重ね塗り)", 13, 20);
        gc.fillText("Font Rendering Quality : フォントレンダリング品質(Canvas LCD 重ね塗り)", 13, 20);
        return canvas;
    }

}

手元の MacOS 上(JavaFX25)での出力は以下。

拡大すると

漢字部分は特にぼやけ、明瞭ではない。

もっと拡大すると

フォントを選べば少しマシに見えることもあるが、いずれにしても日本語は厳しい。 これは Windows でも同様。


巷でよく聞く対応策

以下のシステムプロパティ設定

System.setProperty("prism.lcdtext", "false");

変わらず


以下のシステムプロパティ設定

System.setProperty("prism.text", "t2k");

変わらず


以下のシステムプロパティ設定

System.setProperty("prism.text", "t2k");
System.setProperty("prism", "lcdtext");

変わらず


あと、snapToPixel を有効にするもの。

vbox.setSnapToPixel(true);

まぁ、これはデフォルトで true なので変わるはずがない。


重ね塗り

唯一それなりにマシになるのは、重ね塗り。

    private Canvas canvasRecoat() {
        var canvas = new Canvas(600, 25);
        var gc = canvas.getGraphicsContext2D();
        gc.setFontSmoothingType(FontSmoothingType.LCD);
        gc.setFill(Color.WHITE);
        gc.fillText("Font Rendering Quality : フォントレンダリング品質(Canvas LCD 重ね塗り)", 13, 20);
        gc.fillText("Font Rendering Quality : フォントレンダリング品質(Canvas LCD 重ね塗り)", 13, 20);
        return canvas;
    }

拡大すると


根本的には Prism 側に直接手を入れないとダメそう。


Metal レンダリングパイプライン

JavaFX 27 では Metal が macOS のデフォルトレンダリングパイプラインになる(旧来はOpenGLベースのES2パイプラインがデフォルト)。これで多少は改善されるか?

JavaFX 26 でもシステムプロパティの設定で切り替えることができる。

System.setProperty("prism.order", "mtl,es2,sw");
System.setProperty("prism.verbose", "true");

このあたりの設定は以下を見ると良い

github.com

まぁ、以下のように変化は見られない。