package iiuf.jai; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Hashtable; import java.util.EventListener; import javax.swing.Icon; import javax.media.jai.PlanarImage; import javax.media.jai.TileComputationListener; import javax.media.jai.TileRequest; import iiuf.util.EventListenerList; import iiuf.util.Util; /** (c) 2000, 2001, IIUF, DIUF<p> Display a JAI image. @author $Author: hassan $ @version $Revision: 1.1 $ */ public class DisplayImage implements Icon { protected EventListenerList listeners = new EventListenerList(); protected Hashtable ytable = new Hashtable(); protected PlanarImage image; protected SampleModel sampleModel; protected ColorModel colorModel; protected int minX, minY; protected int width, height; protected int minTileX; protected int maxTileX; protected int minTileY; protected int maxTileY; protected int tileWidth; protected int tileHeight; protected int tileGridXOffset; protected int tileGridYOffset; private Color backgroundColor = null; protected boolean cacheTiles; protected boolean asyncPaint; protected Component paintComponent; /** Constructs a DisplayImage object. @param image The image to visualize. @param cacheTiles Caches tiles inside this object (may be very memory consuming). @param asyncPaint Only paints the parts of the image that are already computed (only in conjunction with cacheTiles). */ public DisplayImage(PlanarImage image, boolean cacheTiles, boolean asyncPaint) { this.image = image; this.cacheTiles = cacheTiles; this.asyncPaint = asyncPaint; if (cacheTiles) { image.addTileComputationListener(new TileComputationListener() { public void tileComputationFailure(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY, Throwable situation) { Util.printStackTrace(situation); } public void tileComputed(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY, Raster tile) { storeTile(tileX, tileY, tile); fireImageChanged(); } public void tileCancelled(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY) { } }); } minTileX = image.getMinTileX(); maxTileX = image.getMinTileX() + image.getNumXTiles() - 1; minTileY = image.getMinTileY(); maxTileY = image.getMinTileY() + image.getNumYTiles() - 1; sampleModel = image.getSampleModel(); colorModel = image.getColorModel(); if (colorModel == null) { colorModel = PlanarImage.createColorModel(image.getSampleModel()); } if (colorModel == null) { throw new IllegalArgumentException("JAIImage is unable to display supplied PlanarImage."); } if (colorModel.getTransparency() != Transparency.OPAQUE) { Object col = image.getProperty("background_color"); if (col != null && col instanceof Color) { backgroundColor = (Color)col; } else { backgroundColor = Color.white; } } minX = image.getMinX(); minY = image.getMinY(); width = image.getWidth(); height = image.getHeight(); tileWidth = image.getTileWidth(); tileHeight = image.getTileHeight(); tileGridXOffset = image.getTileGridXOffset(); tileGridYOffset = image.getTileGridYOffset(); queueTiles(); } /** Queues all the tiles for computation. Otherwise the tiles will only be computed as soon as the image is to be painted. */ public void queueTiles() { // Queue all tiles for computation Point[] tiles = new Point[image.getNumXTiles()*image.getNumYTiles()]; int i=0; for (int y = minTileY; y <= maxTileY; y++) { for (int x = minTileX; x <= maxTileX; x++) { tiles[i++] = new Point(x, y); } } image.queueTiles(tiles); } /** Returns the PlanarImage that is painted with this object. @return PlanarImage painted with this object. */ public PlanarImage getImage() { return image; } /** Returns the width of the image. @return Width of the image. */ public int getIconWidth() { return image.getWidth(); } /** Returns the height of the image. @return Height of the image. */ public int getIconHeight() { return image.getHeight(); } /** Paints the image. @param component Component, into which the image is painted. @param graphics Current graphics context. @param x X position. @param y Y position. */ public void paintIcon(Component component, Graphics graphics, int x, int y) { if (!(graphics instanceof Graphics2D)) { throw new RuntimeException("DisplayImage requires Graphics2D."); } Graphics2D g2d = (Graphics2D) graphics; paintComponent = component; // 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; if (cacheTiles) { Hashtable xtable = (Hashtable) ytable.get(new Integer(tj)); if (xtable != null) { tile = (Raster) xtable.get(new Integer(ti)); } if (tile == null && !asyncPaint) { tile = image.getTile(ti, tj); storeTile(ti, tj, tile); } } else { tile = image.getTile(ti, tj); } 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); } } } } /** Calls repaint in every component that has asked the image to be painted. */ protected void fireImageChanged() { if (paintComponent != null) paintComponent.repaint(); } /** Caches the given tile in our private tile cache. @param tileX X position of the tile. @param tileY Y position of the tile. @param tile Tile to cache. */ protected 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); } /** Some utility functions */ private final int XtoTileX(int x) { return (int) Math.floor((double) (x - tileGridXOffset)/tileWidth); } private final int YtoTileY(int y) { return (int) Math.floor((double) (y - tileGridYOffset)/tileHeight); } private final int TileXtoX(int tx) { return tx*tileWidth + tileGridXOffset; } private final int TileYtoY(int ty) { return ty*tileHeight + tileGridYOffset; } }