有没有办法让 Java 将 PNG 读取为 TYPE_INT_RGB / TYPE_INT_ARGB?

Is there a way to get Java to read a PNG as TYPE_INT_RGB / TYPE_INT_ARGB?

当我使用 javax.imageio.ImageIO.read() 读取 Java 中的 PNG 图像时,生成的 BufferedImageTYPE_3BYTE_BGRTYPE_4BYTE_ABGR 取决于透明度。

我正在处理非常大的图像(64+ 百万像素),并且需要 TYPE_INT_RGB / TYPE_INT_ARGB 格式的图像,这需要昂贵且非常大的内存占用以正确的格式将图像重新绘制到新图像上,这会导致 OOM。

如果我能以某种方式说服 ImageIO 从一开始就以所需格式读取图像,那就更好了 - 有什么办法可以做到这一点吗?谢谢!

是的,可以读入 BufferedImage 的预定义类型,前提是该类型受 reader 插件支持并与之兼容。大多数情况下,TYPE_#BYTE_* 类型与 TYPE_INT_* 类型兼容,标准 PNGImageReader.

就是这种情况

要使其工作,您需要直接访问 ImageReader,并使用采用 ImageReadParamread 方法来控制图像类型。可以使用 ImageReadParam.setDestination(..) 方法读入 pre-allocated 图像,或者只指定图像类型并让 reader 插件使用 ImageReadParam.setDestinationType(..) 为您分配它就像我将在下面展示的那样。

这是一个简短的 stand-alone 代码示例,展示了如何读入特定图像类型:

public static void main(String[] args) throws IOException {
    File input = new File(args[0]);

    try (ImageInputStream stream = ImageIO.createImageInputStream(input)) {
        // Find a suitable reader
        Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
        if (!readers.hasNext()) {
            throw new IIOException("No reader for " + input);
        }

        ImageReader reader = readers.next();
        try {
            reader.setInput(stream);

            // Query the reader for types and select the best match
            ImageTypeSpecifier intPackedType = getIntPackedType(reader);
            System.out.println("intPackedType = " + intPackedType);

            // Pass the type to the reader using read param
            ImageReadParam param = reader.getDefaultReadParam();
            param.setDestinationType(intPackedType);

            // Finally read the image
            BufferedImage image = reader.read(0, param);
            System.out.println("image = " + image);
        }
        finally {
            reader.dispose();
        }
    }
}

private static ImageTypeSpecifier getIntPackedType(ImageReader reader) throws IOException {
    Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);

    while (types.hasNext()) {
        ImageTypeSpecifier spec = types.next();

        switch (spec.getBufferedImageType()) {
            case BufferedImage.TYPE_INT_RGB:
            case BufferedImage.TYPE_INT_ARGB:
                return spec;
            default:
                // continue searching
        }
    }

    return null;
}

使用 PNG 作为输入的我的一次运行的示例输出:

intPackedType = javax.imageio.ImageTypeSpecifier$Packed@707084ba
image = BufferedImage@45ff54e6: type = 2 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000 IntegerInterleavedRaster: width = 100 height = 100 #Bands = 4 xOff = 0 yOff = 0 dataOffset[0] 0

其中 type = 2 表示 BufferedImage.TYPE_INT_ARGB