Can I save a huge PNG without the whole thing being in memory?
I'm saving a very large PNG (25 MB or so) with Java. The problem is that while it's being generated, it's using 3+ gigabytes of memory, which is not ideal since it severely slows down systems with low memory.
The code I'm working with needs to combine a set of tiled images into a single image; in other words, I have nine i开发者_如何学Gomages (PNG):
A1 A2 A3 B1 B2 B3 C1 C2 C3
which need to be combined into a single image.
The code I'm using is this:
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);
Is there a way to make and save an image without having the entire image in memory?
Edit: To draw the images, I'm doing this in a loop:
BufferedImage tile = ImageIO.read(new File("file.png"));
g2d.drawImage(tile, x, y, w, h);
This is being repeated many times (it's usually about 25x25, but sometimes more), so if there is even a small memory leak here, that could be causing the problem.
You can also take a look at this PNGJ library (disclaimer: I coded it), it allows to save a PNG image line by line.
ImageIO.write(image, "png", file);
is internally using com.sun.imageio.plugins.png.PNGImageWriter. That method and that writer expect image to be a rendered image but PNG writting is done by 'bands' so you can make a subclass of RenderedImage that generates the requested bands of the composed large image as the writer ask for that bands to the image.
From PNGImageWriter class:
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 I think this is the only place where the writer reads pixels from you image. You should make a getData(rect) method that computes that rect joining 3 bands from 3 images into one.
*1 As you see it reads bands with a height of 1 pixel.
If the things are as I think you should only need to compose 3 images at a time. There would be no need for the other 6 to be in memory.
I know it is not an easy solution but it might help you if you don't find anything easier.
Would using an external tool be an option? I remember using ImageMagick for similar purpose, you would need to save your smaller images first.
精彩评论