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