Bad performance with Canvas and extensive clipping

Jim Graham james.graham at oracle.com
Tue May 27 21:38:14 UTC 2014


You may have been testing J2D in a pre-retina-aware VM vs. JavaFX which 
was retina aware a little earlier than J2D (due to JavaFX being on a 
slightly more liberal feature policy for new releases).  I think J2D 
went retina-aware in 8.0, are you using 8.0 for those tests?

The screenshot may be because the snapshot and robot mechanisms may not 
be retina-aware yet.

I don't think there are significant differences in the font technologies 
between J2D and FX...

			...jim

On 5/24/14 6:54 AM, Tom Schindl wrote:
> Hi,
>
> another big difference when using the a BufferedImage is to that the
> font rendering is catastrophic, hope to offend nobody. I'm not very good
> a AWT maybe I made a dumb mistake?
>
> See http://downloads.efxclipse.org/font_j2d_fx.png - the j2d font looks
> completely blurred in contrast to the sharp JavaFX Canvas version in the
> foreground.
>
> Similar blurring happens when makeing screenshots of a canvas - I've
> written a small sample application showing problems I am seeing which
> gets me to an image as in this link
> http://downloads.efxclipse.org/screen_compare.png.
>
> Could I somehow use the javafx font-rendering push it to a bitmap and
> draw it on the buffered image?
>
> Anyways those are all only workarounds for javafx canvas inefficiencies
> that e.g. awt does not have.
>
>> package application;
>> 	
>> import java.awt.Graphics2D;
>> import java.awt.RenderingHints;
>> import java.awt.image.BufferedImage;
>>
>> import javafx.application.Application;
>> import javafx.embed.swing.SwingFXUtils;
>> import javafx.geometry.VPos;
>> import javafx.scene.Node;
>> import javafx.scene.Scene;
>> import javafx.scene.SnapshotParameters;
>> import javafx.scene.canvas.Canvas;
>> import javafx.scene.control.Label;
>> import javafx.scene.image.ImageView;
>> import javafx.scene.image.WritableImage;
>> import javafx.scene.layout.HBox;
>> import javafx.scene.layout.VBox;
>> import javafx.scene.paint.Color;
>> import javafx.scene.text.Font;
>> import javafx.stage.Stage;
>>
>>
>> public class Main extends Application {
>> 	private WritableImage img;
>> 	
>> 	@Override
>> 	public void start(Stage primaryStage) {
>> 		try {
>> 			HBox root = new HBox();
>> 			Scene scene = new Scene(root,400,400);
>> 			scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
>> 			
>> 			root.getChildren().add(createCanvas());
>> 			root.getChildren().add(createBufferedCanvas());
>> 			root.getChildren().add(new VBox(new ImageView(img), new Label("Snapshot")));
>> 			
>> 			primaryStage.setScene(scene);
>> 			primaryStage.show();
>> 		} catch(Exception e) {
>> 			e.printStackTrace();
>> 		}
>> 	}
>> 	
>> 	private Node createBufferedCanvas() {
>> 		VBox b = new VBox();
>> 		b.setStyle("-fx-border-style: solid; -fx-border-width: 2px;");
>> 		Canvas c = new Canvas(150, 150);
>> 		BufferedImage img = new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB);
>> 		Graphics2D graphics = img.createGraphics();
>> 		graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
>> 		graphics.setColor(java.awt.Color.BLACK);
>> 		graphics.setFont(new java.awt.Font(Font.getDefault().getName(), java.awt.Font.PLAIN, 20));
>> 		graphics.drawString("Hello World!", 0, 20);
>> 		img.flush();
>> 		c.getGraphicsContext2D().drawImage(SwingFXUtils.toFXImage(img, null),10,10);
>> 		
>> 		b.getChildren().add(c);
>> 		b.getChildren().add(new Label("Buffered-Canvas"));
>> 		
>> 		return b;
>> 	}
>> 	
>> 	private Node createCanvas() {
>> 		VBox b = new VBox();
>> 		b.setStyle("-fx-border-style: solid; -fx-border-width: 2px;");
>> 		Canvas c = new Canvas(150, 150);
>> 		c.getGraphicsContext2D().setFont(Font.font(Font.getDefault().getName(),20));
>> 		c.getGraphicsContext2D().setTextBaseline(VPos.TOP);
>> 		c.getGraphicsContext2D().fillText("Hello World", 10, 10);
>> 		
>> 		SnapshotParameters parameters = new SnapshotParameters();
>> 		parameters.setFill(Color.TRANSPARENT);
>> 		img = c.snapshot(parameters,null);
>> 		
>> 		b.getChildren().add(c);
>> 		b.getChildren().add(new Label("FX-Canvas"));
>> 		return b;
>> 	}
>> 	
>> 	public static void main(String[] args) {
>> 		launch(args);
>> 	}
>> }
>
> Tom
>
> On 24.05.14 02:46, Tom Schindl wrote:
>> Hi,
>>
>> As an experiment I've now written a SWT-GC implementation using a
>> BufferedImage & Graphics2D and transfering the pixels over to JavaFX and
>> the performance is as it is with native SWT.
>>
>> I always thought Canvas works similar to Image and one only draws pixels
>> - looks like that is not the case, having a dep in my application
>> java.awt is not what I'm aiming at but without acceptable performance in
>> conjunction with clipping it looks like i have to go this route :-(
>>
>> Tom
>>
>> On 23.05.14 23:57, Tom Schindl wrote:
>>> In the current usecase it is a rect all time but that's just in this special use case.
>>>
>>> I guess that rect clipping is the most common one so having an optimization for rects and a slow path for none rects might help.
>>>
>>> Tom
>>>
>>> Von meinem iPhone gesendet
>>>
>>>> Am 23.05.2014 um 23:35 schrieb Jim Graham <james.graham at oracle.com>:
>>>>
>>>> Are you clipping to an arbitrary path in all cases or just a rectangle?  Unfortunately we only offer the arbitrary clip-to-current-path method that isn't optimized for basic rectangular clipping and it implements soft clipping.
>>>>
>>>> There is an outstanding tweak that we added faster clipping support for WebNode and we need to start using it for Node.setClipNode(non-rectangle) and Canvas, but we haven't implemented that yet.  (https://javafx-jira.kenai.com/browse/RT-30107)  It basically is a direct "render this texture through that other texture as a clip" operation instead of the current code that runs it through some Blend effect filters.  It would definitely improve your run times, but I'm not sure how much.
>>>>
>>>> Even more savings could be had for rectangular clips if we provided some way to communicate them to the GC...
>>>>
>>>>             ...jim
>>>>
>>>>> On 5/23/14 11:47 AM, Tom Schindl wrote:
>>>>> Hi,
>>>>>
>>>>> Maybe as some of you might know I've been working since sometime on SWT
>>>>> on JavaFX and to implement direct drawing operations we use JavaFX-Canvas.
>>>>>
>>>>> I've today tried to run a heavy direct drawing grid implementation and
>>>>> it performed very bad because it makes heavy use of clipping.
>>>>>
>>>>> For a grid I've counted ~1500 clipping operations the library works
>>>>> something like this:
>>>>>
>>>>> boolean activeClip;
>>>>> Canvas canvas = new Canvas();
>>>>>
>>>>> public void setClipping(PathIterator pathIterator) {
>>>>>    GraphicsContext gc = canvas.getGraphicsContext2D();
>>>>>    if(activeClip) {
>>>>>      gc.restore();
>>>>>      activeClip= false;
>>>>>    }
>>>>>
>>>>>    if( pathIterator == null ) {
>>>>>      return;
>>>>>    }
>>>>>
>>>>>    activeClip = true;
>>>>>    float coords[] = new float[6];
>>>>>    gc.save();
>>>>>         gc.beginPath();
>>>>>
>>>>>         float x = 0;
>>>>>         float y = 0;
>>>>>
>>>>>
>>>>>         gc.moveTo(0, 0);
>>>>>
>>>>>         while( ! pathIterator.isDone() ) {
>>>>>             switch (pathIterator.currentSegment(coords)) {
>>>>>             case PathIterator.SEG_CLOSE:
>>>>>                 gc.lineTo(x, y);
>>>>>                 break;
>>>>>             case PathIterator.SEG_CUBICTO:
>>>>>                 gc.bezierCurveTo(coords[0], coords[1], coords[2], coords[3],
>>>>> coords[4], coords[5]);
>>>>>                 break;
>>>>>             case PathIterator.SEG_LINETO:
>>>>>                 gc.lineTo(coords[0], coords[1]);
>>>>>                 break;
>>>>>             case PathIterator.SEG_MOVETO:
>>>>>                 gc.moveTo(coords[0], coords[1]);
>>>>>                 x = coords[0];
>>>>>                 y = coords[1];
>>>>>                 break;
>>>>>             case PathIterator.SEG_QUADTO:
>>>>>                 gc.quadraticCurveTo(coords[0], coords[1], coords[2], coords[3]);
>>>>>                 break;
>>>>>             default:
>>>>>                 break;
>>>>>             }
>>>>>             pathIterator.next();
>>>>>         }
>>>>>
>>>>>         gc.clip();
>>>>>         gc.closePath();
>>>>> }
>>>>>
>>>>> Am I doing something ultimately wrong, totally wrong? Has anyone an idea
>>>>> how I would work around the problem?
>>>>>
>>>>> Tom
>>>>>
>>
>


More information about the openjfx-dev mailing list