Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine multiple PNGs into one big PNG file?

I have approx. 6000 PNG files (256*256 pixels) and want to combine them into a big PNG holding all of them programmatically.

What's the best/fastest way to do that?

(The purpose is printing on paper, so using some web-technology is not an option and having one, single picture file will eliminate many usage errors.)

I tried fahd's suggestion but I get a NullPointerException when I try to create a BufferedImage with 24576 pixels wide and 15360 pixels high. Any ideas?

like image 487
soc Avatar asked Oct 13 '10 09:10

soc


People also ask

Can you merge PNG files?

How to merge PNG files online. Select or drop your PNG documents to upload for merge. Once upload completes, drag PNG document thumbnails to rearrange them (if needed). Click on Merge Now button to start merge process.

Can a PNG contain multiple images?

PNG itself is strictly a single-image format. However, it may be necessary to store multiple images within one file; for example, this is needed to convert some GIF files.

How do I combine multiple PNG files on a Mac?

Make sure in Preview > Preferences > Images… "When opening files:"… "Open groups of files in the same window" is checked. 2. Select all the files you want to merge, right click on them and choose Open with Preview. 4.


2 Answers

Create a large image which you will write to. Work out its dimensions based on how many rows and columns you want.

    BufferedImage result = new BufferedImage(                                width, height, //work these out                                BufferedImage.TYPE_INT_RGB);     Graphics g = result.getGraphics(); 

Now loop through your images and draw them:

    for(String image : images){         BufferedImage bi = ImageIO.read(new File(image));         g.drawImage(bi, x, y, null);         x += 256;         if(x > result.getWidth()){             x = 0;             y += bi.getHeight();         }     } 

Finally write it out to file:

    ImageIO.write(result,"png",new File("result.png")); 
like image 135
dogbane Avatar answered Oct 14 '22 09:10

dogbane


I had some similar need some time ago (huge images -and, I my case with 16 bitdepth- to have them fully in memory was not an option). And I ended coding a PNG library to do the read/write in a sequential way. In case someone find it useful, it's here.

Updated: here's a sample code:

/**  * Takes several tiles and join them in a single image  *   * @param tiles            Filenames of PNG files to tile  * @param dest            Destination PNG filename  * @param nTilesX            How many tiles per row?  */ public class SampleTileImage {          public static void doTiling(String tiles[], String dest, int nTilesX) {                 int ntiles = tiles.length;                 int nTilesY = (ntiles + nTilesX - 1) / nTilesX; // integer ceil                 ImageInfo imi1, imi2; // 1:small tile   2:big image                 PngReader pngr = new PngReader(new File(tiles[0]));                 imi1 = pngr.imgInfo;                 PngReader[] readers = new PngReader[nTilesX];                 imi2 = new ImageInfo(imi1.cols * nTilesX, imi1.rows * nTilesY, imi1.bitDepth, imi1.alpha, imi1.greyscale,                                 imi1.indexed);                 PngWriter pngw = new PngWriter(new File(dest), imi2, true);                 // copy palette and transparency if necessary (more chunks?)                 pngw.copyChunksFrom(pngr.getChunksList(), ChunkCopyBehaviour.COPY_PALETTE                                 | ChunkCopyBehaviour.COPY_TRANSPARENCY);                 pngr.readSkippingAllRows(); // reads only metadata                              pngr.end(); // close, we'll reopen it again soon                 ImageLineInt line2 = new ImageLineInt(imi2);                 int row2 = 0;                 for (int ty = 0; ty < nTilesY; ty++) {                         int nTilesXcur = ty < nTilesY - 1 ? nTilesX : ntiles - (nTilesY - 1) * nTilesX;                         Arrays.fill(line2.getScanline(), 0);                         for (int tx = 0; tx < nTilesXcur; tx++) { // open several readers                                 readers[tx] = new PngReader(new File(tiles[tx + ty * nTilesX]));                                 readers[tx].setChunkLoadBehaviour(ChunkLoadBehaviour.LOAD_CHUNK_NEVER);                                 if (!readers[tx].imgInfo.equals(imi1))                                         throw new RuntimeException("different tile ? " + readers[tx].imgInfo);                         }                         for (int row1 = 0; row1 < imi1.rows; row1++, row2++) {                                 for (int tx = 0; tx < nTilesXcur; tx++) {                                         ImageLineInt line1 = (ImageLineInt) readers[tx].readRow(row1); // read line                                         System.arraycopy(line1.getScanline(), 0, line2.getScanline(), line1.getScanline().length * tx,                                                         line1.getScanline().length);                                 }                                 pngw.writeRow(line2, row2); // write to full image                         }                         for (int tx = 0; tx < nTilesXcur; tx++)                                 readers[tx].end(); // close readers                 }                 pngw.end(); // close writer         }          public static void main(String[] args) {                 doTiling(new String[] { "t1.png", "t2.png", "t3.png", "t4.png", "t5.png", "t6.png" }, "tiled.png", 2);                 System.out.println("done");         } } 
like image 34
leonbloy Avatar answered Oct 14 '22 09:10

leonbloy