且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

旋转具有透明背景的BufferedImage

更新时间:2023-11-21 11:30:28

我对 Graphics.drawImage()的行为感到有些困惑.也许其他人可以对此发表评论.

但是, Graphics2D.drawRenderedImage()可以解决问题.它需要一个 AffineTransform 来控制旋转.下面的示例很好地工作.您可能对最终图像的大小和旋转图像的位置还有其他要求.

  import javax.imageio.ImageIO;导入java.awt.Graphics2D;导入java.awt.geom.AffineTransform;导入java.awt.image.BufferedImage;导入java.io.File;公共类ImageRotation {公共静态void main(String [] args){ImageRotation旋转= new ImageRotation();rotation.rotate("input.png",45,"output.png");}public void rotation(String inputImageFilename,double angle,String outputImageFilename){尝试 {BufferedImage inputImage = ImageIO.read(新文件(inputImageFilename));BufferedImage outputImage = rotationImage(inputImage,angle);ImageIO.write(outputImage,"PNG",新File(outputImageFilename));} catch(Exception e){抛出新的RuntimeException(e);}}私人BufferedImage rotationImage(BufferedImage sourceImage,双角度){int width = sourceImage.getWidth();int height = sourceImage.getHeight();BufferedImage destImage =新的BufferedImage(宽度,高度,BufferedImage.TYPE_INT_ARGB);Graphics2D g2d = destImage.createGraphics();AffineTransform transform = new AffineTransform();transform.rotate(角度/180 * Math.PI,宽度/2,高度/2);g2d.drawRenderedImage(sourceImage,transform);g2d.dispose();返回destImage;}} 

更新

虽然以上代码适用于大多数PNG,但不适用于

输出图像:

I have an image with transparent background. I'd like to rotate this image to a specific angle and keep the transparent background for the resulting image. For this purpose I use the following method:

public static BufferedImage rotateImage(BufferedImage image, double angle, Color backgroundColor) {
    System.out.println(image.getType());
    double theta = Math.toRadians(angle);
    double sin = Math.abs(Math.sin(theta));
    double cos = Math.abs(Math.cos(theta));
    int w = image.getWidth();
    int h = image.getHeight();
    int newW = (int) Math.floor(w * cos + h * sin);
    int newH = (int) Math.floor(h * cos + w * sin);

    BufferedImage tmp = new BufferedImage(newW, newH, image.getType());
    Graphics2D g2d = tmp.createGraphics();
    if (backgroundColor != null) {
        g2d.setColor(backgroundColor);
        g2d.fillRect(0, 0, newW, newH);
    } 
    g2d.fillRect(0, 0, newW, newH);
    g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    g2d.translate((newW - w) / 2, (newH - h) / 2);
    g2d.rotate(theta, w / 2, h / 2);
    g2d.drawImage(image, 0, 0, null);
    g2d.dispose();
    return tmp;
}

I invoke it with background=null:

BufferedImage image = ImageIO.read(file);
rotateImage(image, 4, null);
ImageIO.write(bi, "PNG", new File("image.png"));

but the background of the resulting image.png is WHITE. What am I doing wrong and how to properly keep the transparent background for image.png?

I'm a bit puzzled about the behavior of Graphics.drawImage(). Maybe somebody else can comment about it.

However, Graphics2D.drawRenderedImage() works a treat. It takes an AffineTransform to control the rotation. The below example nicely works. You probably have additional requirement about the final image size and the location of the rotated image.

import javax.imageio.ImageIO;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;

public class ImageRotation {

    public static void main(String[] args) {
        ImageRotation rotation = new ImageRotation();
        rotation.rotate("input.png", 45, "output.png");
    }

    public void rotate(String inputImageFilename, double angle, String outputImageFilename) {

        try {
            BufferedImage inputImage = ImageIO.read(new File(inputImageFilename));
            BufferedImage outputImage = rotateImage(inputImage, angle);
            ImageIO.write(outputImage, "PNG", new File(outputImageFilename));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private BufferedImage rotateImage(BufferedImage sourceImage, double angle) {
        int width = sourceImage.getWidth();
        int height = sourceImage.getHeight();
        BufferedImage destImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = destImage.createGraphics();

        AffineTransform transform = new AffineTransform();
        transform.rotate(angle / 180 * Math.PI, width / 2 , height / 2);
        g2d.drawRenderedImage(sourceImage, transform);

        g2d.dispose();
        return destImage;
    }
}

Update

While the above code works for most PNGs, it does not work for the image that alexanoid is using. I've analyzed the image:

  • It's a grayscale image without a color palette (PNG color type 0) .
  • It uses simple transparency with a 2 byte long tRNS chunk.

As far as I can tell that's perfectly legal. However, ImageIO does not implement this combination. If the image has no palette, it simply ignores the tRNS chunk and therefore ignores the transparency information. That's most likely a bug.

You basically have two options now:

  1. Look for an alternative library to read PNG files.
  2. Fix the transparency after you have read the PNG file. This only works if know that the image used the particular problematic format.

Input and output for working PNG files

Input image:

Ouptput Image: