/* (C) 2002, DIUF, http://www.unifr.ch/diuf
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package iiuf.xmillum;
import iiuf.jai.Util;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.MultiPixelPackedSampleModel;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.awt.image.renderable.ParameterBlock;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Hashtable;
import javax.media.jai.ImageLayout;
import javax.media.jai.JAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.TileCache;
import javax.media.jai.TileComputationListener;
import javax.media.jai.TileRequest;
import com.sun.media.jai.codec.MemoryCacheSeekableStream;
import com.sun.media.jai.codec.SeekableStream;
/**
* Work around a bug in the native "subsamplebinarytogray" implementation of JAI-1.1.1
*/
import java.util.List;
import javax.media.jai.OperationRegistry;
import javax.media.jai.registry.RIFRegistry;
import java.awt.image.renderable.RenderedImageFactory;
/** End of workaround */
/**
* JAIImageFactory
*
* An ImageFactory working with JAI images.
*
* @author $Author: hassan $
* @version $Revision: 1.1 $
*/
public class JAIImageFactory extends ImageFactory {
static {
/**
* Workaround a bug in the native "subsamplebinarytogray" implementation of JAI-1.1.1
*/
// Get the registry of the default JAI instance.
OperationRegistry registry = JAI.getDefaultInstance().getOperationRegistry();
// Get all RIFs associated with "subsamplebinarytogray" (there
// should be two as installed by default).
List imageFactories = RIFRegistry.getOrderedList(registry,
"subsamplebinarytogray",
"com.sun.media.jai");
if(imageFactories.size() >= 2) {
// Save references to the native and Java RIFs.
RenderedImageFactory nativeFactory = (RenderedImageFactory)imageFactories.get(0);
RenderedImageFactory javaFactory = (RenderedImageFactory)imageFactories.get(1);
// Unset the preferences between the two.
RIFRegistry.unsetPreference(registry,
"subsamplebinarytogray",
"com.sun.media.jai",
nativeFactory,
javaFactory);
// Unregister the native implementation of "subsamplebinarytogray".
RIFRegistry.unregister(registry,
"subsamplebinarytogray",
"com.sun.media.jai",
nativeFactory);
}
/** End of workaround */
// Setup a BIG cache for the JAI images
TileCache cache = JAI.createTileCache(64000000);
cache.setMemoryThreshold(0.75F);
JAI.getDefaultInstance().setTileCache(cache);
}
public JAIImageFactory() {
}
public Image getImage(URL imageURL) throws IOException {
PlanarImage image = loadImage(imageURL);
if (image != null) {
return new JAIImage(image);
} else {
return null;
}
}
PlanarImage loadImage(URL imageURL) throws IOException {
SeekableStream stream = new MemoryCacheSeekableStream(imageURL.openStream());
return JAI.create("stream", stream);
}
public class JAIImage extends Image {
protected PlanarImage original;
public JAIImage() {
}
public JAIImage(PlanarImage i) {
original = i;
}
public int getWidth() {
return original.getWidth();
}
public int getHeight() {
return original.getHeight();
}
PlanarImage scaled;
double scale;
public void resetImage() {
scaled = null;
}
public void paintImage(double s, Graphics g, int x, int y) {
if (scaled == null || s != scale) {
scale = s;
if (scale != 1.0) {
ParameterBlock pb = new ParameterBlock();
pb.addSource(original);
pb.add(new Float(scale));
pb.add(new Float(scale));
if (Util.isBinary(original) && scale < 1.0 && original.getSampleModel() instanceof MultiPixelPackedSampleModel) {
ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] { 8 }, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
SampleModel sm = cm.createCompatibleSampleModel(256, 256);
ImageLayout layout = new ImageLayout(0, 0, 256, 256, sm, cm);
layout.setTileWidth(256);
layout.setTileHeight(256);
RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
scaled = JAI.create("subsamplebinarytogray", pb, hints);
} else {
ImageLayout layout = new ImageLayout();
layout.setTileWidth(256);
layout.setTileHeight(256);
RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
scaled = JAI.create("scale", pb, hints);
}
} else {
scaled = original;
}
setupPainting();
}
Graphics2D g2d = (Graphics2D) g;
// Get the clipping rectangle and translate it into image coordinates.
Rectangle clipBounds = g2d.getClipBounds();
int transX = x;
int transY = y;
// Determine the extent of the clipping region in tile coordinates.
int txmin, txmax, tymin, tymax;
int ti, tj;
// Constrain drawing to the active image area
Rectangle imageRect = new Rectangle(minX + transX, minY + transY, width, height);
if (clipBounds != null) {
txmin = Math.min(Math.max(XtoTileX(clipBounds.x), minTileX), maxTileX);
txmax = Math.min(Math.max(XtoTileX(clipBounds.x + clipBounds.width - 1), minTileX), maxTileX);
tymin = Math.min(Math.max(YtoTileY(clipBounds.y), minTileY), maxTileY);
tymax = Math.min(Math.max(YtoTileY(clipBounds.y + clipBounds.height - 1), minTileY), maxTileY);
g2d.clip(imageRect);
} else {
txmin = minTileX; txmax = maxTileX;
tymin = minTileY; tymax = maxTileY;
g2d.setClip(imageRect);
}
if (backgroundColor != null) {
g2d.setColor(backgroundColor);
}
// Loop over tiles within the clipping region
for (tj = tymin; tj <= tymax; tj++) {
for (ti = txmin; ti <= txmax; ti++) {
int tx = TileXtoX(ti);
int ty = TileYtoY(tj);
Raster tile = null;
Hashtable xtable = (Hashtable) ytable.get(new Integer(tj));
if (xtable != null) {
tile = (Raster) xtable.get(new Integer(ti));
}
if (backgroundColor != null) {
g2d.fillRect(tx+transX, ty+transY, tileWidth, tileHeight);
}
if (tile != null) {
WritableRaster wr = tile.createWritableRaster(sampleModel, tile.getDataBuffer(), new Point(0, 0));
BufferedImage bi = new BufferedImage(colorModel, wr, false, null);
AffineTransform transform = AffineTransform.getTranslateInstance(tx + transX, ty + transY);
g2d.drawImage(bi, transform, null);
}
}
}
}
Hashtable ytable;
SampleModel sampleModel;
ColorModel colorModel;
int minX, minY;
int width, height;
int minTileX;
int maxTileX;
int minTileY;
int maxTileY;
int tileWidth;
int tileHeight;
int tileGridXOffset;
int tileGridYOffset;
Color backgroundColor = null;
void setupPainting() {
ytable = new Hashtable();
scaled.addTileComputationListener(new TileComputationListener() {
public void tileComputationFailure(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY, Throwable situation) {
situation.printStackTrace();
}
public void tileCancelled(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY) {
}
public void tileComputed(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY, Raster tile) {
storeTile(tileX, tileY, tile);
fireImageChanged();
}
});
minTileX = scaled.getMinTileX();
maxTileX = scaled.getMinTileX() + scaled.getNumXTiles() - 1;
minTileY = scaled.getMinTileY();
maxTileY = scaled.getMinTileY() + scaled.getNumYTiles() - 1;
sampleModel = scaled.getSampleModel();
colorModel = scaled.getColorModel();
if (colorModel == null) {
colorModel = PlanarImage.createColorModel(scaled.getSampleModel());
}
if (colorModel == null) {
throw new IllegalArgumentException("JAIImage is unable to display supplied PlanarImage.");
}
if (colorModel.getTransparency() != Transparency.OPAQUE) {
Object col = scaled.getProperty("background_color");
if (col != null) {
backgroundColor = (Color)col;
} else {
backgroundColor = Color.white;
}
}
minX = scaled.getMinX();
minY = scaled.getMinY();
width = scaled.getWidth();
height = scaled.getHeight();
tileWidth = scaled.getTileWidth();
tileHeight = scaled.getTileHeight();
tileGridXOffset = scaled.getTileGridXOffset();
tileGridYOffset = scaled.getTileGridYOffset();
// Queueing tiles for computation
Point[] tiles = new Point[scaled.getNumXTiles() * scaled.getNumYTiles()];
int i=0;
for (int y = minTileY; y <= maxTileY; y++) {
for (int x = minTileX; x <= maxTileX; x++) {
tiles[i++] = new Point(x, y);
}
}
scaled.queueTiles(tiles);
}
int XtoTileX(int x) {
return (int) Math.floor((double) (x - tileGridXOffset)/tileWidth);
}
int YtoTileY(int y) {
return (int) Math.floor((double) (y - tileGridYOffset)/tileHeight);
}
int TileXtoX(int tx) {
return tx*tileWidth + tileGridXOffset;
}
int TileYtoY(int ty) {
return ty*tileHeight + tileGridYOffset;
}
void storeTile(int tileX, int tileY, Raster tile) {
Hashtable xtable = (Hashtable) ytable.get(new Integer(tileY));
if (xtable == null) {
xtable = new Hashtable();
ytable.put(new Integer(tileY), xtable);
}
xtable.put(new Integer(tileX), tile);
}
}
}