0001: /*
0002: * $RCSfile: OpImage.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.1 $
0009: * $Date: 2005/02/11 04:57:12 $
0010: * $State: Exp $
0011: */
0012: package javax.media.jai;
0013:
0014: import com.sun.media.jai.util.ImageUtil;
0015: import com.sun.media.jai.util.JDKWorkarounds;
0016: import java.awt.Dimension;
0017: import java.awt.Point;
0018: import java.awt.Rectangle;
0019: import java.awt.geom.Point2D;
0020: import java.awt.image.ColorModel; // 3-22-00 used in deprecated methods only
0021: import java.awt.image.IndexColorModel; // 3-22-00 used in deprecated mthds only
0022: import java.awt.image.Raster;
0023: import java.awt.image.RenderedImage;
0024: import java.awt.image.SampleModel; // 3-22-00 used in deprecated methods only
0025: import java.awt.image.WritableRaster;
0026: import java.util.ArrayList;
0027: import java.util.Iterator;
0028: import java.util.Map;
0029: import java.util.Set;
0030: import java.util.Vector;
0031: import javax.media.jai.util.CaselessStringKey;
0032:
0033: /**
0034: * This is the base class for all image operations. It provides a home
0035: * for information and functionalities common to all the op-image classes,
0036: * and implements various utility methods that may be useful to a specific
0037: * operation. Image operations may be divided into different categories
0038: * based on their characteristics. A subclass, extending
0039: * <code>OpImage</code>, represents a category and implements methods
0040: * unique and common to those operations. Each individual operator
0041: * should extend the subclass that represents the specific
0042: * category that operator belongs to.
0043: *
0044: * <p> The layout variables of an <code>OpImage</code> are inherited from the
0045: * <code>PlanarImage</code> superclass. The layout should be set when the
0046: * <code>OpImage</code> is constructed. Each subclass must set
0047: * the appropriate layout variables and supply them via the
0048: * <code>ImageLayout</code> argument at construction time. This class
0049: * simply modifies these settings as described in the <code>OpImage</code>
0050: * constructor comments before forwarding the layout to the
0051: * <code>PlanarImage</code> constructor. If a subclass needs to modify
0052: * any of the layout settings subsequent to invoking its superclass
0053: * constructors it should use the <code>setImageLayout()</code> method
0054: * defined in <code>PlanarImage</code> in preference to setting the
0055: * layout variables directly.
0056: *
0057: * <p> A <code>RenderedImage</code>'s pixel data type and number of bands
0058: * are defined by its <code>SampleModel</code>, while the
0059: * <code>ColorModel</code> translates the pixel data into color/alpha
0060: * components in the specific <code>ColorSpace</code> that is associated
0061: * with the <code>ColorModel</code>.
0062: *
0063: * <p> By default, the operators provided by Java Advanced Imaging (JAI)
0064: * operate on the image's pixel data only. That is, the computations
0065: * are performed on the data described by the image's
0066: * <code>SampleModel</code>. No color translation is performed prior
0067: * to the actual computation by the operator, regardless of the type of
0068: * the <code>ColorModel</code> an image has. If a user intends to have
0069: * an operation performed on the color data, he must perform the
0070: * color translation explicitly prior to invoking the operation.
0071: *
0072: * <p> There are those operators that specifically deal with the
0073: * color/alpha data of an image. Such an operator must state its
0074: * behavior in its <code>OperationDescriptor</code> explicitly and
0075: * explain its intended usage of the image's color/alpha component data.
0076: * In such cases, the image's <code>ColorModel</code> as well as the
0077: * associated <code>ColorSpace</code> should be considered.
0078: *
0079: * <p> However there are certain operations, the results of which are
0080: * incorrect when the source has colormapped imagery, i.e. the source
0081: * has an <code>IndexColorModel</code>, and the computations are
0082: * performed on the image's non color transformed pixel data. In JAI,
0083: * such operations are those that are implemented as subclasses of
0084: * {@link AreaOpImage}, {@link GeometricOpImage}, and the "format"
0085: * operation. These operations set the
0086: * {@link JAI#KEY_REPLACE_INDEX_COLOR_MODEL} <code>RenderingHint</code>
0087: * to true, thus ensuring that the operations are performed correctly
0088: * on the colormapped imagery, not treating the indices into the color
0089: * map as pixel data.
0090: *
0091: * <p> The tile cache and scheduler are handled by this class. JAI
0092: * provides a default implementation for <code>TileCache</code> and
0093: * <code>TileScheduler</code>. However, they may be overriden by
0094: * each application. An <code>OpImage</code> may share a common cache
0095: * with other <code>OpImage</code>s, or it may have a private cache of
0096: * its own. To override an existing cache, use the
0097: * <code>setTileCache</code> method; an input argument of <code>null</code>
0098: * indicates that this image should not have a tile cache.
0099: *
0100: * <p> The <code>getTile</code> method may be used to request a tile
0101: * of the image. The default implementation of this method in this
0102: * class first checks whether the requested tile is in the tile cache,
0103: * and if not, uses the default <code>TileScheduler</code> to schedule
0104: * the tile for computation. Once the tile has been computed, it is
0105: * added to the cache and returned as a <code>Raster</code>.
0106: *
0107: * <p> The JAI tile scheduler assumes that when a request is made to
0108: * schedule a tile for computation via the <code>scheduleTile</code>
0109: * method, that tile is not currently in the cache. To avoid a cycle,
0110: * it calls <code>OpImage.computeTile</code> for the actual tile
0111: * computation.
0112: *
0113: * <p> The default implementation of the <code>computeTile</code> method
0114: * in this class first creates a new <code>Raster</code> to represent
0115: * the requested tile, then calls one of the two <code>computeRect</code>
0116: * methods to compute the actual pixel values and store the result in
0117: * the <code>DataBuffer</code> of the <code>Raster</code>.
0118: *
0119: * <p> Two variants of the <code>computeRect</code> method exist.
0120: *
0121: * <p> The first (with input arguments <code>Raster[]</code>,
0122: * <code>WritableRaster</code>, and <code>Rectangle</code>) is used when
0123: * the <code>OpImage</code> is constructed with the
0124: * <code>cobbleSources</code> argument set to <code>true</code>.
0125: * This indicates that the source data must be cobbled into a single
0126: * <code>Raster</code> and that all the necessary source data are provided
0127: * in order to compute the rectangular region of the destination image.
0128: * The source <code>Raster</code> array contains one entry for each
0129: * source image.
0130: *
0131: * <p> The second (with input arguments <code>PlanarImage[]</code>,
0132: * <code>WritableRaster</code>, and <code>Rectangle</code>) is used when
0133: * the <code>OpImage</code> is constructed with the
0134: * <code>cobbleSources</code> argument set to <code>false</code>.
0135: * This indicates that the source data are not cobbled into a single
0136: * <code>Raster</code>; instead an array of <code>PlanarImage</code>s,
0137: * one for each source, supply the source data and each image is
0138: * responsible for performing its own data accesses. This variant is
0139: * generally useful if iterators are to be used for the underlying
0140: * implementation of accessing the image data.
0141: *
0142: * <p> The two <code>computeRect</code> methods are not abstract because
0143: * normally only one needs to be implemented by the subclass depending on
0144: * the <code>cobbleSources</code> value. The default implementation of
0145: * these two methods in this class throws a <code>RuntimeException</code>.
0146: *
0147: * <p> Every operator who follows the above default implementation must
0148: * supply an overridden version of at least one of the
0149: * <code>computeRect</code> method variants, and specify which one is
0150: * to be called via the <code>cobbleSources</code> argument of the
0151: * constructor, or an exception will be thrown at run time.
0152: *
0153: * <p> If a subclass overrides <code>getTile</code> not to call
0154: * <code>computeTile</code>, does not use the JAI implementation of
0155: * <code>TileScheduler</code>, overrides <code>computeTile</code> not to
0156: * call <code>computeRect</code>, or does not follow the above default
0157: * implementation in any way, then it may need to handle issues such as
0158: * tile caching, multi-threading, and etc. by itself and may not need to
0159: * override some of the methods described above. In some cases, some of
0160: * the methods or variables are even irrelevant. However, subclasses
0161: * should be careful when not following the default path for computing
0162: * a tile. Most importantly, when a subclass overrides
0163: * <code>getTile</code>, it should also override <code>computeTile</code>.
0164: *
0165: * <p> To request multiple tiles at a time, it is preferable to
0166: * call the <code>getTiles</code> method with a complete list of the
0167: * requested tiles' indices, than to call <code>getTile</code> once
0168: * per tile. The implementation of <code>getTiles</code> in this class
0169: * is optimized using multi-threading so that multiple tiles are
0170: * computed simultaneously.
0171: *
0172: * @see PlanarImage
0173: * @see AreaOpImage
0174: * @see GeometricOpImage
0175: * @see PointOpImage
0176: * @see StatisticsOpImage
0177: * @see SourcelessOpImage
0178: *
0179: */
0180: public abstract class OpImage extends PlanarImage {
0181:
0182: /**
0183: * A constant indicating that an operation is likely to
0184: * spend its time mainly performing computation.
0185: */
0186: public static final int OP_COMPUTE_BOUND = 1;
0187:
0188: /**
0189: * A constant indicating that an operation is likely to
0190: * spend its time mainly performing local I/O.
0191: */
0192: public static final int OP_IO_BOUND = 2;
0193:
0194: /**
0195: * A constant indicating that an operation is likely to
0196: * spend its time mainly performing network I/O.
0197: */
0198: public static final int OP_NETWORK_BOUND = 3;
0199:
0200: /**
0201: * A constant equal to what would be returned by
0202: * <code>ImageLayout.getValidMask()</code> if all fields were set.
0203: */
0204: private static final int LAYOUT_MASK_ALL = ImageLayout.MIN_X_MASK
0205: | ImageLayout.MIN_Y_MASK | ImageLayout.WIDTH_MASK
0206: | ImageLayout.HEIGHT_MASK
0207: | ImageLayout.TILE_GRID_X_OFFSET_MASK
0208: | ImageLayout.TILE_GRID_Y_OFFSET_MASK
0209: | ImageLayout.TILE_WIDTH_MASK
0210: | ImageLayout.TILE_HEIGHT_MASK
0211: | ImageLayout.SAMPLE_MODEL_MASK
0212: | ImageLayout.COLOR_MODEL_MASK;
0213:
0214: /**
0215: * The cache object used to cache this image's tiles. It may refer
0216: * to a common cache shared by many <code>OpImage</code>s or a private
0217: * cache for this image only. If it is <code>null</code>, it
0218: * indicates that this image does not have a tile cache.
0219: */
0220: protected transient TileCache cache;
0221:
0222: /**
0223: * Metric used to produce an ordered list of tiles. This determines
0224: * which tiles are removed from the cache first if a memory control
0225: * operation is required.
0226: *
0227: * @since JAI 1.1
0228: */
0229: protected Object tileCacheMetric;
0230:
0231: /**
0232: * The scheduler to be used to schedule tile computation.
0233: */
0234: private transient TileScheduler scheduler = JAI
0235: .getDefaultInstance().getTileScheduler();
0236:
0237: /**
0238: * Variable indicating whether the TileScheduler is the Sun implementation.
0239: */
0240: private boolean isSunTileScheduler = false;
0241:
0242: /**
0243: * Indicates which one of the two <code>computeRect</code> variants
0244: * should be called by the <code>computeTile</code> method. If it
0245: * is <code>true</code>, <code>computeRect</code> expects
0246: * contiguous sources.
0247: */
0248: protected boolean cobbleSources;
0249:
0250: /**
0251: * Whether dispose() has been invoked.
0252: */
0253: private boolean isDisposed = false;
0254:
0255: /**
0256: * Flag indicating that tile recycling is enabled for tiles which
0257: * may be referenced outside the API.
0258: */
0259: private boolean isCachedTileRecyclingEnabled = false;
0260:
0261: /**
0262: * A <code>TileRecycler</code> for use in <code>createTile()</code>.
0263: * May be <code>null</code>. This field is set by the configuration
0264: * map passed to {@link #OpImage(Vector,ImageLayout,Map,boolean}.
0265: *
0266: * @since JAI 1.1.2
0267: */
0268: protected TileRecycler tileRecycler;
0269:
0270: /** The default RasterAccessor format tags. */
0271: // XXX This variable should be removed if we stop using RasterAccessor.
0272: private RasterFormatTag[] formatTags = null;
0273:
0274: /**
0275: * Creates a new <code>ImageLayout</code> or forwards the argument
0276: * layout with or without modifications.
0277: *
0278: * <p> If the <code>layout</code> parameter is non-<code>null</code>
0279: * and all its fields are set then it is cloned and returned.
0280: *
0281: * <p> If the <code>layout</code> parameter is non-<code>null</code>
0282: * but some of its fields are not set and there is at least one source
0283: * available, then all fields of the layout which are not set are
0284: * copied from the corresponding attributes of the first source except
0285: * possibly the <code>ColorModel</code>. The <code>ColorModel</code>
0286: * is copied if and only if the destination <code>SampleModel</code> is
0287: * non-<code>null</code> and the <code>ColorModel</code> and
0288: * <code>SampleModel</code> are compatible.
0289: *
0290: * The image's tile dimensions will be set by the first applicable
0291: * means in the following priority-ordered list. Note that each tile
0292: * dimension, the <code>tileWidth</code> and the
0293: * <code>tileHeight</code>, is considered independently :
0294: * <ol>
0295: * <li>Tile dimension set in the <code>ImageLayout</code> (either by
0296: * the user or the operator itself);</li>
0297: * <li>Tile dimension of source, if source is non-<code>null</code>.
0298: * The tile dimension will be clamped to the minimum of that of the
0299: * source tile dimension and the image's corresponding dimension;</li>
0300: * <li>Non-<code>null</code> default tile size returned by
0301: * <code>JAI.getDefaultTileSize()</code>, if the corresponding
0302: * image dimension is at least double the default tile size;</li>
0303: * <li>The dimensions of the image itself;</li>
0304: * </ol>
0305: *
0306: * <p> If the <code>layout</code> parameter is <code>null</code> and
0307: * there is at least one source available, a new layout is created from
0308: * the first source and returned directly.
0309: */
0310: private static ImageLayout layoutHelper(ImageLayout layout,
0311: Vector sources, Map config) {
0312: // Initialize to a reference to the layout passed in.
0313: ImageLayout il = layout;
0314:
0315: // Check the source Vector elements for nulls.
0316: if (sources != null) {
0317: checkSourceVector(sources, true);
0318: }
0319:
0320: // Check the class of the first source to avoid an exception here; a
0321: // class cast exception will be thrown by the PlanarImage constructor.
0322: RenderedImage im = sources != null && sources.size() > 0
0323: && sources.firstElement() instanceof RenderedImage ? (RenderedImage) sources
0324: .firstElement()
0325: : null;
0326:
0327: // Update some or all of the layout if a source is available.
0328: if (im != null) {
0329: // Create a new layout with the source as fallback.
0330: // The ColorModel field is intentionally NOT set.
0331: if (layout == null) {
0332: // Copy entirety of source image layout.
0333: il = layout = new ImageLayout(im);
0334:
0335: // Invalidate the ColorModel setting.
0336: il.unsetValid(ImageLayout.COLOR_MODEL_MASK);
0337: } else {
0338: // Set all fields except ColorModel.
0339: il = new ImageLayout(layout.getMinX(im), layout
0340: .getMinY(im), layout.getWidth(im), layout
0341: .getHeight(im), layout.getTileGridXOffset(im),
0342: layout.getTileGridYOffset(im), layout
0343: .getTileWidth(im), layout
0344: .getTileHeight(im), layout
0345: .getSampleModel(im), null);
0346: }
0347:
0348: // At this point "layout" and "il" are non-null with "layout"
0349: // representing the ImageLayout originally passed in. "il" does
0350: // not yet have its ColorModel field set.
0351:
0352: // Set the ColorModel.
0353: if (layout.isValid(ImageLayout.COLOR_MODEL_MASK)
0354: && layout.getColorModel(null) == null) {
0355: // User wants to force a null ColorModel.
0356: il.setColorModel(null);
0357:
0358: } else if (il.getSampleModel(null) != null) {
0359:
0360: // Target SampleModel is available.
0361:
0362: // Get SampleModel from "il"; guaranteed to be non-null.
0363: SampleModel sm = il.getSampleModel(null);
0364:
0365: // Get ColorModel from "layout".
0366: ColorModel cmLayout = layout.getColorModel(null);
0367:
0368: // First attempt to set the ColorModel to that specified
0369: // in the original layout, if any.
0370: if (cmLayout != null) {
0371: // ColorModel is set in original layout.
0372: if (JDKWorkarounds.areCompatibleDataModels(sm,
0373: cmLayout)) {
0374: // ColorModel is compatible with target SampleModel.
0375: il.setColorModel(cmLayout);
0376:
0377: } else if (layout.getSampleModel(null) == null) {
0378: // SampleModel not set in original layout so
0379: // ColorModel must be incompatible with source
0380: // SampleModel: create a compatible SampleModel.
0381:
0382: // Set the ColorModel to that desired.
0383: il.setColorModel(cmLayout);
0384:
0385: // Derive a new SampleModel from the desired ColorModel
0386: SampleModel derivedSM = cmLayout
0387: .createCompatibleSampleModel(il
0388: .getTileWidth(null), il
0389: .getTileHeight(null));
0390:
0391: // Set the SampleModel to that derived from the CM.
0392: il.setSampleModel(derivedSM);
0393:
0394: }
0395: }
0396:
0397: // If ColorModel not set from ImageLayout, attempt to set
0398: // using a ColorModelFactory and if that fails, attempt to
0399: // use the ColorModel of the source.
0400: if (!il.isValid(ImageLayout.COLOR_MODEL_MASK)
0401: && !setColorModelFromFactory(sm, sources,
0402: config, il)) {
0403: // Get ColorModel from "im", i.e., the source.
0404: ColorModel cmSource = im.getColorModel();
0405: if (cmSource != null
0406: && JDKWorkarounds.areCompatibleDataModels(
0407: sm, cmSource)) {
0408: // Set to source ColorModel.
0409: if (cmSource != null
0410: && cmSource instanceof IndexColorModel
0411: && config != null
0412: && config
0413: .containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL)
0414: && ((Boolean) config
0415: .get(JAI.KEY_REPLACE_INDEX_COLOR_MODEL))
0416: .booleanValue()) {
0417:
0418: ColorModel newCM = PlanarImage
0419: .getDefaultColorModel(sm
0420: .getDataType(), cmSource
0421: .getNumComponents());
0422:
0423: SampleModel newSM;
0424: if (newCM != null) {
0425: newSM = newCM
0426: .createCompatibleSampleModel(il
0427: .getTileWidth(null), il
0428: .getTileHeight(null));
0429: } else {
0430: newSM = RasterFactory
0431: .createPixelInterleavedSampleModel(
0432: sm.getDataType(),
0433: il.getTileWidth(null),
0434: il.getTileHeight(null),
0435: cmSource
0436: .getNumComponents());
0437: }
0438:
0439: il.setSampleModel(newSM);
0440: if (newCM != null)
0441: il.setColorModel(newCM);
0442: } else {
0443: il.setColorModel(cmSource);
0444: }
0445: }
0446: }
0447: } else if (il.getSampleModel(null) == null) { // null SampleModel
0448: // Set to whatever is available.
0449: il.setColorModel(layout.getColorModel(im));
0450: }
0451: // end of block if(im != null)
0452: } else if (il != null) {
0453: // Can only get here if im == null && layout != null.
0454: // Make sure that il is a clone of layout.
0455: il = (ImageLayout) layout.clone();
0456:
0457: // If the ColorModel is set but the SampleModel is not,
0458: // derive a SampleModel from the ColorModel.
0459: if (il.getColorModel(null) != null
0460: && il.getSampleModel(null) == null) {
0461: // Set SampleModel dimensions.
0462: int smWidth = il.getTileWidth(null);
0463: if (smWidth == 0) {
0464: smWidth = 512;
0465: }
0466: int smHeight = il.getTileHeight(null);
0467: if (smHeight == 0) {
0468: smHeight = 512;
0469: }
0470:
0471: // Derive a new SampleModel from the desired ColorModel
0472: SampleModel derivedSM = il.getColorModel(null)
0473: .createCompatibleSampleModel(smWidth, smHeight);
0474:
0475: // Set the SampleModel to that derived from the CM.
0476: il.setSampleModel(derivedSM);
0477: }
0478: } // end of block if(il != null)
0479:
0480: // If no ColorModel is set, then first attempt to set a ColorModel
0481: // using the ColorModelFactory; otherwise set a default ColorModel
0482: // if either the configuration mapping is null, it does not contain
0483: // a mapping of the key KEY_DEFAULT_COLOR_MODEL_ENABLED, or this key
0484: // is mapped to Boolean.TRUE.
0485: if (il != null
0486: && !il.isValid(ImageLayout.COLOR_MODEL_MASK)
0487: && il.getSampleModel(null) != null
0488: && !setColorModelFromFactory(il.getSampleModel(null),
0489: sources, config, il)) {
0490:
0491: ColorModel cm = null;
0492: SampleModel srcSM = il.getSampleModel(null);
0493:
0494: // If it is not a byte image, then probably all the above did not
0495: // manage to set a ColorModel and ImageUtil.getCompatibleColorModel
0496: // will be used to get the ColorModel. However we want to ensure
0497: // that the destination ColorModel is expanded if the ColorModel
0498: // of the source is an IndexColorModel and we've been asked to
0499: // do IndexColorModel expansion
0500: if (im != null
0501: && im.getColorModel() != null
0502: && im.getColorModel() instanceof IndexColorModel
0503: && config != null
0504: && config
0505: .containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL)
0506: && ((Boolean) config
0507: .get(JAI.KEY_REPLACE_INDEX_COLOR_MODEL))
0508: .booleanValue()) {
0509:
0510: IndexColorModel icm = (IndexColorModel) im
0511: .getColorModel();
0512:
0513: // We need to change the ColorModel to a non IndexColorModel
0514: // so that operations such as geometric can take place
0515: // correctly. If the ColorModel is changed the SampleModel
0516: // needs to be changed too, so that they are compatible
0517: cm = PlanarImage.getDefaultColorModel(srcSM
0518: .getDataType(), icm.getNumComponents());
0519:
0520: SampleModel newSM;
0521: if (cm != null) {
0522: newSM = cm.createCompatibleSampleModel(srcSM
0523: .getWidth(), srcSM.getHeight());
0524: } else {
0525: newSM = RasterFactory
0526: .createPixelInterleavedSampleModel(srcSM
0527: .getDataType(), srcSM.getWidth(),
0528: srcSM.getHeight(), icm
0529: .getNumComponents());
0530: }
0531:
0532: il.setSampleModel(newSM);
0533:
0534: } else {
0535:
0536: cm = ImageUtil.getCompatibleColorModel(il
0537: .getSampleModel(null), config);
0538: }
0539:
0540: // Set ColorModel only if method succeeded.
0541: if (cm != null)
0542: il.setColorModel(cm);
0543: }
0544:
0545: // If the tile dimensions were not set in "layout" and are either
0546: // not set in "il" or would yield an untiled image then reset them to
0547: // the global tile size default if each image dimension is at least
0548: // double the respective default tile dimension.
0549: if (layout != null
0550: && il != null
0551: && !layout.isValid(ImageLayout.TILE_WIDTH_MASK
0552: | ImageLayout.TILE_HEIGHT_MASK)) {
0553: Dimension defaultTileSize = JAI.getDefaultTileSize();
0554: if (defaultTileSize != null) {
0555: if (!layout.isValid(ImageLayout.TILE_WIDTH_MASK)) {
0556: if (il.getTileWidth(null) <= 0) {
0557: il.setTileWidth(defaultTileSize.width);
0558: } else {
0559: // Calculate number of tiles along X.
0560: int numX = XToTileX(il.getMinX(null)
0561: + il.getWidth(null) - 1, il
0562: .getTileGridXOffset(null), il
0563: .getTileWidth(null))
0564: - XToTileX(il.getMinX(null), il
0565: .getTileGridXOffset(null), il
0566: .getTileWidth(null)) + 1;
0567:
0568: // Reset if single-tiled and image is big enough in X.
0569: if (numX <= 1
0570: && il.getWidth(null) >= 2 * defaultTileSize.width) {
0571: il.setTileWidth(defaultTileSize.width);
0572: }
0573: }
0574: }
0575:
0576: if (!layout.isValid(ImageLayout.TILE_HEIGHT_MASK)) {
0577: if (il.getTileHeight(null) <= 0) {
0578: il.setTileHeight(defaultTileSize.height);
0579: } else {
0580: // Calculate number of tiles along Y.
0581: int numY = YToTileY(il.getMinY(null)
0582: + il.getHeight(null) - 1, il
0583: .getTileGridYOffset(null), il
0584: .getTileHeight(null))
0585: - YToTileY(il.getMinY(null), il
0586: .getTileGridYOffset(null), il
0587: .getTileHeight(null)) + 1;
0588:
0589: // Reset if single-tiled and image is big enough in Y.
0590: if (numY <= 1
0591: && il.getHeight(null) >= 2 * defaultTileSize.height) {
0592: il.setTileHeight(defaultTileSize.height);
0593: }
0594: }
0595: }
0596: }
0597: }
0598:
0599: // Independently clamp each tile dimension to the respective image
0600: // dimension, if the tile dimensions are not set in any supplied
0601: // ImageLayout, and the tile and image dimensions are both set in
0602: // the ImageLayout to be returned.
0603:
0604: // Tile width
0605: if ((layout == null || !layout
0606: .isValid(ImageLayout.TILE_WIDTH_MASK))
0607: && il.isValid(ImageLayout.TILE_WIDTH_MASK
0608: | ImageLayout.WIDTH_MASK)
0609: && il.getTileWidth(null) > il.getWidth(null)) {
0610: il.setTileWidth(il.getWidth(null));
0611: }
0612:
0613: // Tile height
0614: if ((layout == null || !layout
0615: .isValid(ImageLayout.TILE_HEIGHT_MASK))
0616: && il.isValid(ImageLayout.TILE_HEIGHT_MASK
0617: | ImageLayout.HEIGHT_MASK)
0618: && il.getTileHeight(null) > il.getHeight(null)) {
0619: il.setTileHeight(il.getHeight(null));
0620: }
0621:
0622: return il;
0623: }
0624:
0625: /**
0626: * Set the <code>ColorModel</code> in <code>layout</code> if
0627: * <code>config</code> is non-<code>null</code> and contains a mapping
0628: * for <code>JAI.KEY_COLOR_MODEL_FACTORY</code>. If the
0629: * <code>ColorModelFactory</code> returns a non-<code>null</code>
0630: * <code>ColorModel</code> which is compatible with
0631: * <code>sampleModel</code> it is used to set the <code>ColorModel</code>
0632: * in <code>layout</code>.
0633: *
0634: * @param sampleModel The <code>SampleModel</code> to which the
0635: * <code>ColorModel</code> to be created must correspond;
0636: * may <b>not</b> be <code>null</code>.
0637: * @param sources A <code>List</code> of <code>RenderedImage</code>s;
0638: * may be <code>null</code>.
0639: * @param config A configuration mapping; may be
0640: * <code>null</code>.
0641: * @param layout The image layout; will be updated with the
0642: * <code>ColorModel</code>created by the
0643: * <code>ColorModelFactory</code>
0644: *
0645: * @return Whether the <code>ColorModel</code> in <code>layout</code>
0646: * has been set.
0647: * @exception IllegalArgumentException if <code>sampleModel</code>
0648: * is <code>null</code>.
0649: */
0650: private static boolean setColorModelFromFactory(
0651: SampleModel sampleModel, Vector sources, Map config,
0652: ImageLayout layout) {
0653: boolean isColorModelSet = false;
0654:
0655: if (config != null
0656: && config.containsKey(JAI.KEY_COLOR_MODEL_FACTORY)) {
0657: ColorModelFactory cmf = (ColorModelFactory) config
0658: .get(JAI.KEY_COLOR_MODEL_FACTORY);
0659: ColorModel cm = cmf.createColorModel(sampleModel, sources,
0660: config);
0661: if (cm != null
0662: && JDKWorkarounds.areCompatibleDataModels(
0663: sampleModel, cm)) {
0664: layout.setColorModel(cm);
0665: isColorModelSet = true;
0666: }
0667: }
0668:
0669: return isColorModelSet;
0670: }
0671:
0672: // XXX Note: ColorModel.isCompatibleRaster() is mentioned below but it
0673: // should be ColorModel.isCompatibleSampleModel(). This has a bug
0674: // however (4326636) which is worked around within JAI. The other method
0675: // is mentioned as it does NOT have a bug.
0676: /**
0677: * Constructor.
0678: *
0679: * <p> The image's layout is encapsulated in the <code>layout</code>
0680: * argument. The variables of the image layout which are not set in
0681: * the <code>layout</code> parameter are copied from the first source
0682: * if sources are available. In the case of the <code>ColorModel</code>,
0683: * the copy is performed if and only if the <code>ColorModel</code> is
0684: * compatible with the destination <code>SampleModel</code> and is not
0685: * set by another higher priority mechanism as described presently.</p>
0686: *
0687: * <p> Assuming that there is at least one source, the image's
0688: * <code>ColorModel</code> will be set by the first applicable means
0689: * in the following priority-ordered list:
0690: * <ol>
0691: * <li><code>null</code> <code>ColorModel</code> from
0692: * <code>ImageLayout</code>;</li>
0693: * <li>Non-<code>null</code> <code>ColorModel</code> from
0694: * <code>ImageLayout</code> if compatible with
0695: * <code>SampleModel</code> in <code>ImageLayout</code> or if
0696: * <code>SampleModel</code> in <code>ImageLayout</code> is
0697: * <code>null</code>;</li>
0698: * <li>Value returned by <code>ColorModelFactory</code> set via the
0699: * <code>JAI.KEY_COLOR_MODEL_FACTORY</code> configuration variable if
0700: * compatible with <code>SampleModel</code>;</li>
0701: * <li>An instance of a non-<code>IndexColorModel</code> (or
0702: * <code>null</code> if no compatible non-<code>IndexColorModel</code>
0703: * could be generated), if the source has an <code>IndexColorModel</code>
0704: * and <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code>
0705: * is <code>Boolean.TRUE</code>;</li>
0706: * <li><code>ColorModel</code> of first source if compatible with
0707: * <code>SampleModel</code>;</li>
0708: * <li>Value returned by default method specified by the
0709: * <code>JAI.KEY_DEFAULT_COLOR_MODEL_METHOD</code> configuration variable
0710: * if <code>JAI.KEY_DEFAULT_COLOR_MODEL_ENABLED</code> is
0711: * <code>Boolean.TRUE</code>.</li>
0712: * </ol>
0713: * If it is not possible to set the <code>ColorModel</code> by any of
0714: * these means it will remain <code>null</code>.</p>
0715: *
0716: * The image's tile dimensions will be set by the first applicable
0717: * means in the following priority-ordered list. Note that each tile
0718: * dimension, the <code>tileWidth</code> and the
0719: * <code>tileHeight</code>, is considered independently :
0720: * <ol>
0721: * <li>Tile dimension set in the <code>ImageLayout</code> (either by
0722: * the user or the operator itself);</li>
0723: * <li>Tile dimension of source, if source is non-<code>null</code>.
0724: * The tile dimension will be clamped to the minimum of that of the
0725: * source tile dimension and the image's corresponding dimension;</li>
0726: * <li>Non-<code>null</code> default tile size returned by
0727: * <code>JAI.getDefaultTileSize()</code>, if the corresponding
0728: * image dimension is at least double the default tile size;</li>
0729: * <li>The dimensions of the image itself;</li>
0730: * </ol>
0731: *
0732: * <p> The <code>sources</code> contains a list of immediate sources
0733: * of this image. Elements in the list may not be <code>null</code>.
0734: * If this image has no sources this argument should be <code>null</code>.
0735: * This parameter is forwarded unmodified to the <code>PlanarImage</code>
0736: * constructor.
0737: *
0738: * <p> The <code>configuration</code> contains a mapping of configuration
0739: * variables and image properties. Entries which have keys of type
0740: * <code>RenderingHints.Key</code> are taken to be configuration variables.
0741: * Entries with a key which is either a <code>String</code> or a
0742: * <code>CaselessStringKey</code> are interpreted as image properties.
0743: * This parameter is forwarded unmodified to the <code>PlanarImage</code>
0744: * constructor.
0745: *
0746: * <p> This image class recognizes the configuration variables referenced
0747: * by the following keys:
0748: *
0749: * <ul>
0750: * <li> <code>JAI.KEY_TILE_CACHE</code>: specifies the
0751: * <code>TileCache</code> in which to store the image tiles;
0752: * if this key is not supplied no tile caching will be performed.
0753: * <li> <code>JAI.KEY_TILE_CACHE_METRIC</code>: establishes an
0754: * ordering of tiles stored in the tile cache. This ordering
0755: * is used to determine which tiles will be removed first, if
0756: * a condition causes tiles to be removed from the cache.
0757: * <li> <code>JAI.KEY_TILE_SCHEDULER</code>: specifies the
0758: * <code>TileScheduler</code> to use to schedule tile computation;
0759: * if this key is not supplied the default scheduler will be used.
0760: * <li> <code>JAI.KEY_COLOR_MODEL_FACTORY</code>: specifies a
0761: * <code>ColorModelFactory</code> to be used to generate the
0762: * <code>ColorModel</code> of the image. If such a callback is
0763: * provided it will be invoked if and only if either no
0764: * <code>ImageLayout</code> hint is given, or an <code>ImageLayout</code>
0765: * hint is given but contains a non-<code>null</code>
0766: * <code>ColorModel</code> which is incompatible with the image
0767: * <code>SampleModel</code>. In other words, such a callback provides
0768: * the second priority mechanism for setting the <code>ColorModel</code>
0769: * of the image.</li>
0770: * <li> <code>JAI.KEY_DEFAULT_COLOR_MODEL_ENABLED</code>: specifies whether
0771: * a default <code>ColorModel</code> will be derived
0772: * if none is specified and one cannot be inherited from the first source;
0773: * if this key is not supplied a default <code>ColorModel</code> will be
0774: * computed if necessary.
0775: * <li> <code>JAI.KEY_DEFAULT_COLOR_MODEL_METHOD</code>: specifies the
0776: * method to be used to compute the default <code>ColorModel</code>;
0777: * if this key is not supplied and a default <code>ColorModel</code> is
0778: * required, <code>PlanarImage.createColorModel()</code> will be used to
0779: * compute it.
0780: * <li> <code>JAI.KEY_TILE_FACTORY</code>: specifies a
0781: * {@link TileFactory} to be used to generate the tiles of the
0782: * image via {@link TileFactory#createTile(SampleModel,Point)}. If
0783: * no such configuration variable is given, a new <code>Raster</code>
0784: * will be created for each image tile. This behavior may be
0785: * overridden by subclasses which have alternate means of saving
0786: * memory, for example as in the case of point operations which
0787: * may overwrite a source image not referenced by user code. Note
0788: * that the corresponding instance variable is actually set by
0789: * the superclass constructor.</li>
0790: * <li> <code>JAI.KEY_TILE_RECYCLER</code>: specifies a
0791: * {@link TileRecycler} to be used to recycle the tiles of the
0792: * image when the <code>dispose()</code> method is invoked. If
0793: * such a configuration variable is set, the image has a
0794: * non-<code>null</code> <code>TileCache</code>, and tile recycling
0795: * is enabled, then invoking <code>dispose()</code> will cause each
0796: * of the tiles of this image currently in the cache to be passed to
0797: * the configured <code>TileRecycler</code></li> via
0798: * {@link TileRecycler#recycleTile(Raster)}.</li>
0799: * <li> <code>JAI.KEY_CACHED_TILE_RECYCLING_ENABLED</code>: specifies a
0800: * <code>Boolean</code> value which indicates whether {#dispose()}
0801: * should pass to <code>tileRecycler.recycleTile()</code> any image
0802: * tiles remaining in the cache.</li>
0803: * </ul>
0804: *
0805: * <p> The <code>cobbleSources</code> indicates which one of the two
0806: * variants of the <code>computeRect</code> method should be called.
0807: * If a subclass does not follow the default tile computation scheme,
0808: * then this argument may be irrelevant.
0809: *
0810: * @param layout The layout of this image.
0811: * @param sources The immediate sources of this image.
0812: * @param configuration Configurable attributes of the image including
0813: * configuration variables indexed by
0814: * <code>RenderingHints.Key</code>s and image properties indexed
0815: * by <code>String</code>s or <code>CaselessStringKey</code>s.
0816: * This parameter may be <code>null</code>.
0817: * @param cobbleSources Indicates which variant of the
0818: * <code>computeRect</code> method should be called.
0819: *
0820: * @throws IllegalArgumentException If <code>sources</code>
0821: * is non-<code>null</code> and any object in
0822: * <code>sources</code> is <code>null</code>.
0823: * @throws RuntimeException If default <code>ColorModel</code> setting
0824: * is enabled via a hint in the configuration <code>Map</code>
0825: * and the supplied <code>Method</code> does not conform to the
0826: * requirements stated in the <code>JAI</code> class for the
0827: * hint key <code>KEY_DEFAULT_COLOR_MODEL_METHOD</code>.
0828: *
0829: * @since JAI 1.1
0830: */
0831: public OpImage(Vector sources, ImageLayout layout,
0832: Map configuration, boolean cobbleSources) {
0833: super (layoutHelper(layout, sources, configuration), sources,
0834: configuration);
0835:
0836: if (configuration != null) {
0837: // Get the cache from the configuration map.
0838: Object cacheConfig = configuration.get(JAI.KEY_TILE_CACHE);
0839:
0840: // Ensure that it is a TileCache instance with positive capacity.
0841: if (cacheConfig != null
0842: && cacheConfig instanceof TileCache
0843: && ((TileCache) cacheConfig).getMemoryCapacity() > 0) {
0844: cache = (TileCache) cacheConfig;
0845: }
0846:
0847: // Get the scheduler from the configuration map.
0848: Object schedulerConfig = configuration
0849: .get(JAI.KEY_TILE_SCHEDULER);
0850:
0851: // Ensure that it is a TileScheduler instance.
0852: if (schedulerConfig != null
0853: && schedulerConfig instanceof TileScheduler) {
0854: scheduler = (TileScheduler) schedulerConfig;
0855: }
0856:
0857: try {
0858: // Test whether the TileScheduler is the default type.
0859: Class sunScheduler = Class
0860: .forName("com.sun.media.jai.util.SunTileScheduler");
0861:
0862: isSunTileScheduler = sunScheduler.isInstance(scheduler);
0863: } catch (Exception e) {
0864: // Deliberately ignore any Exceptions.
0865: }
0866:
0867: // Get the tile metric (cost or priority, for example)
0868: tileCacheMetric = configuration
0869: .get(JAI.KEY_TILE_CACHE_METRIC);
0870:
0871: // Set up cached tile recycling flag.
0872: Object recyclingEnabledValue = configuration
0873: .get(JAI.KEY_CACHED_TILE_RECYCLING_ENABLED);
0874: if (recyclingEnabledValue instanceof Boolean) {
0875: isCachedTileRecyclingEnabled = ((Boolean) recyclingEnabledValue)
0876: .booleanValue();
0877: }
0878:
0879: // Set up the TileRecycler.
0880: Object recyclerValue = configuration
0881: .get(JAI.KEY_TILE_RECYCLER);
0882: if (recyclerValue instanceof TileRecycler) {
0883: tileRecycler = (TileRecycler) recyclerValue;
0884: }
0885: }
0886:
0887: this .cobbleSources = cobbleSources;
0888: }
0889:
0890: /**
0891: * A <code>TileComputationListener</code> to pass to the
0892: * <code>TileScheduler</code> to intercept method calls such that the
0893: * computed tiles are added to the <code>TileCache</code> of the image.
0894: */
0895: private class TCL implements TileComputationListener {
0896: OpImage opImage;
0897:
0898: private TCL(OpImage opImage) {
0899: this .opImage = opImage;
0900: }
0901:
0902: public void tileComputed(Object eventSource,
0903: TileRequest[] requests, PlanarImage image, int tileX,
0904: int tileY, Raster tile) {
0905: if (image == opImage) {
0906: // Cache the tile.
0907: addTileToCache(tileX, tileY, tile);
0908: }
0909: }
0910:
0911: public void tileCancelled(Object eventSource,
0912: TileRequest[] requests, PlanarImage image, int tileX,
0913: int tileY) {
0914: // Do nothing.
0915: }
0916:
0917: public void tileComputationFailure(Object eventSource,
0918: TileRequest[] requests, PlanarImage image, int tileX,
0919: int tileY, Throwable situation) {
0920: // Do nothing.
0921: }
0922: }
0923:
0924: /**
0925: * Stores a <code>RenderedImage</code> in a <code>Vector</code>.
0926: *
0927: * @param image The image to be stored in the <code>Vector</code>.
0928: *
0929: * @return A <code>Vector</code> containing the image.
0930: *
0931: * @throws IllegalArgumentException if <code>image</code> is
0932: * <code>null</code>.
0933: *
0934: * @since JAI 1.1
0935: */
0936: protected static Vector vectorize(RenderedImage image) {
0937: if (image == null) {
0938: throw new IllegalArgumentException(JaiI18N
0939: .getString("OpImage3"));
0940: }
0941: Vector v = new Vector(1);
0942: v.addElement(image);
0943: return v;
0944: }
0945:
0946: /**
0947: * Stores two <code>RenderedImage</code>s in a <code>Vector</code>.
0948: *
0949: * @param image1 The first image to be stored in the <code>Vector</code>.
0950: * @param image2 The second image to be stored in the <code>Vector</code>.
0951: *
0952: * @return A <code>Vector</code> containing the images.
0953: *
0954: * @throws IllegalArgumentException if <code>image1</code> or
0955: * <code>image2</code> is <code>null</code>.
0956: *
0957: * @since JAI 1.1
0958: */
0959: protected static Vector vectorize(RenderedImage image1,
0960: RenderedImage image2) {
0961: if (image1 == null || image2 == null) {
0962: throw new IllegalArgumentException(JaiI18N
0963: .getString("OpImage3"));
0964: }
0965: Vector v = new Vector(2);
0966: v.addElement(image1);
0967: v.addElement(image2);
0968: return v;
0969: }
0970:
0971: /**
0972: * Stores three <code>RenderedImage</code>s in a <code>Vector</code>.
0973: *
0974: * @param image1 The first image to be stored in the <code>Vector</code>.
0975: * @param image2 The second image to be stored in the <code>Vector</code>.
0976: * @param image3 The third image to be stored in the <code>Vector</code>.
0977: *
0978: * @return A <code>Vector</code> containing the images.
0979: *
0980: * @throws IllegalArgumentException if <code>image1</code> or
0981: * <code>image2</code> or <code>image3</code> is <code>null</code>.
0982: *
0983: * @since JAI 1.1
0984: */
0985: protected static Vector vectorize(RenderedImage image1,
0986: RenderedImage image2, RenderedImage image3) {
0987: if (image1 == null || image2 == null || image3 == null) {
0988: throw new IllegalArgumentException(JaiI18N
0989: .getString("OpImage3"));
0990: }
0991: Vector v = new Vector(3);
0992: v.addElement(image1);
0993: v.addElement(image2);
0994: v.addElement(image3);
0995: return v;
0996: }
0997:
0998: /**
0999: * Checks the source <code>Vector</code>.
1000: *
1001: * <p>Checks whether the <code>sources</code> parameter is <code>null</code>
1002: * and optionally whether all elements are non-<code>null</code>.
1003: *
1004: * @param sources The source <code>Vector</code>.
1005: * @param checkElements Whether the elements are to be checked.
1006: *
1007: * @return The <code>sources</code> parameter unmodified.
1008: *
1009: * @throws IllegalArgumentException If <code>sources</code>
1010: * is <code>null</code>.
1011: * @throws IllegalArgumentException If <code>checkElements</code>
1012: * is <code>true</code>, <code>sources</code>
1013: * is non-<code>null</code> and any object in
1014: * <code>sources</code> is <code>null</code>.
1015: */
1016: static Vector checkSourceVector(Vector sources,
1017: boolean checkElements) {
1018: // Check for null source Vector.
1019: if (sources == null) {
1020: throw new IllegalArgumentException(JaiI18N
1021: .getString("OpImage2"));
1022: }
1023:
1024: if (checkElements) {
1025: // Check Vector elements.
1026: int numSources = sources.size();
1027: for (int i = 0; i < numSources; i++) {
1028: // Check for null element.
1029: if (sources.get(i) == null) {
1030: throw new IllegalArgumentException(JaiI18N
1031: .getString("OpImage3"));
1032: }
1033: }
1034: }
1035:
1036: return sources;
1037: }
1038:
1039: /**
1040: * Returns the tile cache object of this image by reference.
1041: * If this image does not have a tile cache, this method returns
1042: * <code>null</code>.
1043: *
1044: * @since JAI 1.1
1045: */
1046: public TileCache getTileCache() {
1047: return cache;
1048: }
1049:
1050: /**
1051: * Sets the tile cache object of this image. A <code>null</code>
1052: * input indicates that this image should have no tile cache and
1053: * subsequently computed tiles will not be cached.
1054: *
1055: * <p> The existing cache object is informed to release all the
1056: * currently cached tiles of this image.
1057: *
1058: * @param cache A cache object to be used for caching this image's
1059: * tiles, or <code>null</code> if no tile caching is desired.
1060: */
1061: public void setTileCache(TileCache cache) {
1062: if (this .cache != null) {
1063: this .cache.removeTiles(this );
1064: }
1065: this .cache = cache;
1066: }
1067:
1068: /**
1069: * Retrieves a tile from the tile cache. If this image does not
1070: * have a tile cache, or the requested tile is not currently in
1071: * the cache, this method returns <code>null</code>.
1072: *
1073: * @param tileX The X index of the tile.
1074: * @param tileY The Y index of the tile.
1075: *
1076: * @return The requested tile as a <code>Raster</code> or
1077: * <code>null</code>.
1078: */
1079: protected Raster getTileFromCache(int tileX, int tileY) {
1080: return cache != null ? cache.getTile(this , tileX, tileY) : null;
1081: }
1082:
1083: /**
1084: * Adds a tile to the tile cache. If this image does not have
1085: * a tile cache, this method does nothing.
1086: *
1087: * @param tileX The X index of the tile.
1088: * @param tileY The Y index of the tile.
1089: * @param tile The tile to be added to the cache.
1090: */
1091: protected void addTileToCache(int tileX, int tileY, Raster tile) {
1092: if (cache != null) {
1093: cache.add(this , tileX, tileY, tile, tileCacheMetric);
1094: }
1095: }
1096:
1097: /**
1098: * Returns the <code>tileCacheMetric</code> instance variable by reference.
1099: *
1100: * @since JAI 1.1
1101: */
1102: public Object getTileCacheMetric() {
1103: return tileCacheMetric;
1104: }
1105:
1106: /**
1107: * Returns a tile of this image as a <code>Raster</code>. If the
1108: * requested tile is completely outside of this image's bounds,
1109: * this method returns <code>null</code>.
1110: *
1111: * <p> This method attempts to retrieve the requested tile from the
1112: * cache. If the tile is not currently in the cache, it schedules
1113: * the tile for computation and adds it to the cache once the tile
1114: * has been computed.
1115: *
1116: * <p> If a subclass overrides this method, then it needs to handle
1117: * tile caching and scheduling. It should also override
1118: * <code>computeTile()</code> which may be invoked directly by the
1119: * <code>TileScheduler</code>.
1120: *
1121: * @param tileX The X index of the tile.
1122: * @param tileY The Y index of the tile.
1123: */
1124: public Raster getTile(int tileX, int tileY) {
1125: Raster tile = null; // the requested tile, to be returned
1126:
1127: // Make sure the requested tile is inside this image's boundary.
1128: if (tileX >= getMinTileX() && tileX <= getMaxTileX()
1129: && tileY >= getMinTileY() && tileY <= getMaxTileY()) {
1130: // Check if tile is available in the cache.
1131: tile = getTileFromCache(tileX, tileY);
1132:
1133: if (tile == null) { // tile not in cache
1134: try {
1135: tile = scheduler.scheduleTile(this , tileX, tileY);
1136: } catch (OutOfMemoryError e) {
1137: // Empty the cache and call System.gc()
1138: if (cache != null) {
1139: cache.flush();
1140: System.gc(); //slow
1141: }
1142:
1143: // Need to reissue the tile scheduling.
1144: tile = scheduler.scheduleTile(this , tileX, tileY);
1145: }
1146:
1147: // Cache the result tile.
1148: addTileToCache(tileX, tileY, tile);
1149: }
1150: }
1151:
1152: return tile;
1153: }
1154:
1155: /**
1156: * Computes the image data of a tile.
1157: *
1158: * <p> When a tile is requested via the <code>getTile</code> method
1159: * and that tile is not in this image's tile cache, this method is
1160: * invoked by the <code>TileScheduler</code> to compute the data of
1161: * the new tile. Even though this method is marked <code>public</code>,
1162: * it should not be called by the applications directly. Rather, it
1163: * is meant to be called by the <code>TileScheduler</code> for the
1164: * actual computation.
1165: *
1166: * <p> The implementation of this method in this class assumes that
1167: * the requested tile either intersects the image, or is within the
1168: * image's bounds. It creates a new <code>Raster</code> to
1169: * represent the requested tile, then calls one of the two variants
1170: * of <code>computeRect</code> to calculate the pixels of the
1171: * tile that are within the image's bounds. The value of
1172: * <code>cobbleSources</code> determines which variant of
1173: * <code>computeRect</code> is invoked, as described in the class
1174: * comments.
1175: *
1176: * <p> Subclasses may provide a more optimized implementation of this
1177: * method. If they override this method not to call either variant of
1178: * <code>computeRect</code>, then neither variant of
1179: * <code>computeRect</code> needs to be implemented.
1180: *
1181: * @param tileX The X index of the tile.
1182: * @param tileY The Y index of the tile.
1183: */
1184: public Raster computeTile(int tileX, int tileY) {
1185: // Create a new Raster.
1186: WritableRaster dest = createWritableRaster(sampleModel,
1187: new Point(tileXToX(tileX), tileYToY(tileY)));
1188:
1189: // Determine the active area; tile intersects with image's bounds.
1190: Rectangle destRect = getTileRect(tileX, tileY);
1191:
1192: int numSources = getNumSources();
1193:
1194: if (cobbleSources) {
1195: Raster[] rasterSources = new Raster[numSources];
1196: // Cobble areas
1197: for (int i = 0; i < numSources; i++) {
1198: PlanarImage source = getSource(i);
1199: Rectangle srcRect = mapDestRect(destRect, i);
1200:
1201: // If srcRect is empty, set the Raster for this source to
1202: // null; otherwise pass srcRect to getData(). If srcRect
1203: // is null, getData() will return a Raster containing the
1204: // data of the entire source image.
1205: rasterSources[i] = srcRect != null && srcRect.isEmpty() ? null
1206: : source.getData(srcRect);
1207: }
1208: computeRect(rasterSources, dest, destRect);
1209:
1210: for (int i = 0; i < numSources; i++) {
1211: Raster sourceData = rasterSources[i];
1212: if (sourceData != null) {
1213: PlanarImage source = getSourceImage(i);
1214:
1215: // Recycle the source tile
1216: if (source.overlapsMultipleTiles(sourceData
1217: .getBounds())) {
1218: recycleTile(sourceData);
1219: }
1220: }
1221: }
1222: } else {
1223: PlanarImage[] imageSources = new PlanarImage[numSources];
1224: for (int i = 0; i < numSources; i++) {
1225: imageSources[i] = getSource(i);
1226: }
1227: computeRect(imageSources, dest, destRect);
1228: }
1229:
1230: return dest;
1231: }
1232:
1233: /**
1234: * Computes a rectangle of output, given <code>Raster</code>
1235: * sources. This method should be overridden by
1236: * <code>OpImage</code> subclasses that make use of cobbled
1237: * sources, as determined by the setting of the
1238: * <code>cobbleSources</code> constructor argument to this class.
1239: *
1240: * <p> The source <code>Raster</code>s are guaranteed to include
1241: * at least the area specified by <code>mapDestRect(destRect)</code>
1242: * unless this area is empty or does not intersect the corresponding
1243: * source in which case the source <code>Raster</code>
1244: * will be <code>null</code>. Only the specified destination region
1245: * should be written.</p>
1246: *
1247: * <p> Since the subclasses of <code>OpImage</code> may choose
1248: * between the cobbling and non-cobbling versions of
1249: * <code>computeRect</code>, it is not possible to leave this
1250: * method abstract in <code>OpImage</code>. Instead, a default
1251: * implementation is provided that throws a
1252: * <code>RuntimeException</code>.</p>
1253: *
1254: * @param sources an array of source <code>Raster</code>s, one per
1255: * source image.
1256: * @param dest a <code>WritableRaster</code> to be filled in.
1257: * @param destRect the <code>Rectangle</code> within the
1258: * destination to be written.
1259: *
1260: * @throws RuntimeException If this method is invoked on the subclass
1261: * that sets <code>cobbleSources</code> to <code>true</code>
1262: * but does not supply an implementation of this method.
1263: */
1264: protected void computeRect(Raster[] sources, WritableRaster dest,
1265: Rectangle destRect) {
1266: String className = this .getClass().getName();
1267: throw new RuntimeException(className + " "
1268: + JaiI18N.getString("OpImage0"));
1269: }
1270:
1271: /**
1272: * Computes a rectangle of output, given <code>PlanarImage</code>
1273: * sources. This method should be overridden by
1274: * <code>OpImage</code> subclasses that do not require cobbled
1275: * sources; typically they will instantiate iterators to perform
1276: * source access, but they may access sources directly (via the
1277: * <code>SampleModel</code>/<code>DataBuffer</code> interfaces) if
1278: * they wish.
1279: *
1280: * <p> Since the subclasses of <code>OpImage</code> may choose
1281: * between the cobbling and non-cobbling versions of
1282: * <code>computeRect</code>, it is not possible to leave this
1283: * method abstract in <code>OpImage</code>. Instead, a default
1284: * implementation is provided that throws a
1285: * <code>RuntimeException</code>.
1286: *
1287: * @param sources an array of <code>PlanarImage</code> sources.
1288: * @param dest a <code>WritableRaster</code> to be filled in.
1289: * @param destRect the <code>Rectangle</code> within the
1290: * destination to be written.
1291: *
1292: * @throws RuntimeException If this method is invoked on the subclass
1293: * that sets <code>cobbleSources</code> to <code>false</code>
1294: * but does not supply an implementation of this method.
1295: */
1296: protected void computeRect(PlanarImage[] sources,
1297: WritableRaster dest, Rectangle destRect) {
1298: String className = this .getClass().getName();
1299: throw new RuntimeException(className + " "
1300: + JaiI18N.getString("OpImage1"));
1301: }
1302:
1303: /**
1304: * Returns a list of indices of the tiles of a given source image
1305: * that may be required in order to compute a given tile.
1306: * Ideally, only tiles that will be requested by means of calls to
1307: * the source's <code>getTile()</code> method should be reported.
1308: * The default implementation uses <code>mapDestRect()</code> to
1309: * obtain a conservative estimate.
1310: *
1311: * <p> If no dependencies exist, this method returns
1312: * <code>null</code>.
1313: *
1314: * <p> This method may be used by optimized implementations of JAI
1315: * in order to predict future work and create an optimized
1316: * schedule for performing it.
1317: *
1318: * <p> A given <code>OpImage</code> may mix calls to
1319: * <code>getTile()</code> with calls to other methods such as
1320: * <code>getData()</code> and <code>copyData()</code> in order to
1321: * avoid requesting entire tiles where only a small portion is
1322: * needed. In such a case, this method may be overridden to
1323: * provide a more accurate estimate of the set of
1324: * <code>getTile()</code> calls that will actually be performed.
1325: *
1326: * @param tileX the X index of the tile.
1327: * @param tileY the Y index of the tile.
1328: * @param sourceIndex the index of the source image.
1329: *
1330: * @return An array of <code>Point</code>s indicating the source
1331: * tile dependencies.
1332: *
1333: * @throws IllegalArgumentException If <code>sourceIndex</code> is
1334: * negative or greater than the index of the last source.
1335: */
1336: public Point[] getTileDependencies(int tileX, int tileY,
1337: int sourceIndex) {
1338: if (sourceIndex < 0 || sourceIndex >= getNumSources()) {
1339: // Specified source does not exist for this image.
1340: throw new IllegalArgumentException(JaiI18N
1341: .getString("Generic1"));
1342: }
1343:
1344: Rectangle rect = getTileRect(tileX, tileY);
1345: if (rect.isEmpty()) {
1346: // The tile is outside of the image bounds.
1347: return null;
1348: }
1349:
1350: // Returns a list of tiles that belong to the source specified by
1351: // the <code>sourceIndex</code> argument, which are need to compute
1352: // the pixels within the rectangle region specified by the
1353: // <code>rect</code> argument of this image.
1354: //
1355: // This method uses <code>mapDestRect</code> to conservatively
1356: // determine the source region required. However, only those tiles
1357: // actually inside the source image bound are returned. If the
1358: // region of interest maps completely outside of the source image,
1359: // <code>null</code> is returned.
1360: PlanarImage src = getSource(sourceIndex);
1361: Rectangle srcRect = mapDestRect(rect, sourceIndex);
1362:
1363: int minTileX = src.XToTileX(srcRect.x);
1364: int maxTileX = src.XToTileX(srcRect.x + srcRect.width - 1);
1365:
1366: int minTileY = src.YToTileY(srcRect.y);
1367: int maxTileY = src.YToTileY(srcRect.y + srcRect.height - 1);
1368:
1369: // Make sure the tiles are really inside the source image.
1370: minTileX = Math.max(minTileX, src.getMinTileX());
1371: maxTileX = Math.min(maxTileX, src.getMaxTileX());
1372:
1373: minTileY = Math.max(minTileY, src.getMinTileY());
1374: maxTileY = Math.min(maxTileY, src.getMaxTileY());
1375:
1376: int numXTiles = maxTileX - minTileX + 1;
1377: int numYTiles = maxTileY - minTileY + 1;
1378: if (numXTiles <= 0 || numYTiles <= 0) {
1379: // The tile maps outside of source image bound.
1380: return null;
1381: }
1382:
1383: Point[] ret = new Point[numYTiles * numXTiles];
1384: int i = 0;
1385:
1386: for (int y = minTileY; y <= maxTileY; y++) {
1387: for (int x = minTileX; x <= maxTileX; x++) {
1388: ret[i++] = new Point(x, y);
1389: }
1390: }
1391:
1392: return ret;
1393: }
1394:
1395: /**
1396: * Computes the tiles indicated by the given tile indices. This
1397: * call is preferable to a series of <code>getTile()</code> calls
1398: * because certain implementations can make optimizations based on
1399: * the knowledge that multiple tiles are being asked for at once.
1400: *
1401: * <p> The implementation of this method in this class uses multiple
1402: * threads to compute multiple tiles at a time.
1403: *
1404: * @param tileIndices An array of <code>Point</code>s representing
1405: * tile indices.
1406: *
1407: * @return An array of <code>Raster</code>s containing the tiles
1408: * corresponding to the given tile indices.
1409: *
1410: * @throws IllegalArgumentException If <code>tileIndices</code> is
1411: * <code>null</code>.
1412: */
1413: public Raster[] getTiles(Point[] tileIndices) {
1414: if (tileIndices == null) {
1415: throw new IllegalArgumentException(JaiI18N
1416: .getString("Generic0"));
1417: }
1418:
1419: int numTiles = tileIndices.length; // number of tiles requested
1420:
1421: // The requested tiles, to be returned.
1422: Raster[] tiles = new Raster[numTiles];
1423:
1424: // Indicator for those tiles that actually need to be computed.
1425: boolean[] computeTiles = new boolean[numTiles];
1426:
1427: int minTileX = getMinTileX();
1428: int maxTileX = getMaxTileX();
1429: int minTileY = getMinTileY();
1430: int maxTileY = getMaxTileY();
1431:
1432: int count = 0; // number of tiles need to be computed
1433:
1434: for (int i = 0; i < numTiles; i++) {
1435: int tileX = tileIndices[i].x;
1436: int tileY = tileIndices[i].y;
1437:
1438: // Make sure the tile is inside image boundary.
1439: if (tileX >= minTileX && tileX <= maxTileX
1440: && tileY >= minTileY && tileY <= maxTileY) {
1441: // Check if tile is available in the cache.
1442: tiles[i] = getTileFromCache(tileX, tileY);
1443:
1444: if (tiles[i] == null) {
1445: // Tile not in cache. needs computation.
1446: computeTiles[i] = true;
1447: count++;
1448: }
1449: }
1450: }
1451:
1452: if (count > 0) { // need to compute some tiles
1453: if (count == numTiles) {
1454: // None of the tiles is in cache.
1455: tiles = scheduler.scheduleTiles(this , tileIndices);
1456:
1457: if (cache != null) { // cache these tiles
1458: if (cache != null) {
1459: for (int i = 0; i < numTiles; i++) {
1460: cache.add(this , tileIndices[i].x,
1461: tileIndices[i].y, tiles[i],
1462: tileCacheMetric);
1463: }
1464: }
1465: }
1466:
1467: } else {
1468: // Only schedule those tiles not in cache for computation.
1469: Point[] indices = new Point[count];
1470: count = 0;
1471: for (int i = 0; i < numTiles; i++) {
1472: if (computeTiles[i]) {
1473: indices[count++] = tileIndices[i];
1474: }
1475: }
1476:
1477: // Schedule needed tiles and return.
1478: Raster[] newTiles = scheduler.scheduleTiles(this ,
1479: indices);
1480:
1481: count = 0;
1482: for (int i = 0; i < numTiles; i++) {
1483: if (computeTiles[i]) {
1484: tiles[i] = newTiles[count++];
1485: addTileToCache(tileIndices[i].x,
1486: tileIndices[i].y, tiles[i]);
1487: }
1488: }
1489: }
1490: }
1491:
1492: return tiles;
1493: }
1494:
1495: private static TileComputationListener[] prependListener(
1496: TileComputationListener[] listeners,
1497: TileComputationListener listener) {
1498: if (listeners == null) {
1499: return new TileComputationListener[] { listener };
1500: }
1501:
1502: TileComputationListener[] newListeners = new TileComputationListener[listeners.length + 1];
1503: newListeners[0] = listener;
1504: System.arraycopy(listeners, 0, newListeners, 1,
1505: listeners.length);
1506:
1507: return newListeners;
1508: }
1509:
1510: /**
1511: * Returns an array of indices of tiles which are not cached or
1512: * <code>null</code> if all are cached.
1513: */
1514: /* XXX
1515: private Point[] pruneIndices(Point[] tileIndices) {
1516: if(true)return tileIndices;//XXX
1517: int numIndices = tileIndices.length;
1518:
1519: ArrayList uncachedIndices = new ArrayList(numIndices);
1520:
1521: for(int i = 0; i < numIndices; i++) {
1522: Point p = tileIndices[i];
1523: if(getTileFromCache(p.x, p.y) == null) {
1524: uncachedIndices.add(p);
1525: }
1526: }
1527:
1528: int numUncached = uncachedIndices.size();
1529: return numUncached > 0 ?
1530: (Point[])uncachedIndices.toArray(new Point[numUncached]) : null;
1531: }
1532: */
1533:
1534: /**
1535: * Queues a list of tiles for computation. Registered listeners will
1536: * be notified after each tile has been computed. The event source
1537: * parameter passed to such listeners will be the <code>TileScheduler</code>
1538: * and the image parameter will be this image.
1539: *
1540: * @param tileIndices A list of tile indices indicating which tiles
1541: * to schedule for computation.
1542: * @throws IllegalArgumentException If <code>tileIndices</code> is
1543: * <code>null</code>.
1544: *
1545: * @since JAI 1.1
1546: */
1547: public TileRequest queueTiles(Point[] tileIndices) {
1548: if (tileIndices == null) {
1549: throw new IllegalArgumentException(JaiI18N
1550: .getString("Generic0"));
1551: }
1552:
1553: /* XXX bad idea probably
1554: // Remove any tile indices corresponding to cached tiles.
1555: tileIndices = pruneIndices(tileIndices);
1556:
1557: // Return if no tiles remain, i.e., all are cached.
1558: if(tileIndices == null) {
1559: return;
1560: }
1561: */
1562:
1563: // Get registered listeners.
1564: TileComputationListener[] tileListeners = getTileComputationListeners();
1565:
1566: // Add a listener to cache tiles only if not a SunTileScheduler.
1567: // The SunTileScheduler caches tiles generated by an OpImage but
1568: // this is not a requirement of the specification.
1569: if (!isSunTileScheduler) {
1570: // Create a local listener.
1571: TileComputationListener localListener = new TCL(this );
1572:
1573: // Prepend local listener to array.
1574: tileListeners = prependListener(tileListeners,
1575: localListener);
1576: }
1577:
1578: // Queue the tiles to the scheduler.
1579: return scheduler
1580: .scheduleTiles(this , tileIndices, tileListeners);
1581: }
1582:
1583: /**
1584: * Issue an advisory cancellation request to nullify processing of
1585: * the indicated tiles via the TileScheduler for this image. This
1586: * method should merely forward the request to the associated
1587: * <code>TileScheduler</code>.
1588: *
1589: * @param request The request for which tiles are to be cancelled.
1590: * @param tileIndices The tiles to be cancelled; may be <code>null</code>.
1591: * Any tiles not actually in the <code>TileRequest</code> will be
1592: * ignored.
1593: * @throws IllegalArgumentException If <code>request</code> is
1594: * <code>null</code>.
1595: *
1596: * @since JAI 1.1
1597: */
1598: public void cancelTiles(TileRequest request, Point[] tileIndices) {
1599: if (request == null) {
1600: throw new IllegalArgumentException(JaiI18N
1601: .getString("Generic4"));
1602: }
1603: scheduler.cancelTiles(request, tileIndices);
1604: }
1605:
1606: /**
1607: * Hints that the given tiles might be needed in the near future.
1608: * Some implementations may spawn one or more threads
1609: * to compute the tiles, while others may ignore the hint.
1610: *
1611: * @param tileIndices A list of tile indices indicating which tiles
1612: * to prefetch.
1613: *
1614: * @throws IllegalArgumentException If <code>tileIndices</code> is
1615: * <code>null</code>.
1616: */
1617: public void prefetchTiles(Point[] tileIndices) {
1618: if (tileIndices == null) {
1619: throw new IllegalArgumentException(JaiI18N
1620: .getString("Generic0"));
1621: }
1622:
1623: /* XXX bad idea probably
1624: // Remove any tile indices corresponding to cached tiles.
1625: tileIndices = pruneIndices(tileIndices);
1626: */
1627:
1628: // Return if no tiles remain, i.e., all are cached.
1629: if (tileIndices == null) {
1630: return;
1631: }
1632:
1633: // Prefetch any remaining tiles.
1634: scheduler.prefetchTiles(this , tileIndices);
1635: }
1636:
1637: /**
1638: * Computes the position in the specified source that best
1639: * matches the supplied destination image position. If it
1640: * is not possible to compute the requested position,
1641: * <code>null</code> will be returned. If the point is mapped
1642: * outside the source bounds, the coordinate value or <code>null</code>
1643: * may be returned at the discretion of the implementation.
1644: *
1645: * <p>Floating-point input and output coordinates are supported,
1646: * and recommended when possible. Subclass implementations may
1647: * however use integer computation if necessary for simplicity.</p>
1648: *
1649: * <p>The implementation in this class returns the value of
1650: * <code>pt</code> in the following code snippet:
1651: *
1652: * <pre>
1653: * Rectangle destRect = new Rectangle((int)destPt.getX(),
1654: * (int)destPt.getY(),
1655: * 1, 1);
1656: * Rectangle sourceRect = mapDestRect(destRect, sourceIndex);
1657: * Point2D pt = (Point2D)destPt.clone();
1658: * pt.setLocation(sourceRect.x + (sourceRect.width - 1.0)/2.0,
1659: * sourceRect.y + (sourceRect.height - 1.0)/2.0);
1660: * </pre>
1661: *
1662: * Subclasses requiring different behavior should override this
1663: * method.</p>
1664: *
1665: * @param destPt the position in destination image coordinates
1666: * to map to source image coordinates.
1667: * @param sourceIndex the index of the source image.
1668: *
1669: * @return a <code>Point2D</code> of the same class as
1670: * <code>destPt</code> or <code>null</code>.
1671: *
1672: * @throws IllegalArgumentException if <code>destPt</code> is
1673: * <code>null</code>.
1674: * @throws IndexOutOfBoundsException if <code>sourceIndex</code> is
1675: * negative or greater than or equal to the number of sources.
1676: *
1677: * @since JAI 1.1.2
1678: */
1679: public Point2D mapDestPoint(Point2D destPt, int sourceIndex) {
1680: if (destPt == null) {
1681: throw new IllegalArgumentException(JaiI18N
1682: .getString("Generic0"));
1683: } else if (sourceIndex < 0 || sourceIndex >= getNumSources()) {
1684: throw new IndexOutOfBoundsException(JaiI18N
1685: .getString("Generic1"));
1686: }
1687:
1688: Rectangle destRect = new Rectangle((int) destPt.getX(),
1689: (int) destPt.getY(), 1, 1);
1690:
1691: Rectangle sourceRect = mapDestRect(destRect, sourceIndex);
1692:
1693: Point2D pt = (Point2D) destPt.clone();
1694: pt.setLocation(sourceRect.x + (sourceRect.width - 1.0) / 2.0,
1695: sourceRect.y + (sourceRect.height - 1.0) / 2.0);
1696:
1697: return pt;
1698: }
1699:
1700: /**
1701: * Computes the position in the destination that best
1702: * matches the supplied source image position. If it
1703: * is not possible to compute the requested position,
1704: * <code>null</code> will be returned. If the point is mapped
1705: * outside the destination bounds, the coordinate value or
1706: * <code>null</code> may be returned at the discretion of the
1707: * implementation.
1708: *
1709: * <p>Floating-point input and output coordinates are supported,
1710: * and recommended when possible. Subclass implementations may
1711: * however use integer computation if necessary for simplicity.</p>
1712: *
1713: * <p>The implementation in this class returns the value of
1714: * <code>pt</code> in the following code snippet:
1715: *
1716: * <pre>
1717: * Rectangle sourceRect = new Rectangle((int)sourcePt.getX(),
1718: * (int)sourcePt.getY(),
1719: * 1, 1);
1720: * Rectangle destRect = mapSourceRect(sourceRect, sourceIndex);
1721: * Point2D pt = (Point2D)sourcePt.clone();
1722: * pt.setLocation(destRect.x + (destRect.width - 1.0)/2.0,
1723: * destRect.y + (destRect.height - 1.0)/2.0);
1724: * </pre>
1725: *
1726: * @param sourcePt the position in source image coordinates
1727: * to map to destination image coordinates.
1728: * @param sourceIndex the index of the source image.
1729: *
1730: * @return a <code>Point2D</code> of the same class as
1731: * <code>sourcePt</code> or <code>null</code>.
1732: *
1733: * @throws IllegalArgumentException if <code>sourcePt</code> is
1734: * <code>null</code>.
1735: * @throws IndexOutOfBoundsException if <code>sourceIndex</code> is
1736: * negative or greater than or equal to the number of sources.
1737: *
1738: * @since JAI 1.1.2
1739: */
1740: public Point2D mapSourcePoint(Point2D sourcePt, int sourceIndex) {
1741: if (sourcePt == null) {
1742: throw new IllegalArgumentException(JaiI18N
1743: .getString("Generic0"));
1744: } else if (sourceIndex < 0 || sourceIndex >= getNumSources()) {
1745: throw new IndexOutOfBoundsException(JaiI18N
1746: .getString("Generic1"));
1747: }
1748:
1749: Rectangle sourceRect = new Rectangle((int) sourcePt.getX(),
1750: (int) sourcePt.getY(), 1, 1);
1751:
1752: Rectangle destRect = mapSourceRect(sourceRect, sourceIndex);
1753:
1754: // Return null of destination rectangle is not computable.
1755: if (destRect == null) {
1756: return null;
1757: }
1758:
1759: Point2D pt = (Point2D) sourcePt.clone();
1760: pt.setLocation(destRect.x + (destRect.width - 1.0) / 2.0,
1761: destRect.y + (destRect.height - 1.0) / 2.0);
1762:
1763: return pt;
1764: }
1765:
1766: /**
1767: * Returns a conservative estimate of the destination region that
1768: * can potentially be affected by the pixels of a rectangle of a
1769: * given source. An empty <code>Rectangle</code> may be returned
1770: * if the destination is unaffected by the contents of the source
1771: * rectangle. This is distinct from a <code>null</code> return value
1772: * which serves rather to indicate that it is not possible to
1773: * determine the bounds of the affected region. The safest
1774: * interpretation of a <code>null</code> return value is that the
1775: * entire destination might be affected by any pixel within the
1776: * given source rectangle.
1777: *
1778: * @param sourceRect The <code>Rectangle</code> in source coordinates.
1779: * @param sourceIndex The index of the source image.
1780: *
1781: * @return A <code>Rectangle</code> indicating the potentially
1782: * affected destination region, or <code>null</code> if
1783: * the region is unknown.
1784: *
1785: * @throws IllegalArgumentException If the source index is
1786: * negative or greater than that of the last source.
1787: * @throws IllegalArgumentException If <code>sourceRect</code> is
1788: * <code>null</code>.
1789: */
1790: public abstract Rectangle mapSourceRect(Rectangle sourceRect,
1791: int sourceIndex);
1792:
1793: /**
1794: * Returns a conservative estimate of the region of a specified
1795: * source that is required in order to compute the pixels of a
1796: * given destination rectangle. The computation may as appropriate
1797: * clip the mapped <code>Rectangle</code> to the actual bounds of the
1798: * source or may treat the source as having infinite extent.
1799: * It is therefore the responsibility of the invoking
1800: * object to constrain the region in accordance with its needs.
1801: * Returning an empty <code>Rectangle</code> should indicate that
1802: * the data of the source image in question are not required for the
1803: * computation of the specified destination region. If the entire
1804: * source image might be required to compute this destination
1805: * region, then <code>getSourceImage(sourceIndex).getBounds()</code>
1806: * should be returned.
1807: *
1808: * <p> To illustrate the issue of whether the source should be thought
1809: * to have infinite extent, consider the case wherein computing a
1810: * destination pixel requires multiple source pixels of context. At
1811: * the source image boundary, these pixels might only be available if the
1812: * source data were extrapolated, e.g., using a {@link BorderExtender}.
1813: * If such an extender were available, destination pixels could be
1814: * computed even if they mapped to a region on the source boundary so
1815: * in this case the source could be considered to have infinite extent.
1816: * If no such extender were available, only destination pixels with
1817: * source context contained within the source image bounds could be
1818: * considered so that it might be preferable to clip the rectangle to
1819: * the source bounds.</p>
1820: *
1821: * @param destRect The <code>Rectangle</code> in destination coordinates.
1822: * @param sourceIndex The index of the source image.
1823: *
1824: * @return A non-<code>null</code> <code>Rectangle</code> indicating
1825: * the required source region.
1826: *
1827: * @throws IllegalArgumentException If the source index is
1828: * negative or greater than that of the last source.
1829: * @throws IllegalArgumentException If <code>destRect</code> is
1830: * <code>null</code>.
1831: */
1832: public abstract Rectangle mapDestRect(Rectangle destRect,
1833: int sourceIndex);
1834:
1835: /**
1836: * Returns one of <code>OP_COMPUTE_BOUND</code>,
1837: * <code>OP_IO_BOUND</code>, or <code>OP_NETWORK_BOUND</code> to
1838: * indicate how the operation is likely to spend its time. The
1839: * answer does not affect the output of the operation, but may
1840: * allow a scheduler to parallelize the computation of multiple
1841: * operations more effectively.
1842: *
1843: * <p> The implementation of this method in this class
1844: * returns <code>OP_COMPUTE_BOUND</code>.
1845: */
1846: public int getOperationComputeType() {
1847: return OP_COMPUTE_BOUND;
1848: }
1849:
1850: /**
1851: * Returns <code>true</code> if the <code>OpImage</code> returns an
1852: * unique <code>Raster</code> object every time <code>computeTile</code>
1853: * is called. <code>OpImage</code>s that internally cache
1854: * <code>Raster</code>s and return them via <code>computeTile</code>
1855: * should return <code>false</code> for this method.
1856: *
1857: * <p> The implementation of this method in this class always returns
1858: * <code>true</code>.
1859: */
1860: public boolean computesUniqueTiles() {
1861: return true;
1862: }
1863:
1864: /**
1865: * Uncaches all tiles and calls <code>super.dispose()</code>.
1866: * If a <code>TileRecycler</code> was defined via the configuration
1867: * variable <code>JAI.KEY_TILE_RECYCLER</code> when this image was
1868: * constructed and tile recycling was enabled via the configuration
1869: * variable <code>JAI.KEY_CACHED_TILE_RECYCLING_ENABLED</code>, then each
1870: * of this image's tiles which is currently in the cache will be
1871: * recycled. This method may be invoked more than once although
1872: * invocations after the first one may do nothing.
1873: *
1874: * <p> The results of referencing an image after a call to
1875: * <code>dispose()</code> are undefined.</p>
1876: *
1877: * @since JAI 1.1.2
1878: */
1879: public synchronized void dispose() {
1880: if (isDisposed) {
1881: return;
1882: }
1883:
1884: isDisposed = true;
1885:
1886: if (cache != null) {
1887: if (isCachedTileRecyclingEnabled && tileRecycler != null) {
1888: Raster[] tiles = cache.getTiles(this );
1889: if (tiles != null) {
1890: int numTiles = tiles.length;
1891: for (int i = 0; i < numTiles; i++) {
1892: tileRecycler.recycleTile(tiles[i]);
1893: }
1894: }
1895: }
1896: cache.removeTiles(this );
1897: }
1898: super .dispose();
1899: }
1900:
1901: /**
1902: * Indicates whether the source with the given index has a
1903: * <code>BorderExtender</code>. If the source index is out of bounds
1904: * for the source vector of this <code>OpImage</code> then an
1905: * <code>ArrayIndexOutOfBoundsException</code> may be thrown.
1906: *
1907: * @param sourceIndex The index of the source in question.
1908: * @return <code>true</code> if the indicated source has an extender.
1909: *
1910: * @deprecated as of JAI 1.1.
1911: */
1912: public boolean hasExtender(int sourceIndex) {
1913: if (sourceIndex != 0) {
1914: throw new ArrayIndexOutOfBoundsException();
1915: } else if (this instanceof AreaOpImage) {
1916: return ((AreaOpImage) this ).getBorderExtender() != null;
1917: } else if (this instanceof GeometricOpImage) {
1918: return ((GeometricOpImage) this ).getBorderExtender() != null;
1919: }
1920: return false;
1921: }
1922:
1923: /**
1924: * Returns the effective number of bands of an image with a given
1925: * <code>SampleModel</code> and <code>ColorModel</code>.
1926: * Normally, this is given by
1927: * <code>sampleModel.getNumBands()</code>, but for images with an
1928: * <code>IndexColorModel</code> the effective number of bands is
1929: * given by <code>colorModel.getNumComponents()</code>, since
1930: * a single physical sample represents multiple color components.
1931: *
1932: * @deprecated as of JAI 1.1.
1933: */
1934: public static int getExpandedNumBands(SampleModel sampleModel,
1935: ColorModel colorModel) {
1936: if (colorModel instanceof IndexColorModel) {
1937: return colorModel.getNumComponents();
1938: } else {
1939: return sampleModel.getNumBands();
1940: }
1941: }
1942:
1943: /**
1944: * Returns the image's format tags to be used with
1945: * a <code>RasterAccessor</code>.
1946: *
1947: * <p> This method will compute and cache the tags the first time
1948: * it is called on a particular image. The image's
1949: * <code>SampleModel</code> and <code>ColorModel</code> must be
1950: * set to their final values before calling this method.
1951: *
1952: * @return An array containing <code>RasterFormatTag</code>s for the
1953: * sources in the first <code>getNumSources()</code> elements and a
1954: * <code>RasterFormatTag</code> for the destination in the last element.
1955: */
1956: // XXX This method should be removed if we stop using RasterAccessor.
1957: protected synchronized RasterFormatTag[] getFormatTags() {
1958: if (formatTags == null) {
1959: RenderedImage[] sourceArray = new RenderedImage[getNumSources()];
1960: if (sourceArray.length > 0) {
1961: getSources().toArray(sourceArray);
1962: }
1963: formatTags = RasterAccessor.findCompatibleTags(sourceArray,
1964: this );
1965: }
1966:
1967: return formatTags;
1968: }
1969:
1970: /**
1971: * Returns the value of the instance variable <code>tileRecycler</code>.
1972: *
1973: * @since JAI 1.1.2
1974: */
1975: public TileRecycler getTileRecycler() {
1976: return tileRecycler;
1977: }
1978:
1979: /**
1980: * Creates a <code>WritableRaster</code> at the given tile grid position.
1981: * The superclass method {@link #createWritableRaster(SampleModel,Point)}
1982: * will be invoked with this image's <code>SampleModel</code> and the
1983: * location of the specified tile.
1984: *
1985: * <p>Subclasses should ideally use this method to create destination
1986: * tiles as this method will take advantage of any
1987: * <code>TileFactory</code> specified to the <code>OpImage</code> at
1988: * construction.</p>
1989: *
1990: * @since JAI 1.1.2
1991: */
1992: protected final WritableRaster createTile(int tileX, int tileY) {
1993: return createWritableRaster(sampleModel, new Point(
1994: tileXToX(tileX), tileYToY(tileY)));
1995: }
1996:
1997: /**
1998: * A tile recycling convenience method.
1999: *
2000: * <p>If <code>tileRecycler</code> is non-<code>null</code>, the call
2001: * is forwarded to {@link TileRecycler.recycleTile(Raster)}; otherwise
2002: * the method does nothing.</p>
2003: *
2004: * <p>This method is for use by subclasses which create
2005: * <code>Raster</code>s with limited scope which therefore may easily
2006: * be identified as safe candidates for recycling. This might occur
2007: * for example within
2008: * {@link #computeRect(Raster[],WritableRaster,Rectangle)} or
2009: * {@link #computeTile(int,int)} wherein <code>Raster</code>s may be
2010: * created for use within the method but be eligible for garbage
2011: * collection once the method is exited.</p>
2012: *
2013: * @throws IllegalArgumentException if <code>tile</code> is
2014: * <code>null</code>.
2015: *
2016: * @since JAI 1.1.2
2017: */
2018: protected void recycleTile(Raster tile) {
2019: if (tile == null)
2020: throw new IllegalArgumentException(JaiI18N
2021: .getString("Generic0"));
2022:
2023: if (tileRecycler != null) {
2024: tileRecycler.recycleTile(tile);
2025: }
2026: }
2027: }
|