001: /*
002: * $RCSfile: UntiledOpImage.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:57:23 $
010: * $State: Exp $
011: */
012: package javax.media.jai;
013:
014: import java.awt.Point;
015: import java.awt.Rectangle;
016: import java.awt.image.Raster;
017: import java.awt.image.RenderedImage;
018: import java.awt.image.WritableRaster;
019: import java.util.Map;
020: import java.util.Vector;
021: import javax.media.jai.ImageLayout;
022: import javax.media.jai.PlanarImage;
023:
024: /**
025: * A general class for single-source operations which require cobbled
026: * sources and create an image consisting of a single tile equal in location
027: * and size to the image bounds.
028: *
029: * <p> The output image will have a single tile, regardless of the
030: * <code>ImageLayout</code> settings passed to the constructor. Any
031: * specified settings for tile grid offset and tile dimensions will be
032: * replaced by the image origin and tile dimensions, respectively.
033: *
034: * <p> Subclasses should implement the <code>computeImage</code> method
035: * which requests computation of the entire image at once.
036: *
037: * @see OpImage
038: * @see javax.media.jai.operator.DCTDescriptor
039: * @see javax.media.jai.operator.DFTDescriptor
040: * @see javax.media.jai.operator.ErrorDiffusionDescriptor
041: */
042: public abstract class UntiledOpImage extends OpImage {
043: /**
044: * Creates the <code>ImageLayout</code> for the image. If the
045: * layout parameter is null, create a new <code>ImageLayout</code>
046: * from the supplied <code>RenderedImage</code>. Also, force the tile
047: * grid offset to equal the image origin and the tile width and height
048: * to be equal to the image width and height, respectively, thereby
049: * forcing the image to have a single tile.
050: *
051: * @param layout The <code>ImageLayout</code> to be cloned; may be null.
052: * @param source The <code>RenderedImage</code> the attributes of which
053: * are to be used as fallbacks in creating a new <code>ImageLayout</code>.
054: *
055: * @return The <code>ImageLayout</code> to be used.
056: */
057: private static ImageLayout layoutHelper(ImageLayout layout,
058: Vector sources) {
059: if (sources.size() < 1) {
060: throw new IllegalArgumentException(JaiI18N
061: .getString("Generic5"));
062: }
063:
064: RenderedImage source = (RenderedImage) sources.get(0);
065:
066: ImageLayout il = layout == null ? new ImageLayout()
067: : (ImageLayout) layout.clone();
068:
069: // Force the image to have one tile. For this to obtain with minimal
070: // tile size the tile grid offset must coincide with the image origin.
071: il.setTileGridXOffset(il.getMinX(source));
072: il.setTileGridYOffset(il.getMinY(source));
073: il.setTileWidth(il.getWidth(source));
074: il.setTileHeight(il.getHeight(source));
075:
076: return il;
077: }
078:
079: /**
080: * Constructs an <code>UntiledOpImage</code>. The image origin and
081: * dimensions, <code>SampleModel</code>, and <code>ColorModel</code>
082: * may optionally be specified by an <code>ImageLayout</code> object.
083: * In all cases the tile grid offset will be set to the image origin
084: * and the tile dimensions to the image dimensions. If not specified
085: * in the <code>ImageLayout</code>, the image origin and dimensions
086: * are set to the corresponding attributes of the first source image.
087: * Cobbling will be performed on the source(s) as needed.
088: *
089: * @param sources The immediate sources of this image.
090: * @param configuration Configurable attributes of the image including
091: * configuration variables indexed by
092: * <code>RenderingHints.Key</code>s and image properties indexed
093: * by <code>String</code>s or <code>CaselessStringKey</code>s.
094: * This is simply forwarded to the superclass constructor.
095: * @param layout an <code>ImageLayout</code> optionally containing
096: * the <code>SampleModel</code>, and
097: * <code>ColorModel</code>. The tile grid layout
098: * information will be overridden in order to ensure that
099: * the image has a single tile.
100: *
101: * @throws IllegalArgumentException if <code>sources</code>
102: * is <code>null</code>.
103: * @throws IllegalArgumentException If <code>sources</code>
104: * is non-<code>null</code> and any object in
105: * <code>sources</code> is <code>null</code>.
106: * @throws IllegalArgumentException if <code>sources</code> does not
107: * contain at least one element.
108: * @throws ClassCastException If the first object in <code>sources</code>
109: * is not a <code>RenderedImage</code>.
110: *
111: * @since JAI 1.1
112: */
113: public UntiledOpImage(Vector sources, Map configuration,
114: ImageLayout layout) {
115: super (checkSourceVector(sources, true), layoutHelper(layout,
116: sources), configuration, true);
117: }
118:
119: /**
120: * Constructs an <code>UntiledOpImage</code>. The image origin and
121: * dimensions, <code>SampleModel</code>, and <code>ColorModel</code>
122: * may optionally be specified by an <code>ImageLayout</code> object.
123: * In all cases the tile grid offset will be set to the image origin
124: * and the tile dimensions to the image dimensions. If not specified
125: * in the <code>ImageLayout</code>, the image origin and dimensions
126: * are set to the corresponding attributes of the source image.
127: * Cobbling will be performed on the source as needed.
128: *
129: * @param source a <code>RenderedImage</code>.
130: * @param configuration Configurable attributes of the image including
131: * configuration variables indexed by
132: * <code>RenderingHints.Key</code>s and image properties indexed
133: * by <code>String</code>s or <code>CaselessStringKey</code>s.
134: * This is simply forwarded to the superclass constructor.
135: * @param layout an <code>ImageLayout</code> optionally containing
136: * the <code>SampleModel</code>, and
137: * <code>ColorModel</code>. The tile grid layout
138: * information will be overridden in order to ensure that
139: * the image has a single tile.
140: *
141: * @throws IllegalArgumentException if <code>source</code>
142: * is <code>null</code>.
143: *
144: * @since JAI 1.1
145: */
146: public UntiledOpImage(RenderedImage source, Map configuration,
147: ImageLayout layout) {
148: super (
149: vectorize(source), // vectorize() checks for null source.
150: layoutHelper(layout, vectorize(source)), configuration,
151: true);
152: }
153:
154: /**
155: * Returns the image bounds.
156: *
157: * @param sourceRect the <code>Rectangle</code> in source coordinates
158: * (ignored).
159: * @param sourceIndex the index of the source image (ignored).
160: * @return The image bounds.
161: */
162: public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) {
163: return getBounds();
164: }
165:
166: /**
167: * Returns the bounds of the indicated source image.
168: *
169: * @param destRect the Rectangle in destination coordinates (ignored).
170: * @param sourceIndex the index of the source image.
171: * @return The bounds of the indicated source image.
172: *
173: * @throws IllegalArgumentException if <code>sourceIndex</code> is
174: * negative or greater than the index of the last source.
175: */
176: public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) {
177: if (sourceIndex < 0 || sourceIndex >= getNumSources()) {
178: throw new IllegalArgumentException(JaiI18N
179: .getString("Generic1"));
180: }
181:
182: return getSource(sourceIndex).getBounds();
183: }
184:
185: /**
186: * Computes a tile. All sources are cobbled together and
187: * <code>computeImage</code> is called to produce the single
188: * output tile.
189: *
190: * @param tileX The X index of the tile.
191: * @param tileY The Y index of the tile.
192: */
193: public Raster computeTile(int tileX, int tileY) {
194: // Create a new raster.
195: Point org = new Point(getMinX(), getMinY());
196: WritableRaster dest = createWritableRaster(sampleModel, org);
197:
198: // Determine the active area. Since the image has a single
199: // tile equal in coverage to the image bounds just set this
200: // to the image bounds.
201: Rectangle destRect = getBounds();
202:
203: // Cobble source image(s).
204: int numSources = getNumSources();
205: Raster[] rasterSources = new Raster[numSources];
206: for (int i = 0; i < numSources; i++) {
207: PlanarImage source = getSource(i);
208: Rectangle srcRect = mapDestRect(destRect, i);
209: rasterSources[i] = source.getData(srcRect);
210: }
211:
212: // Compute the image.
213: computeImage(rasterSources, dest, destRect);
214:
215: for (int i = 0; i < numSources; i++) {
216: Raster sourceData = rasterSources[i];
217: if (sourceData != null) {
218: PlanarImage source = getSourceImage(i);
219:
220: // Recycle the source tile
221: if (source
222: .overlapsMultipleTiles(sourceData.getBounds())) {
223: recycleTile(sourceData);
224: }
225: }
226: }
227:
228: return dest;
229: }
230:
231: /**
232: * Calculate the destination image from the source image.
233: *
234: * @param sources The source Rasters; should be the whole image for
235: * each source.
236: * @param dest The destination WritableRaster; should be the whole image.
237: * @param destRect The destination Rectangle; should equal the destination
238: * image bounds.
239: *
240: * @since JAI 1.1
241: */
242: protected abstract void computeImage(Raster[] sources,
243: WritableRaster dest, Rectangle destRect);
244:
245: /**
246: * Returns an array of points indicating the tile dependencies which in
247: * this case is the set of all tiles in the specified source image.
248: *
249: * @since JAI 1.1
250: */
251: public Point[] getTileDependencies(int tileX, int tileY,
252: int sourceIndex) {
253: // Compute the tile dependencies only the first time that this
254: // method is invoked.
255: PlanarImage source = getSource(sourceIndex);
256:
257: int minTileX = source.getMinTileX();
258: int minTileY = source.getMinTileY();
259: int maxTileX = minTileX + source.getNumXTiles() - 1;
260: int maxTileY = minTileY + source.getNumYTiles() - 1;
261:
262: Point[] tileDependencies = new Point[(maxTileX - minTileX + 1)
263: * (maxTileY - minTileY + 1)];
264:
265: int count = 0;
266: for (int ty = minTileY; ty <= maxTileY; ty++) {
267: for (int tx = minTileX; tx <= maxTileX; tx++) {
268: tileDependencies[count++] = new Point(tx, ty);
269: }
270: }
271:
272: return tileDependencies;
273: }
274: }
|