/* * @(#)SimpleRenderedImage.java 3.1 99/01/11 * * Copyright (c) 1997-1999 Sun Microsystems, Inc. All Rights Reserved. * * This software is the confidential and proprietary information of Sun * Microsystems, Inc. ("Confidential Information"). You shall not * disclose such Confidential Information and shall use it only in * accordance with the terms of the license agreement you entered into * with Sun. * * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. */ package jsky.image; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.RenderedImage; import java.awt.image.ColorModel; import java.awt.image.SampleModel; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; /** * A simple class implementing the RenderedImage * interface. Only the getTile() method needs to be * implemented by subclasses. The instance variables must also be * filled in properly. */ public abstract class SimpleRenderedImage implements RenderedImage { /** The X coordinate of the image's upper-left pixel. */ protected int minX; /** The Y coordinate of the image's upper-left pixel. */ protected int minY; /** The image's width in pixels. */ protected int width; /** The image's height in pixels. */ protected int height; /** The width of a tile. */ protected int tileWidth; /** The height of a tile. */ protected int tileHeight; /** The X coordinate of the upper-left pixel of tile (0, 0). */ protected int tileGridXOffset = 0; /** The Y coordinate of the upper-left pixel of tile (0, 0). */ protected int tileGridYOffset = 0; /** The image's SampleModel. */ protected SampleModel sampleModel = null; /** The image's ColorModel. */ protected ColorModel colorModel = null; /** The image's sources, stored in a Vector. */ protected Vector sources = new Vector(); /** A Hashtable containing the image properties. */ protected Hashtable properties = new Hashtable(); public SimpleRenderedImage() { } /** Returns the X coordinate of the leftmost column of the image. */ public int getMinX() { return minX; } /** * Returns the X coordinate of the column immediatetely to the * right of the rightmost column of the image. getMaxX() is * implemented in terms of getMinX() and getWidth() and so does * not need to be implemented by subclasses. */ public final int getMaxX() { return getMinX() + getWidth(); } /** Returns the X coordinate of the uppermost row of the image. */ public int getMinY() { return minY; } /** * Returns the Y coordinate of the row immediately below the * bottom row of the image. getMaxY() is implemented in terms of * getMinY() and getHeight() and so does not need to be * implemented by subclasses. */ public final int getMaxY() { return getMinY() + getHeight(); } /** Returns the width of the image. */ public int getWidth() { return width; } /** Returns the height of the image. */ public int getHeight() { return height; } /** Returns a Rectangle indicating the image bounds. */ public Rectangle getBounds() { return new Rectangle(getMinX(), getMinY(), getWidth(), getHeight()); } /** Returns the width of a tile. */ public int getTileWidth() { return tileWidth; } /** Returns the height of a tile. */ public int getTileHeight() { return tileHeight; } /** * Returns the X coordinate of the upper-left pixel of tile (0, 0). */ public int getTileGridXOffset() { return tileGridXOffset; } /** * Returns the Y coordinate of the upper-left pixel of tile (0, 0). */ public int getTileGridYOffset() { return tileGridYOffset; } /** * Returns the horizontal index of the leftmost column of tiles. * getMinTileX() is implemented in terms of getMinX() * and so does not need to be implemented by subclasses. */ public int getMinTileX() { return XToTileX(getMinX()); } /** * Returns the horizontal index of the rightmost column of tiles. * getMaxTileX() is implemented in terms of getMaxX() * and so does not need to be implemented by subclasses. */ public int getMaxTileX() { return XToTileX(getMaxX() - 1); } /** * Returns the number of tiles along the tile grid in the * horizontal direction. getNumXTiles() is implemented in terms * of getMinTileX() and getMaxTileX() and so does not need to be * implemented by subclasses. */ public int getNumXTiles() { return getMaxTileX() - getMinTileX() + 1; } /** * Returns the vertical index of the uppermost row of tiles. getMinTileY() * is implemented in terms of getMinY() and so does not need to be * implemented by subclasses. */ public int getMinTileY() { return YToTileY(getMinY()); } /** * Returns the vertical index of the bottom row of tiles. getMaxTileY() * is implemented in terms of getMaxY() and so does not need to * be implemented by subclasses. */ public int getMaxTileY() { return YToTileY(getMaxY() - 1); } /** * Returns the number of tiles along the tile grid in the vertical * direction. getNumYTiles() is implemented in terms * of getMinTileY() and getMaxTileY() and so does not need to be * implemented by subclasses. */ public int getNumYTiles() { return getMaxTileY() - getMinTileY() + 1; } /** Returns the SampleModel of the image. */ public SampleModel getSampleModel() { return sampleModel; } /** Returns the ColorModel of the image. */ public ColorModel getColorModel() { return colorModel; } /** * Gets a property from the property set of this image. * If the property name is not recognized, java.awt.Image.UndefinedProperty * will be returned. * * @param name the name of the property to get, as a String. * @return a reference to the property Object, or the value * java.awt.Image.UndefinedProperty. */ public Object getProperty(String name) { // name = name.toLowerCase(); return properties.get(name); } /** * Returns a list of property names that are recognized by this image. * * @return an array of Strings containing valid property names. */ public String[] getPropertyNames() { String[] names = new String[properties.size()]; int index = 0; Enumeration e = properties.keys(); while (e.hasMoreElements()) { String name = (String) e.nextElement(); names[index++] = name; } return names; } // /** // * Returns an array of Strings recognized as names by this // * property source that begin with the supplied prefix. // * If no property names match, null will be returned. // * // * @return an array of Strings giving the valid property names. // */ // public String[] getPropertyNames(String prefix) { // return null; // } // Utility methods. /** * Converts a pixel's X coordinate into a horizontal tile index * relative to a given tile grid layout specified by its X offset * and tile width. */ public static int XToTileX(int x, int tileGridXOffset, int tileWidth) { x -= tileGridXOffset; if (x < 0) { x += 1 - tileWidth; // Force round to -infinity } return x / tileWidth; } /** * Converts a pixel's Y coordinate into a vertical tile index * relative to a given tile grid layout specified by its Y offset * and tile height. */ public static int YToTileY(int y, int tileGridYOffset, int tileHeight) { y -= tileGridYOffset; if (y < 0) { y += 1 - tileHeight; // Force round to -infinity } return y / tileHeight; } /** * Converts a pixel's X coordinate into a horizontal tile index. * This is a convenience method. No attempt is made to detect * out-of-range coordinates. * * @param x the X coordinate of a pixel. * @return the X index of the tile containing the pixel. */ public int XToTileX(int x) { return XToTileX(x, getTileGridXOffset(), getTileWidth()); } /** * Converts a pixel's Y coordinate into a vertical tile index. * This is a convenience method. No attempt is made to detect * out-of-range coordinates. * * @param y the Y coordinate of a pixel. * @return the Y index of the tile containing the pixel. */ public int YToTileY(int y) { return YToTileY(y, getTileGridYOffset(), getTileHeight()); } /** * Converts a horizontal tile index into the X coordinate of its * upper left pixel relative to a given tile grid layout specified * by its X offset and tile width. */ public static int tileXToX(int tx, int tileGridXOffset, int tileWidth) { return tx * tileWidth + tileGridXOffset; } /** * Converts a vertical tile index into the Y coordinate of * its upper left pixel relative to a given tile grid layout * specified by its Y offset and tile height. */ public static int tileYToY(int ty, int tileGridYOffset, int tileHeight) { return ty * tileHeight + tileGridYOffset; } /** * Converts a horizontal tile index into the X coordinate of its * upper left pixel. This is a convenience method. No attempt is made * to detect out-of-range indices. * * @param tx the horizontal index of a tile. * @return the X coordinate of the tile's upper left pixel. */ public int tileXToX(int tx) { return tx * tileWidth + tileGridXOffset; } /** * Converts a vertical tile index into the Y coordinate of its * upper left pixel. This is a convenience method. No attempt is made * to detect out-of-range indices. * * @param ty the vertical index of a tile. * @return the Y coordinate of the tile's upper left pixel. */ public int tileYToY(int ty) { return ty * tileHeight + tileGridYOffset; } public Vector getSources() { return null; } /** * Returns the entire image in a single Raster. For images with * multiple tiles this will require making a copy. * *

The returned Raster is semantically a copy. This means * that updates to the source image will not be reflected in the * returned Raster. For non-writable (immutable) source images, * the returned value may be a reference to the image's internal * data. The returned Raster should be considered non-writable; * any attempt to alter its pixel data (such as by casting it to * WritableRaster or obtaining and modifying its DataBuffer) may * result in undefined behavior. The copyData method should be * used if the returned Raster is to be modified. * * @return a Raster containing a copy of this image's data. */ public Raster getData() { Rectangle rect = new Rectangle(getMinX(), getMinY(), getWidth(), getHeight()); return getData(rect); } /** * Returns an arbitrary rectangular region of the RenderedImage * in a Raster. The rectangle of interest will be clipped against * the image bounds. * *

The returned Raster is semantically a copy. This means * that updates to the source image will not be reflected in the * returned Raster. For non-writable (immutable) source images, * the returned value may be a reference to the image's internal * data. The returned Raster should be considered non-writable; * any attempt to alter its pixel data (such as by casting it to * WritableRaster or obtaining and modifying its DataBuffer) may * result in undefined behavior. The copyData method should be * used if the returned Raster is to be modified. * * @param bounds the region of the RenderedImage to be returned. */ public Raster getData(Rectangle bounds) { int startX = XToTileX(bounds.x); int startY = YToTileY(bounds.y); int endX = XToTileX(bounds.x + bounds.width - 1); int endY = YToTileY(bounds.y + bounds.height - 1); Raster tile; if ((startX == endX) && (startY == endY)) { tile = getTile(startX, startY); return tile.createChild(bounds.x, bounds.y, bounds.width, bounds.height, bounds.x, bounds.y, null); } else { // Create a WritableRaster of the desired size SampleModel sm = sampleModel.createCompatibleSampleModel(bounds.width, bounds.height); // Translate it WritableRaster dest = Raster.createWritableRaster(sm, bounds.getLocation()); for (int j = startY; j <= endY; j++) { for (int i = startX; i <= endX; i++) { tile = getTile(i, j); // Rectangle tileRect = tile.getBounds(); Rectangle intersectRect = bounds.intersection(tile.getBounds()); Raster liveRaster = tile.createChild(intersectRect.x, intersectRect.y, intersectRect.width, intersectRect.height, intersectRect.x, intersectRect.y, null); dest.setDataElements(0, 0, liveRaster); } } return dest; } } /** * Copies an arbitrary rectangular region of the RenderedImage * into a caller-supplied WritableRaster. The region to be * computed is determined by clipping the bounds of the supplied * WritableRaster against the bounds of the image. The supplied * WritableRaster must have a SampleModel that is compatible with * that of the image. * *

If the raster argument is null, the entire image will * be copied into a newly-created WritableRaster with a SampleModel * that is compatible with that of the image. * * @param dest a WritableRaster to hold the returned portion of * the image. * @return a reference to the supplied WritableRaster, or to a * new WritableRaster if the supplied one was null. */ public WritableRaster copyData(WritableRaster dest) { Rectangle bounds; Raster tile; if (dest == null) { bounds = getBounds(); Point p = new Point(minX, minY); /* A SampleModel to hold the entire image. */ SampleModel sm = sampleModel.createCompatibleSampleModel( width, height); dest = Raster.createWritableRaster(sm, p); } else { bounds = dest.getBounds(); } int startX = XToTileX(bounds.x); int startY = YToTileY(bounds.y); int endX = XToTileX(bounds.x + bounds.width - 1); int endY = YToTileY(bounds.y + bounds.height - 1); for (int j = startY; j <= endY; j++) { for (int i = startX; i <= endX; i++) { tile = getTile(i, j); // Rectangle tileRect = tile.getBounds(); Rectangle intersectRect = bounds.intersection(tile.getBounds()); Raster liveRaster = tile.createChild(intersectRect.x, intersectRect.y, intersectRect.width, intersectRect.height, intersectRect.x, intersectRect.y, null); /* * WritableRaster.setDataElements takes into account of * inRaster's minX and minY and add these to x and y. Since * liveRaster has the origin at the correct location, the * following call should not again give these coordinates in * places of x and y. */ dest.setDataElements(0, 0, liveRaster); } } return dest; } }