I am trying to achieve the following 'grid' layout.
The class is extending java.awt.Canvas, and drawing these shapes (or lines) in the paint
function. Why Canvas? Check here, trying to do something similar inititally.
Updated MCVE Code for getting the above '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("StackOverflow 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();
}
}
The problem with the code shown above is that, it is taking awhile (around 3 seconds) to render these lines, whenever I call repaint()
.
Press the "Repaint" button to trigger a repaint in the MCVE.
The lines will get drawn slowly one by one, as shown in the image below:
So the question is:
Is there any reason why drawLine
is so slow? I have tried drawing just as many (if not more) ellipses using g2.draw(some Ellipse2D.Double..) in a similar for loop and there was no issue.
Note: Using jre1.7.0_25, Windows 7, Eclipse
Simple benchmarking using 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
Note: Drawing the 'thin vertical lines' is taking forever!
UPDATE:
Note: Using jre1.8.0_11, Windows 7, Eclipse
Simple benchmarking using 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
Note: Using jre1.8.0_45, Windows 7, Eclipse
Simple benchmarking using 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?
How I run w different jre versions (Not sure if I am doing it correctly!):
Thanks! :)
Do as Michael Dibbets suggested and use buffering.
I replaced
the SO_MCVE.update(Graphics g)
method with one that draws to an offscreen buffer and then draws the buffer:If you want to be even more efficient you can store the buffered image as an attribute and replace it if the dimensions change.
That said, it is very interesting that drawing the vertical lines takes longer time that the horizontal ones. After digging through the internals of
SunGraphics2D
with a debugger and a profiler I still can't explain it.