1 /*
2  * $RCSfile: PerspectiveTransform.java,v $
3  *
4  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
5  *
6  * Use is subject to license terms.
7  *
8  * $Revision: 1.1 $
9  * $Date: 2005/02/11 04:57:15 $
10  * $State: Exp $
11  */
12 package com.lightcrafts.mediax.jai;
13 import java.awt.geom.AffineTransform;
14 import java.awt.geom.Point2D;
15 import java.awt.geom.NoninvertibleTransformException;
16 import java.io.Serializable;
17 
18 
19 /**
20  * A 2D perspective (or projective) transform, used by various OpImages.
21  *
22  * <p> A perspective transformation is capable of mapping an arbitrary
23  * quadrilateral into another arbitrary quadrilateral, while
24  * preserving the straightness of lines.  Unlike an affine
25  * transformation, the parallelism of lines in the source is not
26  * necessarily preserved in the output.
27  *
28  * <p> Such a coordinate transformation can be represented by a 3x3
29  * matrix which transforms homogenous source coordinates
30  * <code>(x,&nbsp;y,&nbsp;1)</code> into destination coordinates
31  * <code>(x',&nbsp;y',&nbsp;w)</code>.  To convert back into non-homogenous
32  * coordinates (X, Y), <code>x'</code> and <code>y'</code> are divided by
33  * <code>w</code>.
34  *
35  * <pre>
36  *	[ x']   [  m00  m01  m02  ] [ x ]   [ m00x + m01y + m02 ]
37  *	[ y'] = [  m10  m11  m12  ] [ y ] = [ m10x + m11y + m12 ]
38  *	[ w ]   [  m20  m21  m22  ] [ 1 ]   [ m20x + m21y + m22 ]
39  *
40  *	  x' = (m00x + m01y + m02)
41  *	  y' = (m10x + m11y + m12)
42  *
43  *        w  = (m20x + m21y + m22)
44  *
45  *        X = x' / w
46  *        Y = y' / w
47  * </pre>
48  */
49 public final class PerspectiveTransform implements Cloneable, Serializable {
50 
51     private static final double PERSPECTIVE_DIVIDE_EPSILON = 1.0e-10;
52 
53     /** An element of the transform matrix. */
54     double m00, m01, m02, m10, m11, m12, m20, m21, m22;
55 
56     /** Constructs an identity PerspectiveTransform. */
PerspectiveTransform()57     public PerspectiveTransform() {
58         m00 = m11 = m22 = 1.0;
59         m01 = m02 = m10 = m12 = m20 = m21 = 0.0;
60     }
61 
62     /**
63      * Constructs a new PerspectiveTransform from 9 floats.
64      * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead.
65      */
PerspectiveTransform(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22)66     public PerspectiveTransform(float m00, float m01, float m02,
67                                 float m10, float m11, float m12,
68                                 float m20, float m21, float m22) {
69         this.m00 = m00;
70         this.m01 = m01;
71         this.m02 = m02;
72         this.m10 = m10;
73         this.m11 = m11;
74         this.m12 = m12;
75         this.m20 = m20;
76         this.m21 = m21;
77         this.m22 = m22;
78     }
79 
80     /**
81      * Constructs a new PerspectiveTransform from 9 doubles.
82      * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead.
83      */
PerspectiveTransform(double m00, double m01, double m02, double m10, double m11, double m12, double m20, double m21, double m22)84     public PerspectiveTransform(double m00, double m01, double m02,
85                                 double m10, double m11, double m12,
86                                 double m20, double m21, double m22) {
87         this.m00 = m00;
88         this.m01 = m01;
89         this.m02 = m02;
90         this.m10 = m10;
91         this.m11 = m11;
92         this.m12 = m12;
93         this.m20 = m20;
94         this.m21 = m21;
95         this.m22 = m22;
96     }
97 
98     /**
99      * Constructs a new PerspectiveTransform from a one-dimensional
100      * array of 9 floats, in row-major order.
101      * The values in the array are assumed to be
102      * { m00 m01 m02 m10 m11 m12 m20 m21 m22 }.
103      * @throws IllegalArgumentException if flatmatrix is null
104      * @throws ArrayIndexOutOfBoundsException if flatmatrix is too small
105      * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead.
106      */
PerspectiveTransform(float[] flatmatrix)107     public PerspectiveTransform(float[] flatmatrix) {
108         if ( flatmatrix == null ) {
109             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
110         }
111 
112         m00 = flatmatrix[0];
113         m01 = flatmatrix[1];
114         m02 = flatmatrix[2];
115         m10 = flatmatrix[3];
116         m11 = flatmatrix[4];
117         m12 = flatmatrix[5];
118         m20 = flatmatrix[6];
119         m21 = flatmatrix[7];
120         m22 = flatmatrix[8];
121     }
122 
123     /**
124      * Constructs a new PerspectiveTransform from a two-dimensional
125      * array of floats.
126      * @throws IllegalArgumentException if matrix is null
127      * @throws ArrayIndexOutOfBoundsException if matrix is too small
128      *
129      * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead.
130      */
PerspectiveTransform(float[][] matrix)131     public PerspectiveTransform(float[][] matrix) {
132         if ( matrix == null ) {
133             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
134         }
135 
136         m00 = matrix[0][0];
137         m01 = matrix[0][1];
138         m02 = matrix[0][2];
139         m10 = matrix[1][0];
140         m11 = matrix[1][1];
141         m12 = matrix[1][2];
142         m20 = matrix[2][0];
143         m21 = matrix[2][1];
144         m22 = matrix[2][2];
145     }
146 
147     /**
148      * Constructs a new PerspectiveTransform from a one-dimensional
149      * array of 9 doubles, in row-major order.
150      * The values in the array are assumed to be
151      * { m00 m01 m02 m10 m11 m12 m20 m21 m22 }.
152      * @throws IllegalArgumentException if flatmatrix is null
153      * @throws ArrayIndexOutOfBoundsException if flatmatrix is too small
154      *
155      * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead.
156      */
PerspectiveTransform(double[] flatmatrix)157     public PerspectiveTransform(double[] flatmatrix) {
158         if ( flatmatrix == null ) {
159             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
160         }
161 
162         m00 = flatmatrix[0];
163         m01 = flatmatrix[1];
164         m02 = flatmatrix[2];
165         m10 = flatmatrix[3];
166         m11 = flatmatrix[4];
167         m12 = flatmatrix[5];
168         m20 = flatmatrix[6];
169         m21 = flatmatrix[7];
170         m22 = flatmatrix[8];
171     }
172 
173     /**
174      * Constructs a new PerspectiveTransform from a two-dimensional
175      * array of doubles.
176      * @throws IllegalArgumentException if matrix is null
177      * @throws ArrayIndexOutOfBoundsException if matrix is too small
178      */
PerspectiveTransform(double[][] matrix)179     public PerspectiveTransform(double[][] matrix) {
180         if ( matrix == null ) {
181             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
182         }
183 
184         m00 = matrix[0][0];
185         m01 = matrix[0][1];
186         m02 = matrix[0][2];
187         m10 = matrix[1][0];
188         m11 = matrix[1][1];
189         m12 = matrix[1][2];
190         m20 = matrix[2][0];
191         m21 = matrix[2][1];
192         m22 = matrix[2][2];
193     }
194 
195     /**
196      * Constructs a new PerspectiveTransform with the same effect
197      * as an existing AffineTransform.
198      * @throws IllegalArgumentException if transform is null
199      */
PerspectiveTransform(AffineTransform transform)200     public PerspectiveTransform(AffineTransform transform) {
201         if ( transform == null ) {
202             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
203         }
204 
205         m00 = transform.getScaleX();
206         m01 = transform.getShearX();
207         m02 = transform.getTranslateX();
208         m10 = transform.getShearY();
209         m11 = transform.getScaleY();
210         m12 = transform.getTranslateY();
211         m20 = 0.0;
212         m21 = 0.0;
213         m22 = 1.0;
214     }
215 
216     /**
217      * Replaces the matrix with its adjoint.
218      */
makeAdjoint()219     private final void makeAdjoint() {
220         double m00p = m11*m22 - m12*m21;
221         double m01p = m12*m20 - m10*m22; // flipped sign
222         double m02p = m10*m21 - m11*m20;
223         double m10p = m02*m21 - m01*m22; // flipped sign
224         double m11p = m00*m22 - m02*m20;
225         double m12p = m01*m20 - m00*m21; // flipped sign
226         double m20p = m01*m12 - m02*m11;
227         double m21p = m02*m10 - m00*m12; // flipped sign
228         double m22p = m00*m11 - m01*m10;
229 
230         // Transpose and copy sub-determinants
231         m00 = m00p;
232         m01 = m10p;
233         m02 = m20p;
234         m10 = m01p;
235         m11 = m11p;
236         m12 = m21p;
237         m20 = m02p;
238         m21 = m12p;
239         m22 = m22p;
240     }
241 
242     /**
243      * Scales the matrix elements so m22 is equal to 1.0.
244      * m22 must not be equal to 0.
245      */
normalize()246     private final void normalize() {
247         double invscale = 1.0/m22;
248         m00 *= invscale;
249         m01 *= invscale;
250         m02 *= invscale;
251         m10 *= invscale;
252         m11 *= invscale;
253         m12 *= invscale;
254         m20 *= invscale;
255         m21 *= invscale;
256         m22 = 1.0;
257     }
258 
getSquareToQuad(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, PerspectiveTransform tx)259     private static final void getSquareToQuad(double x0, double y0,
260                                               double x1, double y1,
261                                               double x2, double y2,
262                                               double x3, double y3,
263                                               PerspectiveTransform tx) {
264         double dx3 = x0 - x1 + x2 - x3;
265         double dy3 = y0 - y1 + y2 - y3;
266 
267         tx.m22 = 1.0F;
268 
269         if ((dx3 == 0.0F) && (dy3 == 0.0F)) { // to do: use tolerance
270             tx.m00 = x1 - x0;
271             tx.m01 = x2 - x1;
272             tx.m02 = x0;
273             tx.m10 = y1 - y0;
274             tx.m11 = y2 - y1;
275             tx.m12 = y0;
276             tx.m20 = 0.0F;
277             tx.m21 = 0.0F;
278         } else {
279             double dx1 = x1 - x2;
280             double dy1 = y1 - y2;
281             double dx2 = x3 - x2;
282             double dy2 = y3 - y2;
283 
284             double invdet = 1.0F/(dx1*dy2 - dx2*dy1);
285             tx.m20 = (dx3*dy2 - dx2*dy3)*invdet;
286             tx.m21 = (dx1*dy3 - dx3*dy1)*invdet;
287             tx.m00 = x1 - x0 + tx.m20*x1;
288             tx.m01 = x3 - x0 + tx.m21*x3;
289             tx.m02 = x0;
290             tx.m10 = y1 - y0 + tx.m20*y1;
291             tx.m11 = y3 - y0 + tx.m21*y3;
292             tx.m12 = y0;
293         }
294     }
295 
296     /**
297      * Creates a PerspectiveTransform that maps the unit square
298      * onto an arbitrary quadrilateral.
299      *
300      * <pre>
301      * (0, 0) -> (x0, y0)
302      * (1, 0) -> (x1, y1)
303      * (1, 1) -> (x2, y2)
304      * (0, 1) -> (x3, y3)
305      * </pre>
306      */
getSquareToQuad(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3)307     public static PerspectiveTransform getSquareToQuad(double x0, double y0,
308                                                        double x1, double y1,
309                                                        double x2, double y2,
310                                                        double x3, double y3) {
311         PerspectiveTransform tx = new PerspectiveTransform();
312         getSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3, tx);
313         return tx;
314     }
315 
316 
317     /**
318      * Creates a PerspectiveTransform that maps the unit square
319      * onto an arbitrary quadrilateral.
320      *
321      * <pre>
322      * (0, 0) -> (x0, y0)
323      * (1, 0) -> (x1, y1)
324      * (1, 1) -> (x2, y2)
325      * (0, 1) -> (x3, y3)
326      * </pre>
327      */
getSquareToQuad(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3)328     public static PerspectiveTransform getSquareToQuad(float x0, float y0,
329                                                        float x1, float y1,
330                                                        float x2, float y2,
331                                                        float x3, float y3) {
332         return getSquareToQuad((double)x0, (double)y0,
333                                (double)x1, (double)y1,
334                                (double)x2, (double)y2,
335                                (double)x3, (double)y3);
336     }
337 
338 
339     /**
340      * Creates a PerspectiveTransform that maps an arbitrary
341      * quadrilateral onto the unit square.
342      *
343      * <pre>
344      * (x0, y0) -> (0, 0)
345      * (x1, y1) -> (1, 0)
346      * (x2, y2) -> (1, 1)
347      * (x3, y3) -> (0, 1)
348      * </pre>
349      */
getQuadToSquare(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3)350     public static PerspectiveTransform getQuadToSquare(double x0, double y0,
351                                                        double x1, double y1,
352                                                        double x2, double y2,
353                                                        double x3, double y3) {
354         PerspectiveTransform tx = new PerspectiveTransform();
355         getSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3, tx);
356         tx.makeAdjoint();
357         return tx;
358     }
359 
360     /**
361      * Creates a PerspectiveTransform that maps an arbitrary
362      * quadrilateral onto the unit square.
363      *
364      * <pre>
365      * (x0, y0) -> (0, 0)
366      * (x1, y1) -> (1, 0)
367      * (x2, y2) -> (1, 1)
368      * (x3, y3) -> (0, 1)
369      * </pre>
370      */
getQuadToSquare(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3)371     public static PerspectiveTransform getQuadToSquare(float x0, float y0,
372                                                        float x1, float y1,
373                                                        float x2, float y2,
374                                                        float x3, float y3) {
375         return getQuadToSquare((double)x0, (double)y0,
376                                (double)x1, (double)y1,
377                                (double)x2, (double)y2,
378                                (double)x3, (double)y3);
379     }
380 
381     /**
382      * Creates a PerspectiveTransform that maps an arbitrary
383      * quadrilateral onto another arbitrary quadrilateral.
384      *
385      * <pre>
386      * (x0, y0) -> (x0p, y0p)
387      * (x1, y1) -> (x1p, y1p)
388      * (x2, y2) -> (x2p, y2p)
389      * (x3, y3) -> (x3p, y3p)
390      * </pre>
391      */
getQuadToQuad(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double x0p, double y0p, double x1p, double y1p, double x2p, double y2p, double x3p, double y3p)392     public static PerspectiveTransform getQuadToQuad(double x0, double y0,
393                                                      double x1, double y1,
394                                                      double x2, double y2,
395                                                      double x3, double y3,
396                                                      double x0p, double y0p,
397                                                      double x1p, double y1p,
398                                                      double x2p, double y2p,
399                                                      double x3p, double y3p) {
400         PerspectiveTransform tx1 =
401                           getQuadToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
402 
403         PerspectiveTransform tx2 =
404                   getSquareToQuad(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
405 
406         tx1.concatenate(tx2);
407         return tx1;
408     }
409 
410 
411     /**
412      * Creates a PerspectiveTransform that maps an arbitrary
413      * quadrilateral onto another arbitrary quadrilateral.
414      *
415      * <pre>
416      * (x0, y0) -> (x0p, y0p)
417      * (x1, y1) -> (x1p, y1p)
418      * (x2, y2) -> (x2p, y2p)
419      * (x3, y3) -> (x3p, y3p)
420      * </pre>
421      */
getQuadToQuad(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float x0p, float y0p, float x1p, float y1p, float x2p, float y2p, float x3p, float y3p)422     public static PerspectiveTransform getQuadToQuad(float x0, float y0,
423                                                      float x1, float y1,
424                                                      float x2, float y2,
425                                                      float x3, float y3,
426                                                      float x0p, float y0p,
427                                                      float x1p, float y1p,
428                                                      float x2p, float y2p,
429                                                      float x3p, float y3p) {
430         return getQuadToQuad((double)x0, (double)y0,
431                               (double)x1, (double)y1,
432                               (double)x2, (double)y2,
433                               (double)x3, (double)y3,
434                               (double)x0p, (double)y0p,
435                               (double)x1p, (double)y1p,
436                               (double)x2p, (double)y2p,
437                               (double)x3p, (double)y3p);
438     }
439 
440     /**
441      * Returns the determinant of the matrix representation of the
442      * transform.
443      */
getDeterminant()444     public double getDeterminant() {
445 	return ( (m00 * ((m11 * m22) - (m12 * m21))) -
446                  (m01 * ((m10 * m22) - (m12 * m20))) +
447                  (m02 * ((m10 * m21) - (m11 * m20))) );
448 
449     }
450 
451     /**
452      * Retrieves the 9 specifiable values in the 3x3 affine
453      * transformation matrix into an array of double precision values.
454      * The values are stored into the array as
455      * { m00 m01 m02 m10 m11 m12 m20 m21 m22 }.
456      *
457      * @param flatmatrix The double array used to store the returned
458      *        values.  The length of the array is assumed to be at
459      *        least 9.
460      * @throws ArrayIndexOutOfBoundsException if flatmatrix is too small
461      * @deprecated as of JAI 1.1 Use double[][] getMatrix(double[][] matrix) instead.
462      */
getMatrix(double[] flatmatrix)463     public double[] getMatrix(double[] flatmatrix) {
464         if (flatmatrix == null) {
465             flatmatrix = new double[9];
466         }
467 
468         flatmatrix[0] = m00;
469         flatmatrix[1] = m01;
470         flatmatrix[2] = m02;
471         flatmatrix[3] = m10;
472         flatmatrix[4] = m11;
473         flatmatrix[5] = m12;
474         flatmatrix[6] = m20;
475         flatmatrix[7] = m21;
476         flatmatrix[8] = m22;
477 
478         return flatmatrix;
479     }
480 
481     /**
482      * Retrieves the 9 specifiable values in the 3x3 affine
483      * transformation matrix into a 2-dimensional array of double
484      * precision values.  The values are stored into the 2-dimensional
485      * array using the row index as the first subscript and the column
486      * index as the second.
487      *
488      * @param matrix The 2-dimensional double array to store the
489      *        returned values.  The array is assumed to be at least 3x3.
490      * @throws ArrayIndexOutOfBoundsException if matrix is too small
491      */
getMatrix(double[][] matrix)492     public double[][] getMatrix(double[][] matrix) {
493         if (matrix == null) {
494             matrix = new double[3][3];
495         }
496 
497         matrix[0][0] = m00;
498         matrix[0][1] = m01;
499         matrix[0][2] = m02;
500         matrix[1][0] = m10;
501         matrix[1][1] = m11;
502         matrix[1][2] = m12;
503         matrix[2][0] = m20;
504         matrix[2][1] = m21;
505         matrix[2][2] = m22;
506 
507         return matrix;
508     }
509 
510     /**
511      * Concatenates this transform with a translation transformation.
512      * This is equivalent to calling concatenate(T), where T is an
513      * PerspectiveTransform represented by the following matrix:
514      * <pre>
515      *		[   1    0    tx  ]
516      *		[   0    1    ty  ]
517      *		[   0    0    1   ]
518      * </pre>
519      */
translate(double tx, double ty)520     public void translate(double tx, double ty) {
521         PerspectiveTransform Tx = new PerspectiveTransform();
522         Tx.setToTranslation(tx, ty);
523         concatenate(Tx);
524     }
525 
526     /**
527      * Concatenates this transform with a rotation transformation.
528      * This is equivalent to calling concatenate(R), where R is an
529      * PerspectiveTransform represented by the following matrix:
530      * <pre>
531      *		[   cos(theta)    -sin(theta)    0   ]
532      *		[   sin(theta)     cos(theta)    0   ]
533      *		[       0              0         1   ]
534      * </pre>
535      * Rotating with a positive angle theta rotates points on the positive
536      * X axis toward the positive Y axis.
537      *
538      * @param theta The angle of rotation in radians.
539      */
rotate(double theta)540     public void rotate(double theta) {
541         PerspectiveTransform Tx = new PerspectiveTransform();
542         Tx.setToRotation(theta);
543         concatenate(Tx);
544     }
545 
546     /**
547      * Concatenates this transform with a translated rotation transformation.
548      * This is equivalent to the following sequence of calls:
549      * <pre>
550      *		translate(x, y);
551      *		rotate(theta);
552      *		translate(-x, -y);
553      * </pre>
554      * Rotating with a positive angle theta rotates points on the positive
555      * X axis toward the positive Y axis.
556      *
557      * @param theta The angle of rotation in radians.
558      * @param x The X coordinate of the origin of the rotation
559      * @param y The Y coordinate of the origin of the rotation
560      */
rotate(double theta, double x, double y)561     public void rotate(double theta, double x, double y) {
562         PerspectiveTransform Tx = new PerspectiveTransform();
563         Tx.setToRotation(theta, x, y);
564         concatenate(Tx);
565     }
566 
567     /**
568      * Concatenates this transform with a scaling transformation.
569      * This is equivalent to calling concatenate(S), where S is an
570      * PerspectiveTransform represented by the following matrix:
571      * <pre>
572      *		[   sx   0    0   ]
573      *		[   0    sy   0   ]
574      *		[   0    0    1   ]
575      * </pre>
576      *
577      * @param sx The X axis scale factor.
578      * @param sy The Y axis scale factor.
579      */
scale(double sx, double sy)580     public void scale(double sx, double sy) {
581         PerspectiveTransform Tx = new PerspectiveTransform();
582         Tx.setToScale(sx, sy);
583         concatenate(Tx);
584     }
585 
586     /**
587      * Concatenates this transform with a shearing transformation.
588      * This is equivalent to calling concatenate(SH), where SH is an
589      * PerspectiveTransform represented by the following matrix:
590      * <pre>
591      *		[   1   shx   0   ]
592      *		[  shy   1    0   ]
593      *		[   0    0    1   ]
594      * </pre>
595      *
596      * @param shx The factor by which coordinates are shifted towards
597      *        the positive X axis direction according to their Y
598      *        coordinate.
599      * @param shy The factor by which coordinates are shifted towards
600      *        the positive Y axis direction according to their X
601      *        coordinate.
602      */
shear(double shx, double shy)603     public void shear(double shx, double shy) {
604         PerspectiveTransform Tx = new PerspectiveTransform();
605         Tx.setToShear(shx, shy);
606         concatenate(Tx);
607     }
608 
609     /**
610      * Resets this transform to the Identity transform.
611      */
setToIdentity()612     public void setToIdentity() {
613         m00 = m11 = m22 = 1.0;
614         m01 = m10 = m02 = m20 = m12 = m21 = 0.0;
615     }
616 
617     /**
618      * Sets this transform to a translation transformation.
619      * The matrix representing this transform becomes:
620      * <pre>
621      *		[   1    0    tx  ]
622      *		[   0    1    ty  ]
623      *		[   0    0    1   ]
624      * </pre>
625      * @param tx The distance by which coordinates are translated in the
626      * X axis direction
627      * @param ty The distance by which coordinates are translated in the
628      * Y axis direction
629      */
setToTranslation(double tx, double ty)630     public void setToTranslation(double tx, double ty) {
631         m00 = 1.0;
632         m01 = 0.0;
633         m02 = tx;
634         m10 = 0.0;
635         m11 = 1.0;
636         m12 = ty;
637         m20 = 0.0;
638         m21 = 0.0;
639         m22 = 1.0;
640     }
641 
642     /**
643      * Sets this transform to a rotation transformation.
644      * The matrix representing this transform becomes:
645      * <pre>
646      *		[   cos(theta)    -sin(theta)    0   ]
647      *		[   sin(theta)     cos(theta)    0   ]
648      *		[       0              0         1   ]
649      * </pre>
650      * Rotating with a positive angle theta rotates points on the positive
651      * X axis toward the positive Y axis.
652      * @param theta The angle of rotation in radians.
653      */
setToRotation(double theta)654     public void setToRotation(double theta) {
655         m00 = Math.cos(theta);
656         m01 = -Math.sin(theta);
657         m02 = 0.0;
658         m10 = - m01;    // Math.sin(theta);
659         m11 = m00;      // Math.cos(theta);
660         m12 = 0.0;
661         m20 = 0.0;
662         m21 = 0.0;
663         m22 = 1.0;
664     }
665 
666     /**
667      * Sets this transform to a rotation transformation
668      * about a specified point (x, y).  This is equivalent
669      * to the following sequence of calls:
670      *
671      * <pre>
672      *		setToTranslate(x, y);
673      *		rotate(theta);
674      *		translate(-x, -y);
675      * </pre>
676      *
677      * Rotating with a positive angle theta rotates points on the positive
678      * X axis toward the positive Y axis.
679      *
680      * @param theta The angle of rotation in radians.
681      * @param x The X coordinate of the origin of the rotation
682      * @param y The Y coordinate of the origin of the rotation
683      */
setToRotation(double theta, double x, double y)684     public void setToRotation(double theta, double x, double y) {
685         setToRotation(theta);
686 	double sin = m10;
687 	double oneMinusCos = 1.0 - m00;
688 	m02 = x * oneMinusCos + y * sin;
689 	m12 = y * oneMinusCos - x * sin;
690     }
691 
692     /**
693      * Sets this transform to a scale transformation
694      * with scale factors sx and sy.
695      * The matrix representing this transform becomes:
696      * <pre>
697      *		[   sx   0    0   ]
698      *		[   0    sy   0   ]
699      *		[   0    0    1   ]
700      * </pre>
701      *
702      * @param sx The X axis scale factor.
703      * @param sy The Y axis scale factor.
704      */
setToScale(double sx, double sy)705     public void setToScale(double sx, double sy) {
706         m00 = sx;
707         m01 = 0.0;
708         m02 = 0.0;
709         m10 = 0.0;
710         m11 = sy;
711         m12 = 0.0;
712         m20 = 0.0;
713         m21 = 0.0;
714         m22 = 1.0;
715     }
716 
717     /**
718      * Sets this transform to a shearing transformation
719      * with shear factors sx and sy.
720      * The matrix representing this transform becomes:
721      * <pre>
722      *		[   1  shx    0   ]
723      *		[ shy    1    0   ]
724      *		[   0    0    1   ]
725      * </pre>
726      *
727      * @param shx The factor by which coordinates are shifted towards
728      *        the positive X axis direction according to their Y
729      *        coordinate.
730      * @param shy The factor by which coordinates are shifted towards
731      *        the positive Y axis direction according to their X
732      *        coordinate.
733      */
setToShear(double shx, double shy)734     public void setToShear(double shx, double shy) {
735         m00 = 1.0;
736         m01 = shx;
737         m02 = 0.0;
738         m10 = shy;
739         m11 = 1.0;
740         m12 = 0.0;
741         m20 = 0.0;
742         m21 = 0.0;
743         m22 = 1.0;
744     }
745 
746     /**
747      * Sets this transform to a given AffineTransform.
748      * @throws IllegalArgumentException if Tx is null
749      */
setTransform(AffineTransform Tx)750     public void setTransform(AffineTransform Tx) {
751         if ( Tx == null ) {
752             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
753         }
754 
755         m00 = Tx.getScaleX();
756         m01 = Tx.getShearX();
757         m02 = Tx.getTranslateX();
758         m10 = Tx.getShearY();
759         m11 = Tx.getScaleY();
760         m12 = Tx.getTranslateY();
761         m20 = 0.0;
762         m21 = 0.0;
763         m22 = 1.0;
764     }
765 
766     /**
767      * Sets this transform to a given PerspectiveTransform.
768      * @throws IllegalArgumentException if Tx is null
769      */
setTransform(PerspectiveTransform Tx)770     public void setTransform(PerspectiveTransform Tx) {
771         if ( Tx == null ) {
772             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
773         }
774 
775         m00 = Tx.m00;
776         m01 = Tx.m01;
777         m02 = Tx.m02;
778         m10 = Tx.m10;
779         m11 = Tx.m11;
780         m12 = Tx.m12;
781         m20 = Tx.m20;
782         m21 = Tx.m21;
783         m22 = Tx.m22;
784     }
785 
786     /**
787      * Sets this transform to a given PerspectiveTransform,
788      * expressed by the elements of its matrix.  <i>Important Note: The
789      * matrix elements in the argument list are in column-major order
790      * unlike those of the constructor, which are in row-major order.</i>
791      * @deprecated as of JAI 1.1 Use double[][] getMatrix(double[][] matrix) instead.
792      */
setTransform(float m00, float m10, float m20, float m01, float m11, float m21, float m02, float m12, float m22)793     public void setTransform(float m00, float m10, float m20,
794                              float m01, float m11, float m21,
795                              float m02, float m12, float m22) {
796         this.m00 = (double)m00;
797         this.m01 = (double)m01;
798         this.m02 = (double)m02;
799         this.m10 = (double)m10;
800         this.m11 = (double)m11;
801         this.m12 = (double)m12;
802         this.m20 = (double)m20;
803         this.m21 = (double)m21;
804         this.m22 = (double)m22;
805     }
806 
807     /**
808      * Sets this transform using a two-dimensional array of double precision
809      * values.  The row index is first, and the column index is second.
810      *
811      * @param matrix The 2D double array to be used for setting this transform.
812      *        The array is assumed to be at least 3x3.
813      * @throws IllegalArgumentException if matrix is null
814      * @throws ArrayIndexOutOfBoundsException if matrix is too small
815      * @since JAI 1.1
816      */
setTransform(double[][] matrix)817     public void setTransform(double[][] matrix) {
818         if ( matrix == null ) {
819             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
820         }
821 
822         m00 = matrix[0][0];
823         m01 = matrix[0][1];
824         m02 = matrix[0][2];
825         m10 = matrix[1][0];
826         m11 = matrix[1][1];
827         m12 = matrix[1][2];
828         m20 = matrix[2][0];
829         m21 = matrix[2][1];
830         m22 = matrix[2][2];
831     }
832 
833     /**
834      * Post-concatenates a given AffineTransform to this transform.
835      * @throws IllegalArgumentException if Tx is null
836      */
concatenate(AffineTransform Tx)837     public void concatenate(AffineTransform Tx) {
838         if ( Tx == null ) {
839             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
840         }
841 
842         // Extend Tx: Tx.m20 = 0, Tx.m21 = 0, Tx.m22 = 1
843 
844         double tx_m00 = Tx.getScaleX();
845         double tx_m01 = Tx.getShearX();
846         double tx_m02 = Tx.getTranslateX();
847         double tx_m10 = Tx.getShearY();
848         double tx_m11 = Tx.getScaleY();
849         double tx_m12 = Tx.getTranslateY();
850 
851         double m00p = m00*tx_m00 + m10*tx_m01 + m20*tx_m02;
852         double m01p = m01*tx_m00 + m11*tx_m01 + m21*tx_m02;
853         double m02p = m02*tx_m00 + m12*tx_m01 + m22*tx_m02;
854         double m10p = m00*tx_m10 + m10*tx_m11 + m20*tx_m12;
855         double m11p = m01*tx_m10 + m11*tx_m11 + m21*tx_m12;
856         double m12p = m02*tx_m10 + m12*tx_m11 + m22*tx_m12;
857         double m20p = m20;
858         double m21p = m21;
859         double m22p = m22;
860 
861         m00 = m00p;
862         m10 = m10p;
863         m20 = m20p;
864         m01 = m01p;
865         m11 = m11p;
866         m21 = m21p;
867         m02 = m02p;
868         m12 = m12p;
869         m22 = m22p;
870     }
871 
872     /**
873      * Post-concatenates a given PerspectiveTransform to this transform.
874      * @throws IllegalArgumentException if Tx is null
875      */
concatenate(PerspectiveTransform Tx)876     public void concatenate(PerspectiveTransform Tx) {
877         if ( Tx == null ) {
878             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
879         }
880 
881         double m00p = m00*Tx.m00 + m10*Tx.m01 + m20*Tx.m02;
882         double m10p = m00*Tx.m10 + m10*Tx.m11 + m20*Tx.m12;
883         double m20p = m00*Tx.m20 + m10*Tx.m21 + m20*Tx.m22;
884         double m01p = m01*Tx.m00 + m11*Tx.m01 + m21*Tx.m02;
885         double m11p = m01*Tx.m10 + m11*Tx.m11 + m21*Tx.m12;
886         double m21p = m01*Tx.m20 + m11*Tx.m21 + m21*Tx.m22;
887         double m02p = m02*Tx.m00 + m12*Tx.m01 + m22*Tx.m02;
888         double m12p = m02*Tx.m10 + m12*Tx.m11 + m22*Tx.m12;
889         double m22p = m02*Tx.m20 + m12*Tx.m21 + m22*Tx.m22;
890 
891         m00 = m00p;
892         m10 = m10p;
893         m20 = m20p;
894         m01 = m01p;
895         m11 = m11p;
896         m21 = m21p;
897         m02 = m02p;
898         m12 = m12p;
899         m22 = m22p;
900     }
901 
902     /**
903      * Pre-concatenates a given AffineTransform to this transform.
904      * @throws IllegalArgumentException if Tx is null
905      */
preConcatenate(AffineTransform Tx)906     public void preConcatenate(AffineTransform Tx) {
907         if ( Tx == null ) {
908             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
909         }
910 
911         // Extend Tx: Tx.m20 = 0, Tx.m21 = 0, Tx.m22 = 1
912 
913         double tx_m00 = Tx.getScaleX();
914         double tx_m01 = Tx.getShearX();
915         double tx_m02 = Tx.getTranslateX();
916         double tx_m10 = Tx.getShearY();
917         double tx_m11 = Tx.getScaleY();
918         double tx_m12 = Tx.getTranslateY();
919 
920         double m00p = tx_m00*m00 + tx_m10*m01;
921         double m01p = tx_m01*m00 + tx_m11*m01;
922         double m02p = tx_m02*m00 + tx_m12*m01 + m02;
923         double m10p = tx_m00*m10 + tx_m10*m11;
924         double m11p = tx_m01*m10 + tx_m11*m11;
925         double m12p = tx_m02*m10 + tx_m12*m11 + m12;
926         double m20p = tx_m00*m20 + tx_m10*m21;
927         double m21p = tx_m01*m20 + tx_m11*m21;
928         double m22p = tx_m02*m20 + tx_m12*m21 + m22;
929 
930         m00 = m00p;
931         m10 = m10p;
932         m20 = m20p;
933         m01 = m01p;
934         m11 = m11p;
935         m21 = m21p;
936         m02 = m02p;
937         m12 = m12p;
938         m22 = m22p;
939     }
940 
941     /**
942      * Pre-concatenates a given PerspectiveTransform to this transform.
943      * @throws IllegalArgumentException if Tx is null
944      */
preConcatenate(PerspectiveTransform Tx)945     public void preConcatenate(PerspectiveTransform Tx) {
946         if ( Tx == null ) {
947             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
948         }
949 
950         double m00p = Tx.m00*m00 + Tx.m10*m01 + Tx.m20*m02;
951         double m10p = Tx.m00*m10 + Tx.m10*m11 + Tx.m20*m12;
952         double m20p = Tx.m00*m20 + Tx.m10*m21 + Tx.m20*m22;
953         double m01p = Tx.m01*m00 + Tx.m11*m01 + Tx.m21*m02;
954         double m11p = Tx.m01*m10 + Tx.m11*m11 + Tx.m21*m12;
955         double m21p = Tx.m01*m20 + Tx.m11*m21 + Tx.m21*m22;
956         double m02p = Tx.m02*m00 + Tx.m12*m01 + Tx.m22*m02;
957         double m12p = Tx.m02*m10 + Tx.m12*m11 + Tx.m22*m12;
958         double m22p = Tx.m02*m20 + Tx.m12*m21 + Tx.m22*m22;
959 
960         m00 = m00p;
961         m10 = m10p;
962         m20 = m20p;
963         m01 = m01p;
964         m11 = m11p;
965         m21 = m21p;
966         m02 = m02p;
967         m12 = m12p;
968         m22 = m22p;
969     }
970 
971     /**
972      * Returns a new PerpectiveTransform that is the inverse
973      * of the current transform.
974      * @throws NoninvertibleTransformException if transform cannot be inverted
975      */
createInverse()976      public PerspectiveTransform createInverse()
977          throws NoninvertibleTransformException, CloneNotSupportedException {
978 
979 	     PerspectiveTransform tx = (PerspectiveTransform)clone();
980 	     tx.makeAdjoint();
981 	     if (Math.abs(tx.m22) <  PERSPECTIVE_DIVIDE_EPSILON) {
982   	       throw new NoninvertibleTransformException(JaiI18N.getString("PerspectiveTransform0"));
983 	     }
984 	     tx.normalize();
985 	     return tx;
986     }
987 
988     /**
989      * Returns a new PerpectiveTransform that is the adjoint,
990      * of the current transform.  The adjoint is defined as
991      * the matrix of cofactors, which in turn are the determinants
992      * of the submatrices defined by removing the row and column
993      * of each element from the original matrix in turn.
994      *
995      * <p> The adjoint is a scalar multiple of the inverse matrix.
996      * Because points to be transformed are converted into homogeneous
997      * coordinates, where scalar factors are irrelevant, the adjoint
998      * may be used in place of the true inverse. Since it is unnecessary
999      * to normalize the adjoint, it is both faster to compute and more
1000      * numerically stable than the true inverse.
1001      */
createAdjoint()1002     public PerspectiveTransform createAdjoint()
1003     throws CloneNotSupportedException{
1004 
1005 	    PerspectiveTransform tx = (PerspectiveTransform)clone();
1006 	    tx.makeAdjoint();
1007 	    return tx;
1008     }
1009 
1010     /**
1011      * Transforms the specified ptSrc and stores the result in ptDst.
1012      * If ptDst is null, a new Point2D object will be allocated before
1013      * storing. In either case, ptDst containing the transformed point
1014      * is returned for convenience.
1015      * Note that ptSrc and ptDst can the same. In this case, the input
1016      * point will be overwritten with the transformed point.
1017      *
1018      * @param ptSrc The array containing the source point objects.
1019      * @param ptDst The array where the transform point objects are returned.
1020      * @throws IllegalArgumentException if ptSrc is null
1021      */
transform(Point2D ptSrc, Point2D ptDst)1022     public Point2D transform(Point2D ptSrc, Point2D ptDst) {
1023         if ( ptSrc == null ) {
1024             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
1025         }
1026 
1027         if (ptDst == null) {
1028             if (ptSrc instanceof Point2D.Double) {
1029                 ptDst = new Point2D.Double();
1030             } else {
1031                 ptDst = new Point2D.Float();
1032             }
1033         }
1034 
1035         double x = ptSrc.getX();
1036         double y = ptSrc.getY();
1037         double w = m20 * x + m21 * y + m22;
1038         ptDst.setLocation((m00 * x + m01 * y + m02) / w,
1039                           (m10 * x + m11 * y + m12) / w);
1040 
1041         return ptDst;
1042     }
1043 
1044     /**
1045      * Transforms an array of point objects by this transform.
1046      * @param ptSrc The array containing the source point objects.
1047      * @param ptDst The array where the transform point objects are returned.
1048      * @param srcOff The offset to the first point object to be transformed
1049      * in the source array.
1050      * @param dstOff The offset to the location where the first transformed
1051      * point object is stored in the destination array.
1052      * @param numPts The number of point objects to be transformed.
1053      * @throws IllegalArgumentException if ptSrc is null
1054      * @throws IllegalArgumentException if ptDst is null
1055      * @throws ArrayIndexOutOfBoundsException if ptSrc is too small
1056      */
transform(Point2D[] ptSrc, int srcOff, Point2D[] ptDst, int dstOff, int numPts)1057     public void transform(Point2D[] ptSrc, int srcOff,
1058 			  Point2D[] ptDst, int dstOff,
1059 			  int numPts) {
1060 
1061         if ( ptSrc == null || ptDst == null ) {
1062             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
1063         }
1064 
1065         while (numPts-- > 0) {
1066             /* Copy source coords into local variables in case src == dst. */
1067             Point2D src = ptSrc[srcOff++];
1068             Point2D dst = ptDst[dstOff++];
1069             if (dst == null) {
1070                 if (src instanceof Point2D.Double) {
1071                     dst = new Point2D.Double();
1072                 } else {
1073                     dst = new Point2D.Float();
1074                 }
1075                 ptDst[dstOff - 1] = dst;
1076             }
1077 
1078             double x = src.getX();
1079             double y = src.getY();
1080             double w = m20 * x + m21 * y + m22;
1081 
1082             if (w == 0) {
1083                 dst.setLocation(x, y);
1084             } else {
1085                 dst.setLocation((m00 * x + m01 * y + m02) / w,
1086                                 (m10 * x + m11 * y + m12) / w);
1087             }
1088         }
1089     }
1090 
1091     /**
1092      * Transforms an array of floating point coordinates by this transform.
1093      * @param srcPts The array containing the source point coordinates.
1094      * Each point is stored as a pair of x,y coordinates.
1095      * @param srcOff The offset to the first point to be transformed
1096      * in the source array.
1097      * @param dstPts The array where the transformed point coordinates are
1098      * returned.  Each point is stored as a pair of x,y coordinates.
1099      * @param dstOff The offset to the location where the first transformed
1100      * point is stored in the destination array.
1101      * @param numPts The number of points to be transformed.
1102      * @throws IllegalArgumentException if srcPts is null
1103      * @throws ArrayIndexOutOfBoundsException if srcPts is too small
1104      */
transform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts)1105     public void transform(float[] srcPts, int srcOff,
1106 			  float[] dstPts, int dstOff,
1107 			  int numPts) {
1108 
1109         if ( srcPts == null ) {
1110             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
1111         }
1112 
1113         if (dstPts == null) {
1114             dstPts = new float[numPts * 2 + dstOff];
1115         }
1116 
1117         while (numPts-- > 0) {
1118             float x = srcPts[srcOff++];
1119             float y = srcPts[srcOff++];
1120             double w = m20 * x + m21 * y + m22;
1121 
1122             if (w == 0) {
1123                 dstPts[dstOff++] = x;
1124                 dstPts[dstOff++] = y;
1125             } else {
1126                 dstPts[dstOff++] = (float)((m00 * x + m01 * y + m02) / w);
1127                 dstPts[dstOff++] = (float)((m10 * x + m11 * y + m12) / w);
1128             }
1129         }
1130     }
1131 
1132     /**
1133      * Transforms an array of double precision coordinates by this transform.
1134      * @param srcPts The array containing the source point coordinates.
1135      * Each point is stored as a pair of x,y coordinates.
1136      * @param dstPts The array where the transformed point coordinates are
1137      * returned.  Each point is stored as a pair of x,y coordinates.
1138      * @param srcOff The offset to the first point to be transformed
1139      * in the source array.
1140      * @param dstOff The offset to the location where the first transformed
1141      * point is stored in the destination array.
1142      * @param numPts The number of point objects to be transformed.
1143      * @throws IllegalArgumentException if srcPts is null
1144      * @throws ArrayIndexOutOfBoundsException if srcPts is too small
1145      */
transform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts)1146     public void transform(double[] srcPts, int srcOff,
1147 			  double[] dstPts, int dstOff,
1148 			  int numPts) {
1149 
1150         if ( srcPts == null ) {
1151             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
1152         }
1153 
1154         if (dstPts == null) {
1155             dstPts = new double[numPts * 2 + dstOff];
1156         }
1157 
1158         while (numPts-- > 0) {
1159             double x = srcPts[srcOff++];
1160             double y = srcPts[srcOff++];
1161             double w = m20 * x + m21 * y + m22;
1162 
1163             if (w == 0) {
1164                 dstPts[dstOff++] = x;
1165                 dstPts[dstOff++] = y;
1166             } else {
1167                 dstPts[dstOff++] = (m00 * x + m01 * y + m02) / w;
1168                 dstPts[dstOff++] = (m10 * x + m11 * y + m12) / w;
1169             }
1170         }
1171     }
1172 
1173     /**
1174      * Transforms an array of floating point coordinates by this transform,
1175      * storing the results into an array of doubles.
1176      * @param srcPts The array containing the source point coordinates.
1177      * Each point is stored as a pair of x,y coordinates.
1178      * @param srcOff The offset to the first point to be transformed
1179      * in the source array.
1180      * @param dstPts The array where the transformed point coordinates are
1181      * returned.  Each point is stored as a pair of x,y coordinates.
1182      * @param dstOff The offset to the location where the first transformed
1183      * point is stored in the destination array.
1184      * @param numPts The number of points to be transformed.
1185      * @throws IllegalArgumentException if srcPts is null
1186      * @throws ArrayIndexOutOfBoundsException if srcPts is too small
1187      */
transform(float[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts)1188     public void transform(float[] srcPts, int srcOff,
1189 			  double[] dstPts, int dstOff,
1190 			  int numPts) {
1191 
1192         if ( srcPts == null ) {
1193             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
1194         }
1195 
1196         if (dstPts == null) {
1197             dstPts = new double[numPts * 2 + dstOff];
1198         }
1199 
1200         while (numPts-- > 0) {
1201             float x = srcPts[srcOff++];
1202             float y = srcPts[srcOff++];
1203             double w = m20 * x + m21 * y + m22;
1204 
1205             if (w == 0) {
1206                 dstPts[dstOff++] = x;
1207                 dstPts[dstOff++] = y;
1208             } else {
1209                 dstPts[dstOff++] = (m00 * x + m01 * y + m02) / w;
1210                 dstPts[dstOff++] = (m10 * x + m11 * y + m12) / w;
1211             }
1212         }
1213     }
1214 
1215     /**
1216      * Transforms an array of double precision coordinates by this transform,
1217      * storing the results into an array of floats.
1218      * @param srcPts The array containing the source point coordinates.
1219      * Each point is stored as a pair of x,y coordinates.
1220      * @param dstPts The array where the transformed point coordinates are
1221      * returned.  Each point is stored as a pair of x,y coordinates.
1222      * @param srcOff The offset to the first point to be transformed
1223      * in the source array.
1224      * @param dstOff The offset to the location where the first transformed
1225      * point is stored in the destination array.
1226      * @param numPts The number of point objects to be transformed.
1227      * @throws IllegalArgumentException if srcPts is null
1228      * @throws ArrayIndexOutOfBoundsException if srcPts is too small
1229      */
transform(double[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts)1230     public void transform(double[] srcPts, int srcOff,
1231 			  float[] dstPts, int dstOff,
1232 			  int numPts) {
1233 
1234         if ( srcPts == null ) {
1235             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
1236         }
1237 
1238         if (dstPts == null) {
1239             dstPts = new float[numPts * 2 + dstOff];
1240         }
1241 
1242         while (numPts-- > 0) {
1243             double x = srcPts[srcOff++];
1244             double y = srcPts[srcOff++];
1245             double w = m20 * x + m21 * y + m22;
1246 
1247             if (w == 0) {
1248                 dstPts[dstOff++] = (float)x;
1249                 dstPts[dstOff++] = (float)y;
1250             } else {
1251                 dstPts[dstOff++] = (float)((m00 * x + m01 * y + m02) / w);
1252                 dstPts[dstOff++] = (float)((m10 * x + m11 * y + m12) / w);
1253             }
1254         }
1255     }
1256 
1257     /**
1258      * Inverse transforms the specified ptSrc and stores the result in ptDst.
1259      * If ptDst is null, a new Point2D object will be allocated before
1260      * storing. In either case, ptDst containing the transformed point
1261      * is returned for convenience.
1262      * Note that ptSrc and ptDst can the same. In this case, the input
1263      * point will be overwritten with the transformed point.
1264      * @param ptSrc The point to be inverse transformed.
1265      * @param ptDst The resulting transformed point.
1266      * @throws NoninvertibleTransformException  if the matrix cannot be
1267      *                                         inverted.
1268      * @throws IllegalArgumentException if ptSrc is null
1269      */
inverseTransform(Point2D ptSrc, Point2D ptDst)1270     public Point2D inverseTransform(Point2D ptSrc, Point2D ptDst)
1271 	throws NoninvertibleTransformException
1272     {
1273         if ( ptSrc == null ) {
1274             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
1275         }
1276 
1277         if (ptDst == null) {
1278 	    if (ptSrc instanceof Point2D.Double) {
1279 		ptDst = new Point2D.Double();
1280 	    } else {
1281 		ptDst = new Point2D.Float();
1282 	    }
1283 	}
1284 	// Copy source coords into local variables in case src == dst
1285 	double x = ptSrc.getX();
1286 	double y = ptSrc.getY();
1287 
1288         double tmp_x = (m11*m22 - m12*m21) * x +
1289             (m02*m21 - m01*m22) * y +
1290             (m01*m12 - m02*m11);
1291         double tmp_y = (m12*m20 - m10*m22) * x +
1292             (m00*m22 - m02*m20) * y +
1293             (m02*m10 - m00*m12);
1294         double w = (m10*m21 - m11*m20) * x +
1295             (m01*m20 - m00*m21) * y +
1296             (m00*m11 - m01*m10);
1297 
1298         double wabs = w;
1299         if (w < 0) {
1300             wabs = - w;
1301         }
1302         if (wabs < PERSPECTIVE_DIVIDE_EPSILON) {
1303             throw new
1304 		NoninvertibleTransformException(
1305 				     JaiI18N.getString("PerspectiveTransform1"));
1306         }
1307 
1308         ptDst.setLocation(tmp_x/w, tmp_y/w);
1309 
1310         return ptDst;
1311     }
1312 
1313     /**
1314      * Inverse transforms an array of double precision coordinates by
1315      * this transform.
1316      * @param srcPts The array containing the source point coordinates.
1317      * Each point is stored as a pair of x,y coordinates.
1318      * @param dstPts The array where the transformed point coordinates are
1319      * returned.  Each point is stored as a pair of x,y coordinates.
1320      * @param srcOff The offset to the first point to be transformed
1321      * in the source array.
1322      * @param dstOff The offset to the location where the first transformed
1323      * point is stored in the destination array.
1324      * @param numPts The number of point objects to be transformed.
1325      * @throws NoninvertibleTransformException  if the matrix cannot be
1326      *                                         inverted.
1327      * @throws IllegalArgumentException if srcPts is null
1328      * @throws ArrayIndexOutOfBoundsException if srcPts is too small
1329      * @throws NoninvertibleTransformException transform cannot be inverted
1330      */
inverseTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts)1331     public void inverseTransform(double[] srcPts, int srcOff,
1332                                  double[] dstPts, int dstOff,
1333                                  int numPts)
1334         throws NoninvertibleTransformException
1335     {
1336         if ( srcPts == null ) {
1337             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
1338         }
1339 
1340         if (dstPts == null) {
1341             dstPts = new double[numPts * 2 + dstOff];
1342         }
1343 
1344         while (numPts-- > 0) {
1345             double x = srcPts[srcOff++];
1346             double y = srcPts[srcOff++];
1347 
1348             double tmp_x = (m11*m22 - m12*m21) * x +
1349                 (m02*m21 - m01*m22) * y +
1350                 (m01*m12 - m02*m11);
1351             double tmp_y = (m12*m20 - m10*m22) * x +
1352                 (m00*m22 - m02*m20) * y +
1353                 (m02*m10 - m00*m12);
1354             double w = (m10*m21 - m11*m20) * x +
1355                 (m01*m20 - m00*m21) * y +
1356                 (m00*m11 - m01*m10);
1357 
1358             double wabs = w;
1359             if (w < 0) {
1360                 wabs = - w;
1361             }
1362             if (wabs < PERSPECTIVE_DIVIDE_EPSILON) {
1363                 throw new NoninvertibleTransformException(
1364 				    JaiI18N.getString("PerspectiveTransform1"));
1365             }
1366 
1367             dstPts[dstOff++] = tmp_x / w;
1368             dstPts[dstOff++] = tmp_y / w;
1369         }
1370     }
1371 
1372     /**
1373      * Returns a String that represents the value of this Object.
1374      */
toString()1375     public String toString() {
1376         StringBuffer sb = new StringBuffer();
1377         sb.append("Perspective transform matrix\n");
1378         sb.append(this.m00);
1379         sb.append("\t");
1380         sb.append(this.m01);
1381         sb.append("\t");
1382         sb.append(this.m02);
1383         sb.append("\n");
1384         sb.append(this.m10);
1385         sb.append("\t");
1386         sb.append(this.m11);
1387         sb.append("\t");
1388         sb.append(this.m12);
1389         sb.append("\n");
1390         sb.append(this.m20);
1391         sb.append("\t");
1392         sb.append(this.m21);
1393         sb.append("\t");
1394         sb.append(this.m22);
1395         sb.append("\n");
1396         return new String(sb);
1397     }
1398 
1399     /**
1400      * Returns the boolean true value if this PerspectiveTransform is an
1401      * identity transform. Returns false otherwise.
1402      */
isIdentity()1403     public boolean isIdentity() {
1404         return m01 == 0.0 && m02 == 0.0 &&
1405             m10 == 0.0 && m12 == 0.0 &&
1406             m20 == 0.0 && m21 == 0.0 &&
1407             m22 != 0.0 && m00/m22 == 1.0 && m11/m22 == 1.0;
1408     }
1409 
1410     /**
1411      * Returns a copy of this PerspectiveTransform object.
1412      */
clone()1413     public Object clone() {
1414 	try {
1415 	    return super.clone();
1416 	} catch (CloneNotSupportedException e) {
1417 	    // this shouldn't happen, since we are Cloneable
1418 	    throw new InternalError();
1419 	}
1420     }
1421 
1422 
1423     /**
1424      * Tests if this PerspectiveTransform equals a supplied one.
1425      *
1426      * @param obj The PerspectiveTransform to be compared to this one.
1427      */
equals(Object obj)1428     public boolean equals(Object obj) {
1429         if (!(obj instanceof PerspectiveTransform)) {
1430             return false;
1431         }
1432 
1433         PerspectiveTransform a = (PerspectiveTransform)obj;
1434 
1435 	return ((m00 == a.m00) && (m10 == a.m10) && (m20 == a.m20) &&
1436 		(m01 == a.m01) && (m11 == a.m11) && (m21 == a.m21) &&
1437 		(m02 == a.m02) && (m12 == a.m12) && (m22 == a.m22));
1438     }
1439 }
1440