// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/image/BufferedImageHelper.java,v $
// $RCSfile: BufferedImageHelper.java,v $
// $Revision: 1.9 $
// $Date: 2006/08/09 21:08:31 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.image;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import java.awt.image.WritableRaster;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import com.bbn.openmap.util.ComponentFactory;
/**
* This class provides some utility methods for creating a BufferedImage. It
* will check to see if the Java Advanced Image package is available and use it
* if it can.
*
* @author dietrick - original implementation and reflection mods.
* @author Fredrik Lyden - JAI inspiration and initial code.
*/
public class BufferedImageHelper {
protected static Logger logger = Logger.getLogger("com.bbn.openmap.io.BufferedImageHelper");
/**
* This class has only static methods, so there is no need to construct
* anything.
*/
private BufferedImageHelper() {
}
/**
* A test/instantiation copy of the JAI object to use if JAI is installed.
*/
private static Object jaiObj = null;
/**
* Flag to use if the JAI has be checked for.
*/
private static boolean checkedForJAI = false;
/**
* Get the JAI class if it's available.
*/
protected static Object getJAI() {
if (!checkedForJAI) {
jaiObj = ComponentFactory.create("javax.media.jai.JAI");
checkedForJAI = true;
}
return jaiObj;
}
/**
* Run the operation on JAI to create BufferedImage. Uses reflection to
* determine if JAI is available.
*
* @param opName JAI opName, like "file" or "url"
* @param param JAI object to use for operation, like the file path (String)
* or URL.
* @return BufferedImage if JAI can be used to create it, null if anything
* goes wrong.
*/
public static BufferedImage getJAIBufferedImage(String opName, Object param) {
boolean DEBUG = logger.isLoggable(Level.FINE);
Object jai = getJAI();
if (jai == null) {
return null;
}
if (DEBUG) {
logger.fine("Using JAI to create image from " + opName);
}
try {
// Do a little reflection to run methods on classes we
// might not know about.
Class[] createArgs = new Class[] {
Class.forName("java.lang.String"),
Class.forName("java.lang.Object")
};
Method createMethod = jai.getClass().getDeclaredMethod("create", createArgs);
Object[] createParams = new Object[] {
opName,
param
};
Object planarImageObject = createMethod.invoke(jai, createParams);
if (planarImageObject != null) {
Method getBufferedImageMethod = planarImageObject.getClass().getMethod("getAsBufferedImage", (Class[]) null);
return (BufferedImage) getBufferedImageMethod.invoke(planarImageObject, (Object[]) null);
}
} catch (ClassNotFoundException cnfe) {
if (DEBUG) {
logger.warning("BufferedImageHelper.getJAIBufferedImage() ClassNotFoundException error: \n" + cnfe.getMessage());
}
} catch (IllegalAccessException iae) {
if (DEBUG) {
logger.warning("BufferedImageHelper.getJAIBufferedImage() IllegalAccessException error: \n" + iae.getMessage());
}
} catch (InvocationTargetException ite) {
if (DEBUG) {
logger.warning("BufferedImageHelper.getJAIBufferedImage() InvocationTargetException error: \n" + ite.getMessage());
}
} catch (NoSuchMethodException nsme) {
if (DEBUG) {
logger.warning("BufferedImageHelper.getJAIBufferedImage() NoSuchMethodException error: " + nsme.toString());
nsme.printStackTrace();
}
} catch (SecurityException se) {
if (DEBUG) {
logger.warning("BufferedImageHelper.getJAIBufferedImage() SecurityException error: \n" + se.getMessage());
}
} catch (Exception e) {
if (DEBUG) {
logger.warning("BufferedImageHelper.getJAIBufferedImage() Exception: \n" + e.getMessage());
}
}
return null;
// All this above to replace this:
// PlanarImage planarImage = JAI.create(opName, param);
// return getBufferedImage(planarImage.getAsBufferedImage(),
// x, y, w, h);
}
/**
* Run the operation on JAI to create BufferedImage. Uses reflection to
* determine if JAI is available. If x or y is not zero, or w and h are not
* the image dimensions, the image returned will be cropped/translated to
* match the values.
*
* @param opName JAI opName, like "file" or "url"
* @param param JAI object to use for operation, like the file path (String)
* or URL.
* @param x x start pixel
* @param y y start pixel
* @param w crop width (-1 uses image width)
* @param h crop height (-1 uses image height)
* @return BufferedImage if JAI can be used to create it, null if anything
* goes wrong.
* @throws InterruptedException
*/
public static BufferedImage getJAIBufferedImage(String opName, Object param, int x, int y, int w, int h)
throws InterruptedException {
BufferedImage bi = getJAIBufferedImage(opName, param);
// If the whole image isn't wanted, do another operation...
if (bi != null && (x != 0 || y != 0 || w > 0 || h > 0)) {
int imageType = BufferedImage.TYPE_INT_RGB;
if (bi.getColorModel().hasAlpha()) {
imageType = BufferedImage.TYPE_INT_ARGB;
}
return getBufferedImage(bi, x, y, w, h, imageType);
}
// else return null or the original image.
return bi;
}
/**
* Return a BufferedImage loaded from a URL.
*
* @return BufferedImage if it can be created, null if anything goes wrong.
* @throws InterruptedException
*/
public static BufferedImage getBufferedImage(URL url)
throws InterruptedException {
return getBufferedImage(url, 0, 0, -1, -1);
}
/**
* Return a BufferedImage loaded from a URL. If JAI isn't available, checks
* the file path to see if it ends in jpg or jpeg, and won't try to use an
* alpha channel if it does.
*
* @param url the source URL
* @param x x start pixel
* @param y y start pixel
* @param w crop width (-1 uses image width)
* @param h crop height (-1 uses image height)
* @return BufferedImage if it can be created, null if anything goes wrong.
* @throws InterruptedException
*/
public static BufferedImage getBufferedImage(URL url, int x, int y, int w, int h)
throws InterruptedException {
if (url == null) {
return null;
}
BufferedImage bi = getJAIBufferedImage("url", url, x, y, w, h);
if (bi != null) {
return bi;
}
logger.fine("BufferedImageHelper.getBufferedImage(URL) can't use JAI, using ImageIcon");
// if JAI is not installed....
ImageIcon ii = new ImageIcon(url);
String path = url.getPath();
boolean noAlpha = path.endsWith("jpg") || path.endsWith("jpeg");
return getBufferedImage(ii, x, y, w, h, !noAlpha);
}
/**
* Return a BufferedImage loaded from a URL. Doesn't use JAI if available.
*
* @param ii an ImageIcon created from the source.
* @param x x start pixel
* @param y y start pixel
* @param w crop width (-1 uses image width)
* @param h crop height (-1 uses image height)
* @param hasAlpha whether the image should be transparent.
* @return BufferedImage if it can be created, null if anything goes wrong.
* @throws InterruptedException
*/
public static BufferedImage getBufferedImage(ImageIcon ii, int x, int y, int w, int h, boolean hasAlpha)
throws InterruptedException {
if (w <= 0)
w = ii.getIconWidth();
if (h <= 0)
h = ii.getIconHeight();
return getBufferedImage(ii.getImage(), x, y, w, h, (hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB));
}
/**
* Return a BufferedImage loaded from a file path.
*
* @return BufferedImage if it can be created, null if anything goes wrong.
* @throws InterruptedException
*/
public static BufferedImage getBufferedImage(String path)
throws InterruptedException {
return getBufferedImage(path, 0, 0, -1, -1);
}
/**
* Return a BufferedImage loaded from an image file path. If JAI isn't
* available, checks the file path to see if it ends in jpg or jpeg, and
* won't try to use an alpha channel if it does.
*
* @param path file path to the image
* @param x x start pixel
* @param y y start pixel
* @param w crop width (-1 uses image width)
* @param h crop height (-1 uses image height)
* @return BufferedImage if it can be created, null if anything goes wrong.
* @throws InterruptedException
*/
public static BufferedImage getBufferedImage(String path, int x, int y, int w, int h)
throws InterruptedException {
BufferedImage bi = getJAIBufferedImage("file", path, x, y, w, h);
if (bi != null) {
return bi;
}
logger.fine("BufferedImageHelper.getBufferedImage(path) can't use JAI, using ImageIcon");
// if JAI is not installed....
ImageIcon ii = new ImageIcon(path);
boolean noAlpha = path.endsWith("jpg") || path.endsWith("jpeg");
return getBufferedImage(ii, x, y, w, h, !noAlpha);
}
/**
* Return a BufferedImage loaded from a Image. The type of image is
* BufferedImage.Type_INT_RGB. If you know the height and width, use them
* because it's slower to have the class figure it out.
*
* @param image the source Image
* @param x x start pixel
* @param y y start pixel
* @param w crop width (-1 uses image width)
* @param h crop height (-1 uses image height)
* @return BufferedImage if it can be created, null if anything goes wrong.
* @throws InterruptedException
*/
public static BufferedImage getBufferedImage(Image image, int x, int y, int w, int h)
throws InterruptedException {
return getBufferedImage(image, x, y, w, h, BufferedImage.TYPE_INT_RGB);
}
/**
* Return a BufferedImage loaded from a Image. If you know the height and
* width, use them because it's slower to have the class figure it out.
*
* @param image the source Image
* @param x x start pixel - the horizontal pixel location in the returned
* image that the provided image will be set.
* @param y y start pixel - the vertical pixel location in the returned
* image that the provided image will be set.
* @param w crop width (-1 uses image width)
* @param h crop height (-1 uses image height)
* @param imageType the image color model. See BufferedImage.
* @return BufferedImage if it can be created, null if anything goes wrong.
* @throws InterruptedException
*/
public static BufferedImage getBufferedImage(Image image, int x, int y, int w, int h, int imageType)
throws InterruptedException {
if (w <= 0 || h <= 0) {
logger.fine("BufferedImageHelper.getBufferedImage() don't know h/w, using pixel grabber");
return getBufferedImageFromPixelGrabber(image, x, y, w, h, imageType);
} else {
BufferedImage bufferedImage = new BufferedImage(w, h, imageType);
Graphics2D g2d = bufferedImage.createGraphics();
g2d.drawImage(image, x, y, null);
g2d.dispose();
return bufferedImage;
}
}
/**
* Return a BufferedImage loaded from a Image, using a PixelGrabber. Good
* for when you have an Image, not a BufferedImage, and don't know the width
* and height. There is a performance penalty with this method, though.
*
* @param image the source Image
* @param x x start pixel - the horizontal pixel location in the returned
* image that the provided image will be set.
* @param y y start pixel - the vertical pixel location in the returned
* image that the provided image will be set.
* @param w crop width (-1 uses image width)
* @param h crop height (-1 uses image height)
* @param imageType the image color model. See BufferedImage.
* @return BufferedImage if it can be created, null if anything goes wrong.
*/
public static BufferedImage getBufferedImageFromPixelGrabber(Image image, int x, int y, int w, int h, int imageType) {
PixelGrabber pg = new PixelGrabber(image, x, y, w, h, true);
int[] pixels = ImageHelper.grabPixels(pg);
if (pixels == null) {
return null;
}
w = pg.getWidth();
h = pg.getHeight();
pg = null;
BufferedImage bi = new BufferedImage(w, h, imageType);
logger.fine("BufferedImageHelper.getBufferedImage(): Got buffered image...");
// bi.setRGB(0, 0, w, h, pixels, 0, w);
/**
* Looking at the standard BufferedImage code, an int[0] is allocated
* for every pixel. Maybe the memory usage is optimized for that, but it
* goes through a call stack for every pixel to do it. Let's just cycle
* through the data and write the pixels directly into the raster.
*/
WritableRaster raster = (WritableRaster) bi.getRaster();
raster.setDataElements(0, 0, w, h, pixels);
logger.fine("BufferedImageHelper.getBufferedImage(): set pixels in image...");
return bi;
}
}