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.gra...@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 >>>> >