为什么 java.awt.Graphics.drawLine 异常缓慢?
Why is java.awt.Graphics.drawLine exceptionally slow?
我正在尝试实现以下 'grid' 布局。
class扩展java.awt.Canvas,在paint
函数中绘制这些形状(或线条)。为什么 Canvas?检查 here,最初尝试做类似的事情。
已更新 MCVE 获取上述内容的代码 'layout':
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
@SuppressWarnings("serial")
public class SO_MCVE extends JPanel {
private DrawingCanvas _drawingCanvas = null;
private JButton repaintBtn;
public SO_MCVE() {
super(new BorderLayout());
_drawingCanvas = new DrawingCanvas();
_drawingCanvas.setSize(new Dimension(600, 600));
JLabel repaintLabel = new JLabel(
"<html><div style=\"text-align: center;\">" +
"REPAINT</html>");
repaintLabel.setHorizontalAlignment(
SwingConstants.CENTER);
repaintBtn = new JButton();
repaintBtn.add(repaintLabel);
repaintBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
_drawingCanvas.triggerRepaint();
}
});
add(_drawingCanvas, BorderLayout.CENTER);
add(repaintBtn, BorderLayout.PAGE_END);
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Whosebug MCVE for drawLine");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new SO_MCVE());
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
UIManager.put("swing.boldMetal", Boolean.FALSE);
createAndShowGUI();
}
});
}
}
@SuppressWarnings("serial")
class DrawingCanvas extends Canvas {
public static final Color lightGreen = new Color(0, 255, 0, 180);
public static final BasicStroke STROKE1PX = new BasicStroke(1.0f);
public static final BasicStroke STROKE3PX = new BasicStroke(3.0f);
private static final int LEFT = 50;
private static final int RIGHT = 550;
private static final int TOP = 50;
private static final int BOTTOM = 550;
private static final double WIDTH = 500.00d;
private static final double HEIGHT = 500.00d;
public DrawingCanvas() {
setBackground(Color.BLACK);
}
public void paint(Graphics g) {
update(g);
}
public void triggerRepaint() {
repaint();
}
public void update(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Dimension dim = getSize();
int w = (int) dim.getWidth();
int h = (int) dim.getHeight();
// Clears the rectangle that was previously drawn
g2.setPaint(Color.BLACK);
g2.fillRect(0, 0, w, h);
drawLines(g2, w, h);
}
/** Draw the lines marking the x-y limits **/
private void drawLines(Graphics2D g2, int w, int h) {
long start = System.nanoTime();
System.out.println("Start of drawLines(): " + start);
// Thick lines
g2.setPaint(Color.GREEN);
g2.setStroke(STROKE3PX);
g2.drawLine(LEFT, 0, LEFT, h);
g2.drawLine(RIGHT, 0, RIGHT, h);
g2.drawLine(0, TOP, w, TOP);
g2.drawLine(0, BOTTOM, w, BOTTOM);
System.out.println("Done drawing thick lines!");
long end = System.nanoTime();
System.out.println("Time taken (ns): " +
(end - start) + ", Time taken(ms): " +
((end - start)/1000/1000));
start = end;
// Thin vertical lines
g2.setPaint(lightGreen);
g2.setStroke(STROKE1PX);
int wInc = ((int) WIDTH) / 50;
for(int i = LEFT; i <= RIGHT; i += wInc) {
g2.drawLine(i, TOP, i, BOTTOM);
}
System.out.println("Done drawing vertical lines!");
end = System.nanoTime();
System.out.println("Time taken (ns): " +
(end - start) + ", Time taken(ms): " +
((end - start)/1000/1000));
start = end;
// Thin horizontal lines
g2.setPaint(lightGreen);
g2.setStroke(STROKE1PX);
int hInc = ((int) HEIGHT) / 50;
for(int i = TOP; i <= BOTTOM; i += hInc) {
g2.drawLine(LEFT, i, RIGHT, i);
}
System.out.println("Done drawing horizontal lines!");
end = System.nanoTime();
System.out.println("Time taken (ns): " +
(end - start) + ", Time taken(ms): " +
((end - start)/1000/1000));
System.out.println();
}
}
上面显示的代码的问题是,每当我调用 repaint()
.
时,渲染这些行需要一段时间(大约 3 秒)
按 "Repaint" 按钮触发 MCVE 中的重绘。
线条会一条一条慢慢画出来,如下图:
所以问题是:
drawLine
这么慢有什么原因吗?我已经尝试在类似的 for 循环中使用 g2.draw(一些 Ellipse2D.Double..) 绘制尽可能多(如果不是更多)的椭圆并且没有问题。
注:使用jre1.7.0_25,Windows 7、Eclipse
使用 System.nanoTime() 的简单基准测试:
Done drawing thick lines!
Time taken (ns): 8858966, Time taken(ms): 8
Done drawing vertical lines!
Time taken (ns): 3649188968, Time taken(ms): 3649
Done drawing horizontal lines!
Time taken (ns): 106730282, Time taken(ms): 106
注意:画'thin vertical lines'要花很长时间!
更新:
注:使用jre1.8.0_11,Windows 7、Eclipse
使用 System.nanoTime() 的简单基准测试:
Done drawing thick lines!
Time taken (ns): 110027, Time taken(ms): 0
Done drawing vertical lines!
Time taken (ns): 185567, Time taken(ms): 0
Done drawing horizontal lines!
Time taken (ns): 195419, Time taken(ms): 0
注:使用jre1.8.0_45,Windows 7、Eclipse
使用 System.nanoTime() 的简单基准测试:
Done drawing thick lines!
Time taken (ns): 6716121, Time taken(ms): 6
Done drawing vertical lines!
Time taken (ns): 2427676380, Time taken(ms): 2427
Done drawing horizontal lines!
Time taken (ns): 83030042, Time taken(ms): 83
Apparently, jre1.8.0_11 works really well?
我如何 运行 使用不同的 jre 版本(不确定我做的是否正确!):
谢谢! :)
按照 Michael Dibbets 的建议进行操作并使用缓冲。
我用绘制到屏幕外缓冲区然后绘制缓冲区的方法替换了 the SO_MCVE.update(Graphics g)
方法:
public void update(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Dimension dim = getSize();
int w = (int) dim.getWidth();
int h = (int) dim.getHeight();
// Create the buffer
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D ig2 = image.createGraphics();
// Paint everythign on the buffer
// Clears the rectangle that was previously drawn
ig2.setPaint(Color.BLACK);
ig2.fillRect(0, 0, w, h);
drawLines(ig2, w, h);
// Paint the buffer
g2.drawImage(image, 0, 0, null);
}
Start of drawLines(): 1832687816359773
Done drawing thick lines!
Time taken (ns): 2212913, Time taken(ms): 2
Done drawing vertical lines!
Time taken (ns): 37676442, Time taken(ms): 37
Done drawing horizontal lines!
Time taken (ns): 6453455, Time taken(ms): 6
如果您想提高效率,可以将缓冲图像存储为属性,并在尺寸发生变化时替换它。
也就是说,画竖线比画横线要花更长的时间,这很有趣。在使用调试器和分析器深入了解 SunGraphics2D
的内部结构后,我仍然无法解释它。
似乎是 Color lightGreen = new Color(0, 255, 0, 180);
中的 alpha 导致了此行为。
移除 alpha 会立即呈现线条。如果您需要 alpha,那么一种解决方案是调整渲染提示。
示例
public void update(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// ...
我正在尝试实现以下 'grid' 布局。
class扩展java.awt.Canvas,在paint
函数中绘制这些形状(或线条)。为什么 Canvas?检查 here,最初尝试做类似的事情。
已更新 MCVE 获取上述内容的代码 'layout':
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
@SuppressWarnings("serial")
public class SO_MCVE extends JPanel {
private DrawingCanvas _drawingCanvas = null;
private JButton repaintBtn;
public SO_MCVE() {
super(new BorderLayout());
_drawingCanvas = new DrawingCanvas();
_drawingCanvas.setSize(new Dimension(600, 600));
JLabel repaintLabel = new JLabel(
"<html><div style=\"text-align: center;\">" +
"REPAINT</html>");
repaintLabel.setHorizontalAlignment(
SwingConstants.CENTER);
repaintBtn = new JButton();
repaintBtn.add(repaintLabel);
repaintBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
_drawingCanvas.triggerRepaint();
}
});
add(_drawingCanvas, BorderLayout.CENTER);
add(repaintBtn, BorderLayout.PAGE_END);
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Whosebug MCVE for drawLine");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new SO_MCVE());
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
UIManager.put("swing.boldMetal", Boolean.FALSE);
createAndShowGUI();
}
});
}
}
@SuppressWarnings("serial")
class DrawingCanvas extends Canvas {
public static final Color lightGreen = new Color(0, 255, 0, 180);
public static final BasicStroke STROKE1PX = new BasicStroke(1.0f);
public static final BasicStroke STROKE3PX = new BasicStroke(3.0f);
private static final int LEFT = 50;
private static final int RIGHT = 550;
private static final int TOP = 50;
private static final int BOTTOM = 550;
private static final double WIDTH = 500.00d;
private static final double HEIGHT = 500.00d;
public DrawingCanvas() {
setBackground(Color.BLACK);
}
public void paint(Graphics g) {
update(g);
}
public void triggerRepaint() {
repaint();
}
public void update(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Dimension dim = getSize();
int w = (int) dim.getWidth();
int h = (int) dim.getHeight();
// Clears the rectangle that was previously drawn
g2.setPaint(Color.BLACK);
g2.fillRect(0, 0, w, h);
drawLines(g2, w, h);
}
/** Draw the lines marking the x-y limits **/
private void drawLines(Graphics2D g2, int w, int h) {
long start = System.nanoTime();
System.out.println("Start of drawLines(): " + start);
// Thick lines
g2.setPaint(Color.GREEN);
g2.setStroke(STROKE3PX);
g2.drawLine(LEFT, 0, LEFT, h);
g2.drawLine(RIGHT, 0, RIGHT, h);
g2.drawLine(0, TOP, w, TOP);
g2.drawLine(0, BOTTOM, w, BOTTOM);
System.out.println("Done drawing thick lines!");
long end = System.nanoTime();
System.out.println("Time taken (ns): " +
(end - start) + ", Time taken(ms): " +
((end - start)/1000/1000));
start = end;
// Thin vertical lines
g2.setPaint(lightGreen);
g2.setStroke(STROKE1PX);
int wInc = ((int) WIDTH) / 50;
for(int i = LEFT; i <= RIGHT; i += wInc) {
g2.drawLine(i, TOP, i, BOTTOM);
}
System.out.println("Done drawing vertical lines!");
end = System.nanoTime();
System.out.println("Time taken (ns): " +
(end - start) + ", Time taken(ms): " +
((end - start)/1000/1000));
start = end;
// Thin horizontal lines
g2.setPaint(lightGreen);
g2.setStroke(STROKE1PX);
int hInc = ((int) HEIGHT) / 50;
for(int i = TOP; i <= BOTTOM; i += hInc) {
g2.drawLine(LEFT, i, RIGHT, i);
}
System.out.println("Done drawing horizontal lines!");
end = System.nanoTime();
System.out.println("Time taken (ns): " +
(end - start) + ", Time taken(ms): " +
((end - start)/1000/1000));
System.out.println();
}
}
上面显示的代码的问题是,每当我调用 repaint()
.
时,渲染这些行需要一段时间(大约 3 秒)
按 "Repaint" 按钮触发 MCVE 中的重绘。
线条会一条一条慢慢画出来,如下图:
所以问题是:
drawLine
这么慢有什么原因吗?我已经尝试在类似的 for 循环中使用 g2.draw(一些 Ellipse2D.Double..) 绘制尽可能多(如果不是更多)的椭圆并且没有问题。
注:使用jre1.7.0_25,Windows 7、Eclipse
使用 System.nanoTime() 的简单基准测试:
Done drawing thick lines!
Time taken (ns): 8858966, Time taken(ms): 8
Done drawing vertical lines!
Time taken (ns): 3649188968, Time taken(ms): 3649
Done drawing horizontal lines!
Time taken (ns): 106730282, Time taken(ms): 106
注意:画'thin vertical lines'要花很长时间!
更新:
注:使用jre1.8.0_11,Windows 7、Eclipse
使用 System.nanoTime() 的简单基准测试:
Done drawing thick lines!
Time taken (ns): 110027, Time taken(ms): 0
Done drawing vertical lines!
Time taken (ns): 185567, Time taken(ms): 0
Done drawing horizontal lines!
Time taken (ns): 195419, Time taken(ms): 0
注:使用jre1.8.0_45,Windows 7、Eclipse
使用 System.nanoTime() 的简单基准测试:
Done drawing thick lines!
Time taken (ns): 6716121, Time taken(ms): 6
Done drawing vertical lines!
Time taken (ns): 2427676380, Time taken(ms): 2427
Done drawing horizontal lines!
Time taken (ns): 83030042, Time taken(ms): 83
Apparently, jre1.8.0_11 works really well?
我如何 运行 使用不同的 jre 版本(不确定我做的是否正确!):
谢谢! :)
按照 Michael Dibbets 的建议进行操作并使用缓冲。
我用绘制到屏幕外缓冲区然后绘制缓冲区的方法替换了 the SO_MCVE.update(Graphics g)
方法:
public void update(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Dimension dim = getSize();
int w = (int) dim.getWidth();
int h = (int) dim.getHeight();
// Create the buffer
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D ig2 = image.createGraphics();
// Paint everythign on the buffer
// Clears the rectangle that was previously drawn
ig2.setPaint(Color.BLACK);
ig2.fillRect(0, 0, w, h);
drawLines(ig2, w, h);
// Paint the buffer
g2.drawImage(image, 0, 0, null);
}
Start of drawLines(): 1832687816359773
Done drawing thick lines!
Time taken (ns): 2212913, Time taken(ms): 2
Done drawing vertical lines!
Time taken (ns): 37676442, Time taken(ms): 37
Done drawing horizontal lines!
Time taken (ns): 6453455, Time taken(ms): 6
如果您想提高效率,可以将缓冲图像存储为属性,并在尺寸发生变化时替换它。
也就是说,画竖线比画横线要花更长的时间,这很有趣。在使用调试器和分析器深入了解 SunGraphics2D
的内部结构后,我仍然无法解释它。
似乎是 Color lightGreen = new Color(0, 255, 0, 180);
中的 alpha 导致了此行为。
移除 alpha 会立即呈现线条。如果您需要 alpha,那么一种解决方案是调整渲染提示。
示例
public void update(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// ...