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