Re: Bad performance with Canvas and extensive clipping
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(
Re: Bad performance with Canvas and extensive clipping
I've filed RT-37300 for this: https://javafx-jira.kenai.com/browse/RT-37300 ...jim On 5/27/14 3:54 PM, Jim Graham wrote: Hi Tom, There are 2 upgrades to consider. One involves new API, but is probably best in the long run. Without API, we'd have to detect if the path were rectangular in the processing of the CLIP command in NGCanvas.java. If the 4 coordinates are an axis aligned rectangle on integer coordinates then we could special case that with g.setClipRect(). There are other considerations, such as: - If there is already a soft non-rect clip, then it should probably not bother with the special case since it won't simplify anything. - If we have special cased the rectangle, then we must track that across save/restore properly. - If we have a special case cliprect and then we get a non-special case rect as the argument of a CLIP command, then we need to resolve it into a singular case (most likely default back to soft clipping). - The processing that tries to detect "are they clearing the entire buffer" needs to be aware of any clip in effect - those tests are done at the javafx.scene.canvas.GraphicsContext level. We could put that fix in with no new API so it could go in as soon as we are satisfied with its stability. If we want to add new API, so that you don't have to construct a path every time you want to do clipRect() and we don't have to decipher your path to figure out that it is a rectangle, then we would have to wait for the next opportunity to add API (FX can add API in between major JDK releases, but there is a process to go through and I don't think we can do it for 8u20 any more). The process for that would be: javafx.scene.canvas.GraphicsContext would need a new method that would take the rectangular clipping parameters and put them into the buffer. The existing fillRect() method would provide a good template. A new "command code constant" would have to be added to represent "This is a clip rectangle request". NGCanvas would then need to digest the new buffer commands and I believe that the existing Prism call g.setClipRect() would work to enable the scissor clip (fast rectangular clipping). The question is what is the proper API? If we have it take doubles, would that imply to developers that there would be soft clipping of the edges similar to if you used a rectangular path and clip()? Right now Node.setClipNode(Rectangle) will do the fast scissor clip (g.setClipRect()) if the coordinates fall on integer axis-aligned coordinates, but it will do soft-edged clipping if there is rotation/skewing, or the coordinates are not integers. That would probably be the best API to mimic since HTML5 doesn't have a similar "cliprect" method... ...jim On 5/27/14 2:57 PM, Tom Schindl wrote: Is there anything I could do to help getting rectangular clipping into JavaFX - I tried to find my way through the sources but I'm not sure I have enough knowledge to provide a patch in this area. BTW it looks like I'm not alone with the clipping performance problem see http://tomsondev.bestsolution.at/2014/05/24/swtonfx-javafx-canvas-with-many-clipping-calls-unacceptable-slow/#comments Tom On 27.05.14 23:47, Jim Graham wrote: Canvas is, essentially, a "draw pixels" mechanism. We have to bundle the requests into a command stream due to threading issues, but when the requests get to the render thread then they get turned into pixels so the command stream is a temporary intermediary. Some of the hw J2D pipelines also have a temporary command stream due to platform threading issues as well. It all depends on which pipeline you use and on which platform in the case of J2D. FX simply normalized the threading on all pipelines/platforms so that we have a separate UI and render thread in all cases, but that concept is not foreign to J2D either. I'm fairly certain that the lack of simple rectangular clipping is probably the biggest cause of your performance problems. We do AA on everything in FX, though, whereas rendering to a BufferedImage by default will be non-AA unless you requested AA using the graphics hints. But on the up-side, we hw accelerate just about every operation in FX so it should be on par with performance there, modulo the lack of rectangular clipping... ...jim On 5/23/14 5:46 PM, 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
Re: Bad performance with Canvas and extensive clipping
Hi Tom, There are 2 upgrades to consider. One involves new API, but is probably best in the long run. Without API, we'd have to detect if the path were rectangular in the processing of the CLIP command in NGCanvas.java. If the 4 coordinates are an axis aligned rectangle on integer coordinates then we could special case that with g.setClipRect(). There are other considerations, such as: - If there is already a soft non-rect clip, then it should probably not bother with the special case since it won't simplify anything. - If we have special cased the rectangle, then we must track that across save/restore properly. - If we have a special case cliprect and then we get a non-special case rect as the argument of a CLIP command, then we need to resolve it into a singular case (most likely default back to soft clipping). - The processing that tries to detect "are they clearing the entire buffer" needs to be aware of any clip in effect - those tests are done at the javafx.scene.canvas.GraphicsContext level. We could put that fix in with no new API so it could go in as soon as we are satisfied with its stability. If we want to add new API, so that you don't have to construct a path every time you want to do clipRect() and we don't have to decipher your path to figure out that it is a rectangle, then we would have to wait for the next opportunity to add API (FX can add API in between major JDK releases, but there is a process to go through and I don't think we can do it for 8u20 any more). The process for that would be: javafx.scene.canvas.GraphicsContext would need a new method that would take the rectangular clipping parameters and put them into the buffer. The existing fillRect() method would provide a good template. A new "command code constant" would have to be added to represent "This is a clip rectangle request". NGCanvas would then need to digest the new buffer commands and I believe that the existing Prism call g.setClipRect() would work to enable the scissor clip (fast rectangular clipping). The question is what is the proper API? If we have it take doubles, would that imply to developers that there would be soft clipping of the edges similar to if you used a rectangular path and clip()? Right now Node.setClipNode(Rectangle) will do the fast scissor clip (g.setClipRect()) if the coordinates fall on integer axis-aligned coordinates, but it will do soft-edged clipping if there is rotation/skewing, or the coordinates are not integers. That would probably be the best API to mimic since HTML5 doesn't have a similar "cliprect" method... ...jim On 5/27/14 2:57 PM, Tom Schindl wrote: Is there anything I could do to help getting rectangular clipping into JavaFX - I tried to find my way through the sources but I'm not sure I have enough knowledge to provide a patch in this area. BTW it looks like I'm not alone with the clipping performance problem see http://tomsondev.bestsolution.at/2014/05/24/swtonfx-javafx-canvas-with-many-clipping-calls-unacceptable-slow/#comments Tom On 27.05.14 23:47, Jim Graham wrote: Canvas is, essentially, a "draw pixels" mechanism. We have to bundle the requests into a command stream due to threading issues, but when the requests get to the render thread then they get turned into pixels so the command stream is a temporary intermediary. Some of the hw J2D pipelines also have a temporary command stream due to platform threading issues as well. It all depends on which pipeline you use and on which platform in the case of J2D. FX simply normalized the threading on all pipelines/platforms so that we have a separate UI and render thread in all cases, but that concept is not foreign to J2D either. I'm fairly certain that the lack of simple rectangular clipping is probably the biggest cause of your performance problems. We do AA on everything in FX, though, whereas rendering to a BufferedImage by default will be non-AA unless you requested AA using the graphics hints. But on the up-side, we hw accelerate just about every operation in FX so it should be on par with performance there, modulo the lack of rectangular clipping... ...jim On 5/23/14 5:46 PM, 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.
Re: Bad performance with Canvas and extensive clipping
Is there anything I could do to help getting rectangular clipping into JavaFX - I tried to find my way through the sources but I'm not sure I have enough knowledge to provide a patch in this area. BTW it looks like I'm not alone with the clipping performance problem see http://tomsondev.bestsolution.at/2014/05/24/swtonfx-javafx-canvas-with-many-clipping-calls-unacceptable-slow/#comments Tom On 27.05.14 23:47, Jim Graham wrote: > Canvas is, essentially, a "draw pixels" mechanism. We have to bundle > the requests into a command stream due to threading issues, but when the > requests get to the render thread then they get turned into pixels so > the command stream is a temporary intermediary. Some of the hw J2D > pipelines also have a temporary command stream due to platform threading > issues as well. It all depends on which pipeline you use and on which > platform in the case of J2D. FX simply normalized the threading on all > pipelines/platforms so that we have a separate UI and render thread in > all cases, but that concept is not foreign to J2D either. > > I'm fairly certain that the lack of simple rectangular clipping is > probably the biggest cause of your performance problems. We do AA on > everything in FX, though, whereas rendering to a BufferedImage by > default will be non-AA unless you requested AA using the graphics hints. > But on the up-side, we hw accelerate just about every operation in FX > so it should be on par with performance there, modulo the lack of > rectangular clipping... > > ...jim > > On 5/23/14 5:46 PM, 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 : 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: >
Re: Bad performance with Canvas and extensive clipping
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 Jav
Re: Bad performance with Canvas and extensive clipping
Canvas is, essentially, a "draw pixels" mechanism. We have to bundle the requests into a command stream due to threading issues, but when the requests get to the render thread then they get turned into pixels so the command stream is a temporary intermediary. Some of the hw J2D pipelines also have a temporary command stream due to platform threading issues as well. It all depends on which pipeline you use and on which platform in the case of J2D. FX simply normalized the threading on all pipelines/platforms so that we have a separate UI and render thread in all cases, but that concept is not foreign to J2D either. I'm fairly certain that the lack of simple rectangular clipping is probably the biggest cause of your performance problems. We do AA on everything in FX, though, whereas rendering to a BufferedImage by default will be non-AA unless you requested AA using the graphics hints. But on the up-side, we hw accelerate just about every operation in FX so it should be on par with performance there, modulo the lack of rectangular clipping... ...jim On 5/23/14 5:46 PM, 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 : 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
Re: Bad performance with Canvas and extensive clipping
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 nati
Re: Bad performance with Canvas and extensive clipping
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 cli
Re: Bad performance with Canvas and extensive clipping
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 : >> >> 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 >>>
Re: Bad performance with Canvas and extensive clipping
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 : > > 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 >>
Re: Bad performance with Canvas and extensive clipping
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