我可以在没有整个东西在内存的情况下保存一个巨大的PNG吗?

2022-09-03 15:31:32

我正在用Java保存一个非常大的PNG(25 MB左右)。问题是,当它被生成时,它使用3+千兆字节的内存,这并不理想,因为它严重减慢了内存不足的系统。

我正在使用的代码需要将一组平铺图像组合成单个图像;换句话说,我有九张图片(PNG):

A1 A2 A3
B1 B2 B3
C1 C2 C3

需要将其组合成单个图像。

我使用的代码是这样的:

image = new BufferedImage(width, height, height, BufferedImage.TYPE_INT_ARGB_PRE);
g2d = image.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);

// draw the 9 images on here at their proper positions...

// save image
g2d.dispose();
File file = getOutputFile();
ImageIO.write(image, "png", file);

有没有办法制作和保存图像,而无需将整个图像存储在内存中?


编辑:为了绘制图像,我在循环中执行此操作:

BufferedImage tile = ImageIO.read(new File("file.png"));
g2d.drawImage(tile, x, y, w, h);

这种情况被重复了很多次(通常是大约25x25,但有时更多),所以如果这里甚至有一个小的内存泄漏,那可能会导致问题。


答案 1

你也可以看看这个PNGJ库(免责声明:我编码了它),它允许逐行保存PNG图像。


答案 2

ImageIO.write(image, "png", file);内部使用 com.sun.imageio.plugins.png.PNGImageWriter。该方法和该作者希望图像是渲染图像,但PNG编写是由“波段”完成的,因此您可以创建一个渲染图像的子类,该子类在作者要求该波段到图像时生成所构成的大图像的请求波段。

从 PNGImageWriter 类:

private void encodePass(ImageOutputStream os,
                        RenderedImage image,
                        int xOffset, int yOffset,
                        int xSkip, int ySkip) throws IOException {
    // (...)
    for (int row = minY + yOffset; row < minY + height; row += ySkip) {
                Rectangle rect = new Rectangle(minX, row, width, 1); // <--- *1
                Raster ras = image.getData(rect); // <--- *2

*2 我认为这是作者从您的图像中读取像素的唯一位置。您应该创建一个 getData(rect) 方法,该方法计算该矩形将 3 个波段从 3 个图像连接成一个波段。

*1 如您所见,它读取高度为 1 像素的波段。

如果事情像我认为的那样,你应该一次只需要组成3张图像。其他 6 个不需要在内存中。

我知道这不是一个简单的解决方案,但如果你没有找到更容易的东西,它可能会帮助你。


推荐