Reputation: 183
I found a performance bottleneck and I don't know how to solve it.
Introduction: I wrote a sub class of JComponent which paints images in a 50 to 50 grid. Each image has the size of 50 to 50 pixel. The performance of "paintComponent" becomes quite bad when the images are "TRANSLUCENT".
So, I did some testing with fillRect instead of drawImage and I got the same behavior when the color has an alpha value.
Here the example code: (just an example code to demonstrate the performance difference)
private final GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
.getDefaultConfiguration();
// ...
protected void paintComponent(final Graphics graphics) {
super.paintComponent(graphics);
final BufferedImage buffer = this.graphicsConfiguration.createCompatibleImage(this.getWidth(), this.getHeight(), Transparency.TRANSLUCENT);
final Graphics2D bufferGraphics = (Graphics2D) buffer.getGraphics();
bufferGraphics.setColor(new Color(110, 110, 110));
for (int dy = 0; dy <= super.getHeight(); dy++) {
for (short dx = 0; dx <= super.getWidth(); dx++) {
bufferGraphics.fillRect(dx, dy, 1, 1);
}
}
graphics.drawImage(buffer, 0, 0, null);
}
The performance becomes worse (around 30 times slower) when "bufferGraphics.setColor(new Color(110, 110, 110));" is replaced by "bufferGraphics.setColor(new Color(110, 110, 110, 110));"
Question: Does anyone have an idea how to improve the performance?
Thanks in advance
Upvotes: 2
Views: 1014
Reputation: 107
As far as tested on Java 13, raw bytewise operation for alphablending on image data array, written from scratch, are faster than calling awt primitives, especially if you have determined considerations on image data format and drawing operations. You can access buffered image pixels directly like this:
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
data = ((DataBufferInt) mage.getRaster().getDataBuffer()).getData();
For example, this technique to logarithmically fade is about 3 times faster than fillrect with 50% alpha:
public void shift(int x0, int y0, int x1, int y1, int shift) {
int channelMask = 0xff & (~(0xFF << (8 - shift)));
int mask = (channelMask << 16) | (channelMask << 8) | (channelMask);
for (int i = y0; i < y1; i++) {
for (int x = i * w + x0; x < i * w + x1; x++)
data[x] = (data[x] >> shift) & mask;
}
}
Upvotes: 0
Reputation: 417592
Yes, draw directly on the graphics
passed to your paintComponent()
. Why do you create a buffered image, draw on it then copy/paint the whole image on the graphics
(and then discard the buffered image)?
Swing is already double-buffered (by default): drawing on the passed graphics
already happens on a buffer which will be made visible at the end of painting process of the JComponent
. So your attempt to double-buffer is completely unnecessary and redundant.
If for some unknown reason you really need this, you should cache the created BufferedImage
and reuse it in subsequent paintComponent()
calls. Swing is single threaded, you don't even have to synchronize it or worry about concurrent access.
Upvotes: 3