001: /*
002: * $RCSfile: Warp.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.Rectangle;
015: import java.awt.geom.Point2D;
016: import java.io.Serializable;
017:
018: /**
019: * A description of an image warp.
020: *
021: * <p> The central method of a <code>Warp</code> is
022: * <code>warpSparseRect()</code>, which returns the source pixel positions
023: * for a specified (subdivided) rectangular region of the output.
024: *
025: * <p> As in the <code>Interpolation</code> class, pixel positions are
026: * represented using scaled integer coordinates, yielding subpixel
027: * accuracy but still allowing the use of integer arithmetic. The
028: * degree of precision is set by means of the
029: * <code>getSubSampleBitsH()</code> and <code>getSubSampleBitsV</code>
030: * parameters to the <code>warpRect()</code> method.
031: *
032: * @see Interpolation
033: * @see WarpAffine
034: * @see WarpGrid
035: * @see WarpPerspective
036: * @see WarpPolynomial
037: * @see WarpQuadratic
038: * @see WarpCubic
039: * @see WarpGeneralPolynomial
040: * @see WarpOpImage
041: */
042: public abstract class Warp extends Object implements Serializable {
043:
044: /** Default constructor. */
045: protected Warp() {
046: }
047:
048: /**
049: * Computes the source subpixel positions for a given rectangular
050: * destination region. The destination region is specified using
051: * normal integral (full pixel) coordinates. The source positions
052: * returned by the method are specified in fixed point, subpixel
053: * coordinates using the <code>subsampleBitsH</code> and
054: * <code>subsampleBitsV</code> parameters.
055: *
056: * <p> The integral destination rectangle coordinates should be
057: * considered pixel indices. The continuous plane of pixels
058: * locates each pixel index at a half-pixel location. For example,
059: * destination pixel (0,0) is located at the real location (0.5, 0.5).
060: * Thus pixels are considered to have a dimension of (1.0 x 1.0) with
061: * their "energy" concentrated in a "delta function" at relative
062: * coordinates (0.5, 0.5).
063: *
064: * <p> Destination to source mappings must keep this (0.5, 0.5) pixel
065: * center in mind when formulating transformation functions. Given
066: * integral destination pixel indices as an input, the fractional
067: * source location, as calculated by functions X(xDst,yDst), Y(xDst,yDst)
068: * is given by:
069: * <pre>
070: *
071: * Xsrc = X(xDst+0.5, yDst+0.5) - 0.5
072: * Ysrc = Y(xDst+0.5, yDst+0.5) - 0.5
073: *
074: * </pre>
075: *
076: * <p> The subtraction of 0.5 in the above formula produces the
077: * source pixel indices (in fractional form) needed to implement
078: * the various types of interpolation algorithms.
079: *
080: * <p>All of the Sun-supplied warp mapping functions perform the
081: * above final subtraction, since they have no knowledge of what
082: * interpolation algorithm will be used by a WarpOpImage implementation.
083: *
084: * <p> As a convenience, an implementation is provided for this
085: * method that calls <code>warpSparseRect()</code>. Subclasses
086: * may wish to provide their own implementations for better
087: * performance.
088: *
089: * @param x The minimum X coordinate of the destination region.
090: * @param y The minimum Y coordinate of the destination region.
091: * @param width The width of the destination region. Must be positive.
092: * @param height The height of the destination region. Must be positive.
093: * @param subsampleBitsH The desired fixed-point precision of the
094: * output X coordinates. Must be positive.
095: * @param subsampleBitsV The desired fixed-point precision of the
096: * output Y coordinates. Must be positive.
097: * @param destRect An int array containing at least
098: * <code>2*width*height</code> elements, or
099: * <code>null</code>. If <code>null</code>, a new array
100: * will be constructed.
101: *
102: * @return A reference to the destRect parameter if it is
103: * non-<code>null</code>, or a new <code>int</code> array
104: * of length <code>2*width*height</code> otherwise.
105: */
106: public int[] warpRect(int x, int y, int width, int height,
107: int subsampleBitsH, int subsampleBitsV, int[] destRect) {
108: if (destRect != null && destRect.length < (width * height * 2)) {
109: throw new IllegalArgumentException(JaiI18N
110: .getString("Warp0"));
111: }
112: return warpSparseRect(x, y, width, height, 1, 1,
113: subsampleBitsH, subsampleBitsV, destRect);
114: }
115:
116: /**
117: * Computes the source subpixel positions for a given rectangular
118: * destination region. The destination region is specified using
119: * normal integral (full pixel) coordinates. The source positions
120: * returned by the method are specified in floating point.
121: *
122: * <p> As a convenience, an implementation is provided for this
123: * method that calls <code>warpSparseRect()</code>. Subclasses
124: * may wish to provide their own implementations for better
125: * performance.
126: *
127: * @param x The minimum X coordinate of the destination region.
128: * @param y The minimum Y coordinate of the destination region.
129: * @param width The width of the destination region.
130: * @param height The height of the destination region.
131: * @param destRect A <code>float</code> array containing at least
132: * <code>2*width*height</code> elements, or
133: * <code>null</code>. If <code>null</code>, a new array
134: * will be constructed.
135: *
136: * @return A reference to the <code>destRect</code> parameter if
137: * it is non-<code>null</code>, or a new <code>float</code>
138: * array of length <code>2*width*height</code> otherwise.
139: * @throws IllegalArgumentException if destRect is too small.
140: */
141: public float[] warpRect(int x, int y, int width, int height,
142: float[] destRect) {
143: if (destRect != null && destRect.length < (width * height * 2)) {
144: throw new IllegalArgumentException(JaiI18N
145: .getString("Warp0"));
146: }
147: return warpSparseRect(x, y, width, height, 1, 1, destRect);
148: }
149:
150: /**
151: * Computes the source subpixel position for a given destination
152: * pixel. The destination pixel is specified using normal
153: * integral (full pixel) coordinates. The source position
154: * returned by the method is specified in fixed point, subpixel
155: * coordinates using the <code>subsampleBitsH</code> and
156: * <code>subsampleBitsV</code> parameters.
157: *
158: * <p> As a convenience, an implementation is provided for this
159: * method that calls <code>warpSparseRect()</code>. Subclasses
160: * may wish to provide their own implementations for better
161: * performance.
162: *
163: * @param x The minimum X coordinate of the destination region.
164: * @param y The minimum Y coordinate of the destination region.
165: * @param subsampleBitsH The desired fixed-point precision of the
166: * output X coordinates.
167: * @param subsampleBitsV The desired fixed-point precision of the
168: * output Y coordinates.
169: * @param destRect An <code>int</code> array containing at least 2
170: * elements, or <code>null</code>. If <code>null</code>, a
171: * new array will be constructed.
172: *
173: * @return A reference to the destRect parameter if it is
174: * non-<code>null</code>, or a new <code>int</code> array
175: * of length 2 otherwise.
176: * @throws IllegalArgumentException if destRect is too small.
177: */
178: public int[] warpPoint(int x, int y, int subsampleBitsH,
179: int subsampleBitsV, int[] destRect) {
180: if (destRect != null && destRect.length < 2) {
181: throw new IllegalArgumentException(JaiI18N
182: .getString("Warp0"));
183: }
184: return warpSparseRect(x, y, 1, 1, 1, 1, subsampleBitsH,
185: subsampleBitsV, destRect);
186: }
187:
188: /**
189: * Computes the source subpixel position for a given destination
190: * pixel. The destination pixel is specified using normal
191: * integral (full pixel) coordinates. The source position
192: * returned by the method is specified in floating point.
193: *
194: * <p> As a convenience, an implementation is provided for this
195: * method that calls <code>warpSparseRect()</code>. Subclasses
196: * may wish to provide their own implementations for better
197: * performance.
198: *
199: * @param x The minimum X coordinate of the destination region.
200: * @param y The minimum Y coordinate of the destination region.
201: * @param destRect A <code>float</code> array containing at least
202: * 2 elements, or <code>null</code>. If <code>null</code>,
203: * a new array will be constructed.
204: *
205: * @return A reference to the <code>destRect</code> parameter if
206: * it is non-<code>null</code>, or a new
207: * <code>float</code> array of length 2 otherwise.
208: * @throws IllegalArgumentException if destRect is too small.
209: */
210: public float[] warpPoint(int x, int y, float[] destRect) {
211: if (destRect != null && destRect.length < 2) {
212: throw new IllegalArgumentException(JaiI18N
213: .getString("Warp0"));
214: }
215: return warpSparseRect(x, y, 1, 1, 1, 1, destRect);
216: }
217:
218: /**
219: * Computes the source subpixel positions for a given rectangular
220: * destination region, subsampled with an integral period. The
221: * destination region is specified using normal integral (full
222: * pixel) coordinates. The source positions returned by the
223: * method are specified in fixed point, subpixel coordinates using
224: * the <code>subsampleBitsH</code> and <code>subsampleBitsV</code>
225: * parameters.
226: *
227: * <p> As a convenience, an implementation is provided for this
228: * method that calls <code>warpSparseRect()</code> with a
229: * <code>float</code> <code>destRect</code> parameter. Subclasses
230: * may wish to provide their own implementations for better
231: * performance.
232: *
233: * @param x the minimum X coordinate of the destination region.
234: * @param y the minimum Y coordinate of the destination region.
235: * @param width the width of the destination region.
236: * @param height the height of the destination region.
237: * @param periodX the horizontal sampling period.
238: * @param periodY the horizontal sampling period.
239: * @param subsampleBitsH The desired fixed-point precision of the
240: * output X coordinates.
241: * @param subsampleBitsV The desired fixed-point precision of the
242: * output Y coordinates.
243: * @param destRect An int array containing at least
244: * 2*((width+periodX-1)/periodX)*((height+periodY-1)/periodY)
245: * elements, or <code>null</code>. If <code>null</code>, a
246: * new array will be constructed.
247: *
248: * @return A reference to the <code>destRect</code> parameter if
249: * it is non-<code>null</code>, or a new <code>int</code>
250: * array otherwise.
251: * @throws IllegalArgumentException if destRect is too small.
252: */
253: public int[] warpSparseRect(int x, int y, int width, int height,
254: int periodX, int periodY, int subsampleBitsH,
255: int subsampleBitsV, int[] destRect) {
256: int nVals = 2 * ((width + periodX - 1) / periodX)
257: * ((height + periodY - 1) / periodY);
258: if (destRect != null && destRect.length < nVals) {
259: throw new IllegalArgumentException(JaiI18N
260: .getString("Warp0"));
261: }
262: float[] fdestRect = warpSparseRect(x, y, width, height,
263: periodX, periodY, (float[]) null);
264: int size = fdestRect.length;
265:
266: if (destRect == null) {
267: destRect = new int[size];
268: }
269:
270: int precH = 1 << subsampleBitsH;
271: int precV = 1 << subsampleBitsV;
272:
273: for (int i = 0; i < size; i += 2) {
274: destRect[i] = (int) Math.floor(fdestRect[i] * precH);
275: destRect[i + 1] = (int) Math
276: .floor(fdestRect[i + 1] * precV);
277: }
278:
279: return destRect;
280: }
281:
282: /**
283: * <p> This method is must be implemented in all concrete subclasses.
284: *
285: * @param x The minimum X coordinate of the destination region.
286: * @param y The minimum Y coordinate of the destination region.
287: * @param width The width of the destination region.
288: * @param height The height of the destination region.
289: * @param periodX The horizontal sampling period.
290: * @param periodY The vertical sampling period.
291: *
292: * @param destRect A <code>float</code> array containing at least
293: * <code>2*((width+periodX-1)/periodX)*
294: * ((height+periodY-1)/periodY)</code>
295: * elements, or <code>null</code>. If <code>null</code>, a
296: * new array will be constructed.
297: *
298: * @return a reference to the <code>destRect</code> parameter if
299: * it is non-<code>null</code>, or a new
300: * <code>float</code> array otherwise.
301: */
302: public abstract float[] warpSparseRect(int x, int y, int width,
303: int height, int periodX, int periodY, float[] destRect);
304:
305: /**
306: * Computes a rectangle that is guaranteed to enclose the region
307: * of the destination that can potentially be affected by the
308: * pixels of a rectangle of a given source.
309: * Unlike the corresponding <code>WarpOpImage</code> method,
310: * this routine may return <code>null</code>
311: * if it is infeasible to compute such a bounding box.
312: *
313: * <p> The default implementation in this class returns <code>null</code>.
314: *
315: * @param sourceRect The Rectangle in source coordinates.
316: *
317: * @return A <code>Rectangle</code> in the destination coordinate
318: * system that enclose the region that can potentially be
319: * affected by the pixels of a rectangle of a given source,
320: * or <code>null</code>.
321: */
322: public Rectangle mapSourceRect(Rectangle sourceRect) {
323: return null;
324: }
325:
326: /**
327: * Computes a Rectangle that is guaranteed to enclose the region
328: * of the source that is required in order to produce a given
329: * rectangular output region.
330: *
331: * @param destRect The Rectangle in destination coordinates.
332: *
333: * @return A <code>Rectangle</code> in the source coordinate
334: * system that is guaranteed to contain all pixels
335: * referenced by the output of <code>warpRect()</code> on
336: * the destination region, or <code>null</code>.
337: *
338: * @throws IllegalArgumentException if <code>destRect</code> is
339: * <code>null</code>.
340: */
341: public Rectangle mapDestRect(Rectangle destRect) {
342: if (destRect == null) {
343: throw new IllegalArgumentException(JaiI18N
344: .getString("Generic0"));
345: }
346:
347: int x = destRect.x;
348: int y = destRect.y;
349: int w = destRect.width; // the column immediately to the right
350: int h = destRect.height; // and bottom of the last column
351:
352: // Alloc an array large enough for the largest destRect side
353: float[] warpPoints = new float[Math.max(w * 2, (h - 2) * 2)];
354: ;
355:
356: // Map the pixels along the edges and find their min and max.
357:
358: // Map Top edge.
359: int length = w * 2; // length for top edge
360: warpSparseRect(x, y, w, 1, 1, 1, warpPoints);
361:
362: // initialize min/maxX/Y to first point
363: float minX = warpPoints[0];
364: float maxX = warpPoints[0];
365: float minY = warpPoints[1];
366: float maxY = warpPoints[1];
367:
368: float this X, this Y;
369:
370: for (int i = 2; i < length; i += 2) {
371: this X = warpPoints[i];
372: this Y = warpPoints[i + 1];
373:
374: if (this X < minX) {
375: minX = this X;
376: } else if (this X > maxX) {
377: maxX = this X;
378: }
379:
380: if (this Y < minY) {
381: minY = this Y;
382: } else if (this Y > maxY) {
383: maxY = this Y;
384: }
385: }
386:
387: // Map bottom edge.
388: warpSparseRect(x, y + h - 1, w, 1, 1, 1, warpPoints);
389:
390: for (int i = 0; i < length; i += 2) {
391: this X = warpPoints[i];
392: this Y = warpPoints[i + 1];
393:
394: if (this X < minX) {
395: minX = this X;
396: } else if (this X > maxX) {
397: maxX = this X;
398: }
399:
400: if (this Y < minY) {
401: minY = this Y;
402: } else if (this Y > maxY) {
403: maxY = this Y;
404: }
405: }
406:
407: // Map left edge.
408: length = (h - 2) * 2;
409: warpSparseRect(x, y + 1, 1, h - 2, 1, 1, warpPoints);
410:
411: for (int i = 0; i < length; i += 2) {
412: this X = warpPoints[i];
413: this Y = warpPoints[i + 1];
414:
415: if (this X < minX) {
416: minX = this X;
417: } else if (this X > maxX) {
418: maxX = this X;
419: }
420:
421: if (this Y < minY) {
422: minY = this Y;
423: } else if (this Y > maxY) {
424: maxY = this Y;
425: }
426: }
427:
428: // Map right edge.
429: warpSparseRect(x + w - 1, y + 1, 1, h - 2, 1, 1, warpPoints);
430:
431: for (int i = 0; i < length; i += 2) {
432: this X = warpPoints[i];
433: this Y = warpPoints[i + 1];
434:
435: if (this X < minX) {
436: minX = this X;
437: } else if (this X > maxX) {
438: maxX = this X;
439: }
440:
441: if (this Y < minY) {
442: minY = this Y;
443: } else if (this Y > maxY) {
444: maxY = this Y;
445: }
446: }
447:
448: x = (int) Math.floor(minX);
449: y = (int) Math.floor(minY);
450: w = (int) Math.ceil(maxX - x) + 1;
451: h = (int) Math.ceil(maxY - y) + 1;
452:
453: return new Rectangle(x, y, w, h);
454: }
455:
456: /**
457: * Computes the source point corresponding to the supplied point.
458: *
459: * <p>This method returns the value of <code>pt</code> in the following
460: * code snippet:
461: *
462: * <pre>
463: * float[] sourceXY = warpSparseRect((int)destPt.getX(),
464: * (int)destPt.getY(),
465: * 1, 1, 1, 1, null);
466: * Point2D pt = (Point2D)destPt.clone();
467: * pt.setLocation(sourceXY[0], sourceXY[1]);
468: * </pre>
469: *
470: * Subclasses requiring different behavior should override this
471: * method. This would be the case for those which desire a more
472: * precise mapping.</p>
473: *
474: * @param destPt the position in destination image coordinates
475: * to map to source image coordinates.
476: *
477: * @return a <code>Point2D</code> of the same class as
478: * <code>destPt</code>.
479: *
480: * @throws IllegalArgumentException if <code>destPt</code> is
481: * <code>null</code>.
482: *
483: * @since JAI 1.1.2
484: */
485: public Point2D mapDestPoint(Point2D destPt) {
486: if (destPt == null) {
487: throw new IllegalArgumentException(JaiI18N
488: .getString("Generic0"));
489: }
490:
491: float[] sourceXY = warpSparseRect((int) destPt.getX(),
492: (int) destPt.getY(), 1, 1, 1, 1, null);
493: Point2D pt = (Point2D) destPt.clone();
494: pt.setLocation(sourceXY[0], sourceXY[1]);
495:
496: return pt;
497: }
498:
499: /**
500: * Computes the destination point corresponding to the supplied point.
501: *
502: * <p>This method returns <code>null</code>. Subclasses requiring
503: * different behavior should override this method.</p>
504: *
505: * @param sourcePt the position in source image coordinates
506: * to map to destination image coordinates.
507: *
508: * @return <code>null</code>.
509: *
510: * @throws IllegalArgumentException if <code>sourcePt</code> is
511: * <code>null</code>.
512: *
513: * @since JAI 1.1.2
514: */
515: public Point2D mapSourcePoint(Point2D sourcePt) {
516: if (sourcePt == null) {
517: throw new IllegalArgumentException(JaiI18N
518: .getString("Generic0"));
519: }
520:
521: return null;
522: }
523: }
|