001: /*
002: * $RCSfile: WarpCubic.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:
017: /**
018: * A cubic-based description of an image warp.
019: *
020: * <p> The source position (x', y') of a point (x, y) is given by the
021: * cubic polynomial:
022: *
023: * <pre>
024: * x' = p(x, y) = c1 + c2*x + c3*y + c4*x^2 + c5*x*y + c6*y^2 +
025: * c7*x^3 + c8*x^2*y + c9*x*y^2 + c10*y^3
026: * y' = q(x, y) = c11 + c12*x + c13*y + c14*x^2 + c15*x*y + c16*y^2 +
027: * c17*x^3 + c18*x^2*y + c19*x*y^2 + c20*y^3
028: * </pre>
029: *
030: * <p> <code>WarpCubic</code> is marked final so that it may be
031: * more easily inlined.
032: *
033: * @see WarpPolynomial
034: *
035: */
036: public final class WarpCubic extends WarpPolynomial {
037:
038: private float c1, c2, c3, c4, c5, c6, c7, c8, c9, c10;
039: private float c11, c12, c13, c14, c15, c16, c17, c18, c19, c20;
040:
041: /**
042: * Constructs a <code>WarpCubic</code> with a given transform mapping
043: * destination pixels into source space. Note that this is
044: * a backward mapping as opposed to the forward mapping used in
045: * AffineOpImage. The coeffs arrays must each contain 10 floats
046: * corresponding to the coefficients c1, c2, etc. as shown in the
047: * class comment.
048: *
049: * @param xCoeffs The 10 destination to source transform coefficients for
050: * the X coordinate.
051: * @param yCoeffs The 10 destination to source transform coefficients for
052: * the Y coordinate.
053: * @param preScaleX The scale factor to apply to input (dest) X positions.
054: * @param preScaleY The scale factor to apply to input (dest) Y positions.
055: * @param postScaleX The scale factor to apply to the result of the X polynomial evaluation
056: * @param postScaleY The scale factor to apply to the result of the Y polynomial evaluation
057: * @throws IllegalArgumentException if the length of the xCoeffs and yCoeffs arrays are not both 10.
058: */
059: public WarpCubic(float[] xCoeffs, float[] yCoeffs, float preScaleX,
060: float preScaleY, float postScaleX, float postScaleY) {
061: super (xCoeffs, yCoeffs, preScaleX, preScaleY, postScaleX,
062: postScaleY);
063:
064: if (xCoeffs.length != 10 || yCoeffs.length != 10) {
065: throw new IllegalArgumentException(JaiI18N
066: .getString("WarpCubic0"));
067: }
068:
069: c1 = xCoeffs[0]; // x coefficients
070: c2 = xCoeffs[1];
071: c3 = xCoeffs[2];
072: c4 = xCoeffs[3];
073: c5 = xCoeffs[4];
074: c6 = xCoeffs[5];
075: c7 = xCoeffs[6];
076: c8 = xCoeffs[7];
077: c9 = xCoeffs[8];
078: c10 = xCoeffs[9];
079:
080: c11 = yCoeffs[0]; // y coefficients
081: c12 = yCoeffs[1];
082: c13 = yCoeffs[2];
083: c14 = yCoeffs[3];
084: c15 = yCoeffs[4];
085: c16 = yCoeffs[5];
086: c17 = yCoeffs[6];
087: c18 = yCoeffs[7];
088: c19 = yCoeffs[8];
089: c20 = yCoeffs[9];
090: }
091:
092: /**
093: * Constructs a <code>WarpCubic</code> with pre- and post-scale
094: * factors of 1.
095: *
096: * @param xCoeffs The 10 destination to source transform coefficients for
097: * the X coordinate.
098: * @param yCoeffs The 10 destination to source transform coefficients for
099: * the Y coordinate.
100: * @throws IllegalArgumentException if the length of the xCoeffs and yCoeffs arrays are not both 10.
101: */
102: public WarpCubic(float[] xCoeffs, float[] yCoeffs) {
103: this (xCoeffs, yCoeffs, 1.0F, 1.0F, 1.0F, 1.0F);
104: }
105:
106: /**
107: * Computes the source subpixel positions for a given rectangular
108: * destination region, subsampled with an integral period. The
109: * destination region is specified using normal integral (full
110: * pixel) coordinates. The source positions returned by the
111: * method are specified in floating point.
112: *
113: * @param x The minimum X coordinate of the destination region.
114: * @param y The minimum Y coordinate of the destination region.
115: * @param width The width of the destination region.
116: * @param height The height of the destination region.
117: * @param periodX The horizontal sampling period.
118: * @param periodY The vertical sampling period.
119: *
120: * @param destRect A <code>float</code> array containing at least
121: * <code>2*((width+periodX-1)/periodX)*
122: * ((height+periodY-1)/periodY)</code>
123: * elements, or <code>null</code>. If <code>null</code>, a
124: * new array will be constructed.
125: *
126: * @return A reference to the <code>destRect</code> parameter if
127: * it is non-<code>null</code>, or a new
128: * <code>float</code> array otherwise.
129: * @throws ArrayBoundsException if destRect is too small
130: */
131: public float[] warpSparseRect(int x, int y, int width, int height,
132: int periodX, int periodY, float[] destRect) {
133:
134: // XXX: This method should do its calculations in doubles
135:
136: if (destRect == null) {
137: destRect = new float[((width + periodX - 1) / periodX)
138: * ((height + periodY - 1) / periodY) * 2];
139: }
140:
141: //
142: // x' = c1 + c2*x + c3*y + c4*x^2 + c5*x*y + c6*y^2 +
143: // c7*x^3 + c8*x^2*y + c9*x*y^2 + c10*y^3
144: // y' = c11 + c12*x + c13*y + c14*x^2 + c15*x*y + c16*y^2 +
145: // c17*x^3 + c18*x^2*y + c19*x*y^2 + c20*y^3
146: //
147:
148: float px1 = periodX * preScaleX; // power for periodX
149: float px2 = px1 * px1;
150: float px3 = px2 * px1;
151:
152: // Delta delta delta x and delta delta delta y
153: float dddx = c7 * 6 * px3;
154: float dddy = c17 * 6 * px3;
155:
156: float x1 = (x + 0.5F) * preScaleX; // power for x
157: float x2 = x1 * x1;
158: float x3 = x2 * x1;
159:
160: width += x;
161: height += y;
162: int index = 0;
163:
164: for (int j = y; j < height; j += periodY) {
165: float y1 = (j + 0.5F) * preScaleY; // power for the current y
166: float y2 = y1 * y1;
167: float y3 = y2 * y1;
168:
169: // The warped position for the first point of the current line
170: float wx = c1 + c2 * x1 + c3 * y1 + c4 * x2 + c5 * x1 * y1
171: + c6 * y2 + c7 * x3 + c8 * x2 * y1 + c9 * x1 * y2
172: + c10 * y3;
173: float wy = c11 + c12 * x1 + c13 * y1 + c14 * x2 + c15 * x1
174: * y1 + c16 * y2 + c17 * x3 + c18 * x2 * y1 + c19
175: * x1 * y2 + c20 * y3;
176:
177: // Delta x and delta y
178: float dx = c2 * px1 + c4 * (2 * x1 * px1 + px2) + c5 * px1
179: * y1 + c7 * (3 * x2 * px1 + 3 * x1 * px2 + px3)
180: + c8 * (2 * x1 * px1 + px2) * y1 + c9 * px1 * y2;
181: float dy = c12 * px1 + c14 * (2 * x1 * px1 + px2) + c15
182: * px1 * y1 + c17
183: * (3 * x2 * px1 + 3 * x1 * px2 + px3) + c18
184: * (2 * x1 * px1 + px2) * y1 + c19 * px1 * y2;
185:
186: // Delta delta x and delta delta y
187: float ddx = c4 * 2 * px2 + c7 * (6 * x1 * px2 + 6 * px3)
188: + c8 * 2 * px2 * y1;
189: float ddy = c14 * 2 * px2 + c17 * (6 * x1 * px2 + 6 * px3)
190: + c18 * 2 * px2 * y1;
191:
192: for (int i = x; i < width; i += periodX) {
193: destRect[index++] = wx * postScaleX - 0.5F;
194: destRect[index++] = wy * postScaleY - 0.5F;
195:
196: wx += dx;
197: wy += dy;
198: dx += ddx;
199: dy += ddy;
200: ddx += dddx;
201: ddy += dddy;
202: }
203: }
204:
205: return destRect;
206: }
207:
208: /**
209: * Computes the source point corresponding to the supplied point.
210: *
211: * <p>This method returns the value of <code>pt</code> in the following
212: * code snippet:
213: *
214: * <pre>
215: * double x1 = (destPt.getX() + 0.5F)*preScaleX;
216: * double x2 = x1*x1;
217: * double x3 = x2*x1;
218: *
219: * double y1 = (destPt.getY() + 0.5F)*preScaleY;
220: * double y2 = y1*y1;
221: * double y3 = y2*y1;
222: *
223: * double sx = c1 + c2*x1 + c3*y1 +
224: * c4*x2 + c5*x1*y1 + c6*y2 +
225: * c7*x3 + c8*x2*y1 + c9*x1*y2 + c10*y3;
226: * double sy = c11 + c12*x1 + c13*y1 +
227: * c14*x2 + c15*x1*y1 + c16*y2 +
228: * c17*x3 + c18*x2*y1 + c19*x1*y2 + c20*y3;
229: *
230: * Point2D pt = (Point2D)destPt.clone();
231: * pt.setLocation(sx*postScaleX - 0.5, sy*postScaleY - 0.5);
232: * </pre>
233: * </p>
234: *
235: * @param destPt the position in destination image coordinates
236: * to map to source image coordinates.
237: *
238: * @return a <code>Point2D</code> of the same class as
239: * <code>destPt</code>.
240: *
241: * @throws IllegalArgumentException if <code>destPt</code> is
242: * <code>null</code>.
243: *
244: * @since JAI 1.1.2
245: */
246: public Point2D mapDestPoint(Point2D destPt) {
247: if (destPt == null) {
248: throw new IllegalArgumentException(JaiI18N
249: .getString("Generic0"));
250: }
251:
252: double x1 = (destPt.getX() + 0.5F) * preScaleX;
253: double x2 = x1 * x1;
254: double x3 = x2 * x1;
255:
256: double y1 = (destPt.getY() + 0.5F) * preScaleY;
257: double y2 = y1 * y1;
258: double y3 = y2 * y1;
259:
260: double sx = c1 + c2 * x1 + c3 * y1 + c4 * x2 + c5 * x1 * y1
261: + c6 * y2 + c7 * x3 + c8 * x2 * y1 + c9 * x1 * y2 + c10
262: * y3;
263: double sy = c11 + c12 * x1 + c13 * y1 + c14 * x2 + c15 * x1
264: * y1 + c16 * y2 + c17 * x3 + c18 * x2 * y1 + c19 * x1
265: * y2 + c20 * y3;
266:
267: Point2D pt = (Point2D) destPt.clone();
268: pt.setLocation(sx * postScaleX - 0.5, sy * postScaleY - 0.5);
269:
270: return pt;
271: }
272: }
|