0001: /*
0002: * $RCSfile: PlanarImage.java,v $
0003: *
0004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * Use is subject to license terms.
0007: *
0008: * $Revision: 1.3 $
0009: * $Date: 2006/06/16 19:55:56 $
0010: * $State: Exp $
0011: */
0012: package javax.media.jai;
0013:
0014: import java.awt.Graphics;
0015: import java.awt.Image;
0016: import java.awt.Point;
0017: import java.awt.Rectangle;
0018: import java.awt.Transparency;
0019: import java.awt.color.ColorSpace;
0020: import java.awt.image.BufferedImage;
0021: import java.awt.image.ComponentSampleModel;
0022: import java.awt.image.ColorModel;
0023: import java.awt.image.DataBuffer;
0024: import java.awt.image.DataBufferByte;
0025: import java.awt.image.DataBufferShort;
0026: import java.awt.image.DataBufferUShort;
0027: import java.awt.image.DataBufferInt;
0028: import java.awt.image.DirectColorModel;
0029: import java.awt.image.IndexColorModel;
0030: import java.awt.image.MultiPixelPackedSampleModel;
0031: import java.awt.image.Raster;
0032: import java.awt.image.RenderedImage;
0033: import java.awt.image.SampleModel;
0034: import java.awt.image.SinglePixelPackedSampleModel;
0035: import java.awt.image.WritableRaster;
0036: import java.awt.image.WritableRenderedImage;
0037: import java.beans.PropertyChangeListener;
0038: import java.lang.ref.WeakReference;
0039: import java.util.Collections;
0040: import java.util.Enumeration;
0041: import java.util.Hashtable;
0042: import java.util.HashSet;
0043: import java.util.Iterator;
0044: import java.util.List;
0045: import java.util.Map;
0046: import java.util.Set;
0047: import java.util.Vector;
0048: import javax.media.jai.RasterFactory;
0049: import com.sun.media.jai.util.DataBufferUtils;
0050: import com.sun.media.jai.util.ImageUtil;
0051: import com.sun.media.jai.util.JDKWorkarounds;
0052: import com.sun.media.jai.util.PropertyUtil;
0053: import javax.media.jai.util.CaselessStringKey;
0054:
0055: /**
0056: * A <code>RenderedImage</code> is expressed as a collection of pixels.
0057: * A pixel is defined as a 1-by-1 square; its origin is the top-left
0058: * corner of the square (0, 0), and its energy center is located at the
0059: * center of the square (0.5, 0.5).
0060: *
0061: * <p> This is the fundamental base class of Java Advanced Imaging (JAI)
0062: * that represents a two-dimensional <code>RenderedImage</code>.
0063: *
0064: * <p> This class provides a home for the information and functionalities
0065: * common to all the JAI classes that implement the
0066: * <code>RenderedImage</code> interface, such as the image's layout,
0067: * sources, properties, etc. The image layout, sources, and properties
0068: * may be set either at construction or subsequently using one of the
0069: * mutator methods supplied for the respective attribute. In general
0070: * this class does not perform sanity checking on the state of its
0071: * variables so it is very important that subclasses set them correctly.
0072: * This is of particular importance with respect to the image layout.
0073: *
0074: * <p> The layout of a <code>PlanarImage</code> is specified by variables
0075: * <code>minX</code>, <code>minY</code>, <code>width</code>,
0076: * <code>height</code>, <code>tileGridXOffset</code>,
0077: * <code>tileGridYOffset</code>, <code>tileWidth</code>,
0078: * <code>tileHeight</code>, <code>sampleModel</code>, and
0079: * <code>colorModel</code>. These variables do not have any default settings
0080: * so subclasses must set the appropriate ones at construction via the
0081: * <code>ImageLayout</code> argument or subsequently using
0082: * <code>setImageLayout()</code>. Otherwise, unexpected errors may occur.
0083: * Although these variables have <code>protected</code> access, it is
0084: * strongly recommended that subclasses not set the values of these variables
0085: * directly but rather via <code>setImageLayout()</code> which performs a
0086: * certain few initializations based on the layout values. The variables
0087: * are defined to have <code>protected</code> access for convenience.
0088: *
0089: * <p> A <code>PlanarImage</code> may have any number of
0090: * <code>RenderedImage</code> sources or no source at all.
0091: *
0092: * <p> All non-JAI <code>RenderedImage</code> instances must be
0093: * converted into <code>PlanarImage</code>s by means of the
0094: * <code>RenderedImageAdapter</code> and
0095: * <code>WritableRenderedImageAdapter</code> classes. The
0096: * <code>wrapRenderedImage</code> method provides a convenient interface
0097: * to both add a wrapper and take a snapshot if the image is writable.
0098: * All of the <code>PlanarImage</code> constructors perform this wrapping
0099: * automatically. Images that already extend <code>PlanarImage</code>
0100: * will be returned unchanged by <code>wrapRenderedImage</code>; that
0101: * is, it is idempotent.
0102: *
0103: * <p> Going in the other direction, existing code that makes use of
0104: * the <code>RenderedImage</code> interface will be able to use
0105: * <code>PlanarImage</code>s directly, without any changes or
0106: * recompilation. Therefore, within JAI, two-dimensional images are
0107: * returned from methods as <code>PlanarImage</code>s, even though
0108: * incoming <code>RenderedImages</code> are accepted as arguments directly.
0109: *
0110: * <p> A <code>PlanarImage</code> may also have any number of properties
0111: * of any type. If or how a property is used depends on the individual
0112: * subclass. This class only stores the property information. If any
0113: * <code>PropertyChangeListener</code>s are registered they will receive
0114: * a <code>PropertySourceChangeEvent</code> for each change in an image
0115: * property.
0116: *
0117: * <p> In general, methods in this class are implemented such that they
0118: * use any class variables directly instead of through their accessors for
0119: * performance reasons. Subclasses need to be careful when overriding this
0120: * class' variable accessors that other appropriate methods are overriden
0121: * as well.
0122: *
0123: * <p> <code>PlanarImage</code> implements a <code>createSnapshot</code>
0124: * method that produces a new, immutable image with a copy of this
0125: * image's current contents. In practice, this snapshot is only a
0126: * virtual copy; it is managed by the <code>SnapshotImage</code> class
0127: * in such a way as to minimize copying and memory footprint generally.
0128: * Multiple calls to <code>createSnapshot</code> make use of a single
0129: * <code>SnapshotImage</code> per <code>PlanarImage</code> in order to
0130: * centralize version management. These mechanisms are transparent to
0131: * the API user and are discussed here only for edification.
0132: *
0133: * <p> The source and sink lists have the effect of creating a graph
0134: * structure between a set of <code>PlanarImage</code>s. Note that
0135: * the practice of making such bidirectional connections between
0136: * images means that the garbage collector will not inform us when all
0137: * user references to a node are lost, since there will still be
0138: * internal references up until the point where the entire graph is
0139: * detached from user space. A solution is available in the form of
0140: * <em>Reference Objects</em>; see <a
0141: * href="http://java.sun.com/j2se/1.5.0/docs/guide/refobs/">
0142: * http://java.sun.com/j2se/1.5.0/docs/guide/refobs/</a> for
0143: * more information. These classes include <em>weak references</em>
0144: * that allow the Garbage Collector (GC) to collect objects they
0145: * reference, setting the reference to <code>null</code> in the process.
0146: *
0147: * <p> The reference problem requires us to be careful about how we
0148: * define the <i>reachability</i> of directed acyclic graph (DAG) nodes.
0149: * If we were to allow nodes to be reached by arbitrary graph traversal,
0150: * we would be unable to garbage collect any subgraphs of an active
0151: * graph at all since any node may be reached from any other. Instead,
0152: * we define the set of reachable nodes as those that may be accessed
0153: * directly from a reference in user code, or that are the source (not
0154: * sink) of a reachable node. Reachable nodes are always accessible,
0155: * whether they are reached by traversing upwards or downwards in the DAG.
0156: *
0157: * <p> A DAG may also contain nodes that are not reachable, that is,
0158: * they require a downward traversal at some point. For example,
0159: * assume a node <code>A</code> is reachable, and a call to
0160: * <code>A.getSinks()</code> yields a <code>Vector</code> containing a
0161: * reference to a previously unreachable node <code>B</code>. The
0162: * node <code>B</code> naturally becomes reachable by virtue of the
0163: * new user reference pointing to it. However, if the user were to
0164: * relinquish that reference, the node might be garbage collected, and
0165: * a future call to <code>A.getSinks()</code> might no longer include
0166: * <code>B</code> in its return value.
0167: *
0168: * <p> Because the set of sinks of a node is inherently unstable, only
0169: * the <code>getSinks</code> method is provided for external access to
0170: * the sink vector at a node. A hypothetical method such as
0171: * <code>getSink</code> or <code>getNumSinks</code> would produce
0172: * confusing results should a sink be garbage collected between that
0173: * call and a subsequent call to <code>getSinks</code>.
0174: *
0175: * @see java.awt.image.RenderedImage
0176: * @see java.lang.ref.Reference
0177: * @see java.lang.ref.WeakReference
0178: * @see ImageJAI
0179: * @see OpImage
0180: * @see RenderedImageAdapter
0181: * @see SnapshotImage
0182: * @see TiledImage
0183: */
0184: public abstract class PlanarImage implements ImageJAI, RenderedImage {
0185:
0186: /** The UID for this image. */
0187: private Object UID;
0188:
0189: /**
0190: * The X coordinate of the image's top-left pixel.
0191: */
0192: protected int minX;
0193:
0194: /**
0195: * The Y coordinate of the image's top-left pixel.
0196: */
0197: protected int minY;
0198:
0199: /**
0200: * The image's width in number of pixels.
0201: */
0202: protected int width;
0203:
0204: /**
0205: * The image's height in number of pixels.
0206: */
0207: protected int height;
0208:
0209: /** The image's bounds. */
0210: // Initialize to an empty Rectangle so this object may always
0211: // be used as a mutual exclusion lock in getBounds().
0212: private Rectangle bounds = new Rectangle();
0213:
0214: /**
0215: * The X coordinate of the top-left pixel of tile (0, 0).
0216: */
0217: protected int tileGridXOffset;
0218:
0219: /**
0220: * The Y coordinate of the top-left pixel of tile (0, 0).
0221: */
0222: protected int tileGridYOffset;
0223:
0224: /**
0225: * The width of a tile in number of pixels.
0226: */
0227: protected int tileWidth;
0228:
0229: /**
0230: * The height of a tile in number of pixels.
0231: */
0232: protected int tileHeight;
0233:
0234: /**
0235: * The image's <code>SampleModel</code>.
0236: */
0237: protected SampleModel sampleModel = null;
0238:
0239: /**
0240: * The image's <code>ColorModel</code>.
0241: */
0242: protected ColorModel colorModel = null;
0243:
0244: /**
0245: * A <code>TileFactory</code> for use in
0246: * {@link #createWritableRaster(SampleModel,Point)}.
0247: * This field will be <code>null</code> unless initialized via the
0248: * configuration properties passed to
0249: * {@link #PlanarImage(ImageLayout,Vector,Map)}.
0250: *
0251: * @since JAI 1.1.2
0252: */
0253: protected TileFactory tileFactory = null;
0254:
0255: /** The <code>PlanarImage</code> sources of the image. */
0256: private Vector sources = null;
0257:
0258: /** A set of <code>WeakReference</code>s to the sinks of the image. */
0259: private Vector sinks = null;
0260:
0261: /**
0262: * A helper object to manage firing events.
0263: *
0264: * @since JAI 1.1
0265: */
0266: protected PropertyChangeSupportJAI eventManager = null;
0267:
0268: /**
0269: * A helper object to manage the image properties.
0270: *
0271: * @since JAI 1.1
0272: */
0273: protected WritablePropertySourceImpl properties = null;
0274:
0275: /**
0276: * A <code>SnapshotImage</code> that will centralize tile
0277: * versioning for this image.
0278: */
0279: private SnapshotImage snapshot = null;
0280:
0281: /** A <code>WeakReference</code> to this image. */
0282: private WeakReference weakThis;
0283:
0284: /** Cache of registered <code>TileComputationListener</code>s. */
0285: private Set tileListeners = null;
0286:
0287: private boolean disposed = false;
0288:
0289: /** Array copy size, used by "cobble" methods. */
0290: private static final int MIN_ARRAYCOPY_SIZE = 64;
0291:
0292: /**
0293: * The default constructor.
0294: *
0295: * <p> The <code>eventManager</code> and <code>properties</code>
0296: * helper fields are initialized by this constructor; no other
0297: * non-private fields are set.
0298: */
0299: public PlanarImage() {
0300: this .weakThis = new WeakReference(this );
0301:
0302: // Create an event manager.
0303: eventManager = new PropertyChangeSupportJAI(this );
0304:
0305: // Copy the properties by reference.
0306: this .properties = new WritablePropertySourceImpl(null, null,
0307: eventManager);
0308: this .UID = ImageUtil.generateID(this );
0309: }
0310:
0311: /**
0312: * Constructor.
0313: *
0314: * <p> The image's layout is encapsulated in the <code>layout</code>
0315: * argument. Note that no verification is performed to determine whether
0316: * the image layout has been set either at construction or subsequently.
0317: *
0318: * <p> This constructor does not provide any default settings for
0319: * the layout variables so all of those that will be used later must
0320: * be set in the <code>layout</code> argument or subsequently via
0321: * <code>setImageLayout()</code> before the values are used.
0322: * Otherwise, unexpected errors may occur.
0323:
0324: * <p> If the <code>SampleModel</code> is non-<code>null</code> and the
0325: * supplied tile dimensions are positive, then if the dimensions of the
0326: * supplied <code>SampleModel</code> differ from the tile dimensions, a
0327: * new <code>SampleModel</code> will be created for the image from the
0328: * supplied <code>SampleModel</code> but with dimensions equal to those
0329: * of a tile.
0330: *
0331: * <p> If both the <code>SampleModel</code> and the <code>ColorModel</code>
0332: * in the supplied <code>ImageLayout</code> are non-<code>null</code>
0333: * they will be tested for compatibility. If the test fails an
0334: * exception will be thrown. The test is that
0335: *
0336: * <ul>
0337: * <li> <code>ColorModel.isCompatibleSampleModel()</code> invoked on
0338: * the <code>SampleModel</code> must return <code>true</code>, and
0339: * <li> if the <code>ColorModel</code> is a
0340: * <code>ComponentColorModel</code> then:
0341: * <ul>
0342: * <li>the number of bands of the <code>SampleModel</code> must equal
0343: * the number of components of the <code>ColorModel</code>, and
0344: * <li><code>SampleModel.getSampleSize(b) >= ColorModel.getComponentSize(b)</code>
0345: * for all bands <code>b</code>.
0346: * </ul>
0347: * </ul>
0348: *
0349: * <p> The <code>sources</code> parameter contains a list of immediate
0350: * sources of this image none of which may be <code>null</code>. All
0351: * <code>RenderedImage</code>s in the list are automatically converted
0352: * into <code>PlanarImage</code>s when necessary. If this image has
0353: * no source, this argument should be <code>null</code>.
0354: *
0355: * <p> The <code>properties</code> parameter contains a mapping of image
0356: * properties. All map entries which have a key which is either a
0357: * <code>String</code> or a <code>CaselessStringKey</code> are interpreted
0358: * as image properties and will be copied to the property database of
0359: * this image. This parameter may be <code>null</code>.
0360: *
0361: * <p>If a {@link TileFactory}-valued mapping of the key
0362: * {@link JAI#KEY_TILE_FACTORY} is present in
0363: * <code>properties</code>, then set the instance variable
0364: * <code>tileFactory</code> to the specified <code>TileFactory</code>.
0365: * This <code>TileFactory</code> will be used by
0366: * {@link #createWritableRaster(SampleModel,Point)} to create
0367: * <code>Raster</code>s, notably in {@link #getData(Rectangle)},
0368: * {@link #copyData(WritableRaster)}, and
0369: * {@link #getExtendedData(Rectangle,BorderExtender)}.</p>
0370: *
0371: * <p> The event and property helper fields are initialized by this
0372: * constructor.
0373: *
0374: * @param layout The layout of this image or <code>null</code>.
0375: * @param sources The immediate sources of this image or
0376: * <code>null</code>.
0377: * @param properties A <code>Map</code> containing the properties of
0378: * this image or <code>null</code>.
0379: *
0380: * @throws IllegalArgumentException if a
0381: * <code>ColorModel</code> is specified in the layout and it is
0382: * incompatible with the <code>SampleModel</code>
0383: * @throws IllegalArgumentException If <code>sources</code>
0384: * is non-<code>null</code> and any object in
0385: * <code>sources</code> is <code>null</code>.
0386: *
0387: * @since JAI 1.1
0388: */
0389: public PlanarImage(ImageLayout layout, Vector sources,
0390: Map properties) {
0391: this ();
0392:
0393: // Set the image layout.
0394: if (layout != null) {
0395: setImageLayout(layout);
0396: }
0397:
0398: // Set the image sources. All source Vector elements must be non-null.
0399: // If any source is a RenderedImage it is converted to a PlanarImage
0400: // before being set.
0401: if (sources != null) {
0402: setSources(sources);
0403: }
0404:
0405: if (properties != null) {
0406: // Add properties from parameter.
0407: this .properties.addProperties(properties);
0408:
0409: // Set tileFactory if key present.
0410: if (properties.containsKey(JAI.KEY_TILE_FACTORY)) {
0411: Object factoryValue = properties
0412: .get(JAI.KEY_TILE_FACTORY);
0413:
0414: // Check the class type in case 'properties' is not
0415: // an instance of RenderingHints.
0416: if (factoryValue instanceof TileFactory) {
0417: this .tileFactory = (TileFactory) factoryValue;
0418: }
0419: }
0420: }
0421: }
0422:
0423: /**
0424: * Sets the image bounds, tile grid layout,
0425: * <code>SampleModel</code> and <code>ColorModel</code> using
0426: * values from an <code>ImageLayout</code> object.
0427: *
0428: * <p> If either of the tile dimensions is not set in the passed in
0429: * <code>ImageLayout</code> object, then the tile dimension in question
0430: * will be set to the corresponding image dimension.
0431: *
0432: * <p> If either of the tile grid offsets is not set in the passed in
0433: * <code>ImageLayout</code> object, then the tile grid offset in
0434: * question will be set to 0. The same is true for the <code>minX</code>
0435: * , <code>minY</code>, <code>width</code> and <code>height</code>
0436: * fields, if no value is set in the passed in <code>ImageLayout</code>
0437: * object, they will be set to 0.
0438: *
0439: * <p> If the <code>SampleModel</code> is non-<code>null</code> and the
0440: * supplied tile dimensions are positive, then if the dimensions of the
0441: * supplied <code>SampleModel</code> differ from the tile dimensions, a
0442: * new <code>SampleModel</code> will be created for the image from the
0443: * supplied <code>SampleModel</code> but with dimensions equal to those
0444: * of a tile.
0445: *
0446: * <p> If both the <code>SampleModel</code> and the <code>ColorModel</code>
0447: * in the supplied <code>ImageLayout</code> are non-<code>null</code>
0448: * they will be tested for compatibility. If the test fails an
0449: * exception will be thrown. The test is that
0450: *
0451: * <ul>
0452: * <li> <code>ColorModel.isCompatibleSampleModel()</code> invoked on
0453: * the <code>SampleModel</code> must return <code>true</code>, and
0454: * <li> if the <code>ColorModel</code> is a
0455: * <code>ComponentColorModel</code> then:
0456: * <ul>
0457: * <li>the number of bands of the <code>SampleModel</code> must equal
0458: * the number of components of the <code>ColorModel</code>, and
0459: * <li><code>SampleModel.getSampleSize(b) >= ColorModel.getComponentSize(b)</code>
0460: * for all bands <code>b</code>.
0461: * </ul>
0462: * </ul>
0463: *
0464: * @param layout an ImageLayout that is used to selectively
0465: * override the image's layout, <code>SampleModel</code>,
0466: * and <code>ColorModel</code>. Only valid fields, i.e.,
0467: * those for which <code>ImageLayout.isValid()</code> returns
0468: * <code>true</code> for the appropriate mask, are used.
0469: *
0470: * @throws <code>IllegalArgumentException</code> if <code>layout</code>
0471: * is <code>null</code>.
0472: * @throws <code>IllegalArgumentException</code> if a
0473: * <code>ColorModel</code> is specified in the layout and it is
0474: * incompatible with the <code>SampleModel</code>
0475: *
0476: * @since JAI 1.1
0477: */
0478: protected void setImageLayout(ImageLayout layout) {
0479: if (layout == null) {
0480: throw new IllegalArgumentException(JaiI18N
0481: .getString("Generic0"));
0482: } else {
0483: // Set image bounds.
0484: if (layout.isValid(ImageLayout.MIN_X_MASK)) {
0485: minX = layout.getMinX(null);
0486: }
0487: if (layout.isValid(ImageLayout.MIN_Y_MASK)) {
0488: minY = layout.getMinY(null);
0489: }
0490: if (layout.isValid(ImageLayout.WIDTH_MASK)) {
0491: width = layout.getWidth(null);
0492: }
0493: if (layout.isValid(ImageLayout.HEIGHT_MASK)) {
0494: height = layout.getHeight(null);
0495: }
0496:
0497: // Set tile grid parameters.
0498: if (layout.isValid(ImageLayout.TILE_GRID_X_OFFSET_MASK)) {
0499: tileGridXOffset = layout.getTileGridXOffset(null);
0500: }
0501: if (layout.isValid(ImageLayout.TILE_GRID_Y_OFFSET_MASK)) {
0502: tileGridYOffset = layout.getTileGridYOffset(null);
0503: }
0504: if (layout.isValid(ImageLayout.TILE_WIDTH_MASK)) {
0505: tileWidth = layout.getTileWidth(null);
0506: } else {
0507: tileWidth = width;
0508: }
0509: if (layout.isValid(ImageLayout.TILE_HEIGHT_MASK)) {
0510: tileHeight = layout.getTileHeight(null);
0511: } else {
0512: tileHeight = height;
0513: }
0514:
0515: // Set SampleModel.
0516: if (layout.isValid(ImageLayout.SAMPLE_MODEL_MASK)) {
0517: sampleModel = layout.getSampleModel(null);
0518: }
0519:
0520: // Make the SampleModel dimensions equal to those of a tile.
0521: if (sampleModel != null
0522: && tileWidth > 0
0523: && tileHeight > 0
0524: && (sampleModel.getWidth() != tileWidth || sampleModel
0525: .getHeight() != tileHeight)) {
0526: sampleModel = sampleModel.createCompatibleSampleModel(
0527: tileWidth, tileHeight);
0528: }
0529:
0530: // Set ColorModel.
0531: if (layout.isValid(ImageLayout.COLOR_MODEL_MASK)) {
0532: colorModel = layout.getColorModel(null);
0533: }
0534: if (colorModel != null && sampleModel != null) {
0535: if (!JDKWorkarounds.areCompatibleDataModels(
0536: sampleModel, colorModel)) {
0537: throw new IllegalArgumentException(JaiI18N
0538: .getString("PlanarImage5"));
0539: /* XXX Begin debugging statements: to be deleted
0540: System.err.println("\n----- ERROR: "+
0541: JaiI18N.getString("PlanarImage5"));
0542: System.err.println(getClass().getName());
0543: System.err.println(sampleModel.getClass().getName()+": "+
0544: sampleModel);
0545: System.err.println("Transfer type = "+
0546: sampleModel.getTransferType());
0547: System.err.println(colorModel.getClass().getName()+": "+
0548: colorModel);
0549: System.err.println("");
0550: XXX End debugging statements */
0551: }
0552: }
0553: }
0554: }
0555:
0556: /**
0557: * Wraps an arbitrary <code>RenderedImage</code> to produce a
0558: * <code>PlanarImage</code>. <code>PlanarImage</code> adds
0559: * various properties to an image, such as source and sink vectors
0560: * and the ability to produce snapshots, that are necessary for
0561: * JAI.
0562: *
0563: * <p> If the image is already a <code>PlanarImage</code>, it is
0564: * simply returned unchanged. Otherwise, the image is wrapped in
0565: * a <code>RenderedImageAdapter</code> or
0566: * <code>WritableRenderedImageAdapter</code> as appropriate.
0567: *
0568: * @param image The <code>RenderedImage</code> to be converted into
0569: * a <code>PlanarImage</code>.
0570: *
0571: * @return A <code>PlanarImage</code> containing <code>image</code>'s
0572: * pixel data.
0573: *
0574: * @throws IllegalArgumentException If <code>image</code> is
0575: * <code>null</code>.
0576: */
0577: public static PlanarImage wrapRenderedImage(RenderedImage image) {
0578: if (image == null) {
0579: throw new IllegalArgumentException(JaiI18N
0580: .getString("Generic0"));
0581: }
0582:
0583: if (image instanceof PlanarImage) {
0584: return (PlanarImage) image;
0585: } else if (image instanceof WritableRenderedImage) {
0586: return new WritableRenderedImageAdapter(
0587: (WritableRenderedImage) image);
0588: } else {
0589: return new RenderedImageAdapter(image);
0590: }
0591: }
0592:
0593: /**
0594: * Creates a snapshot, that is, a virtual copy of the image's
0595: * current contents. If the image is not a
0596: * <code>WritableRenderedImage</code>, it is returned unchanged.
0597: * Otherwise, a <code>SnapshotImage</code> is created and the
0598: * result of calling its <code>createSnapshot()</code> is
0599: * returned.
0600: *
0601: * @return A <code>PlanarImage</code> with immutable contents.
0602: */
0603: public PlanarImage createSnapshot() {
0604: if (this instanceof WritableRenderedImage) {
0605: if (snapshot == null) {
0606: synchronized (this ) {
0607: snapshot = new SnapshotImage(this );
0608: }
0609: }
0610: return snapshot.createSnapshot();
0611:
0612: } else {
0613: return this ;
0614: }
0615: }
0616:
0617: /**
0618: * Returns the X coordinate of the left-most column of the image.
0619: * The default implementation returns the corresponding instance variable.
0620: */
0621: public int getMinX() {
0622: return minX;
0623: }
0624:
0625: /**
0626: * Returns the X coordinate of the column immediately to the right
0627: * of the right-most column of the image.
0628: *
0629: * <p> This method is implemented in terms of <code>getMinX()</code>
0630: * and <code>getWidth()</code> so that subclasses which override
0631: * those methods do not need to override this one.
0632: */
0633: public int getMaxX() {
0634: return getMinX() + getWidth();
0635: }
0636:
0637: /**
0638: * Returns the Y coordinate of the top-most row of the image.
0639: * The default implementation returns the corresponding instance variable.
0640: */
0641: public int getMinY() {
0642: return minY;
0643: }
0644:
0645: /**
0646: * Returns the Y coordinate of the row immediately below the
0647: * bottom-most row of the image.
0648: *
0649: * <p> This method is implemented in terms of <code>getMinY()</code>
0650: * and <code>getHeight()</code> so that subclasses which override
0651: * those methods do not need to override this one.
0652: */
0653: public int getMaxY() {
0654: return getMinY() + getHeight();
0655: }
0656:
0657: /**
0658: * Returns the width of the image in number of pixels.
0659: * The default implementation returns the corresponding instance variable.
0660: */
0661: public int getWidth() {
0662: return width;
0663: }
0664:
0665: /**
0666: * Returns the height of the image in number of pixels.
0667: * The default implementation returns the corresponding instance variable.
0668: */
0669: public int getHeight() {
0670: return height;
0671: }
0672:
0673: /**
0674: * Retrieve the number of image bands. Note that this will not equal
0675: * the number of color components if the image has an
0676: * <code>IndexColorModel</code>. This is equivalent to calling
0677: * <code>getSampleModel().getNumBands()</code>.
0678: *
0679: * @since JAI 1.1
0680: */
0681: public int getNumBands() {
0682: return getSampleModel().getNumBands();
0683: }
0684:
0685: /**
0686: * Returns the image's bounds as a <code>Rectangle</code>.
0687: *
0688: * <p> The image's bounds are defined by the values returned by
0689: * <code>getMinX()</code>, <code>getMinY()</code>,
0690: * <code>getWidth()</code>, and <code>getHeight()</code>.
0691: * A <code>Rectangle</code> is created based on these four methods and
0692: * cached in this class. Each time that this method is invoked, the
0693: * bounds of this <code>Rectangle</code> are updated with the values
0694: * returned by the four aforementioned accessors.
0695: *
0696: * <p> Because this method returns the <code>bounds</code> variable
0697: * by reference, the caller should not change the settings of the
0698: * <code>Rectangle</code>. Otherwise, unexpected errors may occur.
0699: * Likewise, if the caller expects this variable to be immutable it
0700: * should clone the returned <code>Rectangle</code> if there is any
0701: * possibility that it might be changed by the <code>PlanarImage</code>.
0702: * This may generally occur only for instances of <code>RenderedOp</code>.
0703: */
0704: public Rectangle getBounds() {
0705: synchronized (bounds) {
0706: bounds.setBounds(getMinX(), getMinY(), getWidth(),
0707: getHeight());
0708: }
0709:
0710: return bounds;
0711: }
0712:
0713: /**
0714: * Returns the X coordinate of the top-left pixel of tile (0, 0).
0715: * The default implementation returns the corresponding instance variable.
0716: */
0717: public int getTileGridXOffset() {
0718: return tileGridXOffset;
0719: }
0720:
0721: /**
0722: * Returns the Y coordinate of the top-left pixel of tile (0, 0).
0723: * The default implementation returns the corresponding instance variable.
0724: */
0725: public int getTileGridYOffset() {
0726: return tileGridYOffset;
0727: }
0728:
0729: /**
0730: * Returns the width of a tile of this image in number of pixels.
0731: * The default implementation returns the corresponding instance variable.
0732: */
0733: public int getTileWidth() {
0734: return tileWidth;
0735: }
0736:
0737: /**
0738: * Returns the height of a tile of this image in number of pixels.
0739: * The default implementation returns the corresponding instance variable.
0740: */
0741: public int getTileHeight() {
0742: return tileHeight;
0743: }
0744:
0745: /**
0746: * Returns the horizontal index of the left-most column of tiles.
0747: *
0748: * <p> This method is implemented in terms of the static method
0749: * <code>XToTileX()</code> applied to the values returned by primitive
0750: * layout accessors and so does not need to be implemented by subclasses.
0751: */
0752: public int getMinTileX() {
0753: return XToTileX(getMinX(), getTileGridXOffset(), getTileWidth());
0754: }
0755:
0756: /**
0757: * Returns the horizontal index of the right-most column of tiles.
0758: *
0759: * <p> This method is implemented in terms of the static method
0760: * <code>XToTileX()</code> applied to the values returned by primitive
0761: * layout accessors and so does not need to be implemented by subclasses.
0762: */
0763: public int getMaxTileX() {
0764: return XToTileX(getMinX() + getWidth() - 1,
0765: getTileGridXOffset(), getTileWidth());
0766: }
0767:
0768: /**
0769: * Returns the number of tiles along the tile grid in the
0770: * horizontal direction.
0771: *
0772: * <p> This method is implemented in terms of the static method
0773: * <code>XToTileX()</code> applied to the values returned by primitive
0774: * layout accessors and so does not need to be implemented by subclasses.
0775: */
0776: public int getNumXTiles() {
0777: int x = getMinX();
0778: int tx = getTileGridXOffset();
0779: int tw = getTileWidth();
0780: return XToTileX(x + getWidth() - 1, tx, tw)
0781: - XToTileX(x, tx, tw) + 1;
0782: }
0783:
0784: /**
0785: * Returns the vertical index of the top-most row of tiles.
0786: *
0787: * <p> This method is implemented in terms of the static method
0788: * <code>YToTileY()</code> applied to the values returned by primitive
0789: * layout accessors and so does not need to be implemented by subclasses.
0790: */
0791: public int getMinTileY() {
0792: return YToTileY(getMinY(), getTileGridYOffset(),
0793: getTileHeight());
0794: }
0795:
0796: /**
0797: * Returns the vertical index of the bottom-most row of tiles.
0798: *
0799: * <p> This method is implemented in terms of the static method
0800: * <code>YToTileY()</code> applied to the values returned by primitive
0801: * layout accessors and so does not need to be implemented by subclasses.
0802: */
0803: public int getMaxTileY() {
0804: return YToTileY(getMinY() + getHeight() - 1,
0805: getTileGridYOffset(), getTileHeight());
0806: }
0807:
0808: /**
0809: * Returns the number of tiles along the tile grid in the vertical
0810: * direction.
0811: *
0812: * <p> This method is implemented in terms of the static method
0813: * <code>YToTileY()</code> applied to the values returned by primitive
0814: * layout accessors and so does not need to be implemented by subclasses.
0815: */
0816: public int getNumYTiles() {
0817: int y = getMinY();
0818: int ty = getTileGridYOffset();
0819: int th = getTileHeight();
0820: return YToTileY(y + getHeight() - 1, ty, th)
0821: - YToTileY(y, ty, th) + 1;
0822: }
0823:
0824: /**
0825: * Converts a pixel's X coordinate into a horizontal tile index
0826: * relative to a given tile grid layout specified by its X offset
0827: * and tile width.
0828: *
0829: * <p> If <code>tileWidth < 0</code>, the results of this method
0830: * are undefined. If <code>tileWidth == 0</code>, an
0831: * <code>ArithmeticException</code> will be thrown.
0832: *
0833: * @throws ArithmeticException If <code>tileWidth == 0</code>.
0834: */
0835: public static int XToTileX(int x, int tileGridXOffset, int tileWidth) {
0836: x -= tileGridXOffset;
0837: if (x < 0) {
0838: x += 1 - tileWidth; // force round to -infinity (ceiling)
0839: }
0840: return x / tileWidth;
0841: }
0842:
0843: /**
0844: * Converts a pixel's Y coordinate into a vertical tile index
0845: * relative to a given tile grid layout specified by its Y offset
0846: * and tile height.
0847: *
0848: * <p> If <code>tileHeight < 0</code>, the results of this method
0849: * are undefined. If <code>tileHeight == 0</code>, an
0850: * <code>ArithmeticException</code> will be thrown.
0851: *
0852: * @throws ArithmeticException If <code>tileHeight == 0</code>.
0853: */
0854: public static int YToTileY(int y, int tileGridYOffset,
0855: int tileHeight) {
0856: y -= tileGridYOffset;
0857: if (y < 0) {
0858: y += 1 - tileHeight; // force round to -infinity (ceiling)
0859: }
0860: return y / tileHeight;
0861: }
0862:
0863: /**
0864: * Converts a pixel's X coordinate into a horizontal tile index.
0865: * No attempt is made to detect out-of-range coordinates.
0866: *
0867: * <p> This method is implemented in terms of the static method
0868: * <code>XToTileX()</code> applied to the values returned by primitive
0869: * layout accessors and so does not need to be implemented by subclasses.
0870: *
0871: * @param x the X coordinate of a pixel.
0872: *
0873: * @return the X index of the tile containing the pixel.
0874: *
0875: * @throws ArithmeticException If the tile width of this image is 0.
0876: */
0877: public int XToTileX(int x) {
0878: return XToTileX(x, getTileGridXOffset(), getTileWidth());
0879: }
0880:
0881: /**
0882: * Converts a pixel's Y coordinate into a vertical tile index. No
0883: * attempt is made to detect out-of-range coordinates.
0884: *
0885: * <p> This method is implemented in terms of the static method
0886: * <code>YToTileY()</code> applied to the values returned by primitive
0887: * layout accessors and so does not need to be implemented by subclasses.
0888: *
0889: * @param y the Y coordinate of a pixel.
0890: *
0891: * @return the Y index of the tile containing the pixel.
0892: *
0893: * @throws ArithmeticException If the tile height of this image is 0.
0894: */
0895: public int YToTileY(int y) {
0896: return YToTileY(y, getTileGridYOffset(), getTileHeight());
0897: }
0898:
0899: /**
0900: * Converts a horizontal tile index into the X coordinate of its
0901: * upper left pixel relative to a given tile grid layout specified
0902: * by its X offset and tile width.
0903: */
0904: public static int tileXToX(int tx, int tileGridXOffset,
0905: int tileWidth) {
0906: return tx * tileWidth + tileGridXOffset;
0907: }
0908:
0909: /**
0910: * Converts a vertical tile index into the Y coordinate of
0911: * its upper left pixel relative to a given tile grid layout
0912: * specified by its Y offset and tile height.
0913: */
0914: public static int tileYToY(int ty, int tileGridYOffset,
0915: int tileHeight) {
0916: return ty * tileHeight + tileGridYOffset;
0917: }
0918:
0919: /**
0920: * Converts a horizontal tile index into the X coordinate of its
0921: * upper left pixel. No attempt is made to detect out-of-range
0922: * indices.
0923: *
0924: * <p> This method is implemented in terms of the static method
0925: * <code>tileXToX()</code> applied to the values returned by primitive
0926: * layout accessors and so does not need to be implemented by subclasses.
0927: *
0928: * @param tx the horizontal index of a tile.
0929: * @return the X coordinate of the tile's upper left pixel.
0930: */
0931: public int tileXToX(int tx) {
0932: return tileXToX(tx, getTileGridXOffset(), getTileWidth());
0933: }
0934:
0935: /**
0936: * Converts a vertical tile index into the Y coordinate of its
0937: * upper left pixel. No attempt is made to detect out-of-range
0938: * indices.
0939: *
0940: * <p> This method is implemented in terms of the static method
0941: * <code>tileYToY()</code> applied to the values returned by primitive
0942: * layout accessors and so does not need to be implemented by subclasses.
0943: *
0944: * @param ty the vertical index of a tile.
0945: * @return the Y coordinate of the tile's upper left pixel.
0946: */
0947: public int tileYToY(int ty) {
0948: return tileYToY(ty, getTileGridYOffset(), getTileHeight());
0949: }
0950:
0951: /**
0952: * Returns a <code>Rectangle</code> indicating the active area of
0953: * a given tile. The <code>Rectangle</code> is defined as the
0954: * intersection of the tile area and the image bounds. No attempt
0955: * is made to detect out-of-range indices; tile indices lying
0956: * completely outside of the image will result in returning an
0957: * empty <code>Rectangle</code> (width and/or height less than or
0958: * equal to 0).
0959: *
0960: * <p> This method is implemented in terms of the primitive layout
0961: * accessors and so does not need to be implemented by subclasses.
0962: *
0963: * @param tileX The X index of the tile.
0964: * @param tileY The Y index of the tile.
0965: *
0966: * @return A <code>Rectangle</code>
0967: */
0968: public Rectangle getTileRect(int tileX, int tileY) {
0969: return getBounds().intersection(
0970: new Rectangle(tileXToX(tileX), tileYToY(tileY),
0971: getTileWidth(), getTileHeight()));
0972: }
0973:
0974: /**
0975: * Returns the <code>SampleModel</code> of the image.
0976: * The default implementation returns the corresponding instance variable.
0977: */
0978: public SampleModel getSampleModel() {
0979: return sampleModel;
0980: }
0981:
0982: /**
0983: * Returns the <code>ColorModel</code> of the image.
0984: * The default implementation returns the corresponding instance variable.
0985: */
0986: public ColorModel getColorModel() {
0987: return colorModel;
0988: }
0989:
0990: /**
0991: * Returns a <code>ComponentColorModel</code> created based on
0992: * the indicated <code>dataType</code> and <code>numBands</code>.
0993: *
0994: * <p> The <code>dataType</code> must be one of <code>DataBuffer</code>'s
0995: * <code>TYPE_BYTE</code>, <code>TYPE_USHORT</code>,
0996: * <code>TYPE_INT</code>, <code>TYPE_FLOAT</code>, or
0997: * <code>TYPE_DOUBLE</code>.
0998: *
0999: * <p> The <code>numBands</code> may range from 1 to 4, with the
1000: * following <code>ColorSpace</code> and alpha settings:
1001: * <ul>
1002: * <li> <code>numBands = 1</code>: <code>CS_GRAY</code> without alpha;
1003: * <li> <code>numBands = 2</code>: <code>CS_GRAY</code> with alpha;
1004: * <li> <code>numBands = 3</code>: <code>CS_sRGB</code> without alpha;
1005: * <li> <code>numBands = 4</code>: <code>CS_sRGB</code> with alpha.
1006: * </ul>
1007: * The transparency is set to <code>Transparency.TRANSLUCENT</code> if
1008: * alpha is used and to <code>Transparency.OPAQUE</code> otherwise.
1009: *
1010: * <p> All other inputs result in a <code>null</code> return value.
1011: *
1012: * @param dataType The data type of the <code>ColorModel</code>.
1013: * @param numBands The number of bands of the pixels the created
1014: * <code>ColorModel</code> is going to work with.
1015: *
1016: * @since JAI 1.1
1017: */
1018: public static ColorModel getDefaultColorModel(int dataType,
1019: int numBands) {
1020: if (dataType < DataBuffer.TYPE_BYTE
1021: || dataType == DataBuffer.TYPE_SHORT
1022: || dataType > DataBuffer.TYPE_DOUBLE || numBands < 1
1023: || numBands > 4) {
1024: return null;
1025: }
1026:
1027: ColorSpace cs = numBands <= 2 ? ColorSpace
1028: .getInstance(ColorSpace.CS_GRAY) : ColorSpace
1029: .getInstance(ColorSpace.CS_sRGB);
1030:
1031: boolean useAlpha = (numBands == 2) || (numBands == 4);
1032: int transparency = useAlpha ? Transparency.TRANSLUCENT
1033: : Transparency.OPAQUE;
1034:
1035: return RasterFactory.createComponentColorModel(dataType, cs,
1036: useAlpha, false, transparency);
1037:
1038: }
1039:
1040: /**
1041: * Creates a <code>ColorModel</code> that may be used with the
1042: * specified <code>SampleModel</code>. If a suitable
1043: * <code>ColorModel</code> cannot be found, this method returns
1044: * <code>null</code>.
1045: *
1046: * <p> Suitable <code>ColorModel</code>s are guaranteed to exist
1047: * for all instances of <code>ComponentSampleModel</code> whose
1048: * <code>dataType</code> is not <code>DataBuffer.TYPE_SHORT</code>
1049: * and with no more than 4 bands. A <code>ComponentColorModel</code>
1050: * of either type CS_GRAY or CS_sRGB is returned.
1051: *
1052: * <p> For 1- and 3- banded <code>SampleModel</code>s, the returned
1053: * <code>ColorModel</code> will be opaque. For 2- and 4-banded
1054: * <code>SampleModel</code>s, the output will use alpha transparency.
1055: *
1056: * <p> Additionally, an instance of <code>DirectColorModel</code>
1057: * will be created for instances of
1058: * <code>SinglePixelPackedSampleModel</code> with no more than 4 bands.
1059: *
1060: * <p> Finally, an instance of <code>IndexColorModel</code>
1061: * will be created for instances of
1062: * <code>MultiPixelPackedSampleModel</code> with a single band and a
1063: * pixel bit stride of unity. This represents the case of binary data.
1064: *
1065: * <p> This method is intended as an useful utility for the creation
1066: * of simple <code>ColorModel</code>s for some common cases.
1067: * In more complex situations, it may be necessary to instantiate
1068: * the appropriate <code>ColorModel</code>s directly.
1069: *
1070: * @return An instance of <code>ColorModel</code> that is suitable for
1071: * the supplied <code>SampleModel</code>, or <code>null</code>.
1072: *
1073: * @throws IllegalArgumentException If <code>sm</code> is
1074: * <code>null</code>.
1075: */
1076: public static ColorModel createColorModel(SampleModel sm) {
1077: if (sm == null) {
1078: throw new IllegalArgumentException(JaiI18N
1079: .getString("Generic0"));
1080: }
1081:
1082: int bands = sm.getNumBands();
1083: if (bands < 1 || bands > 4) {
1084: return null;
1085: }
1086:
1087: if (sm instanceof ComponentSampleModel) {
1088: return getDefaultColorModel(sm.getDataType(), bands);
1089:
1090: } else if (sm instanceof SinglePixelPackedSampleModel) {
1091: SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
1092:
1093: int[] bitMasks = sppsm.getBitMasks();
1094: int rmask = 0;
1095: int gmask = 0;
1096: int bmask = 0;
1097: int amask = 0;
1098:
1099: int numBands = bitMasks.length;
1100: if (numBands <= 2) {
1101: rmask = gmask = bmask = bitMasks[0];
1102: if (numBands == 2) {
1103: amask = bitMasks[1];
1104: }
1105: } else {
1106: rmask = bitMasks[0];
1107: gmask = bitMasks[1];
1108: bmask = bitMasks[2];
1109: if (numBands == 4) {
1110: amask = bitMasks[3];
1111: }
1112: }
1113:
1114: int[] sampleSize = sppsm.getSampleSize();
1115: int bits = 0;
1116: for (int i = 0; i < sampleSize.length; i++) {
1117: bits += sampleSize[i];
1118: }
1119:
1120: return new DirectColorModel(bits, rmask, gmask, bmask,
1121: amask);
1122:
1123: } else if (ImageUtil.isBinary(sm)) {
1124: byte[] comp = new byte[] { (byte) 0x00, (byte) 0xFF };
1125:
1126: return new IndexColorModel(1, 2, comp, comp, comp);
1127:
1128: } else { // unable to create an suitable ColorModel
1129: return null;
1130: }
1131: }
1132:
1133: /**
1134: * Returns the value of the instance variable <code>tileFactory</code>.
1135: *
1136: * @since JAI 1.1.2
1137: */
1138: public TileFactory getTileFactory() {
1139: return tileFactory;
1140: }
1141:
1142: /**
1143: * Returns the number of immediate <code>PlanarImage</code> sources
1144: * this image has. If this image has no source, this method returns
1145: * 0.
1146: */
1147: public int getNumSources() {
1148: return sources == null ? 0 : sources.size();
1149: }
1150:
1151: /**
1152: * Returns this image's immediate source(s) in a <code>Vector</code>.
1153: * If this image has no source, this method returns <code>null</code>.
1154: */
1155: public Vector getSources() {
1156: if (getNumSources() == 0) {
1157: return null;
1158: } else {
1159: synchronized (sources) {
1160: return (Vector) sources.clone();
1161: }
1162: }
1163: }
1164:
1165: /**
1166: * Returns the immediate source indicated by the index. If there
1167: * is no source corresponding to the specified index, this method
1168: * throws an exception.
1169: *
1170: * @param index The index of the desired source.
1171: *
1172: * @return A <code>PlanarImage</code> source.
1173: *
1174: * @throws ArrayIndexOutOfBoundsException If this image has no
1175: * immediate source, or if the index is negative or greater
1176: * than the maximum source index.
1177: *
1178: * @deprecated as of JAI 1.1. Use <code>getSourceImage()</code>.
1179: * @see PlanarImage#getSourceImage(int)
1180: */
1181: public PlanarImage getSource(int index) {
1182: if (sources == null) {
1183: throw new ArrayIndexOutOfBoundsException(JaiI18N
1184: .getString("PlanarImage0"));
1185: }
1186:
1187: synchronized (sources) {
1188: return (PlanarImage) sources.get(index);
1189: }
1190: }
1191:
1192: /**
1193: * Sets the list of sources from a given <code>List</code> of
1194: * <code>PlanarImage</code>s. All of the existing sources are
1195: * discarded. Any <code>RenderedImage</code> sources in the supplied
1196: * list are wrapped using <code>wrapRenderedImage()</code>. The list
1197: * of sinks of each prior <code>PlanarImage</code> source and of each
1198: * current unwrapped <code>PlanarImage</code> source is adjusted as
1199: * necessary such that this image is a sink of all such current sources
1200: * but is removed as a sink of all such prior sources which are not
1201: * also current.
1202: *
1203: * @param sourceList a <code>List</code> of <code>PlanarImage</code>s.
1204: *
1205: * @throws IllegalArgumentException If <code>sourceList</code> is
1206: * <code>null</code> or contains any <code>null</code> elements.
1207: */
1208: protected void setSources(List sourceList) {
1209: if (sourceList == null) {
1210: throw new IllegalArgumentException(JaiI18N
1211: .getString("Generic0"));
1212: }
1213:
1214: int size = sourceList.size();
1215:
1216: synchronized (this ) {
1217: if (sources != null) {
1218: // Remove this image as a sink of prior PlanarImage sources.
1219: Iterator it = sources.iterator();
1220: while (it.hasNext()) {
1221: Object src = it.next();
1222: if (src instanceof PlanarImage) {
1223: ((PlanarImage) src).removeSink(this );
1224: }
1225: }
1226: }
1227: sources = new Vector(size);
1228: }
1229:
1230: synchronized (sources) {
1231: for (int i = 0; i < size; i++) {
1232: Object sourceElement = sourceList.get(i);
1233: if (sourceElement == null) {
1234: throw new IllegalArgumentException(JaiI18N
1235: .getString("PlanarImage7"));
1236: }
1237:
1238: sources
1239: .add(sourceElement instanceof RenderedImage ? wrapRenderedImage((RenderedImage) sourceElement)
1240: : sourceElement);
1241:
1242: // Add as a sink of any PlanarImage source.
1243: if (sourceElement instanceof PlanarImage) {
1244: ((PlanarImage) sourceElement).addSink(this );
1245: }
1246: }
1247: }
1248: }
1249:
1250: /**
1251: * Removes all the sources of this image. This image is removed from
1252: * the list of sinks of any prior <code>PlanarImage</code>s sources.
1253: */
1254: protected void removeSources() {
1255: if (sources != null) {
1256: synchronized (this ) {
1257: if (sources != null) {
1258: // Remove this image as a sink of prior PlanarImage sources.
1259: Iterator it = sources.iterator();
1260: while (it.hasNext()) {
1261: Object src = it.next();
1262: if (src instanceof PlanarImage) {
1263: ((PlanarImage) src).removeSink(this );
1264: }
1265: }
1266: }
1267: sources = null;
1268: }
1269: }
1270: }
1271:
1272: /**
1273: * Returns the immediate source indicated by the index. If there
1274: * is no source corresponding to the specified index, this method
1275: * throws an exception.
1276: *
1277: * @param index The index of the desired source.
1278: *
1279: * @return A <code>PlanarImage</code> source.
1280: *
1281: * @throws ArrayIndexOutOfBoundsException If this image has no
1282: * immediate source, or if the index is negative or greater
1283: * than the maximum source index.
1284: *
1285: * @since JAI 1.1
1286: */
1287: public PlanarImage getSourceImage(int index) {
1288: if (sources == null) {
1289: throw new ArrayIndexOutOfBoundsException(JaiI18N
1290: .getString("PlanarImage0"));
1291: }
1292:
1293: synchronized (sources) {
1294: return (PlanarImage) sources.get(index);
1295: }
1296: }
1297:
1298: /**
1299: * Returns the immediate source indicated by the index. If there
1300: * is no source corresponding to the specified index, this method
1301: * throws an exception.
1302: *
1303: * @param index The index of the desired source.
1304: *
1305: * @return An <code>Object</code> source.
1306: *
1307: * @throws ArrayIndexOutOfBoundsException If this image has no
1308: * immediate source, or if the index is negative or greater
1309: * than the maximum source index.
1310: *
1311: * @since JAI 1.1
1312: */
1313: public Object getSourceObject(int index) {
1314: if (sources == null) {
1315: throw new ArrayIndexOutOfBoundsException(JaiI18N
1316: .getString("PlanarImage0"));
1317: }
1318:
1319: synchronized (sources) {
1320: return sources.get(index);
1321: }
1322: }
1323:
1324: /**
1325: * Adds an <code>Object</code> source to the list of sources.
1326: * If the source is a <code>RenderedImage</code> it is wrapped using
1327: * <code>wrapRenderedImage()</code>. If the unwrapped source is a
1328: * <code>PlanarImage</code> then this image is added to its list of sinks.
1329: *
1330: * @param source An <code>Object</code> to be added as an
1331: * immediate source of this image.
1332: *
1333: * @throws IllegalArgumentException If <code>source</code> is
1334: * <code>null</code>.
1335: *
1336: * @since JAI 1.1
1337: */
1338: protected void addSource(Object source) {
1339: if (source == null) {
1340: throw new IllegalArgumentException(JaiI18N
1341: .getString("Generic0"));
1342: }
1343:
1344: if (sources == null) {
1345: synchronized (this ) {
1346: sources = new Vector();
1347: }
1348: }
1349:
1350: synchronized (sources) {
1351: // Add the source wrapping it if necessary.
1352: sources
1353: .add(source instanceof RenderedImage ? wrapRenderedImage((RenderedImage) source)
1354: : source);
1355: }
1356:
1357: if (source instanceof PlanarImage) {
1358: ((PlanarImage) source).addSink(this );
1359: }
1360: }
1361:
1362: /**
1363: * Sets an immediate source of this image. The source to be replaced
1364: * with the new input <code>Object</code> is referred to by its
1365: * index. This image must already have a source corresponding to the
1366: * specified index. If the source is a <code>RenderedImage</code> it is
1367: * wrapped using <code>wrapRenderedImage()</code>. If the unwrapped
1368: * source is a <code>PlanarImage</code> then this image is added to its
1369: * list of sinks. If a <code>PlanarImage</code> source previously
1370: * existed at this index, this image is removed from its list of sinks.
1371: *
1372: * @param source A <code>Object</code> source to be set.
1373: * @param index The index of the source to be set.
1374: *
1375: * @throws ArrayIndexOutOfBoundsException If this image has no
1376: * immediate source, or if there is no source corresponding
1377: * to the index value.
1378: * @throws IllegalArgumentException If <code>source</code> is
1379: * <code>null</code>.
1380: *
1381: * @since JAI 1.1
1382: */
1383: protected void setSource(Object source, int index) {
1384: if (source == null) {
1385: throw new IllegalArgumentException(JaiI18N
1386: .getString("Generic0"));
1387: }
1388:
1389: if (sources == null) {
1390: throw new ArrayIndexOutOfBoundsException(JaiI18N
1391: .getString("PlanarImage0"));
1392: }
1393:
1394: synchronized (sources) {
1395: if (index < sources.size()
1396: && sources.get(index) instanceof PlanarImage) {
1397: getSourceImage(index).removeSink(this );
1398: }
1399: sources
1400: .set(
1401: index,
1402: source instanceof RenderedImage ? wrapRenderedImage((RenderedImage) source)
1403: : source);
1404: }
1405: if (source instanceof PlanarImage) {
1406: ((PlanarImage) source).addSink(this );
1407: }
1408: }
1409:
1410: /**
1411: * Removes an <code>Object</code> source from the list of sources.
1412: * If the source is a <code>PlanarImage</code> then this image
1413: * is removed from its list of sinks.
1414: *
1415: * @param source The <code>Object</code> source to be removed.
1416: *
1417: * @return <code>true</code> if the element was present,
1418: * <code>false</code> otherwise.
1419: *
1420: * @throws IllegalArgumentException If <code>source</code> is
1421: * <code>null</code>.
1422: *
1423: * @since JAI 1.1
1424: */
1425: protected boolean removeSource(Object source) {
1426: if (source == null) {
1427: throw new IllegalArgumentException(JaiI18N
1428: .getString("Generic0"));
1429: }
1430:
1431: if (sources == null) {
1432: return false;
1433: }
1434:
1435: synchronized (sources) {
1436: if (source instanceof PlanarImage) {
1437: ((PlanarImage) source).removeSink(this );
1438: }
1439: return sources.remove(source);
1440: }
1441: }
1442:
1443: /**
1444: * Returns a <code>Vector</code> containing the currently available
1445: * <code>PlanarImage</code> sinks of this image (images for which
1446: * this image is a source), or <code>null</code> if no sinks are
1447: * present.
1448: *
1449: * <p> Sinks are stored using weak references. This means that
1450: * the set of sinks may change between calls to
1451: * <code>getSinks()</code> if the garbage collector happens to
1452: * identify a sink as not otherwise reachable (reachability is
1453: * discussed in the class comments for this class).
1454: *
1455: * <p> Since the pool of sinks may change as garbage collection
1456: * occurs, <code>PlanarImage</code> does not implement either a
1457: * <code>getSink(int index)</code> or a <code>getNumSinks()</code>
1458: * method. Instead, the caller must call <code>getSinks()</code>,
1459: * which returns a Vector of normal references. As long as the
1460: * returned <code>Vector</code> is referenced from user code, the
1461: * images it references are reachable and may be reliably
1462: * accessed.
1463: */
1464: public Vector getSinks() {
1465: Vector v = null;
1466:
1467: if (sinks != null) {
1468: synchronized (sinks) {
1469: int size = sinks.size();
1470: v = new Vector(size);
1471: for (int i = 0; i < size; i++) {
1472: Object o = ((WeakReference) sinks.get(i)).get();
1473:
1474: if (o != null) {
1475: v.add(o);
1476: }
1477: }
1478: }
1479:
1480: if (v.size() == 0) {
1481: v = null;
1482: }
1483: }
1484: return v;
1485: }
1486:
1487: /**
1488: * Adds an <code>Object</code> sink to the list of sinks.
1489: *
1490: * @return <code>true</code> if the element was added,
1491: * <code>false</code> otherwise.
1492: *
1493: * @throws IllegalArgumentException if
1494: * <code>sink</code> is <code>null</code>.
1495: *
1496: * @since JAI 1.1
1497: */
1498: public synchronized boolean addSink(Object sink) {
1499: if (sink == null) {
1500: throw new IllegalArgumentException(JaiI18N
1501: .getString("Generic0"));
1502: }
1503:
1504: if (sinks == null) {
1505: sinks = new Vector();
1506: }
1507:
1508: boolean result = false;
1509: if (sink instanceof PlanarImage) {
1510: result = sinks.add(((PlanarImage) sink).weakThis);
1511: } else {
1512: result = sinks.add(new WeakReference(sink));
1513: }
1514:
1515: return result;
1516: }
1517:
1518: /**
1519: * Removes an <code>Object</code> sink from the list of sinks.
1520: *
1521: * @return <code>true</code> if the element was present,
1522: * <code>false</code> otherwise.
1523: *
1524: * @throws IllegalArgumentException if
1525: * <code>sink</code> is <code>null</code>.
1526: *
1527: * @since JAI 1.1
1528: */
1529: public synchronized boolean removeSink(Object sink) {
1530: if (sink == null) {
1531: throw new IllegalArgumentException(JaiI18N
1532: .getString("Generic0"));
1533: }
1534:
1535: if (sinks == null) {
1536: return false;
1537: }
1538:
1539: boolean result = false;
1540: if (sink instanceof PlanarImage) {
1541: result = sinks.remove(((PlanarImage) sink).weakThis);
1542: } else {
1543: Iterator it = sinks.iterator();
1544: while (it.hasNext()) {
1545: Object referent = ((WeakReference) it.next()).get();
1546: if (referent == sink) {
1547: // Remove the sink.
1548: it.remove();
1549: result = true;
1550: // Do not break: could be more than one.
1551: } else if (referent == null) {
1552: // A cleared reference: might as well remove it.
1553: it.remove(); // ignore return value here.
1554: }
1555: }
1556: }
1557:
1558: return result;
1559: }
1560:
1561: /**
1562: * Adds a <code>PlanarImage</code> sink to the list of sinks.
1563: *
1564: * @param sink A <code>PlanarImage</code> to be added as a sink.
1565: *
1566: * @throws IllegalArgumentException If <code>sink</code> is
1567: * <code>null</code>.
1568: *
1569: * @deprecated as of JAI 1.1. Use <code>addSink(Object)</code> instead.
1570: */
1571: protected void addSink(PlanarImage sink) {
1572: if (sink == null) {
1573: throw new IllegalArgumentException(JaiI18N
1574: .getString("Generic0"));
1575: }
1576:
1577: if (sinks == null) {
1578: synchronized (this ) {
1579: sinks = new Vector();
1580: }
1581: }
1582:
1583: synchronized (sinks) {
1584: sinks.add(sink.weakThis);
1585: }
1586: }
1587:
1588: /**
1589: * Removes a <code>PlanarImage</code> sink from the list of sinks.
1590: *
1591: * @param sink A <code>PlanarImage</code> sink to be removed.
1592: *
1593: * @return <code>true</code> if the element was present,
1594: * <code>false</code> otherwise.
1595: *
1596: * @throws IllegalArgumentException If <code>sink</code> is
1597: * <code>null</code>.
1598: * @throws IndexOutOfBoundsException If <code>sink</code> is not
1599: * in the sink list.
1600: *
1601: * @deprecated as of JAI 1.1. Use <code>removeSink(Object)</code> instead.
1602: */
1603: protected boolean removeSink(PlanarImage sink) {
1604: if (sink == null) {
1605: throw new IllegalArgumentException(JaiI18N
1606: .getString("Generic0"));
1607: }
1608:
1609: if (sinks == null) {
1610: return false;
1611: }
1612:
1613: synchronized (sinks) {
1614: return sinks.remove(sink.weakThis);
1615: }
1616: }
1617:
1618: /** Removes all the sinks of this image. */
1619: public void removeSinks() {
1620: if (sinks != null) {
1621: synchronized (this ) {
1622: sinks = null;
1623: }
1624: }
1625: }
1626:
1627: /**
1628: * Returns the internal <code>Hashtable</code> containing the
1629: * image properties by reference.
1630: */
1631: protected Hashtable getProperties() {
1632: return (Hashtable) properties.getProperties();
1633: }
1634:
1635: /**
1636: * Sets the <code>Hashtable</code> containing the image properties
1637: * to a given <code>Hashtable</code>. The <code>Hashtable</code>
1638: * is incorporated by reference and must not be altered by other
1639: * classes after this method is called.
1640: */
1641: protected void setProperties(Hashtable properties) {
1642: this .properties.addProperties(properties);
1643: }
1644:
1645: /**
1646: * Gets a property from the property set of this image. If the
1647: * property name is not recognized,
1648: * <code>java.awt.Image.UndefinedProperty</code> will be returned.
1649: *
1650: * @param name the name of the property to get, as a <code>String</code>.
1651: *
1652: * @return A reference to the property <code>Object</code>, or the value
1653: * <code>java.awt.Image.UndefinedProperty</code>.
1654: *
1655: * @exception IllegalArgumentException if <code>propertyName</code>
1656: * is <code>null</code>.
1657: */
1658: public Object getProperty(String name) {
1659: return properties.getProperty(name);
1660: }
1661:
1662: /**
1663: * Returns the class expected to be returned by a request for
1664: * the property with the specified name. If this information
1665: * is unavailable, <code>null</code> will be returned.
1666: *
1667: * @exception IllegalArgumentException if <code>name</code>
1668: * is <code>null</code>.
1669: *
1670: * @return The <code>Class</code> expected to be return by a
1671: * request for the value of this property or <code>null</code>.
1672: *
1673: * @since JAI 1.1
1674: */
1675: public Class getPropertyClass(String name) {
1676: return properties.getPropertyClass(name);
1677: }
1678:
1679: /**
1680: * Sets a property on a <code>PlanarImage</code>. Some
1681: * <code>PlanarImage</code> subclasses may ignore attempts to set
1682: * properties.
1683: *
1684: * @param name a <code>String</code> containing the property's name.
1685: * @param value the property, as a general <code>Object</code>.
1686: *
1687: * @throws IllegalArgumentException If <code>name</code> or
1688: * <code>value</code> is <code>null</code>.
1689: */
1690: public void setProperty(String name, Object value) {
1691: properties.setProperty(name, value);
1692: }
1693:
1694: /**
1695: * Removes the named property from the <code>PlanarImage</code>.
1696: * Some <code>PlanarImage</code> subclasses may ignore attempts to
1697: * remove properties.
1698: *
1699: * @exception IllegalArgumentException if <code>name</code>
1700: * is <code>null</code>.
1701: *
1702: * @since JAI 1.1
1703: */
1704: public void removeProperty(String name) {
1705: properties.removeProperty(name);
1706: }
1707:
1708: /**
1709: * Returns a list of property names that are recognized by this image
1710: * or <code>null</code> if none are recognized.
1711: *
1712: * @return an array of <code>String</code>s containing valid
1713: * property names or <code>null</code>.
1714: */
1715: public String[] getPropertyNames() {
1716: return properties.getPropertyNames();
1717: }
1718:
1719: /**
1720: * Returns an array of <code>String</code>s recognized as names by
1721: * this property source that begin with the supplied prefix. If
1722: * no property names match, <code>null</code> will be returned.
1723: * The comparison is done in a case-independent manner.
1724: *
1725: * <p> The default implementation calls
1726: * <code>getPropertyNames()</code> and searches the list of names
1727: * for matches.
1728: *
1729: * @return an array of <code>String</code>s giving the valid
1730: * property names.
1731: *
1732: * @throws IllegalArgumentException If <code>prefix</code> is
1733: * <code>null</code>.
1734: */
1735: public String[] getPropertyNames(String prefix) {
1736: return PropertyUtil
1737: .getPropertyNames(getPropertyNames(), prefix);
1738: }
1739:
1740: /**
1741: * Add a PropertyChangeListener to the listener list. The
1742: * listener is registered for all properties.
1743: *
1744: * @since JAI 1.1
1745: */
1746: public void addPropertyChangeListener(
1747: PropertyChangeListener listener) {
1748: eventManager.addPropertyChangeListener(listener);
1749: }
1750:
1751: /**
1752: * Add a PropertyChangeListener for a specific property. The
1753: * listener will be invoked only when a call on
1754: * firePropertyChange names that specific property. The case of
1755: * the name is ignored.
1756: *
1757: * @since JAI 1.1
1758: */
1759: public void addPropertyChangeListener(String propertyName,
1760: PropertyChangeListener listener) {
1761: eventManager.addPropertyChangeListener(propertyName
1762: .toLowerCase(), listener);
1763: }
1764:
1765: /**
1766: * Remove a PropertyChangeListener from the listener list. This
1767: * removes a PropertyChangeListener that was registered for all
1768: * properties.
1769: *
1770: * @since JAI 1.1
1771: */
1772: public void removePropertyChangeListener(
1773: PropertyChangeListener listener) {
1774: eventManager.removePropertyChangeListener(listener);
1775: }
1776:
1777: /**
1778: * Remove a PropertyChangeListener for a specific property. The case
1779: * of the name is ignored.
1780: *
1781: * @since JAI 1.1
1782: */
1783: public void removePropertyChangeListener(String propertyName,
1784: PropertyChangeListener listener) {
1785: eventManager.removePropertyChangeListener(propertyName
1786: .toLowerCase(), listener);
1787: }
1788:
1789: private synchronized Set getTileComputationListeners(
1790: boolean createIfNull) {
1791: if (createIfNull && tileListeners == null) {
1792: tileListeners = Collections.synchronizedSet(new HashSet());
1793: }
1794: return tileListeners;
1795: }
1796:
1797: /**
1798: * Adds a <code>TileComputationListener</code> to the list of
1799: * registered <code>TileComputationListener</code>s. This listener
1800: * will be notified when tiles requested via <code>queueTiles()</code>
1801: * have been computed.
1802: *
1803: * @param listener The <code>TileComputationListener</code> to register.
1804: * @throws IllegalArgumentException if <code>listener</code> is
1805: * <code>null</code>.
1806: *
1807: * @since JAI 1.1
1808: */
1809: public synchronized void addTileComputationListener(
1810: TileComputationListener listener) {
1811: if (listener == null) {
1812: throw new IllegalArgumentException(JaiI18N
1813: .getString("Generic0"));
1814: }
1815:
1816: Set listeners = getTileComputationListeners(true);
1817:
1818: listeners.add(listener);
1819: }
1820:
1821: /**
1822: * Removes a <code>TileComputationListener</code> from the list of
1823: * registered <code>TileComputationListener</code>s.
1824: *
1825: * @param listener The <code>TileComputationListener</code> to unregister.
1826: * @throws IllegalArgumentException if <code>listener</code> is
1827: * <code>null</code>.
1828: *
1829: * @since JAI 1.1
1830: */
1831: public synchronized void removeTileComputationListener(
1832: TileComputationListener listener) {
1833: if (listener == null) {
1834: throw new IllegalArgumentException(JaiI18N
1835: .getString("Generic0"));
1836: }
1837:
1838: Set listeners = getTileComputationListeners(false);
1839:
1840: if (listeners != null) {
1841: listeners.remove(listener);
1842: }
1843: }
1844:
1845: /**
1846: * Retrieves a snapshot of the set of all registered
1847: * <code>TileComputationListener</code>s as of the moment this
1848: * method is invoked.
1849: *
1850: * @return All <code>TileComputationListener</code>s or
1851: * <code>null</code> if there are none.
1852: *
1853: * @since JAI 1.1
1854: */
1855: public TileComputationListener[] getTileComputationListeners() {
1856:
1857: Set listeners = getTileComputationListeners(false);
1858:
1859: if (listeners == null) {
1860: return null;
1861: }
1862:
1863: return (TileComputationListener[]) listeners
1864: .toArray(new TileComputationListener[listeners.size()]);
1865: }
1866:
1867: /**
1868: * Within a given rectangle, store the list of tile seams of both
1869: * X and Y directions into the corresponding split sequence.
1870: *
1871: * @param xSplits An <code>IntegerSequence</code> to which the
1872: * tile seams in the X direction are to be added.
1873: * @param ySplits An <code>IntegerSequence</code> to which the
1874: * tile seams in the Y direction are to be added.
1875: * @param rect The rectangular region of interest.
1876: *
1877: * @throws IllegalArgumentException If <code>xSplits</code>,
1878: * <code>ySplits</code>, or <code>rect</code>
1879: * is <code>null</code>.
1880: */
1881: public void getSplits(IntegerSequence xSplits,
1882: IntegerSequence ySplits, Rectangle rect) {
1883: if (xSplits == null || ySplits == null || rect == null) {
1884: throw new IllegalArgumentException(JaiI18N
1885: .getString("Generic0"));
1886: }
1887:
1888: int minTileX = XToTileX(rect.x);
1889: int maxTileX = XToTileX(rect.x + rect.width - 1);
1890: int xTilePos = tileXToX(minTileX);
1891: for (int i = minTileX; i <= maxTileX; i++) {
1892: xSplits.insert(xTilePos);
1893: xTilePos += tileWidth;
1894: }
1895:
1896: int minTileY = YToTileY(rect.y);
1897: int maxTileY = YToTileY(rect.y + rect.height - 1);
1898: int yTilePos = tileYToY(minTileY);
1899: for (int i = minTileY; i <= maxTileY; i++) {
1900: ySplits.insert(yTilePos);
1901: yTilePos += tileHeight;
1902: }
1903: }
1904:
1905: /**
1906: * Returns an array containing the indices of all tiles which overlap
1907: * the specified <code>Rectangle</code>. If the <code>Rectangle</code>
1908: * does not intersect the image bounds then <code>null</code> will be
1909: * returned. If an array is returned, it will be ordered in terms of
1910: * the row major ordering of its contained tile indices. If the
1911: * specified <code>Rectangle</code> is <code>null</code>, the tile
1912: * indicies for the entire image will be returned.
1913: *
1914: * @param region The <code>Rectangle</code> of interest.
1915: * @return An array of the indices of overlapping tiles or
1916: * <code>null</code> if <code>region</code> does not intersect
1917: * the image bounds.
1918: *
1919: * @since JAI 1.1
1920: */
1921: public Point[] getTileIndices(Rectangle region) {
1922: if (region == null) {
1923: region = (Rectangle) getBounds().clone();
1924: } else if (!region.intersects(getBounds())) {
1925: return null;
1926: } else {
1927: region = region.intersection(getBounds());
1928: if (region.isEmpty()) {
1929: return null;
1930: }
1931: }
1932:
1933: if (region == null) {
1934: region = getBounds();
1935: } else {
1936: Rectangle r = new Rectangle(getMinX(), getMinY(),
1937: getWidth() + 1, getHeight() + 1);
1938: if (!region.intersects(r)) {
1939: return null;
1940: } else {
1941: region = region.intersection(r);
1942: }
1943: }
1944:
1945: int minTileX = XToTileX(region.x);
1946: int maxTileX = XToTileX(region.x + region.width - 1);
1947: int minTileY = YToTileY(region.y);
1948: int maxTileY = YToTileY(region.y + region.height - 1);
1949:
1950: Point[] tileIndices = new Point[(maxTileY - minTileY + 1)
1951: * (maxTileX - minTileX + 1)];
1952:
1953: int tileIndexOffset = 0;
1954: for (int ty = minTileY; ty <= maxTileY; ty++) {
1955: for (int tx = minTileX; tx <= maxTileX; tx++) {
1956: tileIndices[tileIndexOffset++] = new Point(tx, ty);
1957: }
1958: }
1959:
1960: return tileIndices;
1961: }
1962:
1963: /**
1964: * Returns <code>true</code> if and only if the intersection of
1965: * the specified <code>Rectangle</code> with the image bounds
1966: * overlaps more than one tile.
1967: *
1968: * @throws IllegalArgumentException if <code>rect</code> is
1969: * <code>null</code>.
1970: */
1971: public boolean overlapsMultipleTiles(Rectangle rect) {
1972: if (rect == null) {
1973: throw new IllegalArgumentException("rect == null!");
1974: }
1975:
1976: Rectangle xsect = rect.intersection(getBounds());
1977:
1978: // 'true' if and only if non-empty and more than one tile in
1979: // either horizontal or vertical direction.
1980: return !xsect.isEmpty()
1981: && (XToTileX(xsect.x) != XToTileX(xsect.x + xsect.width
1982: - 1) || YToTileY(xsect.y) != YToTileY(xsect.y
1983: + xsect.height - 1));
1984: }
1985:
1986: /**
1987: * Creates a <code>WritableRaster</code> with the specified
1988: * <code>SampleModel</code> and location. If <code>tileFactory</code>
1989: * is non-<code>null</code>, it will be used to create the
1990: * <code>WritableRaster</code>; otherwise
1991: * {@link RasterFactory#createWritableRaster(SampleModel,Point)}
1992: * will be used.
1993: *
1994: * @param sampleModel The <code>SampleModel</code> to use.
1995: * @param location The origin of the <code>WritableRaster</code>; if
1996: * <code>null</code>, <code>(0, 0)</code> will be used.
1997: *
1998: * @throws IllegalArgumentException if <code>sampleModel</code> is
1999: * <code>null</code>.
2000: *
2001: * @since JAI 1.1.2
2002: */
2003: protected final WritableRaster createWritableRaster(
2004: SampleModel sampleModel, Point location) {
2005:
2006: if (sampleModel == null) {
2007: throw new IllegalArgumentException("sampleModel == null!");
2008: }
2009:
2010: return tileFactory != null ? tileFactory.createTile(
2011: sampleModel, location) : RasterFactory
2012: .createWritableRaster(sampleModel, location);
2013: }
2014:
2015: /**
2016: * Returns the entire image in a single <code>Raster</code>. For
2017: * images with multiple tiles this will require creating a new
2018: * <code>Raster</code> and copying data from multiple tiles into
2019: * it ("cobbling").
2020: *
2021: * <p>The returned <code>Raster</code> is semantically a copy.
2022: * This means that subsequent updates to this image will not be
2023: * reflected in the returned <code>Raster</code>. For non-writable
2024: * (immutable) images, the returned value may be a reference to the
2025: * image's internal data. The returned <code>Raster</code> should
2026: * be considered non-writable; any attempt to alter its pixel data
2027: * (such as by casting it to a <code>WritableRaster</code> or obtaining
2028: * and modifying its <code>DataBuffer</code>) may result in undefined
2029: * behavior. The <code>copyData</code> method should be used if the
2030: * returned <code>Raster</code> is to be modified.
2031: *
2032: * <p> For a very large image, more than
2033: * <code>Integer.MAX_VALUE</code> entries could be required in the
2034: * returned <code>Raster</code>'s underlying data array. Since
2035: * the Java language does not permit such an array, an
2036: * <code>IllegalArgumentException</code> will be thrown.
2037: *
2038: * @return A <code>Raster</code> containing the entire image data.
2039: *
2040: * @throws IllegalArgumentException If the size of the returned data
2041: * is too large to be stored in a single <code>Raster</code>.
2042: */
2043: public Raster getData() {
2044: return getData(null);
2045: }
2046:
2047: /**
2048: * Returns a specified region of this image in a <code>Raster</code>.
2049: *
2050: * <p> The returned <code>Raster</code> is semantically a copy.
2051: * This means that subsequent updates to this image will not be
2052: * reflected in the returned <code>Raster</code>. For non-writable
2053: * (immutable) images, the returned value may be a reference to the
2054: * image's internal data. The returned <code>Raster</code> should
2055: * be considered non-writable; any attempt to alter its pixel data
2056: * (such as by casting it to a <code>WritableRaster</code> or obtaining
2057: * and modifying its <code>DataBuffer</code>) may result in undefined
2058: * behavior. The <code>copyData</code> method should be used if the
2059: * returned <code>Raster</code> is to be modified.
2060: *
2061: * <p> The region of the image to be returned is specified by a
2062: * <code>Rectangle</code>. This region may go beyond this image's
2063: * boundary. If so, the pixels in the areas outside this image's
2064: * boundary are left unset. Use <code>getExtendedData</code> if
2065: * a specific extension policy is required.
2066: *
2067: * <p> The <code>region</code> parameter may also be
2068: * <code>null</code>, in which case the entire image data is
2069: * returned in the <code>Raster</code>.
2070: *
2071: * <p> If <code>region</code> is non-<code>null</code> but does
2072: * not intersect the image bounds at all, an
2073: * <code>IllegalArgumentException</code> will be thrown.
2074: *
2075: * <p> It is possible to request a region of an image that would
2076: * require more than <code>Integer.MAX_VALUE</code> entries
2077: * in the returned <code>Raster</code>'s underlying data array.
2078: * Since the Java language does not permit such an array,
2079: * an <code>IllegalArgumentException</code> will be thrown.
2080: *
2081: * @param region The rectangular region of this image to be
2082: * returned, or <code>null</code>.
2083: *
2084: * @return A <code>Raster</code> containing the specified image data.
2085: *
2086: * @throws IllegalArgumentException If the region does not
2087: * intersect the image bounds.
2088: * @throws IllegalArgumentException If the size of the returned data
2089: * is too large to be stored in a single <code>Raster</code>.
2090: */
2091: public Raster getData(Rectangle region) {
2092: Rectangle b = getBounds(); // image's bounds
2093:
2094: if (region == null) {
2095: region = b;
2096: } else if (!region.intersects(b)) {
2097: throw new IllegalArgumentException(JaiI18N
2098: .getString("PlanarImage4"));
2099: }
2100:
2101: // Get the intersection of the region and the image bounds.
2102: Rectangle xsect = region == b ? region : region.intersection(b);
2103:
2104: // Compute tile indices over the intersection.
2105: int startTileX = XToTileX(xsect.x);
2106: int startTileY = YToTileY(xsect.y);
2107: int endTileX = XToTileX(xsect.x + xsect.width - 1);
2108: int endTileY = YToTileY(xsect.y + xsect.height - 1);
2109:
2110: if (startTileX == endTileX && startTileY == endTileY
2111: && getTileRect(startTileX, startTileY).contains(region)) {
2112: // Requested region is within a single tile.
2113: Raster tile = getTile(startTileX, startTileY);
2114:
2115: if (this instanceof WritableRenderedImage) {
2116: // Returned Raster must not change if the corresponding
2117: // image data are modified so if this image is mutable
2118: // a copy must be created.
2119: SampleModel sm = tile.getSampleModel();
2120: if (sm.getWidth() != region.width
2121: || sm.getHeight() != region.height) {
2122: sm = sm.createCompatibleSampleModel(region.width,
2123: region.height);
2124: }
2125: WritableRaster destinationRaster = createWritableRaster(
2126: sm, region.getLocation());
2127: Raster sourceRaster = tile.getBounds().equals(region) ? tile
2128: : tile.createChild(region.x, region.y,
2129: region.width, region.height, region.x,
2130: region.y, null);
2131: JDKWorkarounds.setRect(destinationRaster, sourceRaster);
2132: return destinationRaster;
2133: } else {
2134: // Image is immutable so returning the tile or a child
2135: // thereof is acceptable.
2136: return tile.getBounds().equals(region) ? tile
2137: : tile.createChild(region.x, region.y,
2138: region.width, region.height, region.x,
2139: region.y, null);
2140: }
2141: } else {
2142: // Extract a region crossing tiles into a new WritableRaster
2143: WritableRaster dstRaster;
2144: SampleModel srcSM = getSampleModel();
2145: int dataType = srcSM.getDataType();
2146: int nbands = srcSM.getNumBands();
2147: boolean isBandChild = false;
2148:
2149: ComponentSampleModel csm = null;
2150: int[] bandOffs = null;
2151:
2152: boolean fastCobblePossible = false;
2153: if (srcSM instanceof ComponentSampleModel) {
2154: csm = (ComponentSampleModel) srcSM;
2155: int ps = csm.getPixelStride();
2156: boolean isBandInt = (ps == 1 && nbands > 1);
2157: isBandChild = (ps > 1 && nbands != ps);
2158: if ((!isBandChild) && (!isBandInt)) {
2159: bandOffs = csm.getBandOffsets();
2160: int i;
2161: for (i = 0; i < nbands; i++) {
2162: if (bandOffs[i] >= nbands) {
2163: break;
2164: }
2165: }
2166: if (i == nbands) {
2167: fastCobblePossible = true;
2168: }
2169: }
2170: }
2171:
2172: if (fastCobblePossible) {
2173: // For acceptable cases of ComponentSampleModel,
2174: // use an optimized cobbler which directly accesses the
2175: // tile DataBuffers, using arraycopy whenever possible.
2176: try {
2177: SampleModel interleavedSM = RasterFactory
2178: .createPixelInterleavedSampleModel(
2179: dataType, region.width,
2180: region.height, nbands, region.width
2181: * nbands, bandOffs);
2182: dstRaster = createWritableRaster(interleavedSM,
2183: region.getLocation());
2184: } catch (IllegalArgumentException e) {
2185: throw new IllegalArgumentException(JaiI18N
2186: .getString("PlanarImage2"));
2187: }
2188:
2189: switch (dataType) {
2190: case DataBuffer.TYPE_BYTE:
2191: cobbleByte(region, dstRaster);
2192: break;
2193: case DataBuffer.TYPE_SHORT:
2194: cobbleShort(region, dstRaster);
2195: break;
2196: case DataBuffer.TYPE_USHORT:
2197: cobbleUShort(region, dstRaster);
2198: break;
2199: case DataBuffer.TYPE_INT:
2200: cobbleInt(region, dstRaster);
2201: break;
2202: case DataBuffer.TYPE_FLOAT:
2203: cobbleFloat(region, dstRaster);
2204: break;
2205: case DataBuffer.TYPE_DOUBLE:
2206: cobbleDouble(region, dstRaster);
2207: break;
2208: default:
2209: break;
2210: }
2211: } else {
2212: SampleModel sm = sampleModel;
2213: if (sm.getWidth() != region.width
2214: || sm.getHeight() != region.height) {
2215: sm = sm.createCompatibleSampleModel(region.width,
2216: region.height);
2217: }
2218:
2219: try {
2220: dstRaster = createWritableRaster(sm, region
2221: .getLocation());
2222: } catch (IllegalArgumentException e) {
2223: throw new IllegalArgumentException(JaiI18N
2224: .getString("PlanarImage2"));
2225: }
2226:
2227: for (int j = startTileY; j <= endTileY; j++) {
2228: for (int i = startTileX; i <= endTileX; i++) {
2229: Raster tile = getTile(i, j);
2230:
2231: Rectangle subRegion = region.intersection(tile
2232: .getBounds());
2233: Raster subRaster = tile.createChild(
2234: subRegion.x, subRegion.y,
2235: subRegion.width, subRegion.height,
2236: subRegion.x, subRegion.y, null);
2237:
2238: if (sm instanceof ComponentSampleModel
2239: && isBandChild) {
2240: // Need to handle this case specially, since
2241: // setDataElements will not copy band child images
2242: switch (sm.getDataType()) {
2243: case DataBuffer.TYPE_FLOAT:
2244: dstRaster
2245: .setPixels(
2246: subRegion.x,
2247: subRegion.y,
2248: subRegion.width,
2249: subRegion.height,
2250: subRaster
2251: .getPixels(
2252: subRegion.x,
2253: subRegion.y,
2254: subRegion.width,
2255: subRegion.height,
2256: new float[nbands
2257: * subRegion.width
2258: * subRegion.height]));
2259: break;
2260: case DataBuffer.TYPE_DOUBLE:
2261: dstRaster
2262: .setPixels(
2263: subRegion.x,
2264: subRegion.y,
2265: subRegion.width,
2266: subRegion.height,
2267: subRaster
2268: .getPixels(
2269: subRegion.x,
2270: subRegion.y,
2271: subRegion.width,
2272: subRegion.height,
2273: new double[nbands
2274: * subRegion.width
2275: * subRegion.height]));
2276: break;
2277: default:
2278: dstRaster
2279: .setPixels(
2280: subRegion.x,
2281: subRegion.y,
2282: subRegion.width,
2283: subRegion.height,
2284: subRaster
2285: .getPixels(
2286: subRegion.x,
2287: subRegion.y,
2288: subRegion.width,
2289: subRegion.height,
2290: new int[nbands
2291: * subRegion.width
2292: * subRegion.height]));
2293: break;
2294: }
2295: } else {
2296: JDKWorkarounds
2297: .setRect(dstRaster, subRaster);
2298: }
2299:
2300: }
2301: }
2302: }
2303:
2304: return dstRaster;
2305: }
2306: }
2307:
2308: /** Copies the entire image into a single raster. */
2309: public WritableRaster copyData() {
2310: return copyData(null);
2311: }
2312:
2313: /**
2314: * Copies an arbitrary rectangular region of this image's pixel
2315: * data into a caller-supplied <code>WritableRaster</code>.
2316: * The region to be copied is defined as the boundary of the
2317: * <code>WritableRaster</code>, which can be obtained by calling
2318: * <code>WritableRaster.getBounds()</code>.
2319: *
2320: * <p>The supplied <code>WritableRaster</code> may have a region
2321: * that extends beyond this image's boundary, in which case only
2322: * pixels in the part of the region that intersects this image
2323: * are copied. The areas outside of this image's boundary are left
2324: * untouched.
2325: *
2326: * <p>The supplied <code>WritableRaster</code> may also be
2327: * <code>null</code>, in which case the entire image is copied
2328: * into a newly-created <code>WritableRaster</code> with a
2329: * <code>SampleModel</code> that is compatible with that of
2330: * this image.
2331: *
2332: * @param raster A <code>WritableRaster</code> to hold the copied
2333: * pixel data of this image.
2334: *
2335: * @return A reference to the supplied <code>WritableRaster</code>,
2336: * or to a new <code>WritableRaster</code> if the supplied
2337: * one was <code>null</code>.
2338: */
2339: public WritableRaster copyData(WritableRaster raster) {
2340: Rectangle region; // the region to be copied
2341: if (raster == null) { // copy the entire image
2342: region = getBounds();
2343:
2344: SampleModel sm = getSampleModel();
2345: if (sm.getWidth() != region.width
2346: || sm.getHeight() != region.height) {
2347: sm = sm.createCompatibleSampleModel(region.width,
2348: region.height);
2349: }
2350: raster = createWritableRaster(sm, region.getLocation());
2351: } else {
2352: region = raster.getBounds().intersection(getBounds());
2353:
2354: if (region.isEmpty()) { // Raster is outside of image's boundary
2355: return raster;
2356: }
2357: }
2358:
2359: int startTileX = XToTileX(region.x);
2360: int startTileY = YToTileY(region.y);
2361: int endTileX = XToTileX(region.x + region.width - 1);
2362: int endTileY = YToTileY(region.y + region.height - 1);
2363:
2364: SampleModel[] sampleModels = { getSampleModel() };
2365: int tagID = RasterAccessor.findCompatibleTag(sampleModels,
2366: raster.getSampleModel());
2367:
2368: RasterFormatTag srcTag = new RasterFormatTag(getSampleModel(),
2369: tagID);
2370: RasterFormatTag dstTag = new RasterFormatTag(raster
2371: .getSampleModel(), tagID);
2372:
2373: for (int ty = startTileY; ty <= endTileY; ty++) {
2374: for (int tx = startTileX; tx <= endTileX; tx++) {
2375: Raster tile = getTile(tx, ty);
2376: Rectangle subRegion = region.intersection(tile
2377: .getBounds());
2378:
2379: RasterAccessor s = new RasterAccessor(tile, subRegion,
2380: srcTag, getColorModel());
2381: RasterAccessor d = new RasterAccessor(raster,
2382: subRegion, dstTag, null);
2383: ImageUtil.copyRaster(s, d);
2384: }
2385: }
2386: return raster;
2387: }
2388:
2389: /**
2390: * Copies an arbitrary rectangular region of the
2391: * <code>RenderedImage</code> into a caller-supplied
2392: * <code>WritableRaster</code>. The portion of the supplied
2393: * <code>WritableRaster</code> that lies outside of the bounds of
2394: * the image is computed by calling the given
2395: * <code>BorderExtender</code>. The supplied
2396: * <code>WritableRaster</code> must have a
2397: * <code>SampleModel</code> that is compatible with that of the
2398: * image.
2399: *
2400: * @param dest a <code>WritableRaster</code> to hold the returned
2401: * portion of the image.
2402: * @param extender an instance of <code>BorderExtender</code>.
2403: *
2404: * @throws IllegalArgumentException If <code>dest</code> or
2405: * <code>extender</code> is <code>null</code>.
2406: */
2407: public void copyExtendedData(WritableRaster dest,
2408: BorderExtender extender) {
2409: if (dest == null || extender == null) {
2410: throw new IllegalArgumentException(JaiI18N
2411: .getString("Generic0"));
2412: }
2413:
2414: // If the Raster is within the image just copy directly.
2415: Rectangle destBounds = dest.getBounds();
2416: Rectangle imageBounds = getBounds();
2417: if (imageBounds.contains(destBounds)) {
2418: copyData(dest);
2419: return;
2420: }
2421:
2422: // Get the intersection of the Raster and image bounds.
2423: Rectangle isect = imageBounds.intersection(destBounds);
2424:
2425: if (!isect.isEmpty()) {
2426: // Copy image data into the dest Raster.
2427: WritableRaster isectRaster = dest.createWritableChild(
2428: isect.x, isect.y, isect.width, isect.height,
2429: isect.x, isect.y, null);
2430: copyData(isectRaster);
2431: }
2432:
2433: // Extend the Raster.
2434: extender.extend(dest, this );
2435: }
2436:
2437: /**
2438: * Returns a copy of an arbitrary rectangular region of this image
2439: * in a <code>Raster</code>. The portion of the rectangle of
2440: * interest ouside the bounds of the image will be computed by
2441: * calling the given <code>BorderExtender</code>. If the region
2442: * falls entirely within the image, <code>extender</code> will not
2443: * be used in any way. Thus it is possible to use a
2444: * <code>null</code> value for <code>extender</code> when it is
2445: * known that no actual extension will be required.
2446: *
2447: * <p> The returned <code>Raster</code> should be considered
2448: * non-writable; any attempt to alter its pixel data (such as by
2449: * casting it to a <code>WritableRaster</code> or obtaining and
2450: * modifying its <code>DataBuffer</code>) may result in undefined
2451: * behavior. The <code>copyExtendedData</code> method should be
2452: * used if the returned <code>Raster</code> is to be modified.
2453: *
2454: * @param region the region of the image to be returned.
2455: * @param extender an instance of <code>BorderExtender</code>,
2456: * used only if the region exceeds the image bounds,
2457: * or <code>null</code>.
2458: * @return a <code>Raster</code> containing the extended data.
2459: *
2460: * @throws IllegalArgumentException If <code>region</code> is
2461: * <code>null</code>.
2462: * @throws IllegalArgumentException If the region exceeds the image
2463: * bounds and <code>extender</code> is <code>null</code>.
2464: */
2465: public Raster getExtendedData(Rectangle region,
2466: BorderExtender extender) {
2467: if (region == null) {
2468: throw new IllegalArgumentException(JaiI18N
2469: .getString("Generic0"));
2470: }
2471:
2472: if (getBounds().contains(region)) {
2473: return getData(region);
2474: }
2475:
2476: if (extender == null) {
2477: throw new IllegalArgumentException(JaiI18N
2478: .getString("Generic0"));
2479: }
2480:
2481: // Create a WritableRaster of the desired size
2482: SampleModel destSM = getSampleModel();
2483: if (destSM.getWidth() != region.width
2484: || destSM.getHeight() != region.height) {
2485: destSM = destSM.createCompatibleSampleModel(region.width,
2486: region.height);
2487: }
2488:
2489: // Translate it
2490: WritableRaster dest = createWritableRaster(destSM, region
2491: .getLocation());
2492:
2493: copyExtendedData(dest, extender);
2494: return dest;
2495: }
2496:
2497: /**
2498: * Returns a copy of this image as a <code>BufferedImage</code>.
2499: * A subarea of the image may be copied by supplying a
2500: * <code>Rectangle</code> parameter; if it is set to
2501: * <code>null</code>, the entire image is copied. The supplied
2502: * Rectangle will be clipped to the image bounds. The image's
2503: * <code>ColorModel</code> may be overridden by supplying a
2504: * non-<code>null</code> second argument. The resulting
2505: * <code>ColorModel</code> must be non-<code>null</code> and
2506: * appropriate for the image's <code>SampleModel</code>.
2507: *
2508: * <p> The resulting <code>BufferedImage</code> will contain the
2509: * full requested area, but will always have its top-left corner
2510: * translated (0, 0) as required by the <code>BufferedImage</code>
2511: * interface.
2512: *
2513: * @param rect The <code>Rectangle</code> of the image to be
2514: * copied, or <code>null</code> to indicate that the
2515: * entire image is to be copied.
2516: *
2517: * @param cm A <code>ColorModel</code> used to override
2518: * this image's <code>ColorModel</code>, or <code>null</code>.
2519: * The caller is responsible for supplying a
2520: * <code>ColorModel</code> that is compatible with the image's
2521: * <code>SampleModel</code>.
2522: *
2523: * @throws IllegalArgumentException If an incompatible, non-null
2524: * <code>ColorModel</code> is supplied.
2525: * @throws IllegalArgumentException If no <code>ColorModel</code> is
2526: * supplied, and the image <code>ColorModel</code> is
2527: * <code>null</code>.
2528: */
2529: public BufferedImage getAsBufferedImage(Rectangle rect,
2530: ColorModel cm) {
2531: if (cm == null) {
2532: cm = getColorModel();
2533: if (cm == null) {
2534: throw new IllegalArgumentException(JaiI18N
2535: .getString("PlanarImage6"));
2536: }
2537: }
2538:
2539: if (!JDKWorkarounds.areCompatibleDataModels(sampleModel, cm)) {
2540: throw new IllegalArgumentException(JaiI18N
2541: .getString("PlanarImage3"));
2542: }
2543:
2544: if (rect == null) {
2545: rect = getBounds();
2546: } else {
2547: rect = getBounds().intersection(rect);
2548: }
2549:
2550: SampleModel sm = sampleModel.getWidth() != rect.width
2551: || sampleModel.getHeight() != rect.height ? sampleModel
2552: .createCompatibleSampleModel(rect.width, rect.height)
2553: : sampleModel;
2554:
2555: WritableRaster ras = createWritableRaster(sm, rect
2556: .getLocation());
2557: copyData(ras);
2558:
2559: if (rect.x != 0 || rect.y != 0) {
2560: // Move Raster to (0, 0)
2561: ras = RasterFactory.createWritableChild(ras, rect.x,
2562: rect.y, rect.width, rect.height, 0, 0, null);
2563: }
2564:
2565: return new BufferedImage(cm, ras, cm.isAlphaPremultiplied(),
2566: null);
2567: }
2568:
2569: /**
2570: * Returns a copy of the entire image as a
2571: * <code>BufferedImage</code>. The image's
2572: * <code>ColorModel</code> must be non-<code>null</code>, and
2573: * appropriate for the image's <code>SampleModel</code>.
2574: *
2575: * @see java.awt.image.BufferedImage
2576: */
2577: public BufferedImage getAsBufferedImage() {
2578: return getAsBufferedImage(null, null);
2579: }
2580:
2581: /**
2582: * Returns a <code>Graphics</code> object that may be used to draw
2583: * into this image. By default, an
2584: * <code>IllegalAccessError</code> is thrown. Subclasses that
2585: * support such drawing, such as <code>TiledImage</code>, may
2586: * override this method to return a suitable <code>Graphics</code>
2587: * object.
2588: */
2589: public Graphics getGraphics() {
2590: throw new IllegalAccessError(JaiI18N.getString("PlanarImage1"));
2591: }
2592:
2593: /**
2594: * Returns tile (<code>tileX</code>, <code>tileY</code>) as a
2595: * <code>Raster</code>. Note that <code>tileX</code> and
2596: * <code>tileY</code> are indices into the tile array, not pixel
2597: * locations.
2598: *
2599: * <p> Subclasses must override this method to return a
2600: * non-<code>null</code> value for all tile indices between
2601: * <code>getMinTile{X,Y}</code> and <code>getMaxTile{X,Y}</code>,
2602: * inclusive. Tile indices outside of this region should result
2603: * in a return value of <code>null</code>.
2604: *
2605: * @param tileX The X index of the requested tile in the tile array.
2606: * @param tileY The Y index of the requested tile in the tile array.
2607: */
2608: public abstract Raster getTile(int tileX, int tileY);
2609:
2610: /**
2611: * Returns the <code>Raster</code>s indicated by the
2612: * <code>tileIndices</code> array. This call allows certain
2613: * <code>PlanarImage</code> subclasses such as
2614: * <code>OpImage</code> to take advantage of the knowledge that
2615: * multiple tiles are requested at once.
2616: *
2617: * @param tileIndices An array of Points representing tile indices.
2618: *
2619: * @return An array of <code>Raster</code> containing the tiles
2620: * corresponding to the given tile indices.
2621: *
2622: * @throws IllegalArgumentException If <code>tileIndices</code> is
2623: * <code>null</code>.
2624: */
2625: public Raster[] getTiles(Point[] tileIndices) {
2626: if (tileIndices == null) {
2627: throw new IllegalArgumentException(JaiI18N
2628: .getString("Generic0"));
2629: }
2630:
2631: int size = tileIndices.length;
2632: Raster tiles[] = new Raster[size];
2633:
2634: for (int i = 0; i < tileIndices.length; i++) {
2635: Point p = tileIndices[i];
2636: tiles[i] = getTile(p.x, p.y);
2637: }
2638:
2639: return tiles;
2640: }
2641:
2642: /**
2643: * Computes and returns all tiles in the image. The tiles are returned
2644: * in a sequence corresponding to the row-major order of their respective
2645: * tile indices. The returned array may of course be ignored, e.g., in
2646: * the case of a subclass which caches the tiles and the intent is to
2647: * force their computation.
2648: */
2649: public Raster[] getTiles() {
2650: return getTiles(getTileIndices(getBounds()));
2651: }
2652:
2653: /**
2654: * Queues a list of tiles for computation. Registered listeners
2655: * will be notified after each tile has been computed.
2656: *
2657: * <p> The <code>TileScheduler</code> of the default instance of the
2658: * <code>JAI</code> class is used to process the tiles. If this
2659: * <code>TileScheduler</code> has a positive parallelism this
2660: * method will be non-blocking. The event source parameter passed to
2661: * such listeners will be the <code>TileScheduler</code> and the image
2662: * parameter will be this image.
2663: *
2664: * @param tileIndices A list of tile indices indicating which tiles
2665: * to schedule for computation.
2666: * @throws IllegalArgumentException If <code>tileIndices</code> is
2667: * <code>null</code>.
2668: *
2669: * @since JAI 1.1
2670: */
2671: public TileRequest queueTiles(Point[] tileIndices) {
2672: if (tileIndices == null) {
2673: throw new IllegalArgumentException(JaiI18N
2674: .getString("Generic0"));
2675: }
2676:
2677: TileComputationListener[] listeners = getTileComputationListeners();
2678: return JAI.getDefaultInstance().getTileScheduler()
2679: .scheduleTiles(this , tileIndices, listeners);
2680: }
2681:
2682: /**
2683: * Issue an advisory cancellation request to nullify processing of
2684: * the indicated tiles. It is legal to implement this method as a no-op.
2685: *
2686: * <p> The cancellation request is forwarded to the
2687: * <code>TileScheduler</code> of the default instance of the
2688: * <code>JAI</code> class.
2689: *
2690: * @param request The request for which tiles are to be cancelled.
2691: * @param tileIndices The tiles to be cancelled; may be <code>null</code>.
2692: * Any tiles not actually in the <code>TileRequest</code> will be
2693: * ignored.
2694: * @throws IllegalArgumentException If <code>request</code> is
2695: * <code>null</code>.
2696: *
2697: * @since JAI 1.1
2698: */
2699: public void cancelTiles(TileRequest request, Point[] tileIndices) {
2700: if (request == null) {
2701: throw new IllegalArgumentException(JaiI18N
2702: .getString("Generic4"));
2703: }
2704:
2705: JAI.getDefaultInstance().getTileScheduler().cancelTiles(
2706: request, tileIndices);
2707: }
2708:
2709: /**
2710: * Hints that the given tiles might be needed in the near future.
2711: * Some implementations may spawn a thread or threads
2712: * to compute the tiles while others may ignore the hint.
2713: *
2714: * <p> The <code>TileScheduler</code> of the default instance of the
2715: * <code>JAI</code> class is used to prefetch the tiles. If this
2716: * <code>TileScheduler</code> has a positive prefetch parallelism
2717: * this method will be non-blocking.
2718: *
2719: * @param tileIndices A list of tile indices indicating which tiles
2720: * to prefetch.
2721: *
2722: * @throws IllegalArgumentException If <code>tileIndices</code> is
2723: * <code>null</code>.
2724: */
2725: public void prefetchTiles(Point[] tileIndices) {
2726: if (tileIndices == null) {
2727: throw new IllegalArgumentException(JaiI18N
2728: .getString("Generic0"));
2729: }
2730:
2731: JAI.getDefaultInstance().getTileScheduler().prefetchTiles(this ,
2732: tileIndices);
2733: }
2734:
2735: /**
2736: * Provides a hint that an image will no longer be accessed from a
2737: * reference in user space. The results are equivalent to those
2738: * that occur when the program loses its last reference to this
2739: * image, the garbage collector discovers this, and finalize is
2740: * called. This can be used as a hint in situations where waiting
2741: * for garbage collection would be overly conservative.
2742: *
2743: * <p> <code>PlanarImage</code> defines this method to remove the
2744: * image being disposed from the list of sinks in all of its
2745: * source images. Subclasses should call
2746: * <code>super.dispose()</code> in their <code>dispose</code>
2747: * methods, if any.
2748: *
2749: * <p> The results of referencing an image after a call to
2750: * <code>dispose()</code> are undefined.
2751: */
2752: public synchronized void dispose() {
2753: // Do nothing if dispose() has been called previously
2754: if (disposed) {
2755: return;
2756: }
2757: disposed = true;
2758:
2759: // Retrieve the sources as a Vector rather than using getSource()
2760: // to enable compatibility with subclasses which may have sources
2761: // which are not PlanarImages, e.g., as in RenderedOp.
2762: Vector srcs = getSources();
2763: if (srcs != null) {
2764: int numSources = srcs.size();
2765: for (int i = 0; i < numSources; i++) {
2766: Object src = srcs.get(i);
2767: if (src instanceof PlanarImage) {
2768: ((PlanarImage) src).removeSink(this );
2769: }
2770: }
2771: }
2772: }
2773:
2774: /**
2775: * Performs cleanup prior to garbage collection.
2776: *
2777: * <p> <code>PlanarImage</code> defines this method to invoke
2778: * the <code>dispose()</code> method.</p>
2779: *
2780: * @exception <code>Throwable</code> if an error occurs in the
2781: * garbage collector.
2782: */
2783: protected void finalize() throws Throwable {
2784: dispose();
2785: }
2786:
2787: /** For debugging. */
2788: private void printBounds() {
2789: System.out.println("Bounds: [x=" + getMinX() + ", y="
2790: + getMinY() + ", width=" + getWidth() + ", height="
2791: + getHeight() + "]");
2792: }
2793:
2794: /** For debugging. */
2795: private void printTile(int i, int j) {
2796: int xmin = i * getTileWidth() + getTileGridXOffset();
2797: int ymin = j * getTileHeight() + getTileGridYOffset();
2798:
2799: Rectangle imageBounds = getBounds();
2800: Rectangle tileBounds = new Rectangle(xmin, ymin,
2801: getTileWidth(), getTileHeight());
2802: tileBounds = tileBounds.intersection(imageBounds);
2803:
2804: Raster tile = getTile(i, j);
2805:
2806: Rectangle realTileBounds = new Rectangle(tile.getMinX(), tile
2807: .getMinY(), tile.getWidth(), tile.getHeight());
2808: System.out
2809: .println("Tile bounds (actual) = " + realTileBounds);
2810: System.out.println("Tile bounds (computed) = " + tileBounds);
2811:
2812: xmin = tileBounds.x;
2813: ymin = tileBounds.y;
2814: int xmax = tileBounds.x + tileBounds.width - 1;
2815: int ymax = tileBounds.y + tileBounds.height - 1;
2816: int numBands = getSampleModel().getNumBands();
2817: int[] val = new int[numBands];
2818: int pi, pj;
2819:
2820: for (pj = ymin; pj <= ymax; pj++) {
2821: for (pi = xmin; pi <= xmax; pi++) {
2822: tile.getPixel(pi, pj, val);
2823: if (numBands == 1) {
2824: System.out.print("(" + val[0] + ") ");
2825: } else if (numBands == 3) {
2826: System.out.print("(" + val[0] + "," + val[1] + ","
2827: + val[2] + ") ");
2828: }
2829: }
2830: System.out.println();
2831: }
2832: }
2833:
2834: /**
2835: * Returns a <code>String</code> which includes the basic information
2836: * of this image.
2837: *
2838: * @since JAI 1.1
2839: */
2840: public String toString() {
2841: return "PlanarImage[" + "minX=" + minX + " minY=" + minY
2842: + " width=" + width + " height=" + height
2843: + " tileGridXOffset=" + tileGridXOffset
2844: + " tileGridYOffset=" + tileGridYOffset + " tileWidth="
2845: + tileWidth + " tileHeight=" + tileHeight
2846: + " sampleModel=" + sampleModel + " colorModel="
2847: + colorModel + "]";
2848: }
2849:
2850: private void cobbleByte(Rectangle bounds, Raster dstRaster) {
2851:
2852: ComponentSampleModel dstSM = (ComponentSampleModel) dstRaster
2853: .getSampleModel();
2854:
2855: int startX = XToTileX(bounds.x);
2856: int startY = YToTileY(bounds.y);
2857: int rectXend = bounds.x + bounds.width - 1;
2858: int rectYend = bounds.y + bounds.height - 1;
2859: int endX = XToTileX(rectXend);
2860: int endY = YToTileY(rectYend);
2861:
2862: //
2863: // Get parameters of destination raster
2864: //
2865: DataBufferByte dstDB = (DataBufferByte) dstRaster
2866: .getDataBuffer();
2867: byte[] dst = dstDB.getData();
2868: int dstPS = dstSM.getPixelStride();
2869: int dstSS = dstSM.getScanlineStride();
2870:
2871: boolean tileParamsSet = false;
2872: ComponentSampleModel srcSM = null;
2873: int srcPS = 0, srcSS = 0;
2874: int xOrg, yOrg;
2875: int srcX1, srcY1, srcX2, srcY2, srcW, srcH;
2876:
2877: for (int y = startY; y <= endY; y++) {
2878: for (int x = startX; x <= endX; x++) {
2879: Raster tile = getTile(x, y);
2880: if (tile == null) {
2881: //
2882: // Out-of-bounds tile. Zero fill will be supplied
2883: // since dstRaster is initialized to zero
2884: //
2885: continue;
2886: }
2887:
2888: if (!tileParamsSet) {
2889: //
2890: // These are constant for all tiles,
2891: // so only set them once.
2892: //
2893: srcSM = (ComponentSampleModel) tile
2894: .getSampleModel();
2895: srcPS = srcSM.getPixelStride();
2896: srcSS = srcSM.getScanlineStride();
2897: tileParamsSet = true;
2898: }
2899:
2900: //
2901: // Intersect the tile and the rectangle
2902: // Avoid use of Math.min/max
2903: //
2904: yOrg = y * tileHeight + tileGridYOffset;
2905: srcY1 = yOrg;
2906: srcY2 = srcY1 + tileHeight - 1;
2907: if (bounds.y > srcY1)
2908: srcY1 = bounds.y;
2909: if (rectYend < srcY2)
2910: srcY2 = rectYend;
2911: srcH = srcY2 - srcY1 + 1;
2912:
2913: xOrg = x * tileWidth + tileGridXOffset;
2914: srcX1 = xOrg;
2915: srcX2 = srcX1 + tileWidth - 1;
2916: if (bounds.x > srcX1)
2917: srcX1 = bounds.x;
2918: if (rectXend < srcX2)
2919: srcX2 = rectXend;
2920: srcW = srcX2 - srcX1 + 1;
2921:
2922: int dstX = srcX1 - bounds.x;
2923: int dstY = srcY1 - bounds.y;
2924:
2925: // Get the actual data array
2926: DataBufferByte srcDB = (DataBufferByte) tile
2927: .getDataBuffer();
2928: byte[] src = srcDB.getData();
2929:
2930: int nsamps = srcW * srcPS;
2931: boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE);
2932:
2933: int ySrcIdx = (srcY1 - yOrg) * srcSS + (srcX1 - xOrg)
2934: * srcPS;
2935: int yDstIdx = dstY * dstSS + dstX * dstPS;
2936: if (useArrayCopy) {
2937: for (int row = 0; row < srcH; row++) {
2938: System.arraycopy(src, ySrcIdx, dst, yDstIdx,
2939: nsamps);
2940: ySrcIdx += srcSS;
2941: yDstIdx += dstSS;
2942: }
2943: } else {
2944: for (int row = 0; row < srcH; row++) {
2945: int xSrcIdx = ySrcIdx;
2946: int xDstIdx = yDstIdx;
2947: int xEnd = xDstIdx + nsamps;
2948: while (xDstIdx < xEnd) {
2949: dst[xDstIdx++] = src[xSrcIdx++];
2950: }
2951: ySrcIdx += srcSS;
2952: yDstIdx += dstSS;
2953: }
2954: }
2955: }
2956: }
2957: }
2958:
2959: private void cobbleShort(Rectangle bounds, Raster dstRaster) {
2960:
2961: ComponentSampleModel dstSM = (ComponentSampleModel) dstRaster
2962: .getSampleModel();
2963:
2964: int startX = XToTileX(bounds.x);
2965: int startY = YToTileY(bounds.y);
2966: int rectXend = bounds.x + bounds.width - 1;
2967: int rectYend = bounds.y + bounds.height - 1;
2968: int endX = XToTileX(rectXend);
2969: int endY = YToTileY(rectYend);
2970:
2971: //
2972: // Get parameters of destination raster
2973: //
2974: DataBufferShort dstDB = (DataBufferShort) dstRaster
2975: .getDataBuffer();
2976: short[] dst = dstDB.getData();
2977: int dstPS = dstSM.getPixelStride();
2978: int dstSS = dstSM.getScanlineStride();
2979:
2980: boolean tileParamsSet = false;
2981: ComponentSampleModel srcSM = null;
2982: int srcPS = 0, srcSS = 0;
2983: int xOrg, yOrg;
2984: int srcX1, srcY1, srcX2, srcY2, srcW, srcH;
2985:
2986: for (int y = startY; y <= endY; y++) {
2987: for (int x = startX; x <= endX; x++) {
2988: Raster tile = getTile(x, y);
2989: if (tile == null) {
2990: //
2991: // Out-of-bounds tile. Zero fill will be supplied
2992: // since dstRaster is initialized to zero
2993: //
2994: continue;
2995: }
2996:
2997: if (!tileParamsSet) {
2998: //
2999: // These are constant for all tiles,
3000: // so only set them once.
3001: //
3002: srcSM = (ComponentSampleModel) tile
3003: .getSampleModel();
3004: srcPS = srcSM.getPixelStride();
3005: srcSS = srcSM.getScanlineStride();
3006: tileParamsSet = true;
3007: }
3008:
3009: //
3010: // Intersect the tile and the rectangle
3011: // Avoid use of Math.min/max
3012: //
3013: yOrg = y * tileHeight + tileGridYOffset;
3014: srcY1 = yOrg;
3015: srcY2 = srcY1 + tileHeight - 1;
3016: if (bounds.y > srcY1)
3017: srcY1 = bounds.y;
3018: if (rectYend < srcY2)
3019: srcY2 = rectYend;
3020: srcH = srcY2 - srcY1 + 1;
3021:
3022: xOrg = x * tileWidth + tileGridXOffset;
3023: srcX1 = xOrg;
3024: srcX2 = srcX1 + tileWidth - 1;
3025: if (bounds.x > srcX1)
3026: srcX1 = bounds.x;
3027: if (rectXend < srcX2)
3028: srcX2 = rectXend;
3029: srcW = srcX2 - srcX1 + 1;
3030:
3031: int dstX = srcX1 - bounds.x;
3032: int dstY = srcY1 - bounds.y;
3033:
3034: // Get the actual data array
3035: DataBufferShort srcDB = (DataBufferShort) tile
3036: .getDataBuffer();
3037: short[] src = srcDB.getData();
3038:
3039: int nsamps = srcW * srcPS;
3040: boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE);
3041:
3042: int ySrcIdx = (srcY1 - yOrg) * srcSS + (srcX1 - xOrg)
3043: * srcPS;
3044: int yDstIdx = dstY * dstSS + dstX * dstPS;
3045: if (useArrayCopy) {
3046: for (int row = 0; row < srcH; row++) {
3047: System.arraycopy(src, ySrcIdx, dst, yDstIdx,
3048: nsamps);
3049: ySrcIdx += srcSS;
3050: yDstIdx += dstSS;
3051: }
3052: } else {
3053: for (int row = 0; row < srcH; row++) {
3054: int xSrcIdx = ySrcIdx;
3055: int xDstIdx = yDstIdx;
3056: int xEnd = xDstIdx + nsamps;
3057: while (xDstIdx < xEnd) {
3058: dst[xDstIdx++] = src[xSrcIdx++];
3059: }
3060: ySrcIdx += srcSS;
3061: yDstIdx += dstSS;
3062: }
3063: }
3064: }
3065: }
3066: }
3067:
3068: private void cobbleUShort(Rectangle bounds, Raster dstRaster) {
3069:
3070: ComponentSampleModel dstSM = (ComponentSampleModel) dstRaster
3071: .getSampleModel();
3072:
3073: int startX = XToTileX(bounds.x);
3074: int startY = YToTileY(bounds.y);
3075: int rectXend = bounds.x + bounds.width - 1;
3076: int rectYend = bounds.y + bounds.height - 1;
3077: int endX = XToTileX(rectXend);
3078: int endY = YToTileY(rectYend);
3079:
3080: //
3081: // Get parameters of destination raster
3082: //
3083: DataBufferUShort dstDB = (DataBufferUShort) dstRaster
3084: .getDataBuffer();
3085: short[] dst = dstDB.getData();
3086: int dstPS = dstSM.getPixelStride();
3087: int dstSS = dstSM.getScanlineStride();
3088:
3089: boolean tileParamsSet = false;
3090: ComponentSampleModel srcSM = null;
3091: int srcPS = 0, srcSS = 0;
3092: int xOrg, yOrg;
3093: int srcX1, srcY1, srcX2, srcY2, srcW, srcH;
3094:
3095: for (int y = startY; y <= endY; y++) {
3096: for (int x = startX; x <= endX; x++) {
3097: Raster tile = getTile(x, y);
3098: if (tile == null) {
3099: //
3100: // Out-of-bounds tile. Zero fill will be supplied
3101: // since dstRaster is initialized to zero
3102: //
3103: continue;
3104: }
3105:
3106: if (!tileParamsSet) {
3107: //
3108: // These are constant for all tiles,
3109: // so only set them once.
3110: //
3111: srcSM = (ComponentSampleModel) tile
3112: .getSampleModel();
3113: srcPS = srcSM.getPixelStride();
3114: srcSS = srcSM.getScanlineStride();
3115: tileParamsSet = true;
3116: }
3117:
3118: //
3119: // Intersect the tile and the rectangle
3120: // Avoid use of Math.min/max
3121: //
3122: yOrg = y * tileHeight + tileGridYOffset;
3123: srcY1 = yOrg;
3124: srcY2 = srcY1 + tileHeight - 1;
3125: if (bounds.y > srcY1)
3126: srcY1 = bounds.y;
3127: if (rectYend < srcY2)
3128: srcY2 = rectYend;
3129: srcH = srcY2 - srcY1 + 1;
3130:
3131: xOrg = x * tileWidth + tileGridXOffset;
3132: srcX1 = xOrg;
3133: srcX2 = srcX1 + tileWidth - 1;
3134: if (bounds.x > srcX1)
3135: srcX1 = bounds.x;
3136: if (rectXend < srcX2)
3137: srcX2 = rectXend;
3138: srcW = srcX2 - srcX1 + 1;
3139:
3140: int dstX = srcX1 - bounds.x;
3141: int dstY = srcY1 - bounds.y;
3142:
3143: // Get the actual data array
3144: DataBufferUShort srcDB = (DataBufferUShort) tile
3145: .getDataBuffer();
3146: short[] src = srcDB.getData();
3147:
3148: int nsamps = srcW * srcPS;
3149: boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE);
3150:
3151: int ySrcIdx = (srcY1 - yOrg) * srcSS + (srcX1 - xOrg)
3152: * srcPS;
3153: int yDstIdx = dstY * dstSS + dstX * dstPS;
3154: if (useArrayCopy) {
3155: for (int row = 0; row < srcH; row++) {
3156: System.arraycopy(src, ySrcIdx, dst, yDstIdx,
3157: nsamps);
3158: ySrcIdx += srcSS;
3159: yDstIdx += dstSS;
3160: }
3161: } else {
3162: for (int row = 0; row < srcH; row++) {
3163: int xSrcIdx = ySrcIdx;
3164: int xDstIdx = yDstIdx;
3165: int xEnd = xDstIdx + nsamps;
3166: while (xDstIdx < xEnd) {
3167: dst[xDstIdx++] = src[xSrcIdx++];
3168: }
3169: ySrcIdx += srcSS;
3170: yDstIdx += dstSS;
3171: }
3172: }
3173: }
3174: }
3175: }
3176:
3177: private void cobbleInt(Rectangle bounds, Raster dstRaster) {
3178:
3179: ComponentSampleModel dstSM = (ComponentSampleModel) dstRaster
3180: .getSampleModel();
3181:
3182: int startX = XToTileX(bounds.x);
3183: int startY = YToTileY(bounds.y);
3184: int rectXend = bounds.x + bounds.width - 1;
3185: int rectYend = bounds.y + bounds.height - 1;
3186: int endX = XToTileX(rectXend);
3187: int endY = YToTileY(rectYend);
3188:
3189: //
3190: // Get parameters of destination raster
3191: //
3192: DataBufferInt dstDB = (DataBufferInt) dstRaster.getDataBuffer();
3193: int[] dst = dstDB.getData();
3194: int dstPS = dstSM.getPixelStride();
3195: int dstSS = dstSM.getScanlineStride();
3196:
3197: boolean tileParamsSet = false;
3198: ComponentSampleModel srcSM = null;
3199: int srcPS = 0, srcSS = 0;
3200: int xOrg, yOrg;
3201: int srcX1, srcY1, srcX2, srcY2, srcW, srcH;
3202:
3203: for (int y = startY; y <= endY; y++) {
3204: for (int x = startX; x <= endX; x++) {
3205: Raster tile = getTile(x, y);
3206: if (tile == null) {
3207: //
3208: // Out-of-bounds tile. Zero fill will be supplied
3209: // since dstRaster is initialized to zero
3210: //
3211: continue;
3212: }
3213:
3214: if (!tileParamsSet) {
3215: //
3216: // These are constant for all tiles,
3217: // so only set them once.
3218: //
3219: srcSM = (ComponentSampleModel) tile
3220: .getSampleModel();
3221: srcPS = srcSM.getPixelStride();
3222: srcSS = srcSM.getScanlineStride();
3223: tileParamsSet = true;
3224: }
3225:
3226: //
3227: // Intersect the tile and the rectangle
3228: // Avoid use of Math.min/max
3229: //
3230: yOrg = y * tileHeight + tileGridYOffset;
3231: srcY1 = yOrg;
3232: srcY2 = srcY1 + tileHeight - 1;
3233: if (bounds.y > srcY1)
3234: srcY1 = bounds.y;
3235: if (rectYend < srcY2)
3236: srcY2 = rectYend;
3237: srcH = srcY2 - srcY1 + 1;
3238:
3239: xOrg = x * tileWidth + tileGridXOffset;
3240: srcX1 = xOrg;
3241: srcX2 = srcX1 + tileWidth - 1;
3242: if (bounds.x > srcX1)
3243: srcX1 = bounds.x;
3244: if (rectXend < srcX2)
3245: srcX2 = rectXend;
3246: srcW = srcX2 - srcX1 + 1;
3247:
3248: int dstX = srcX1 - bounds.x;
3249: int dstY = srcY1 - bounds.y;
3250:
3251: // Get the actual data array
3252: DataBufferInt srcDB = (DataBufferInt) tile
3253: .getDataBuffer();
3254: int[] src = srcDB.getData();
3255:
3256: int nsamps = srcW * srcPS;
3257: boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE);
3258:
3259: int ySrcIdx = (srcY1 - yOrg) * srcSS + (srcX1 - xOrg)
3260: * srcPS;
3261: int yDstIdx = dstY * dstSS + dstX * dstPS;
3262: if (useArrayCopy) {
3263: for (int row = 0; row < srcH; row++) {
3264: System.arraycopy(src, ySrcIdx, dst, yDstIdx,
3265: nsamps);
3266: ySrcIdx += srcSS;
3267: yDstIdx += dstSS;
3268: }
3269: } else {
3270: for (int row = 0; row < srcH; row++) {
3271: int xSrcIdx = ySrcIdx;
3272: int xDstIdx = yDstIdx;
3273: int xEnd = xDstIdx + nsamps;
3274: while (xDstIdx < xEnd) {
3275: dst[xDstIdx++] = src[xSrcIdx++];
3276: }
3277: ySrcIdx += srcSS;
3278: yDstIdx += dstSS;
3279: }
3280: }
3281: }
3282: }
3283: }
3284:
3285: private void cobbleFloat(Rectangle bounds, Raster dstRaster) {
3286:
3287: ComponentSampleModel dstSM = (ComponentSampleModel) dstRaster
3288: .getSampleModel();
3289:
3290: int startX = XToTileX(bounds.x);
3291: int startY = YToTileY(bounds.y);
3292: int rectXend = bounds.x + bounds.width - 1;
3293: int rectYend = bounds.y + bounds.height - 1;
3294: int endX = XToTileX(rectXend);
3295: int endY = YToTileY(rectYend);
3296:
3297: //
3298: // Get parameters of destination raster
3299: //
3300: DataBuffer dstDB = dstRaster.getDataBuffer();
3301: float[] dst = DataBufferUtils.getDataFloat(dstDB);
3302: int dstPS = dstSM.getPixelStride();
3303: int dstSS = dstSM.getScanlineStride();
3304:
3305: boolean tileParamsSet = false;
3306: ComponentSampleModel srcSM = null;
3307: int srcPS = 0, srcSS = 0;
3308: int xOrg, yOrg;
3309: int srcX1, srcY1, srcX2, srcY2, srcW, srcH;
3310:
3311: for (int y = startY; y <= endY; y++) {
3312: for (int x = startX; x <= endX; x++) {
3313: Raster tile = getTile(x, y);
3314: if (tile == null) {
3315: //
3316: // Out-of-bounds tile. Zero fill will be supplied
3317: // since dstRaster is initialized to zero
3318: //
3319: continue;
3320: }
3321:
3322: if (!tileParamsSet) {
3323: //
3324: // These are constant for all tiles,
3325: // so only set them once.
3326: //
3327: srcSM = (ComponentSampleModel) tile
3328: .getSampleModel();
3329: srcPS = srcSM.getPixelStride();
3330: srcSS = srcSM.getScanlineStride();
3331: tileParamsSet = true;
3332: }
3333:
3334: //
3335: // Intersect the tile and the rectangle
3336: // Avoid use of Math.min/max
3337: //
3338: yOrg = y * tileHeight + tileGridYOffset;
3339: srcY1 = yOrg;
3340: srcY2 = srcY1 + tileHeight - 1;
3341: if (bounds.y > srcY1)
3342: srcY1 = bounds.y;
3343: if (rectYend < srcY2)
3344: srcY2 = rectYend;
3345: srcH = srcY2 - srcY1 + 1;
3346:
3347: xOrg = x * tileWidth + tileGridXOffset;
3348: srcX1 = xOrg;
3349: srcX2 = srcX1 + tileWidth - 1;
3350: if (bounds.x > srcX1)
3351: srcX1 = bounds.x;
3352: if (rectXend < srcX2)
3353: srcX2 = rectXend;
3354: srcW = srcX2 - srcX1 + 1;
3355:
3356: int dstX = srcX1 - bounds.x;
3357: int dstY = srcY1 - bounds.y;
3358:
3359: // Get the actual data array
3360: DataBuffer srcDB = tile.getDataBuffer();
3361: float[] src = DataBufferUtils.getDataFloat(srcDB);
3362:
3363: int nsamps = srcW * srcPS;
3364: boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE);
3365:
3366: int ySrcIdx = (srcY1 - yOrg) * srcSS + (srcX1 - xOrg)
3367: * srcPS;
3368: int yDstIdx = dstY * dstSS + dstX * dstPS;
3369: if (useArrayCopy) {
3370: for (int row = 0; row < srcH; row++) {
3371: System.arraycopy(src, ySrcIdx, dst, yDstIdx,
3372: nsamps);
3373: ySrcIdx += srcSS;
3374: yDstIdx += dstSS;
3375: }
3376: } else {
3377: for (int row = 0; row < srcH; row++) {
3378: int xSrcIdx = ySrcIdx;
3379: int xDstIdx = yDstIdx;
3380: int xEnd = xDstIdx + nsamps;
3381: while (xDstIdx < xEnd) {
3382: dst[xDstIdx++] = src[xSrcIdx++];
3383: }
3384: ySrcIdx += srcSS;
3385: yDstIdx += dstSS;
3386: }
3387: }
3388: }
3389: }
3390: }
3391:
3392: private void cobbleDouble(Rectangle bounds, Raster dstRaster) {
3393:
3394: ComponentSampleModel dstSM = (ComponentSampleModel) dstRaster
3395: .getSampleModel();
3396:
3397: int startX = XToTileX(bounds.x);
3398: int startY = YToTileY(bounds.y);
3399: int rectXend = bounds.x + bounds.width - 1;
3400: int rectYend = bounds.y + bounds.height - 1;
3401: int endX = XToTileX(rectXend);
3402: int endY = YToTileY(rectYend);
3403:
3404: //
3405: // Get parameters of destination raster
3406: //
3407: DataBuffer dstDB = dstRaster.getDataBuffer();
3408: double[] dst = DataBufferUtils.getDataDouble(dstDB);
3409: int dstPS = dstSM.getPixelStride();
3410: int dstSS = dstSM.getScanlineStride();
3411:
3412: boolean tileParamsSet = false;
3413: ComponentSampleModel srcSM = null;
3414: int srcPS = 0, srcSS = 0;
3415: int xOrg, yOrg;
3416: int srcX1, srcY1, srcX2, srcY2, srcW, srcH;
3417:
3418: for (int y = startY; y <= endY; y++) {
3419: for (int x = startX; x <= endX; x++) {
3420: Raster tile = getTile(x, y);
3421: if (tile == null) {
3422: //
3423: // Out-of-bounds tile. Zero fill will be supplied
3424: // since dstRaster is initialized to zero
3425: //
3426: continue;
3427: }
3428:
3429: if (!tileParamsSet) {
3430: //
3431: // These are constant for all tiles,
3432: // so only set them once.
3433: //
3434: srcSM = (ComponentSampleModel) tile
3435: .getSampleModel();
3436: srcPS = srcSM.getPixelStride();
3437: srcSS = srcSM.getScanlineStride();
3438: tileParamsSet = true;
3439: }
3440:
3441: //
3442: // Intersect the tile and the rectangle
3443: // Avoid use of Math.min/max
3444: //
3445: yOrg = y * tileHeight + tileGridYOffset;
3446: srcY1 = yOrg;
3447: srcY2 = srcY1 + tileHeight - 1;
3448: if (bounds.y > srcY1)
3449: srcY1 = bounds.y;
3450: if (rectYend < srcY2)
3451: srcY2 = rectYend;
3452: srcH = srcY2 - srcY1 + 1;
3453:
3454: xOrg = x * tileWidth + tileGridXOffset;
3455: srcX1 = xOrg;
3456: srcX2 = srcX1 + tileWidth - 1;
3457: if (bounds.x > srcX1)
3458: srcX1 = bounds.x;
3459: if (rectXend < srcX2)
3460: srcX2 = rectXend;
3461: srcW = srcX2 - srcX1 + 1;
3462:
3463: int dstX = srcX1 - bounds.x;
3464: int dstY = srcY1 - bounds.y;
3465:
3466: // Get the actual data array
3467: DataBuffer srcDB = tile.getDataBuffer();
3468: double[] src = DataBufferUtils.getDataDouble(srcDB);
3469:
3470: int nsamps = srcW * srcPS;
3471: boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE);
3472:
3473: int ySrcIdx = (srcY1 - yOrg) * srcSS + (srcX1 - xOrg)
3474: * srcPS;
3475: int yDstIdx = dstY * dstSS + dstX * dstPS;
3476: if (useArrayCopy) {
3477: for (int row = 0; row < srcH; row++) {
3478: System.arraycopy(src, ySrcIdx, dst, yDstIdx,
3479: nsamps);
3480: ySrcIdx += srcSS;
3481: yDstIdx += dstSS;
3482: }
3483: } else {
3484: for (int row = 0; row < srcH; row++) {
3485: int xSrcIdx = ySrcIdx;
3486: int xDstIdx = yDstIdx;
3487: int xEnd = xDstIdx + nsamps;
3488: while (xDstIdx < xEnd) {
3489: dst[xDstIdx++] = src[xSrcIdx++];
3490: }
3491: ySrcIdx += srcSS;
3492: yDstIdx += dstSS;
3493: }
3494: }
3495: }
3496: }
3497: }
3498:
3499: /**
3500: * Returns a unique identifier (UID) for this <code>PlanarImage</code>.
3501: * This UID may be used when the potential redundancy of the value
3502: * returned by the <code>hashCode()</code> method is unacceptable.
3503: * An example of this is in generating a key for storing image tiles
3504: * in a cache.
3505: */
3506: public Object getImageID() {
3507: return UID;
3508: }
3509: }
|