I have looked into this issue and cannot see how to do this in a single call to system classes.
Graphics2D is an abstract instance, implemented as SunGraphics2D. The source code is available at for example docjar, and so we could potentially just 'do the same, but different' by copy some code. However, methods to paint an image depends on some 'pipe' classes which are not available. Although you do stuff with classloading, you will probably hit some native, optimized class which cannot be manipulated to do the theoretically optimal approach; all you get is image painting as squares.
However we can make an approach in which our own, non-native (read:slow?), code runs as little as possible, and not depending on the image size but rather the (relatively) low area in the round rect. Also, without copying the images in memory, consuming a lot of memory. But if you have a lot of memory, then obviously, a cached image is faster after the instance has been created.
Alternative 1:
import java.awt.Composite;
import java.awt.CompositeContext;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import javax.swing.JLabel;
public class TPanel2 extends JLabel implements Composite, CompositeContext {
private int w = 300;
private int h = 200;
private int cornerRadius = 20;
private int[] roundRect; // first quadrant
private BufferedImage image;
private int[][] first = new int[cornerRadius][];
private int[][] second = new int[cornerRadius][];
private int[][] third = new int[cornerRadius][];
private int[][] forth = new int[cornerRadius][];
public TPanel2() {
setOpaque(false);
setPreferredSize(new Dimension(w, h));
setMaximumSize(new Dimension(w, h));
setMinimumSize(new Dimension(w, h));
// calculate round rect
roundRect = new int[cornerRadius];
for(int i = 0; i < roundRect.length; i++) {
roundRect[i] = (int)(Math.cos(Math.asin(1 - ((double)i)/20))*20); // x for y
}
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); // all black
}
@Override
public void paintComponent(Graphics g) {
// discussion:
// We have to work with the passed Graphics object.
if(g instanceof Graphics2D) {
Graphics2D g2d = (Graphics2D) g;
// draw the whole image and save the corners
g2d.setComposite(this);
g2d.drawImage(image, 0, 0, null);
} else {
super.paintComponent(g);
}
}
@Override
public CompositeContext createContext(ColorModel srcColorModel,
ColorModel dstColorModel, RenderingHints hints) {
return this;
}
@Override
public void dispose() {
}
@Override
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
// lets assume image pixels >> round rect pixels
// lets also assume bulk operations are optimized
// copy current pixels
for(int i = 0; i < cornerRadius; i++) {
// quadrants
// from top to buttom
// first
first[i] = (int[]) dstOut.getDataElements(src.getWidth() - (cornerRadius - roundRect[i]), i, cornerRadius - roundRect[i], 1, first[i]);
// second
second[i] = (int[]) dstOut.getDataElements(0, i, cornerRadius - roundRect[i], 1, second[i]);
// from buttom to top
// third
third[i] = (int[]) dstOut.getDataElements(0, src.getHeight() - i - 1, cornerRadius - roundRect[i], 1, third[i]);
// forth
forth[i] = (int[]) dstOut.getDataElements(src.getWidth() - cornerRadius + roundRect[i], src.getHeight() - i - 1, cornerRadius - roundRect[i], 1, forth[i]);
}
// overwrite entire image as a square
dstOut.setRect(src);
// copy previous pixels back in corners
for(int i = 0; i < cornerRadius; i++) {
// first
dstOut.setDataElements(src.getWidth() - cornerRadius + roundRect[i], i, first[i].length, 1, second[i]);
// second
dstOut.setDataElements(0, i, second[i].length, 1, second[i]);
// third
dstOut.setDataElements(0, src.getHeight() - i - 1, third[i].length, 1, third[i]);
// forth
dstOut.setDataElements(src.getWidth() - cornerRadius + roundRect[i], src.getHeight() - i - 1, forth[i].length, 1, forth[i]);
}
}
}
Alternative 2:
import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.CompositeContext;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import javax.swing.JLabel;
public class TPanel extends JLabel implements Composite, CompositeContext {
private int w = 300;
private int h = 200;
private int cornerRadius = 20;
private int[] roundRect; // first quadrant
private BufferedImage image;
private boolean initialized = false;
private int[][] first = new int[cornerRadius][];
private int[][] second = new int[cornerRadius][];
private int[][] third = new int[cornerRadius][];
private int[][] forth = new int[cornerRadius][];
public TPanel() {
setOpaque(false);
setPreferredSize(new Dimension(w, h));
setMaximumSize(new Dimension(w, h));
setMinimumSize(new Dimension(w, h));
// calculate round rect
roundRect = new int[cornerRadius];
for(int i = 0; i < roundRect.length; i++) {
roundRect[i] = (int)(Math.cos(Math.asin(1 - ((double)i)/20))*20); // x for y
}
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); // all black
}
@Override
public void paintComponent(Graphics g) {
if(g instanceof Graphics2D) {
Graphics2D g2d = (Graphics2D) g;
// draw 1 + 2 rectangles and copy pixels from image. could also be 1 rectangle + 4 edges
g2d.setComposite(AlphaComposite.Src);
g2d.drawImage(image, cornerRadius, 0, image.getWidth() - cornerRadius - cornerRadius, image.getHeight(), null);
g2d.drawImage(image, 0, cornerRadius, cornerRadius, image.getHeight() - cornerRadius - cornerRadius, null);
g2d.drawImage(image, image.getWidth() - cornerRadius, cornerRadius, image.getWidth(), image.getHeight() - cornerRadius, image.getWidth() - cornerRadius, cornerRadius, image.getWidth(), image.getHeight() - cornerRadius, null);
// draw the corners using our own logic
g2d.setComposite(this);
g2d.drawImage(image, 0, 0, null);
} else {
super.paintComponent(g);
}
}
@Override
public CompositeContext createContext(ColorModel srcColorModel,
ColorModel dstColorModel, RenderingHints hints) {
return this;
}
@Override
public void dispose() {
}
@Override
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
// assume only corners need painting
if(!initialized) {
// copy image pixels
for(int i = 0; i < cornerRadius; i++) {
// quadrants
// from top to buttom
// first
first[i] = (int[]) src.getDataElements(src.getWidth() - cornerRadius, i, roundRect[i], 1, first[i]);
// second
second[i] = (int[]) src.getDataElements(cornerRadius - roundRect[i], i, roundRect[i], 1, second[i]);
// from buttom to top
// third
third[i] = (int[]) src.getDataElements(cornerRadius - roundRect[i], src.getHeight() - i - 1, roundRect[i], 1, third[i]);
// forth
forth[i] = (int[]) src.getDataElements(src.getWidth() - cornerRadius, src.getHeight() - i - 1, roundRect[i], 1, forth[i]);
}
initialized = true;
}
// copy image pixels into corners
for(int i = 0; i < cornerRadius; i++) {
// first
dstOut.setDataElements(src.getWidth() - cornerRadius, i, first[i].length, 1, second[i]);
// second
dstOut.setDataElements(cornerRadius - roundRect[i], i, second[i].length, 1, second[i]);
// third
dstOut.setDataElements(cornerRadius - roundRect[i], src.getHeight() - i - 1, third[i].length, 1, third[i]);
// forth
dstOut.setDataElements(src.getWidth() - cornerRadius, src.getHeight() - i - 1, forth[i].length, 1, forth[i]);
}
}
}
Hope this helps, this is somewhat of a second-best solution but that's life (this is when some graphics-guru comes and proves me wrong (??)..) ;-)