javacanvasjavafxpaintgraphicscontext

JavaFx canvas filltext rendering quality


I am working on a JavaFx application which use canvas to represent a diagram. The canvas is painting text using graphicsContext.fillText(). In the image below the canvas is on the right, on the left is a Label using the same font. My question is which renering parameter should I use to make the right text look the same as on the left ?

Label vs Canvas using Monospaced font

public class SampleRenderingIssue extends Application {
    private final StackPane root = new StackPane();
    private final Scene scene = new Scene(root, 300, 250);
    private final BorderPane pane = new BorderPane();
    private final Canvas canvas = new Canvas();


    @Override
    public void start(Stage stage) {
        stage.setTitle("Sample Canvas");

        VBox vbox = new VBox();
        VBox.setVgrow(pane, Priority.ALWAYS);
        vbox.getChildren().addAll( pane );

        pane.getChildren().add(canvas);
        root.getChildren().add(vbox);
        stage.setScene(scene);
        stage.sizeToScene();
        setupCanvasPane();
        stage.show();
        Platform.runLater(()-> paint());
    }
    private void setupCanvasPane(){
        canvas.widthProperty().bind(pane.widthProperty());
        canvas.heightProperty().bind(pane.heightProperty());
        pane.widthProperty().addListener((o,p,c)-> paint());
        paint();
    }

    public void paint(){
        GraphicsContext gr = canvas.getGraphicsContext2D();
        gr.clearRect( 0,0, canvas.getWidth(), canvas.getHeight() );
        gr.setFill( Color.web("#222222") );
        gr.fillRect( 0,0,canvas.getWidth(), canvas.getHeight());
        gr.setStroke( Color.WHITE );
        gr.setFill( Color.WHITE );
        gr.setLineWidth( 1d );
        gr.strokeLine( 0,0, canvas.getWidth(), canvas.getHeight() );
        gr.setFont( Font.font( "Candara"));
        gr.fillText("This is a text", 100, 100 );
        gr.setFont( Font.font( "Monospaced"));
        gr.fillText("This is a text", 100, 120 );
    }

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

}

The issue reproduces on a FullHd display where the Windows scale in Control Panel is set to 125% ( default value for a notebook ).

enter image description here


Solution

  • I also had poor text rendering quality in a Canvas. I display dense text in a small font size and it looked quite ugly before.

    This may or may not be the same problem you have been running up against, since my Windows scaling is at 100%, but I've discovered in my case it was caused by the Canvas not using the same ClearType implementation as the rest of the UI widgets do. It seems to be set to GREY mode instead of LCD. It's a simple one line fix:

            GraphicsContext gc = canvas.getGraphicsContext2D();
            gc.setFontSmoothingType(FontSmoothingType.LCD);
            gc.setFont(Font.font("Consolas", 10));
            gc.fillText("Example ABC 123", 10, 10);
    

    Here is the result, in each case the top line is the rendered version, and the bottom line is a Label in the scene. I've included a zoomed in version, where you can clearly see Canvas is not doing the LCD subpixel rendering correctly

    enter image description here

    In the right handle example they appear to be identical, and my visual quality is restored.