001: /*
002: * $RCSfile: MlibWarpGridOpImage.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.2 $
009: * $Date: 2005/12/15 18:35:47 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.mlib;
013:
014: import java.awt.Point;
015: import java.awt.Rectangle;
016: import java.awt.image.DataBuffer;
017: import java.awt.image.Raster;
018: import java.awt.image.RenderedImage;
019: import java.awt.image.WritableRaster;
020: import javax.media.jai.BorderExtender;
021: import javax.media.jai.ImageLayout;
022: import javax.media.jai.Interpolation;
023: import java.util.Map;
024: import javax.media.jai.RasterFactory;
025: import javax.media.jai.WarpOpImage;
026: import javax.media.jai.WarpGrid;
027:
028: import com.sun.medialib.mlib.*;
029: import com.sun.media.jai.util.ImageUtil;
030:
031: /**
032: * An <code>OpImage</code> implementing the Grid "Warp" operation
033: * using mediaLib.
034: *
035: * <p> With warp operations, there is no forward mapping (from source to
036: * destination). JAI images are tiled, while mediaLib does not handle
037: * tiles and consider each tile an individual image. For each tile in
038: * destination, in order not to cobble the entire source image, the
039: * <code>computeTile</code> method in this class attemps to do a backward
040: * mapping on the tile region using the pixels along the perimeter of the
041: * rectangular region. The hope is that the mapped source rectangle
042: * should include all source pixels needed for this particular destination
043: * tile. However, with certain unusual warp points, an inner destination
044: * pixel may be mapped outside of the mapped perimeter pixels. In this
045: * case, this destination pixel is not filled, and left black.
046: *
047: * @see javax.media.jai.operator.WarpDescriptor
048: * @see MlibWarpRIF
049: *
050: * @since 1.0
051: *
052: */
053: final class MlibWarpGridOpImage extends WarpOpImage {
054:
055: /** X grid settings. */
056: private int xStart;
057: private int xStep;
058: private int xNumCells;
059: private int xEnd;
060:
061: /** Y grid settings. */
062: private int yStart;
063: private int yStep;
064: private int yNumCells;
065: private int yEnd;
066:
067: /** Grid points. */
068: private float[] xWarpPos;
069: private float[] yWarpPos;
070:
071: /**
072: * Indicates what kind of interpolation to use; may be
073: * <code>Constants.MLIB_NEAREST</code>,
074: * <code>Constants.MLIB_BILINEAR</code>,
075: * or <code>Constants.MLIB_BICUBIC</code>,
076: * and was determined in <code>MlibWarpRIF.create()</code>.
077: */
078: private int filter;
079:
080: /**
081: * Returns the minimum bounding box of the region of the specified
082: * source to which a particular <code>Rectangle</code> of the
083: * destination will be mapped.
084: *
085: * @param destRect the <code>Rectangle</code> in destination coordinates.
086: * @param sourceIndex the index of the source image.
087: *
088: * @return a <code>Rectangle</code> indicating the source bounding box,
089: * or <code>null</code> if the bounding box is unknown.
090: *
091: * @throws IllegalArgumentException if <code>sourceIndex</code> is
092: * negative or greater than the index of the last source.
093: * @throws IllegalArgumentException if <code>destRect</code> is
094: * <code>null</code>.
095: */
096: protected Rectangle backwardMapRect(Rectangle destRect,
097: int sourceIndex) {
098: // Superclass method will throw documented exceptions if needed.
099: Rectangle wrect = super .backwardMapRect(destRect, sourceIndex);
100:
101: // "Dilate" the backwarp mapped rectangle to account for
102: // the lack of being able to know the floating point result of
103: // mapDestRect() and to mimic what is done in AffineOpImage.
104: // See bug 4518223 for more information.
105: wrect.setBounds(wrect.x - 1, wrect.y - 1, wrect.width + 2,
106: wrect.height + 2);
107:
108: return wrect;
109: }
110:
111: /**
112: * Constructs a <code>MlibWarpGridOpImage</code>.
113: *
114: * @param source The source image.
115: * @param layout The destination image layout.
116: * @param warp An object defining the warp algorithm.
117: * @param interp An object describing the interpolation method.
118: */
119: public MlibWarpGridOpImage(RenderedImage source,
120: BorderExtender extender, Map config, ImageLayout layout,
121: WarpGrid warp, Interpolation interp, int filter,
122: double[] backgroundValues) {
123: super (source, layout, config, true, extender, interp, warp,
124: backgroundValues);
125:
126: this .filter = filter; // interpolation
127:
128: xStart = warp.getXStart();
129: xStep = warp.getXStep();
130: xNumCells = warp.getXNumCells();
131: xEnd = xStart + xStep * xNumCells;
132:
133: yStart = warp.getYStart();
134: yStep = warp.getYStep();
135: yNumCells = warp.getYNumCells();
136: yEnd = yStart + yStep * yNumCells;
137:
138: xWarpPos = warp.getXWarpPos();
139: yWarpPos = warp.getYWarpPos();
140: }
141:
142: /**
143: * Computes a tile. A new <code>WritableRaster</code> is created to
144: * represent the requested tile. Its width and height equals to this
145: * image's tile width and tile height respectively. If the requested
146: * tile lies outside of the image's boundary, the created raster is
147: * returned with all of its pixels set to 0.
148: *
149: * <p> This method overrides the method in <code>WarpOpImage</code>
150: * and performs source cobbling when necessary. MediaLib is used to
151: * calculate the actual warping.
152: *
153: * @param tileX The X index of the tile.
154: * @param tileY The Y index of the tile.
155: *
156: * @return The tile as a <code>Raster</code>.
157: */
158: public Raster computeTile(int tileX, int tileY) {
159: /* The origin of the tile. */
160: Point org = new Point(tileXToX(tileX), tileYToY(tileY));
161:
162: /* Create a new WritableRaster to represent this tile. */
163: WritableRaster dest = createWritableRaster(sampleModel, org);
164:
165: /* Find the intersection between this tile and the writable bounds. */
166: Rectangle rect0 = new Rectangle(org.x, org.y, tileWidth,
167: tileHeight);
168: Rectangle destRect = rect0.intersection(computableBounds);
169: Rectangle destRect1 = rect0.intersection(getBounds());
170:
171: if (destRect.isEmpty()) {
172: if (setBackground) {
173: ImageUtil.fillBackground(dest, destRect1,
174: backgroundValues);
175: }
176: return dest; // tile completely outside of writable bounds
177: }
178:
179: if (!destRect1.equals(destRect)) {
180: // beware that destRect1 contains destRect
181: ImageUtil.fillBordersWithBackgroundValues(destRect1,
182: destRect, dest, backgroundValues);
183: }
184:
185: Raster[] sources = new Raster[1];
186: Rectangle srcBounds = getSourceImage(0).getBounds();
187:
188: int x0 = destRect.x; // first x point
189: int x1 = x0 + destRect.width - 1; // last x point
190: int y0 = destRect.y; // first y point
191: int y1 = y0 + destRect.height - 1; // last y point
192:
193: if (x0 >= xEnd || x1 < xStart || y0 >= yEnd || y1 < yStart) {
194: /* Tile is completely outside of warp grid; do copy. */
195: Rectangle rect = srcBounds.intersection(destRect);
196:
197: if (!rect.isEmpty()) {
198: sources[0] = getSourceImage(0).getData(rect);
199: copyRect(sources, dest, rect);
200:
201: // Recycle the source tile
202: if (getSourceImage(0).overlapsMultipleTiles(rect)) {
203: recycleTile(sources[0]);
204: }
205: }
206:
207: return dest;
208: }
209:
210: if (x0 < xStart) { // region left of warp grid
211: Rectangle rect = srcBounds.intersection(new Rectangle(x0,
212: y0, xStart - x0, y1 - y0 + 1));
213:
214: if (!rect.isEmpty()) {
215: sources[0] = getSourceImage(0).getData(rect);
216: copyRect(sources, dest, rect);
217:
218: // Recycle the source tile
219: if (getSourceImage(0).overlapsMultipleTiles(rect)) {
220: recycleTile(sources[0]);
221: }
222: }
223:
224: x0 = xStart;
225: }
226:
227: if (x1 >= xEnd) { // region right of warp grid
228: Rectangle rect = srcBounds.intersection(new Rectangle(xEnd,
229: y0, x1 - xEnd + 1, y1 - y0 + 1));
230:
231: if (!rect.isEmpty()) {
232: sources[0] = getSourceImage(0).getData(rect);
233: copyRect(sources, dest, rect);
234:
235: // Recycle the source tile
236: if (getSourceImage(0).overlapsMultipleTiles(rect)) {
237: recycleTile(sources[0]);
238: }
239: }
240:
241: x1 = xEnd - 1;
242: }
243:
244: if (y0 < yStart) { // region above warp grid
245: Rectangle rect = srcBounds.intersection(new Rectangle(x0,
246: y0, x1 - x0 + 1, yStart - y0));
247:
248: if (!rect.isEmpty()) {
249: sources[0] = getSourceImage(0).getData(rect);
250: copyRect(sources, dest, rect);
251:
252: // Recycle the source tile
253: if (getSourceImage(0).overlapsMultipleTiles(rect)) {
254: recycleTile(sources[0]);
255: }
256: }
257:
258: y0 = yStart;
259: }
260:
261: if (y1 >= yEnd) { // region below warp grid
262: Rectangle rect = srcBounds.intersection(new Rectangle(x0,
263: yEnd, x1 - x0 + 1, y1 - yEnd + 1));
264:
265: if (!rect.isEmpty()) {
266: sources[0] = getSourceImage(0).getData(rect);
267: copyRect(sources, dest, rect);
268:
269: // Recycle the source tile
270: if (getSourceImage(0).overlapsMultipleTiles(rect)) {
271: recycleTile(sources[0]);
272: }
273: }
274:
275: y1 = yEnd - 1;
276: }
277:
278: /* The region within the warp grid. */
279: destRect = new Rectangle(x0, y0, x1 - x0 + 1, y1 - y0 + 1);
280:
281: /* Map destination rectangle to source space. */
282: Rectangle srcRect = backwardMapRect(destRect, 0).intersection(
283: srcBounds);
284:
285: if (!srcRect.isEmpty()) {
286: /* Add the interpolation paddings. */
287: int l = interp == null ? 0 : interp.getLeftPadding();
288: int r = interp == null ? 0 : interp.getRightPadding();
289: int t = interp == null ? 0 : interp.getTopPadding();
290: int b = interp == null ? 0 : interp.getBottomPadding();
291:
292: srcRect = new Rectangle(srcRect.x - l, srcRect.y - t,
293: srcRect.width + l + r, srcRect.height + t + b);
294:
295: sources[0] = getBorderExtender() != null ? getSourceImage(0)
296: .getExtendedData(srcRect, extender)
297: : getSourceImage(0).getData(srcRect);
298:
299: computeRect(sources, dest, destRect);
300:
301: // Recycle the source tile
302: if (getSourceImage(0).overlapsMultipleTiles(srcRect)) {
303: recycleTile(sources[0]);
304: }
305: }
306:
307: return dest;
308: }
309:
310: /**
311: * Performs the "grid warp" operation on a rectangular region of
312: * the image.
313: */
314: protected void computeRect(Raster[] sources, WritableRaster dest,
315: Rectangle destRect) {
316: int formatTag = MediaLibAccessor.findCompatibleTag(sources,
317: dest);
318:
319: Raster source = sources[0];
320:
321: MediaLibAccessor srcMA = new MediaLibAccessor(source, source
322: .getBounds(), formatTag);
323: MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect,
324: formatTag);
325:
326: mediaLibImage[] srcMLI = srcMA.getMediaLibImages();
327: mediaLibImage[] dstMLI = dstMA.getMediaLibImages();
328:
329: switch (dstMA.getDataType()) {
330: case DataBuffer.TYPE_BYTE:
331: case DataBuffer.TYPE_USHORT:
332: case DataBuffer.TYPE_SHORT:
333: case DataBuffer.TYPE_INT:
334: if (setBackground)
335: for (int i = 0; i < dstMLI.length; i++) {
336: Image.GridWarp2(dstMLI[i], srcMLI[i], xWarpPos,
337: yWarpPos, source.getMinX(), source
338: .getMinY(), xStart - destRect.x,
339: xStep, xNumCells, yStart - destRect.y,
340: yStep, yNumCells, filter,
341: Constants.MLIB_EDGE_DST_NO_WRITE,
342: intBackgroundValues);
343: }
344: else
345: for (int i = 0; i < dstMLI.length; i++) {
346: Image.GridWarp(dstMLI[i], srcMLI[i], xWarpPos,
347: yWarpPos, source.getMinX(), source
348: .getMinY(), xStart - destRect.x,
349: xStep, xNumCells, yStart - destRect.y,
350: yStep, yNumCells, filter,
351: Constants.MLIB_EDGE_DST_NO_WRITE);
352: MlibUtils.clampImage(dstMLI[i], getColorModel());
353: }
354: break;
355:
356: case DataBuffer.TYPE_FLOAT:
357: case DataBuffer.TYPE_DOUBLE:
358: if (setBackground)
359: for (int i = 0; i < dstMLI.length; i++) {
360: Image.GridWarp2_Fp(dstMLI[i], srcMLI[i], xWarpPos,
361: yWarpPos, source.getMinX(), source
362: .getMinY(), xStart - destRect.x,
363: xStep, xNumCells, yStart - destRect.y,
364: yStep, yNumCells, filter,
365: Constants.MLIB_EDGE_DST_NO_WRITE,
366: backgroundValues);
367: }
368: else
369: for (int i = 0; i < dstMLI.length; i++) {
370: Image.GridWarp_Fp(dstMLI[i], srcMLI[i], xWarpPos,
371: yWarpPos, source.getMinX(), source
372: .getMinY(), xStart - destRect.x,
373: xStep, xNumCells, yStart - destRect.y,
374: yStep, yNumCells, filter,
375: Constants.MLIB_EDGE_DST_NO_WRITE);
376: }
377: break;
378:
379: default:
380: throw new RuntimeException(JaiI18N.getString("Generic2"));
381: }
382:
383: if (dstMA.isDataCopy()) {
384: dstMA.clampDataArrays();
385: dstMA.copyDataToRaster();
386: }
387: }
388:
389: /**
390: * Copies the pixels of a rectangle from source <code>Raster</code>
391: * to destination <code>Raster</code> using mediaLib. This method
392: * is used to copy pixels outside of the warp grid.
393: */
394: private void copyRect(Raster[] sources, WritableRaster dest,
395: Rectangle destRect) {
396: int formatTag = MediaLibAccessor.findCompatibleTag(sources,
397: dest);
398:
399: MediaLibAccessor srcMA = new MediaLibAccessor(sources[0],
400: destRect, formatTag);
401: MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect,
402: formatTag);
403:
404: mediaLibImage[] srcMLI = srcMA.getMediaLibImages();
405: mediaLibImage[] dstMLI = dstMA.getMediaLibImages();
406:
407: for (int i = 0; i < dstMLI.length; i++) {
408: Image.Copy(dstMLI[i], srcMLI[i]);
409: }
410:
411: if (dstMA.isDataCopy()) {
412: dstMA.copyDataToRaster();
413: }
414: }
415: }
|