20

I want to make a image with rounded corners. A image will come from input and I will make it rounded corner then save it. I use pure java. How can I do that? I need a function like

public void makeRoundedCorner(Image image, File outputFile){
.....
}

Schema

Edit : Added an image for information.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Ali Davut
  • 660
  • 1
  • 9
  • 16

3 Answers3

37

I suggest this method that takes an image and produces an image and keeps the image IO outside:

Edit: I finally managed to make Java2D soft-clip the graphics with the help of Java 2D Trickery: Soft Clipping by Chris Campbell. Sadly, this isn't something Java2D supports out of the box with some RenderhingHint.

public static BufferedImage makeRoundedCorner(BufferedImage image, int cornerRadius) {
    int w = image.getWidth();
    int h = image.getHeight();
    BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);

    Graphics2D g2 = output.createGraphics();
    
    // This is what we want, but it only does hard-clipping, i.e. aliasing
    // g2.setClip(new RoundRectangle2D ...)

    // so instead fake soft-clipping by first drawing the desired clip shape
    // in fully opaque white with antialiasing enabled...
    g2.setComposite(AlphaComposite.Src);
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setColor(Color.WHITE);
    g2.fill(new RoundRectangle2D.Float(0, 0, w, h, cornerRadius, cornerRadius));
    
    // ... then compositing the image on top,
    // using the white shape from above as alpha source
    g2.setComposite(AlphaComposite.SrcAtop);
    g2.drawImage(image, 0, 0, null);
    
    g2.dispose();
    
    return output;
}

Here's a test driver:

public static void main(String[] args) throws IOException {
    BufferedImage icon = ImageIO.read(new File("icon.png"));
    BufferedImage rounded = makeRoundedCorner(icon, 20);
    ImageIO.write(rounded, "png", new File("icon.rounded.png"));
}

This it what the input/output of the above method looks like:

Input:

input image

Ugly, jagged output with setClip():

jagged with setclip

Nice, smooth output with composite trick:

smooth with composite trick

Close up of the corners on gray background (setClip() obviously left, composite right):

closeup corners on gray bacjground

simon04
  • 3,054
  • 29
  • 25
Philipp Reichart
  • 20,771
  • 6
  • 58
  • 65
  • The corners are aliased, though, and no obvious `RenderingHint` does what I want... I'll update my code if I find a way to make smooth corners. – Philipp Reichart Sep 29 '11 at 21:57
  • you have to create four Lines and Circles and put that altogether, put here EmptyBorders and space inside corners re-calculate per pixel and remove that, but why to reinvent the whell for that is there official API – mKorbel Sep 29 '11 at 22:03
  • @mKorbel Why go Swing for a simple Java2D task? Even if that would solve the aliasing issue, it would be overkill IMHO. The above code does work, minus style points for the aliasing. – Philipp Reichart Sep 29 '11 at 22:06
  • Java2D also works in headless environments, Swing not so much (although this wasn't a requirement by the OP) – Philipp Reichart Sep 29 '11 at 22:10
  • It works, thanks. I can use your answer for now, but I need smooth corners. If you find a way, you add. Thanks Philipp Reichart. – Ali Davut Sep 29 '11 at 22:26
  • It might be worth adding the need for an additional rendering hint: `RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE` to ensure that the output is symmetrical. You'll notice in your examples that the bottom right corner is a little different to the top left. – Gary Feb 07 '14 at 20:38
  • I have referenced this answer on an [answer](https://stackoverflow.com/a/66312943/5292302) how to add rounded border in japer report, thanks for a great answer that solves a problem also in this library. – Petter Friberg Feb 22 '21 at 09:26
3

I am writing a follow up to Philipp Reichart's answer. the answer of as an answer.

To remove the white background (seems to be black in the pictures), change g2.setComposite(AlphaComposite.SrcAtop); to g2.setComposite(AlphaComposite.SrcIn);

This was a big problem for me because I have different images with transparency that I don't want to lose.

My original image:
enter image description here

If I use g2.setComposite(AlphaComposite.SrcAtop);:
enter image description here

When I use g2.setComposite(AlphaComposite.SrcIn); the background is transparent.

ossobuko
  • 851
  • 8
  • 25
0

I found another way using TexturePaint:

                ImageObserver obs = ...;
                int w = img.getWidth(obs);
                int h = img.getHeight(obs);

                // any shape can be used
                Shape clipShape = new RoundRectangle2D.Double(0, 0, w, h, 20, 20);

                // create a BufferedImage with transparency
                BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
                Graphics2D bg = bi.createGraphics();

                // make BufferedImage fully transparent
                bg.setComposite(AlphaComposite.Clear);
                bg.fillRect(0, 0, w, h);
                bg.setComposite(AlphaComposite.SrcOver);

                // copy/paint the actual image into the BufferedImage
                bg.drawImage(img, 0, 0, w, h, obs);

                // set the image to be used as TexturePaint on the target Graphics
                g.setPaint(new TexturePaint(bi, new Rectangle2D.Float(0, 0, w, h)));

                // activate AntiAliasing
                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

                // translate the origin to where you want to paint the image
                g.translate(x, y);

                // draw the Image
                g.fill(clipShape);

                // reset paint
                g.setPaint(null);

This code can be simplified if you have a non-animated image, by creating the BufferedImage only once and keeping it for each paint.

If your image is animated though you have to recreate the BufferedImage on each paint. (Or at least i have not found a better solution for this yet.)

Frederic Leitenberger
  • 1,949
  • 24
  • 32