001: /*
002: * $RCSfile: MlibWarpPolynomialOpImage.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:48 $
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.WarpPolynomial;
027:
028: import com.sun.medialib.mlib.*;
029: import com.sun.media.jai.util.ImageUtil;
030:
031: /**
032: * An <code>OpImage</code> implementing the polynomial "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 MlibWarpPolynomialOpImage extends WarpOpImage {
054:
055: /** The x and y coefficients. */
056: private double[] xCoeffs;
057: private double[] yCoeffs;
058:
059: /**
060: * Indicates what kind of interpolation to use; may be
061: * <code>Constants.MLIB_NEAREST</code>,
062: * <code>Constants.MLIB_BILINEAR</code>,
063: * or <code>Constants.MLIB_BICUBIC</code>,
064: * and was determined in <code>MlibWarpRIF.create()</code>.
065: */
066: private int filter;
067:
068: /** The pre and post scale factors. */
069: private double preScaleX;
070: private double preScaleY;
071: private double postScaleX;
072: private double postScaleY;
073:
074: /**
075: * Constructs a <code>MlibWarpPolynomialOpImage</code>.
076: *
077: * @param source The source image.
078: * @param layout The destination image layout.
079: * @param warp An object defining the warp algorithm.
080: * @param interp An object describing the interpolation method.
081: */
082: public MlibWarpPolynomialOpImage(RenderedImage source,
083: BorderExtender extender, Map config, ImageLayout layout,
084: WarpPolynomial warp, Interpolation interp, int filter,
085: double[] backgroundValues) {
086: super (source, layout, config, true, extender, interp, warp,
087: backgroundValues);
088:
089: float[] xc = warp.getXCoeffs();
090: float[] yc = warp.getYCoeffs();
091: int size = xc.length;
092:
093: xCoeffs = new double[size]; // X and Y coefficients as doubles
094: yCoeffs = new double[size];
095: for (int i = 0; i < size; i++) {
096: xCoeffs[i] = xc[i];
097: yCoeffs[i] = yc[i];
098: }
099:
100: this .filter = filter; // interpolation
101:
102: preScaleX = warp.getPreScaleX(); // pre/post factors
103: preScaleY = warp.getPreScaleY();
104: postScaleX = warp.getPostScaleX();
105: postScaleY = warp.getPostScaleY();
106: }
107:
108: /**
109: * Returns the minimum bounding box of the region of the specified
110: * source to which a particular <code>Rectangle</code> of the
111: * destination will be mapped.
112: *
113: * @param destRect the <code>Rectangle</code> in destination coordinates.
114: * @param sourceIndex the index of the source image.
115: *
116: * @return a <code>Rectangle</code> indicating the source bounding box,
117: * or <code>null</code> if the bounding box is unknown.
118: *
119: * @throws IllegalArgumentException if <code>sourceIndex</code> is
120: * negative or greater than the index of the last source.
121: * @throws IllegalArgumentException if <code>destRect</code> is
122: * <code>null</code>.
123: */
124: protected Rectangle backwardMapRect(Rectangle destRect,
125: int sourceIndex) {
126: // Superclass method will throw documented exceptions if needed.
127: Rectangle wrect = super .backwardMapRect(destRect, sourceIndex);
128:
129: // "Dilate" the backwarp mapped rectangle to account for
130: // the lack of being able to know the floating point result of
131: // mapDestRect() and to mimic what is done in AffineOpImage.
132: // See bug 4518223 for more information.
133: wrect.setBounds(wrect.x - 1, wrect.y - 1, wrect.width + 2,
134: wrect.height + 2);
135:
136: return wrect;
137: }
138:
139: /**
140: * Computes a tile. A new <code>WritableRaster</code> is created to
141: * represent the requested tile. Its width and height equals to this
142: * image's tile width and tile height respectively. If the requested
143: * tile lies outside of the image's boundary, the created raster is
144: * returned with all of its pixels set to 0.
145: *
146: * <p> This method overrides the method in <code>WarpOpImage</code>
147: * and performs source cobbling when necessary. MediaLib is used to
148: * calculate the actual warping.
149: *
150: * @param tileX The X index of the tile.
151: * @param tileY The Y index of the tile.
152: *
153: * @return The tile as a <code>Raster</code>.
154: */
155: public Raster computeTile(int tileX, int tileY) {
156: /* The origin of the tile. */
157: Point org = new Point(tileXToX(tileX), tileYToY(tileY));
158:
159: /* Create a new WritableRaster to represent this tile. */
160: WritableRaster dest = createWritableRaster(sampleModel, org);
161:
162: /* Find the intersection between this tile and the writable bounds. */
163: Rectangle rect = new Rectangle(org.x, org.y, tileWidth,
164: tileHeight);
165: Rectangle destRect = rect.intersection(computableBounds);
166: Rectangle destRect1 = rect.intersection(getBounds());
167: if (destRect.isEmpty()) {
168: if (setBackground) {
169: ImageUtil.fillBackground(dest, destRect1,
170: backgroundValues);
171: }
172: return dest; // tile completely outside of writable bounds
173: }
174:
175: /* Map destination rectangle to source space. */
176: Rectangle srcRect = backwardMapRect(destRect, 0).intersection(
177: getSourceImage(0).getBounds());
178:
179: if (srcRect.isEmpty()) {
180: if (setBackground) {
181: ImageUtil.fillBackground(dest, destRect1,
182: backgroundValues);
183: }
184: return dest; // outside of source bounds
185: }
186:
187: if (!destRect1.equals(destRect)) {
188: // beware that destRect1 contains destRect
189: ImageUtil.fillBordersWithBackgroundValues(destRect1,
190: destRect, dest, backgroundValues);
191: }
192:
193: /* Add the interpolation paddings. */
194: int l = interp == null ? 0 : interp.getLeftPadding();
195: int r = interp == null ? 0 : interp.getRightPadding();
196: int t = interp == null ? 0 : interp.getTopPadding();
197: int b = interp == null ? 0 : interp.getBottomPadding();
198:
199: srcRect = new Rectangle(srcRect.x - l, srcRect.y - t,
200: srcRect.width + l + r, srcRect.height + t + b);
201:
202: /* Cobble source into one Raster. */
203: Raster[] sources = new Raster[1];
204: sources[0] = getBorderExtender() != null ? getSourceImage(0)
205: .getExtendedData(srcRect, extender) : getSourceImage(0)
206: .getData(srcRect);
207:
208: computeRect(sources, dest, destRect);
209:
210: // Recycle the source tile
211: if (getSourceImage(0).overlapsMultipleTiles(srcRect)) {
212: recycleTile(sources[0]);
213: }
214:
215: return dest;
216: }
217:
218: /**
219: * Performs the "Warp" operation on a rectangular region of
220: * the same.
221: */
222: protected void computeRect(Raster[] sources, WritableRaster dest,
223: Rectangle destRect) {
224: Raster source = sources[0];
225:
226: /* Find the mediaLib data tag. */
227: int formatTag = MediaLibAccessor.findCompatibleTag(sources,
228: dest);
229:
230: MediaLibAccessor srcMA = new MediaLibAccessor(source, source
231: .getBounds(), formatTag);
232: MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect,
233: formatTag);
234:
235: mediaLibImage[] srcMLI = srcMA.getMediaLibImages();
236: mediaLibImage[] dstMLI = dstMA.getMediaLibImages();
237:
238: switch (dstMA.getDataType()) {
239: case DataBuffer.TYPE_BYTE:
240: case DataBuffer.TYPE_USHORT:
241: case DataBuffer.TYPE_SHORT:
242: case DataBuffer.TYPE_INT:
243: if (setBackground)
244: for (int i = 0; i < dstMLI.length; i++) {
245: Image.PolynomialWarp2(dstMLI[i], srcMLI[i],
246: xCoeffs, yCoeffs, destRect.x, destRect.y,
247: source.getMinX(), source.getMinY(),
248: preScaleX, preScaleY, postScaleX,
249: postScaleY, filter,
250: Constants.MLIB_EDGE_DST_NO_WRITE,
251: intBackgroundValues);
252: }
253: else
254: for (int i = 0; i < dstMLI.length; i++) {
255: Image.PolynomialWarp(dstMLI[i], srcMLI[i], xCoeffs,
256: yCoeffs, destRect.x, destRect.y, source
257: .getMinX(), source.getMinY(),
258: preScaleX, preScaleY, postScaleX,
259: postScaleY, filter,
260: Constants.MLIB_EDGE_DST_NO_WRITE);
261: MlibUtils.clampImage(dstMLI[i], getColorModel());
262: }
263: break;
264:
265: case DataBuffer.TYPE_FLOAT:
266: case DataBuffer.TYPE_DOUBLE:
267: if (setBackground)
268: for (int i = 0; i < dstMLI.length; i++) {
269: Image.PolynomialWarp2_Fp(dstMLI[i], srcMLI[i],
270: xCoeffs, yCoeffs, destRect.x, destRect.y,
271: source.getMinX(), source.getMinY(),
272: preScaleX, preScaleY, postScaleX,
273: postScaleY, filter,
274: Constants.MLIB_EDGE_DST_NO_WRITE,
275: backgroundValues);
276: }
277: else
278: for (int i = 0; i < dstMLI.length; i++) {
279: Image.PolynomialWarp_Fp(dstMLI[i], srcMLI[i],
280: xCoeffs, yCoeffs, destRect.x, destRect.y,
281: source.getMinX(), source.getMinY(),
282: preScaleX, preScaleY, postScaleX,
283: postScaleY, filter,
284: Constants.MLIB_EDGE_DST_NO_WRITE);
285: }
286: break;
287:
288: default:
289: throw new RuntimeException(JaiI18N.getString("Generic2"));
290: }
291:
292: if (dstMA.isDataCopy()) {
293: dstMA.clampDataArrays();
294: dstMA.copyDataToRaster();
295: }
296: }
297: }
|