JavaFX Tip 2: Sharp Drawing with Canvas API

When I initially started out working with the Canvas API I noticed that the results of my rendering code were somewhat blurry and even worse, inconsistent. Some lines were blurry, others sharp. Coming from Swing it took me some time to realize that this was caused by the coordinate system of JavaFX, which allows for double precision rendering.
To solve this problem all that is needed is to use coordinates “in the middle”. So in my code you now find a lot of methods called snapXZY() (similar methods can be found in the JavaFX code itself), which first casts the given coordinate to an integer and then adds .5 to it. The following screenshot shows the difference when using this approach.

Bildschirmfoto 2014-04-10 um 11.59.49
The code below was used for this example:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

/**
 * Tip 2: How to render sharp lines in a canvas.
 */
public class Tip2DrawingSharpLinesInCanvas extends 
        Application {

	class MyCanvas extends Canvas {

		public MyCanvas(boolean drawSharpLines) {

			setWidth(150);
			setHeight(150);

			double w = getWidth();
			double h = getHeight();

			GraphicsContext gc = getGraphicsContext2D();
			gc.clearRect(0, 0, w, h);

			gc.setStroke(Color.GRAY);
			gc.strokeRect(0, 0, w, h);

			for (double y = 20; y <= h - 20; y += 10) {
				if (drawSharpLines) {
					// Snap the y coordinate 
					gc.strokeLine(10, 
                                   snap(y), 
                                   w - 10, 
                                   snap(y));
				} else {
					gc.strokeLine(10, y, w - 10, y);
				}
			}
		}
		
		private double snap(double y) {
			return ((int) y) + .5;
		}
	}

	@Override
	public void start(Stage stage) throws Exception {
		MyCanvas canvasBlurry = new MyCanvas(false);
		MyCanvas canvasSharp = new MyCanvas(true);

		Label labelBlurry = new Label("Blurry");
		Label labelSharp = new Label("Sharp");

		VBox.setMargin(canvasBlurry, new Insets(10));
		VBox.setMargin(canvasSharp, new Insets(10));

		VBox.setMargin(labelBlurry, 
                     new Insets(10, 10, 0, 10));
		VBox.setMargin(labelSharp, 
                     new Insets(10, 10, 0, 10));

		VBox box = new VBox();
		box.getChildren().add(labelBlurry);
		box.getChildren().add(canvasBlurry);
		box.getChildren().add(labelSharp);
		box.getChildren().add(canvasSharp);

		stage.setScene(new Scene(box));
		stage.setTitle("Tip 2: Sharp Lines in Canvas");
		stage.show();
	}

	public static void main(String[] args) {
		launch(args);
	}
}
About these ads
Tagged

4 thoughts on “JavaFX Tip 2: Sharp Drawing with Canvas API

  1. Mikael Grev says:

    I don’t know JavaFX that much yet but I expect snap to only work for odd widths? for even stroke widths one should NOT add .5, right? At least that’s how it works in Swing’s PURE mode and in Android’s Canvas.

  2. dlemmermann says:

    I will check. I must admit that so far I hardly ever set the line / stroke width.

  3. dlemmermann says:

    I did check … and you are right. This is only needed for odd stroke width: 1, 3, 5, ….

  4. Werner Lehmann says:

    The curious thing is that the FX guys snap to integer numbers instead, see Region.snapSize() for example. To my experience that does not work and adding 0.5 does the trick (and we talked about that before) but I don’t really understand when to do it and when not to do it, and why the FX snap works differently.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Pixel Perfect

Where pixels have a home

Tomsondev Blog

Tom's opensource development

Carl's FX Blog

Just Ordinary Users of the API

Jasper Potts's Blog

Jasper Potts's Blog on Java and Life

Jasper Potts's Blog

Where pixels have a home

Exploding Pixels

Exposing the pixels behind beautiful user interfaces

Follow

Get every new post delivered to your Inbox.

Join 25 other followers

%d bloggers like this: