旋转具有透明背景的 BufferedImage

Rotate BufferedImage with transparent background

我有一张透明背景的图片。我想将此图像旋转到特定角度并为生成的图像保留透明背景。为此,我使用以下方法:

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;
}

我用 background=null 调用它:

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

但是生成的 image.png 的背景是白色的。我做错了什么以及如何正确保持 image.png 的透明背景?

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

然而,Graphics2D.drawRenderedImage() 是一种享受。它需要一个 AffineTransform 来控制旋转。下面的例子很好用。您可能对最终图像大小和旋转图像的位置有其他要求。

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;
    }
}

更新

虽然上述代码适用于大多数 PNG,但不适用于 alexanoid 使用的 image。我已经分析了图像:

  • 这是一张没有调色板的灰度图像(PNG 颜色类型 0)。
  • 它使用带有 2 字节长 tRNS 块的简单透明度。

据我所知,这是完全合法的。但是,ImageIO 没有实现这种组合。如果图像没有调色板,它会简单地忽略 tRNS 块,因此忽略透明度信息。这很可能是一个错误。

你现在基本上有两个选择:

  1. 寻找替代库来读取 PNG 文件。
  2. 阅读PNG文件后修复透明度。这只有在知道图像使用了特定的有问题的格式时才有效。

工作 PNG 文件的输入和输出

输入图像:

输出图像: