JavaFx Gui与Swing的性能

我写了两个简单的程序,都绘制了相同的Sierpinski三角形: 在此处输入图像描述
一个程序使用swing实现,一个使用javafx。 存在非常显着的性能差异,swing实现持续快得多:

在此处输入图像描述

(在这个测试案例中:在1秒内摆动.Javafx超过12秒)
我的javafx实现是否可以预料或存在什么问题?

Swing实现

import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Polygon; import java.awt.image.BufferedImage; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; public class SimpleSrpnskTriSw { private Triangles triPanel; SimpleSrpnskTriSw(int numberOfLevels){ JFrame frame = new JFrame("Sierpinski Triangles (swing)"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); triPanel = new Triangles(); frame.add(triPanel, BorderLayout.CENTER); frame.pack(); frame.setResizable(false); frame.setVisible(true); triPanel.draw(numberOfLevels); } class Triangles extends JPanel{ private static final int PANEL_WIDTH =600, PANEL_HEIGHT = 600; private static final int TRI_WIDTH= 500, TRI_HEIGHT= 500; private static final int SIDE_GAP = (PANEL_WIDTH - TRI_WIDTH)/2; private static final int TOP_GAP = (PANEL_HEIGHT - TRI_HEIGHT)/2; private int countTriangles; private long startTime; boolean working; private int numberOfLevels = 0; Triangles() { setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT)); startTime = System.currentTimeMillis(); countTriangles = 0; working = true; draw(); } void draw(int numLevels) { numberOfLevels = numLevels; working = true; draw(); } void draw() { startTime = System.currentTimeMillis(); countTriangles = 0; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { repaint(); } }); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.setFont(new Font("Ariel", Font.PLAIN, 14)); if(working) { g.setColor(getBackground()); g.fillRect(0,0,PANEL_WIDTH,PANEL_HEIGHT); g.setColor(getForeground()); g.drawString("Working.........", 15, 15); working = false; return; } if(numberOfLevels <= 0 ) { return; } Point top = new Point(PANEL_WIDTH/2, TOP_GAP); Point left = new Point(SIDE_GAP, TOP_GAP+ TRI_HEIGHT); Point right = new Point(SIDE_GAP + TRI_WIDTH, TOP_GAP+ TRI_HEIGHT); BufferedImage bi = getBufferedImage(top, left, right); Graphics2D g2d = (Graphics2D) g; g2d.drawImage(bi,0,0, this); g.drawString("Number of triangles: "+ countTriangles, 15, 15); g.drawString("Time : "+ (System.currentTimeMillis()- startTime)+ " mili seconds", 15, 35); g.drawString("Levels: "+ numberOfLevels, 15, 50); } private BufferedImage getBufferedImage(Point top, Point left, Point right) { BufferedImage bi = new BufferedImage(PANEL_WIDTH,PANEL_HEIGHT, BufferedImage.TYPE_INT_ARGB); drawTriangle(bi, numberOfLevels, top, left, right); return bi; } private void drawTriangle(BufferedImage bi, int levels, Point top, Point left, Point right) { if(levels < 0) { return ; } countTriangles++; Graphics g = bi.getGraphics(); g.setColor(Color.RED); Polygon tri = new Polygon(); tri.addPoint(top.x, top.y); //use top,left right rather than fixed points tri.addPoint(left.x, left.y); tri.addPoint(right.x, right.y); g.drawPolygon(tri); // Get the midpoint on each edge in the triangle Point p12 = midpoint(top, left); Point p23 = midpoint(left, right); Point p31 = midpoint(right, top); // recurse on 3 triangular areas drawTriangle(bi, levels - 1, top, p12, p31); drawTriangle(bi, levels - 1, p12, left, p23); drawTriangle(bi, levels - 1, p31, p23, right); } private Point midpoint(Point p1, Point p2) { return new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2); } } public static void main(String[] args) { new SimpleSrpnskTriSw(13); } } 

JavaFx实现

 import javafx.application.Application; import javafx.application.Platform; import javafx.concurrent.Task; import javafx.geometry.Insets; import javafx.geometry.Point2D; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.image.WritableImage; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.stage.Stage; public class SimpleSrpnskTriFx extends Application { private final int PADDING = 5; private static int numberOfLevels; public static void launch(String... args){ numberOfLevels = 8; if((args != null) && (args.length > 0)) { try { int num = Integer.parseInt(args[0]); numberOfLevels = num ; } catch (NumberFormatException ex) { ex.printStackTrace(); return; } } Application.launch(args); } @Override public void start(Stage stage) { stage.setOnCloseRequest((ae) -> { Platform.exit(); System.exit(0); }); stage.setTitle("Sierpinski Triangles (fx)"); BorderPane mainPane = new BorderPane(); mainPane.setPadding(new Insets(PADDING)); Pane triPanel = new Triangles(); BorderPane.setAlignment(triPanel, Pos.CENTER); mainPane.setCenter(triPanel); Scene scene = new Scene(mainPane); stage.setScene(scene); stage.centerOnScreen(); stage.setResizable(false); stage.show(); } class Triangles extends AnchorPane{ private static final int PANEL_WIDTH =600, PANEL_HEIGHT = 600; private static final int TRI_WIDTH= 500, TRI_HEIGHT= 500; private static final int SIDE_GAP = (PANEL_WIDTH - TRI_WIDTH)/2; private static final int TOP_GAP = (PANEL_HEIGHT - TRI_HEIGHT)/2; private int countTriangles; private long startTime; private Point2D top, left, right; private Canvas canvas; private Canvas backgroundCanvas; private GraphicsContext gc; Triangles(){ setPrefSize(PANEL_WIDTH, PANEL_HEIGHT); canvas = getCanvas(); backgroundCanvas = getCanvas(); gc = backgroundCanvas.getGraphicsContext2D(); getChildren().add(canvas); draw(numberOfLevels); } void draw(int numberLevels) { Platform.runLater(new Runnable() { @Override public void run() { canvas.getGraphicsContext2D().fillText("Working....",5,15); setStartPoints(); startTime = System.currentTimeMillis(); countTriangles = 0; RunTask task = new RunTask(numberLevels, top, left, right); Thread thread = new Thread(task); thread.setDaemon(true); thread.start(); } }); } private void drawTriangle( int levels, Point2D top, Point2D left, Point2D right) { if(levels < 0) {//add stop criteria return ; } gc.strokePolygon( //implementing with strokeLine did not make much difference new double[]{ top.getX(),left.getX(),right.getX() }, new double[]{ top.getY(),left.getY(), right.getY() }, 3); countTriangles++; //Get the midpoint on each edge in the triangle Point2D p12 = midpoint(top, left); Point2D p23 = midpoint(left, right); Point2D p31 = midpoint(right, top); // recurse on 3 triangular areas drawTriangle(levels - 1, top, p12, p31); drawTriangle(levels - 1, p12, left, p23); drawTriangle(levels - 1, p31, p23, right); } private void setStartPoints() { top = new Point2D(getPrefWidth()/2, TOP_GAP); left = new Point2D(SIDE_GAP, TOP_GAP + TRI_HEIGHT); right = new Point2D(SIDE_GAP + TRI_WIDTH, TOP_GAP + TRI_WIDTH); } private Point2D midpoint(Point2D p1, Point2D p2) { return new Point2D((p1.getX() + p2.getX()) / 2, (p1.getY() + p2.getY()) / 2); } private void updateGraphics(boolean success){ if(success) { copyCanvas(); GraphicsContext gc = canvas.getGraphicsContext2D(); gc.fillText("Number of triangles: "+ countTriangles,5,15); gc.fillText("Time : "+ (System.currentTimeMillis()- startTime )+ " mili seconds", 5,35); gc.fillText("Levels: "+ numberOfLevels,5,55); } } private Canvas getCanvas() { Canvas canvas = new Canvas(); canvas.widthProperty().bind(widthProperty()); canvas.heightProperty().bind(heightProperty()); canvas.getGraphicsContext2D().setStroke(Color.RED); canvas.getGraphicsContext2D().setLineWidth(0.3f); return canvas; } private void copyCanvas() { WritableImage image = backgroundCanvas.snapshot(null, null); canvas.getGraphicsContext2D().drawImage(image, 0, 0); } /** */ class RunTask extends Task{ private int levels; private Point2D top, left; private Point2D right; RunTask(int levels, Point2D top, Point2D left, Point2D right){ this.levels = levels; this.top = top; this.left = left; this.right = right; startTime = System.currentTimeMillis(); countTriangles = 0; } @Override public Void call() { drawTriangle(levels,top, left, right); return null; } @Override protected void succeeded() { updateGraphics(true); super.succeeded(); } @Override protected void failed() { updateGraphics(false); } } } public static void main(String[] args) { launch("13"); } } 

Swing示例将图像展为600 2 = 360,000像素。 相比之下,JavaFX示例在最终渲染时会渲染近240万个重叠多边形。 请注意,您的JavaFX示例测量组成分形的时间在场景图中渲染分形的时间。

如果要保留包含分形的笔划,请在Canvas组合结果,如此处所示。

如果平面Image足够,则将结果组合在BufferedImage ,将其转换为JavaFX Image ,并将其显示在ImageView ,如下所示。 JavaFX结果比我硬件上的Swing示例一秒。

因为SwingFXUtils.toFXImage制作副本 ,后台Task可以继续更新单个BufferedImage同时通过updateValue()发布临时Image结果。

图片

 import java.awt.Color; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Polygon; import java.awt.image.BufferedImage; import javafx.application.Application; import javafx.embed.swing.SwingFXUtils; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.image.WritableImage; import javafx.scene.layout.StackPane; import javafx.stage.Stage; /** * @see https://stackoverflow.com/q/44136040/230513 */ public class BufferedImageTest extends Application { private static final int PANEL_WIDTH = 600, PANEL_HEIGHT = 600; private static final int TRI_WIDTH = 500, TRI_HEIGHT = 500; private static final int SIDE_GAP = (PANEL_WIDTH - TRI_WIDTH) / 2; private static final int TOP_GAP = (PANEL_HEIGHT - TRI_HEIGHT) / 2; private final int numberOfLevels = 13; private int countTriangles; @Override public void start(Stage stage) { stage.setTitle("BufferedImageTest"); StackPane root = new StackPane(); Scene scene = new Scene(root); root.getChildren().add(new ImageView(createImage())); stage.setScene(scene); stage.show(); } private Image createImage() { BufferedImage bi = new BufferedImage( PANEL_WIDTH, PANEL_HEIGHT, BufferedImage.TYPE_INT_ARGB); Graphics2D g = bi.createGraphics(); g.setPaint(Color.white); g.fillRect(0, 0, PANEL_WIDTH, PANEL_HEIGHT); Point top = new Point(PANEL_WIDTH / 2, TOP_GAP); Point left = new Point(SIDE_GAP, TOP_GAP + TRI_HEIGHT); Point right = new Point(SIDE_GAP + TRI_WIDTH, TOP_GAP + TRI_HEIGHT); g.setColor(Color.red); long startTime = System.currentTimeMillis(); drawTriangle(g, numberOfLevels, top, left, right); g.setPaint(Color.black); g.drawString("Number of triangles: " + countTriangles, 15, 15); g.drawString("Time : " + (System.currentTimeMillis() - startTime) + " ms", 15, 35); g.drawString("Levels: " + numberOfLevels, 15, 50); WritableImage image = SwingFXUtils.toFXImage(bi, null); g.dispose(); return image; } private void drawTriangle(Graphics2D g, int levels, Point top, Point left, Point right) { if (levels < 0) { return; } countTriangles++; Polygon tri = new Polygon(); tri.addPoint(top.x, top.y); tri.addPoint(left.x, left.y); tri.addPoint(right.x, right.y); g.drawPolygon(tri); // Get the midpoint on each edge in the triangle Point p12 = midpoint(top, left); Point p23 = midpoint(left, right); Point p31 = midpoint(right, top); // recurse on 3 triangular areas drawTriangle(g, levels - 1, top, p12, p31); drawTriangle(g, levels - 1, p12, left, p23); drawTriangle(g, levels - 1, p31, p23, right); } private Point midpoint(Point p1, Point p2) { return new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2); } public static void main(String[] args) { launch(args); } }