5 Replies Latest reply on May 10, 2007 12:39 AM by EJP

    Using Memory Mapping to Read in a Buffered Image

    807606
      Hi,

      I am tyring to read in a large image that has been exported from Visio rather than draw it through code with the 3rd Party component we are using called JGo from Northwoods.

      To prevent a drop in quality when the image is printed on our plotter at A0 the image has been drawn at 4A0 and exported as png.

      However as you would expect the image causes an OutOfMemory exception if we do not pass in -Xmx1024m. It is not realistic for us to deploy the app using Xmx parameter.

      Instead it was suggested that I use a memory mapping to load the image but even when I use the smallest image I get an OutOfMemory Exception - but I imagine this is because of another reason as the image I am using for debugging is small and can be loaded in a regular ImageIcon in a Jlabel.

      I have googled this, read countless forums and done my homework but cannot figure what is wrong. The code below has been adapted from another forum (see comments) about using a BufferedImage to write a large g2d to file. I wish to go the other way and read a large png.

      The question:
      If you look at the code you will see I use a ImageReadParam to read the image, I am not sure this correct. How do I read a stupendously large image into Java without running out of memory. I am flexible and am not limited to using png and can use any Visio export.

      Neil
      import java.awt.BorderLayout;
      import java.awt.LayoutManager;
      import java.awt.Point;
      import java.awt.image.BufferedImage;
      import java.awt.image.ColorModel;
      import java.awt.image.DataBuffer;
      import java.awt.image.DirectColorModel;
      import java.awt.image.SampleModel;
      import java.awt.image.WritableRaster;
      import java.io.File;
      import java.io.IOException;
      import java.io.RandomAccessFile;
      import java.net.URISyntaxException;
      import java.net.URL;
      import java.nio.ByteBuffer;
      import java.nio.IntBuffer;
      import java.nio.channels.FileChannel;
      import java.util.Iterator;
      
      import javax.imageio.ImageIO;
      import javax.imageio.ImageReadParam;
      import javax.imageio.ImageReader;
      import javax.imageio.stream.ImageInputStream;
      import javax.swing.ImageIcon;
      import javax.swing.JFrame;
      import javax.swing.JLabel;
      import javax.swing.filechooser.FileSystemView;
      
      
      
      /**
       * The idea is to create a buffered image that writes its data back to a swap file rather than memory
       * 
       * http://forums.java.net/jive/thread.jspa?messageID=204993
       * 
       * 1. Create a memory mapped file. What you get is a ByteBuffer instance.
       * 2. Create a DataBuffer subclass, which's getElem() and setElem() methods delegate to the ByteBuffer's get() and put() methods.
       * 3. Create a BufferedImage that wraps the above DataBuffer
       *
       */
      public class MyByteBuffer {
          
          public static void main(String[] args) throws Exception {
           MyByteBuffer mybb = new MyByteBuffer();
          }
          
          MyByteBuffer() {
           super();
           this.init();
           this.createGui();
          }
      
          void init() {
           try {
               int h = 20000;
               int w = 2000;
      
               // Create memory mapped file
               FileSystemView fsv = FileSystemView.getFileSystemView();
               File file = new File(fsv.getDefaultDirectory(), "swap.mem");
               // Create a read-write memory-mapped file
               FileChannel rwChannel = new RandomAccessFile(file, "rw").getChannel(); // Create file and read write to it
               // Map file to mem by decorating it with bytebuffer
               ByteBuffer wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, 6*w*h/*(int)rwChannel.size()*/); //negative size exception is raised here if w == 20000
               IntBuffer iBuf = wrBuf.asIntBuffer(); // Bytebuffer behave as int buffer to store pixel values
      
               // Custom data buffer - delegates to bytebuffer so held in file rather than memory
               DataBufferSubClass db = new DataBufferSubClass(DataBuffer.TYPE_INT,3*h*w);
               db.setWrBuf(iBuf);
      
               ColorModel cm = new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff); // translates pixel values (ints) to r-g-b-a components
               SampleModel sm = cm.createCompatibleSampleModel(w,h); // Used to locate values in data buffer
               WritableRaster wr = new MyWritableRaster(sm, db, new Point(0,0)); // provide pixel writing capabilities of raster, a rectangular arrays of pixels
               BufferedImage bim = new BufferedImage(cm, wr, true, null); // Create buffered image with components
      
               URL url;
               url = url = Resource.class.getResource("myLogo.png");
               File path = new File(url.toURI());
               
               Iterator readers = ImageIO.getImageReadersByFormatName("png");
               ImageReader reader = (ImageReader)readers.next();
      
               ImageInputStream iis = ImageIO.createImageInputStream(path);
               reader.setInput(iis, true, false);
      
               ImageReadParam param = reader.getDefaultReadParam();
               param.setDestination(bim);
      
               int imageIndex = 0;
               reader.read(imageIndex);
      
               bufferedImage = bim;
           }
           catch (IOException e) {
               e.printStackTrace();
           }
           catch (URISyntaxException e) {
               e.printStackTrace();
           }
          }
      
          BufferedImage bufferedImage;
      
          void createGui() {
      
           // Create a new JFrame.
           JFrame frame = new JFrame("BufferedImage");         
           frame.setSize(400, 400);
           frame.setExtendedState(javax.swing.JFrame.MAXIMIZED_BOTH);
           frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      
           LayoutManager layout;
      //     layout = new FlowLayout(FlowLayout.LEFT, 5, 5);
           layout = new BorderLayout(5, 5);
      //     layout = new GridLayout();
      //     layout = new SpringLayout();
           frame.setLayout(layout);
      
           frame.add(new JLabel(new ImageIcon(bufferedImage)));
           
           frame.setVisible(true);
          }
      
          public class DataBufferSubClass extends DataBuffer {
      
           private IntBuffer wrBuf;
      
           public void setWrBuf(IntBuffer wrBuf) {
               this.wrBuf = wrBuf;
           }
      
           // CONSTRUCTORS
           public DataBufferSubClass(int dataType, int size) {
               super(dataType, size);
           }
      
           public DataBufferSubClass(int dataType, int size, int numBanks) {
               super(dataType, size, numBanks);
           }
      
           public DataBufferSubClass(int dataType, int size, int numBanks, int offset) {
               super(dataType, size, numBanks, offset);
           }
      
           public DataBufferSubClass(int dataType, int size, int numBanks,
                int[] offsets) {
               super(dataType, size, numBanks, offsets);
           }
      
      
           @Override
           public int getElem(int bank, int i) {
               return wrBuf.get(i); // delegate to byte buffer to store pixel values on disk
           }
      
           @Override
           public void setElem(int bank, int i, int val) {
               wrBuf.put(i, val);  // delegate to byte buffer to store pixel values on disk
           }
          }
      
          public class MyWritableRaster extends WritableRaster{
           public MyWritableRaster(SampleModel sampleModel, DataBuffer dataBuffer, Point origin) {
               super(sampleModel, dataBuffer, origin); // Constructor not visible
           }
          }
      }