Bad performance with Canvas and extensive clipping

Tom Schindl tom.schindl at bestsolution.at
Tue May 27 21:54:04 UTC 2014


I'm on java8u5!

Tom

On 27.05.14 23:38, Jim Graham wrote:
> 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