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