Search code examples
javaimagebinarypngchunks

I'm stuck with converting a very large binary file to PNG in Java


Context

I'm trying to converting a binary file (this is a raw memory file, dumped from pmemsave instruction of QEmu monitor) to PNG file with RGB color. That's mean each 3 bytes equal Red-Green-Blue color in 1 pixel of PNG file.

My idea is below:

content in binary file: 10001000 10100010 11000101 # and so on...
convert to RGB color in 1 pixel of PNG file: #88a2c5

In my imagine, the workflow of this program is same as this:

# define the chunk size is 128MB
CHUNK_SIZE = 134217728 

# create new png file
png.new("dest.png", format=RGB)

# open file for writing content
png.open_for_read()

while not end of source file: 
  data = source.read(CHUNK_SIZE)
  # another process...
  png.append(data)

png.save_on_disk()

Problem

I think it's very easy to do it by using ImageIO from javax. But my source file is really big (almost 2.1GB in size), so that the method ImageIO.write can't handle as well. By default, this method does not support chunking (it will destroy content in dest file if it exist).

How can I solve this problem? It's really happy for me to have any solution with modern feature of Java (like stream or something else)


Solution

  • The below code uses pngj 2.1.2.1

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    
    import ar.com.hjg.pngj.FilterType;
    import ar.com.hjg.pngj.ImageInfo;
    import ar.com.hjg.pngj.PngWriter;
    
    public class RGB1 {
    
    public static void main(String[] args) throws IOException {
    
        final int CHUNK_SIZE = 134217728;
        final int SIDE = 5; // Dimensions hard coded to square 5 X 5, please change as needed
    
        byte[] buf = new byte[CHUNK_SIZE];
    
        int br, i,j,row=0;
        int[] arr = new int[SIDE*3];// 3 Bytes per pixel
    
        FileInputStream fileInputStream=new FileInputStream("C:\\Don\\source.bin");
        OutputStream os = new FileOutputStream("C:\\Don\\dest.png");
    
        ImageInfo imi = new ImageInfo(SIDE, SIDE, 8, false);
    
        PngWriter pngw = new PngWriter(os, imi);
        pngw.setCompLevel(9);// maximum compression
        pngw.setFilterType(FilterType.FILTER_ADAPTIVE_FAST); 
        //Please Refer https://github.com/leonbloy/pngj/blob/master/src/main/java/ar/com/hjg/pngj/FilterType.java
    
        br = fileInputStream.read(buf);
        j=0; 
        System.out.println("Bytes Read: " + br);
        while(br>0)
        {
            for(i=0;i<br;i++)
            {
                arr[j++] = buf[i] & 0xFF;
    
                if(j==SIDE*3)
                {
                    pngw.writeRowInt(arr);
    
                    j=0;
                    row++;
                    if(row==SIDE) break;
                }
    
            }
            if(row==SIDE) break;
            br = fileInputStream.read(buf);
            System.out.println("Bytes Read: " + br);
        }
    
        pngw.end();
    
        System.out.println("Done.");
        fileInputStream.close();
    
    }
    
    }