// **********************************************************************
//
// <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.4.2.5 $
// $Date: 2006/08/09 21:01:22 $
// $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 javax.swing.ImageIcon;
import com.bbn.openmap.util.ComponentFactory;
import com.bbn.openmap.util.Debug;
/**
* 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 implemenation and reflection mods.
* @author Fredrik Lyden - JAI inspiration and initial code.
*/
public class 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 == false) {
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 = Debug.debugging("bufferedimage");
Object jai = getJAI();
if (jai == null) {
return null;
}
if (DEBUG) {
Debug.output("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) {
Debug.error("BufferedImageHelper.getJAIBufferedImage() ClassNotFoundException error: \n"
+ cnfe.getMessage());
}
} catch (IllegalAccessException iae) {
if (DEBUG) {
Debug.error("BufferedImageHelper.getJAIBufferedImage() IllegalAccessException error: \n"
+ iae.getMessage());
}
} catch (InvocationTargetException ite) {
if (DEBUG) {
Debug.error("BufferedImageHelper.getJAIBufferedImage() InvocationTargetException error: \n"
+ ite.getMessage());
}
} catch (NoSuchMethodException nsme) {
if (DEBUG) {
Debug.error("BufferedImageHelper.getJAIBufferedImage() NoSuchMethodException error: "
+ nsme.toString());
nsme.printStackTrace();
}
} catch (SecurityException se) {
if (DEBUG) {
Debug.error("BufferedImageHelper.getJAIBufferedImage() SecurityException error: \n"
+ se.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.
*
* @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;
}
if (Debug.debugging("bufferedimage")) {
Debug.output("BufferedImageHelper.getBufferedImage(URL) can't use JAI, using ImageIcon");
}
// if JAI is not installed....
ImageIcon ii = new ImageIcon(url);
if (w <= 0)
w = ii.getIconWidth();
if (h <= 0)
h = ii.getIconHeight();
return getBufferedImage(ii.getImage(),
x,
y,
w,
h,
BufferedImage.TYPE_INT_ARGB);
}
/**
* 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.
*
* @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;
}
if (Debug.debugging("bufferedimage")) {
Debug.output("BufferedImageHelper.getBufferedImage(path) can't use JAI, using ImageIcon");
}
// if JAI is not installed....
ImageIcon ii = new ImageIcon(path);
if (w <= 0)
w = ii.getIconWidth();
if (h <= 0)
h = ii.getIconHeight();
return getBufferedImage(ii.getImage(),
x,
y,
w,
h,
BufferedImage.TYPE_INT_ARGB);
}
/**
* 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) {
if (Debug.debugging("bufferedimage")) {
Debug.output("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);
if (Debug.debugging("imagehelper")) {
Debug.output("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);
if (Debug.debugging("imagehelper")) {
Debug.output("BufferedImageHelper.getBufferedImage(): set pixels in image...");
}
return bi;
}
}