The Source for Java Technology Collaboration


Home | Help | Changes | Index | Search | Go

RenderedImageProxy

This code is released as Public Domain by its author, Andrew Rowbottom. If you find any bugs or can improve on it and are willing to contribute your changes back into the PD please feel free to edit this page.

package googleMapTool.mapStitch;

import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;

import java.lang.ref.SoftReference;

import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import javax.media.jai.JAI;
import javax.media.jai.ParameterBlockJAI;

/**
 * Class that proxys a rendered image (created via a ParameterBlockJAI)
 * so that the raster can be garbage collected at will. Obviously this impacts
 * performance, but can significantly improve memory usage
 */
public class RenderedImageProxy
   implements RenderedImage
{
   private final Map properties;
   private final transient ParameterBlockJAI constructionData;
   private final transient RenderingHints hints;
   private final String[] propertyNames;

   // cahces for the simple things
   private final int height;
   private final int minTileX;
   private final int minTileY;
   private final int minX;
   private final int minY;
   private final int numXTiles;
   private final int numYTiles;
   private final int tileGridXOffset;
   private final int tileGridYOffset;
   private final int tileHeight;
   private final int tileWidth;
   private final int width;

   /** used to weakly cache the image */
   private transient SoftReference jaiImgRef = null;
   private String lastLoadType = "neverLoaded";

   public RenderedImageProxy(ParameterBlockJAI constructionData, RenderingHints hints) {
      this.constructionData = constructionData;
      this.hints            = hints;

      RenderedImage img     = getImage();
      propertyNames         = img.getPropertyNames();
      height                = img.getHeight();
      minTileX              = img.getMinTileX();
      minTileY              = img.getMinTileY();
      minX                  = img.getMinX();
      minY                  = img.getMinY();
      numXTiles             = img.getNumXTiles();
      numYTiles             = img.getNumYTiles();
      tileGridXOffset       = img.getTileGridXOffset();
      tileGridYOffset       = img.getTileGridYOffset();
      tileHeight            = img.getTileHeight();
      width                 = img.getWidth();
      tileWidth             = img.getTileWidth();

      // not sure if we should cache the properties 
      properties = new HashMap();

      for (int i = 0; i < propertyNames.length; i++) {
         properties.put(propertyNames[i], img.getProperty(propertyNames[i]));
      }
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getColorModel()
    */
   public ColorModel getColorModel() {
      return getImage().getColorModel();
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getData()
    */
   public Raster getData() {
      return getImage().getData();
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getData(java.awt.Rectangle)
    */
   public Raster getData(Rectangle rect) {
      return getImage().getData(rect);
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getHeight()
    */
   public int getHeight() {
      return height;
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getMinTileX()
    */
   public int getMinTileX() {
      return minTileX;
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getMinTileY()
    */
   public int getMinTileY() {
      return minTileY;
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getMinX()
    */
   public int getMinX() {
      return minX;
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getMinY()
    */
   public int getMinY() {
      return minY;
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getNumXTiles()
    */
   public int getNumXTiles() {
      return numXTiles;
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getNumYTiles()
    */
   public int getNumYTiles() {
      return numYTiles;
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getProperty(java.lang.String)
    */
   public Object getProperty(String name) { //TODO fix this.

      return properties.get(name);
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getPropertyNames()
    */
   public String[] getPropertyNames() {
      return (String[]) propertyNames.clone();
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getSampleModel()
    */
   public SampleModel getSampleModel() {
      return getImage().getSampleModel();
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getSources()
    */
   public Vector getSources() {
      return getImage().getSources(); // passthrough because may contain a reference to the raster
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getTile(int, int)
    */
   public Raster getTile(int tileX, int tileY) {
      return getImage().getTile(tileX, tileY);
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getTileGridXOffset()
    */
   public int getTileGridXOffset() {
      return tileGridXOffset;
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getTileGridYOffset()
    */
   public int getTileGridYOffset() {
      return tileGridYOffset;
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getTileHeight()
    */
   public int getTileHeight() {
      return tileHeight;
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getTileWidth()
    */
   public int getTileWidth() {
      return tileWidth;
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#getWidth()
    */
   public int getWidth() {
      return width;
   }

   /* (non-Javadoc)
    * @see java.awt.image.RenderedImage#copyData(java.awt.image.WritableRaster)
    */
   public WritableRaster copyData(WritableRaster raster) {
      return getImage().copyData(raster);
   }

   private synchronized RenderedImage getImage() {
      if ((jaiImgRef != null) && (jaiImgRef.get() != null)) {
         return (RenderedImage) jaiImgRef.get();
      }

      if ((jaiImgRef == null) || (jaiImgRef.get() == null)) {
         if (jaiImgRef == null) {
            System.out.println("Loading image for the first time from " + constructionData + " using JAI");
         }
         else {
            System.out.println("Loading image which has been GC'd from " + constructionData + " using JAI");
         }

         System.out.println("Debug: Recent call stack:");

         StackTraceElement[] stack = new Throwable().getStackTrace();

         for (int i = 0; i < Math.min(stack.length, 5); i++) {
            System.out.println(stack[i]);
         }
      }

      System.out.println("LastLoadType was:" + lastLoadType);
      lastLoadType = "loading";

      try {
         return loadImage(); // first attempt, no OOM errors yet!
      }
      catch (OutOfMemoryError e) {
         // first attempt failed because of memory issues
         // do a GC and try again! 
         // sometimes this helps!
         System.err.println("Failed to load because of memory issues, doing a GC and trying again");
         System.gc();

         try {
            Thread.sleep(250);
         }
         catch (InterruptedException e1) {}

         System.gc();

         try {
            Thread.sleep(250);
         }
         catch (InterruptedException e2) {}

         System.gc();

         try {
            Thread.sleep(250);
         }
         catch (InterruptedException e3) {}

         return loadImage(); // second attempt (a)
      }
      catch (RuntimeException re) {
         // runtimeException, dig in it to see if the original cause was out of memory 
         Throwable t = re;

         while (t != null) {
            if (t instanceof OutOfMemoryError) {
               break;
            }

            t = t.getCause();
         }

         // if t is null then we reached the top of the exception causes exception list without seeing
         // an Out of Memory, so just rethrow  
         if (t != null) {
            throw re;
         }
         else {
            // first attempt failed because of memory issues (hidden inside a runtime exception)
            // do a GC and try again, this has been known to work
            System.err.println("Failed to load because of memory issues, doing a GC and trying again");
            System.gc();

            try {
               Thread.sleep(250);
            }
            catch (InterruptedException e1) {}

            System.gc();

            try {
               Thread.sleep(250);
            }
            catch (InterruptedException e2) {}

            System.gc();

            try {
               Thread.sleep(250);
            }
            catch (InterruptedException e3) {}

            return loadImage(); // second attempt (b)
         }
      }
      finally {
         System.out.println("LoadedAsType:" + lastLoadType);
      }
   }

   private RenderedImage loadImage() {
      try {
         RenderedImage image = JAI.create(constructionData.getOperationDescriptor().getName(), constructionData);

         jaiImgRef = new SoftReference(image);
         System.out.println("Loaded");

         return image;
      }
      catch (OutOfMemoryError ome) {
         throw ome; // don't hide out of memory errors
      }
      catch (Exception ioe) {
         throw new RuntimeException(ioe);
      }
   }
}


Discussion about RenderedImageProxy

Topic RenderedImageProxy . { Edit | Ref-By | Printable | Diffs r1 | More }
 XML java.net RSS

Revision r1 - 20 Mar 2007 - 13:17:18 - Main.rummyr
Parents: JAI > JAI_SampleCode