我写了两个简单的程序,两个都绘制了相同的Sierpinski三角形:
一个程序使用swing实现,一个使用javafx。
存在非常显着的性能差异,摆动实施持续快得多:
(在这个测试案例中:在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<Void>{
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");
}
}
答案 0 :(得分:17)
Swing示例将图像展平 600 2 = 360,000像素。相比之下,JavaFX示例在最终渲染时会触发近240万个重叠多边形。请注意,您的JavaFX示例测量两者组成分形的时间和在scene graph中呈现分形的时间。
如果要保留包含分形的笔画,请将结果组成Canvas
,如图here所示。
如果广告Image
已足够,请将结果复制到BufferedImage
,将其转换为JavaFX Image
,然后将其显示在ImageView
中,如下所示。 JavaFX结果比我硬件上的Swing示例快更快。
由于SwingFXUtils.toFXImage
制作副本,后台Task<Image>
可以继续更新单个BufferedImage
,同时通过{{发布临时Image
结果1}}。
updateValue()