Bad performance with Canvas and extensive clipping
Jim Graham
james.graham at oracle.com
Tue May 27 23:24:19 UTC 2014
My apologies. If Swing was managing the back buffer for you then they
would make it retina-aware for you on 8.0. If you are creating your own
BufferedImage then it will not be retina-scaled unless you do that
yourself. Right now we are working to get the "Swing embedded in FX"
mechanisms to be retina-aware and it isn't a trivial task.
I'll have to look and see how Swing manages it to know if you can tap
into the same mechanisms, but it involves creating an image twice as big
as you need and then rendering into it with a default graphics scale of
2.0 and then making sure you render it at the right size to the
destination. (Which is shoveling pixels into a WritableImage I presume?
You'd have to make sure to set the right fitWidth/Height to keep them
at the right destination scale.) Swing is already doing that for you
with the hidden back buffer, but it is hard to redirect that to FX. The
Swing-to-FX mechanism will soon be able to do that for you, but your
project may not be happy pretending it is a Swing component to achieve
that goal.
Or, we could get Canvas to do faster rectangular clipping...
...jim
On 5/27/14 2:54 PM, Tom Schindl wrote:
> 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