• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics;
18 
19 import java.awt.geom.AffineTransform;
20 import java.awt.geom.NoninvertibleTransformException;
21 
22 
23 /**
24  * A matrix implementation overridden by the LayoutLib bridge.
25  */
26 public class Matrix extends _Original_Matrix {
27 
28     float mValues[] = new float[9];
29 
30     /**
31      * Create an identity matrix
32      */
Matrix()33     public Matrix() {
34         reset();
35     }
36 
37     /**
38      * Create a matrix that is a (deep) copy of src
39      * @param src The matrix to copy into this matrix
40      */
Matrix(Matrix src)41     public Matrix(Matrix src) {
42         set(src);
43     }
44 
45     /**
46      * Creates a Matrix object from the float array. The array becomes the internal storage
47      * of the object.
48      * @param data
49      */
Matrix(float[] data)50     private Matrix(float[] data) {
51         assert data.length != 9;
52         mValues = data;
53     }
54 
55     @Override
finalize()56     public void finalize() throws Throwable {
57         // pass
58     }
59 
60     //---------- Custom Methods
61 
62     /**
63      * Adds the given transformation to the current Matrix
64      * <p/>This in effect does this = this*matrix
65      * @param matrix
66      */
addTransform(float[] matrix)67     private void addTransform(float[] matrix) {
68         float[] tmp = new float[9];
69 
70         // first row
71         tmp[0] = matrix[0] * mValues[0] + matrix[1] * mValues[3] + matrix[2] * mValues[6];
72         tmp[1] = matrix[0] * mValues[1] + matrix[1] * mValues[4] + matrix[2] * mValues[7];
73         tmp[2] = matrix[0] * mValues[2] + matrix[1] * mValues[5] + matrix[2] * mValues[8];
74 
75         // 2nd row
76         tmp[3] = matrix[3] * mValues[0] + matrix[4] * mValues[3] + matrix[5] * mValues[6];
77         tmp[4] = matrix[3] * mValues[1] + matrix[4] * mValues[4] + matrix[5] * mValues[7];
78         tmp[5] = matrix[3] * mValues[2] + matrix[4] * mValues[5] + matrix[5] * mValues[8];
79 
80         // 3rd row
81         tmp[6] = matrix[6] * mValues[0] + matrix[7] * mValues[3] + matrix[8] * mValues[6];
82         tmp[7] = matrix[6] * mValues[1] + matrix[7] * mValues[4] + matrix[8] * mValues[7];
83         tmp[8] = matrix[6] * mValues[2] + matrix[7] * mValues[5] + matrix[8] * mValues[8];
84 
85         // copy the result over to mValues
86         mValues = tmp;
87     }
88 
getTransform()89     public AffineTransform getTransform() {
90         return new AffineTransform(mValues[0], mValues[1], mValues[2],
91                 mValues[3], mValues[4], mValues[5]);
92     }
93 
hasPerspective()94     public boolean hasPerspective() {
95         return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1);
96     }
97 
98     //----------
99 
100     /**
101      * Returns true if the matrix is identity.
102      * This maybe faster than testing if (getType() == 0)
103      */
104     @Override
isIdentity()105     public boolean isIdentity() {
106         for (int i = 0, k = 0; i < 3; i++) {
107             for (int j = 0; j < 3; j++, k++) {
108                 if (mValues[k] != ((i==j) ? 1 : 0)) {
109                     return false;
110                 }
111             }
112         }
113 
114         return true;
115     }
116 
117     /**
118      * Returns true if will map a rectangle to another rectangle. This can be
119      * true if the matrix is identity, scale-only, or rotates a multiple of 90
120      * degrees.
121      */
122     @Override
rectStaysRect()123     public boolean rectStaysRect() {
124         return (computeTypeMask() & kRectStaysRect_Mask) != 0;
125     }
126 
127     /**
128      * (deep) copy the src matrix into this matrix. If src is null, reset this
129      * matrix to the identity matrix.
130      */
set(Matrix src)131     public void set(Matrix src) {
132         if (src == null) {
133             reset();
134         } else {
135             System.arraycopy(src.mValues, 0, mValues, 0, mValues.length);
136         }
137     }
138 
139     @Override
set(_Original_Matrix src)140     public void set(_Original_Matrix src) {
141         throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
142     }
143 
144     /** Returns true if obj is a Matrix and its values equal our values.
145     */
146     @Override
equals(Object obj)147     public boolean equals(Object obj) {
148         if (obj != null && obj instanceof Matrix) {
149             Matrix matrix = (Matrix)obj;
150             for (int i = 0 ; i < 9 ; i++) {
151                 if (mValues[i] != matrix.mValues[i]) {
152                     return false;
153                 }
154             }
155 
156             return true;
157         }
158 
159         return false;
160     }
161 
162     /** Set the matrix to identity */
163     @Override
reset()164     public void reset() {
165         for (int i = 0, k = 0; i < 3; i++) {
166             for (int j = 0; j < 3; j++, k++) {
167                 mValues[k] = ((i==j) ? 1 : 0);
168             }
169         }
170     }
171 
172     /** Set the matrix to translate by (dx, dy). */
173     @Override
setTranslate(float dx, float dy)174     public void setTranslate(float dx, float dy) {
175         mValues[0] = 1;
176         mValues[1] = 0;
177         mValues[2] = dx;
178         mValues[3] = 0;
179         mValues[4] = 1;
180         mValues[5] = dy;
181         mValues[6] = 0;
182         mValues[7] = 0;
183         mValues[8] = 1;
184     }
185 
186     /**
187      * Set the matrix to scale by sx and sy, with a pivot point at (px, py).
188      * The pivot point is the coordinate that should remain unchanged by the
189      * specified transformation.
190      */
191     @Override
setScale(float sx, float sy, float px, float py)192     public void setScale(float sx, float sy, float px, float py) {
193         // TODO: do it in one pass
194 
195         // translate so that the pivot is in 0,0
196         mValues[0] = 1;
197         mValues[1] = 0;
198         mValues[2] = -px;
199         mValues[3] = 0;
200         mValues[4] = 1;
201         mValues[5] = -py;
202         mValues[6] = 0;
203         mValues[7] = 0;
204         mValues[8] = 1;
205 
206         // scale
207         addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
208         // translate back the pivot
209         addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
210     }
211 
212     /** Set the matrix to scale by sx and sy. */
213     @Override
setScale(float sx, float sy)214     public void setScale(float sx, float sy) {
215         mValues[0] = sx;
216         mValues[1] = 0;
217         mValues[2] = 0;
218         mValues[3] = 0;
219         mValues[4] = sy;
220         mValues[5] = 0;
221         mValues[6] = 0;
222         mValues[7] = 0;
223         mValues[8] = 1;
224     }
225 
226     /**
227      * Set the matrix to rotate by the specified number of degrees, with a pivot
228      * point at (px, py). The pivot point is the coordinate that should remain
229      * unchanged by the specified transformation.
230      */
231     @Override
setRotate(float degrees, float px, float py)232     public void setRotate(float degrees, float px, float py) {
233         // TODO: do it in one pass
234 
235         // translate so that the pivot is in 0,0
236         mValues[0] = 1;
237         mValues[1] = 0;
238         mValues[2] = -px;
239         mValues[3] = 0;
240         mValues[4] = 1;
241         mValues[5] = -py;
242         mValues[6] = 0;
243         mValues[7] = 0;
244         mValues[8] = 1;
245 
246         // scale
247         double rad = Math.toRadians(degrees);
248         float cos = (float)Math.cos(rad);
249         float sin = (float)Math.sin(rad);
250         addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
251         // translate back the pivot
252         addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
253     }
254 
255     /**
256      * Set the matrix to rotate about (0,0) by the specified number of degrees.
257      */
258     @Override
setRotate(float degrees)259     public void setRotate(float degrees) {
260         double rad = Math.toRadians(degrees);
261         float cos = (float)Math.cos(rad);
262         float sin = (float)Math.sin(rad);
263 
264         mValues[0] = cos;
265         mValues[1] = -sin;
266         mValues[2] = 0;
267         mValues[3] = sin;
268         mValues[4] = cos;
269         mValues[5] = 0;
270         mValues[6] = 0;
271         mValues[7] = 0;
272         mValues[8] = 1;
273     }
274 
275     /**
276      * Set the matrix to rotate by the specified sine and cosine values, with a
277      * pivot point at (px, py). The pivot point is the coordinate that should
278      * remain unchanged by the specified transformation.
279      */
280     @Override
setSinCos(float sinValue, float cosValue, float px, float py)281     public void setSinCos(float sinValue, float cosValue, float px, float py) {
282         // TODO: do it in one pass
283 
284         // translate so that the pivot is in 0,0
285         mValues[0] = 1;
286         mValues[1] = 0;
287         mValues[2] = -px;
288         mValues[3] = 0;
289         mValues[4] = 1;
290         mValues[5] = -py;
291         mValues[6] = 0;
292         mValues[7] = 0;
293         mValues[8] = 1;
294 
295         // scale
296         addTransform(new float[] { cosValue, -sinValue, 0, sinValue, cosValue, 0, 0, 0, 1 });
297         // translate back the pivot
298         addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
299     }
300 
301     /** Set the matrix to rotate by the specified sine and cosine values. */
302     @Override
setSinCos(float sinValue, float cosValue)303     public void setSinCos(float sinValue, float cosValue) {
304         mValues[0] = cosValue;
305         mValues[1] = -sinValue;
306         mValues[2] = 0;
307         mValues[3] = sinValue;
308         mValues[4] = cosValue;
309         mValues[5] = 0;
310         mValues[6] = 0;
311         mValues[7] = 0;
312         mValues[8] = 1;
313     }
314 
315     /**
316      * Set the matrix to skew by sx and sy, with a pivot point at (px, py).
317      * The pivot point is the coordinate that should remain unchanged by the
318      * specified transformation.
319      */
320     @Override
setSkew(float kx, float ky, float px, float py)321     public void setSkew(float kx, float ky, float px, float py) {
322         // TODO: do it in one pass
323 
324         // translate so that the pivot is in 0,0
325         mValues[0] = 1;
326         mValues[1] = 0;
327         mValues[2] = -px;
328         mValues[3] = 0;
329         mValues[4] = 1;
330         mValues[5] = -py;
331         mValues[6] = 0;
332         mValues[7] = 0;
333         mValues[8] = 1;
334 
335         // scale
336         addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
337         // translate back the pivot
338         addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
339     }
340 
341     /** Set the matrix to skew by sx and sy. */
342     @Override
setSkew(float kx, float ky)343     public void setSkew(float kx, float ky) {
344         mValues[0] = 1;
345         mValues[1] = kx;
346         mValues[2] = -0;
347         mValues[3] = ky;
348         mValues[4] = 1;
349         mValues[5] = 0;
350         mValues[6] = 0;
351         mValues[7] = 0;
352         mValues[8] = 1;
353     }
354 
355     /**
356      * Set the matrix to the concatenation of the two specified matrices,
357      * returning true if the the result can be represented. Either of the two
358      * matrices may also be the target matrix. this = a * b
359      */
setConcat(Matrix a, Matrix b)360     public boolean setConcat(Matrix a, Matrix b) {
361         if (a == this) {
362             preConcat(b);
363         } else if (b == this) {
364             postConcat(b);
365         } else {
366             Matrix tmp = new Matrix(b);
367             tmp.addTransform(a.mValues);
368             set(tmp);
369         }
370 
371         return true;
372     }
373 
374     @Override
setConcat(_Original_Matrix a, _Original_Matrix b)375     public boolean setConcat(_Original_Matrix a, _Original_Matrix b) {
376         throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
377     }
378 
379     /**
380      * Preconcats the matrix with the specified translation.
381      * M' = M * T(dx, dy)
382      */
383     @Override
preTranslate(float dx, float dy)384     public boolean preTranslate(float dx, float dy) {
385         // create a matrix that will be multiply by this
386         Matrix m = new Matrix(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 });
387         m.addTransform(this.mValues);
388 
389         System.arraycopy(m.mValues, 0, mValues, 0, 9);
390         return true;
391     }
392 
393     /**
394      * Preconcats the matrix with the specified scale.
395      * M' = M * S(sx, sy, px, py)
396      */
397     @Override
preScale(float sx, float sy, float px, float py)398     public boolean preScale(float sx, float sy, float px, float py) {
399         Matrix m = new Matrix();
400         m.setScale(sx, sy, px, py);
401         m.addTransform(mValues);
402         set(m);
403 
404         return true;
405     }
406 
407     /**
408      * Preconcats the matrix with the specified scale.
409      * M' = M * S(sx, sy)
410      */
411     @Override
preScale(float sx, float sy)412     public boolean preScale(float sx, float sy) {
413         Matrix m = new Matrix();
414         m.setScale(sx, sy);
415         m.addTransform(mValues);
416         set(m);
417 
418         return true;
419     }
420 
421     /**
422      * Preconcats the matrix with the specified rotation.
423      * M' = M * R(degrees, px, py)
424      */
425     @Override
preRotate(float degrees, float px, float py)426     public boolean preRotate(float degrees, float px, float py) {
427         Matrix m = new Matrix();
428         m.setRotate(degrees, px, py);
429         m.addTransform(mValues);
430         set(m);
431 
432         return true;
433     }
434 
435     /**
436      * Preconcats the matrix with the specified rotation.
437      * M' = M * R(degrees)
438      */
439     @Override
preRotate(float degrees)440     public boolean preRotate(float degrees) {
441         Matrix m = new Matrix();
442         m.setRotate(degrees);
443         m.addTransform(mValues);
444         set(m);
445 
446         return true;
447     }
448 
449     /**
450      * Preconcats the matrix with the specified skew.
451      * M' = M * K(kx, ky, px, py)
452      */
453     @Override
preSkew(float kx, float ky, float px, float py)454     public boolean preSkew(float kx, float ky, float px, float py) {
455         Matrix m = new Matrix();
456         m.setSkew(kx, ky, px, py);
457         m.addTransform(mValues);
458         set(m);
459 
460         return true;
461     }
462 
463     /**
464      * Preconcats the matrix with the specified skew.
465      * M' = M * K(kx, ky)
466      */
467     @Override
preSkew(float kx, float ky)468     public boolean preSkew(float kx, float ky) {
469         Matrix m = new Matrix();
470         m.setSkew(kx, ky);
471         m.addTransform(mValues);
472         set(m);
473 
474         return true;
475     }
476 
477     /**
478      * Preconcats the matrix with the specified matrix.
479      * M' = M * other
480      */
preConcat(Matrix other)481     public boolean preConcat(Matrix other) {
482         Matrix m = new Matrix(other);
483         other.addTransform(mValues);
484         set(m);
485 
486         return true;
487     }
488 
489     @Override
preConcat(_Original_Matrix other)490     public boolean preConcat(_Original_Matrix other) {
491         throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
492     }
493 
494     /**
495      * Postconcats the matrix with the specified translation.
496      * M' = T(dx, dy) * M
497      */
498     @Override
postTranslate(float dx, float dy)499     public boolean postTranslate(float dx, float dy) {
500         addTransform(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 });
501         return true;
502     }
503 
504     /**
505      * Postconcats the matrix with the specified scale.
506      * M' = S(sx, sy, px, py) * M
507      */
508     @Override
postScale(float sx, float sy, float px, float py)509     public boolean postScale(float sx, float sy, float px, float py) {
510         // TODO: do it in one pass
511         // translate so that the pivot is in 0,0
512         addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
513         // scale
514         addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
515         // translate back the pivot
516         addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
517 
518         return true;
519     }
520 
521     /**
522      * Postconcats the matrix with the specified scale.
523      * M' = S(sx, sy) * M
524      */
525     @Override
postScale(float sx, float sy)526     public boolean postScale(float sx, float sy) {
527         addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
528         return true;
529     }
530 
531     /**
532      * Postconcats the matrix with the specified rotation.
533      * M' = R(degrees, px, py) * M
534      */
535     @Override
postRotate(float degrees, float px, float py)536     public boolean postRotate(float degrees, float px, float py) {
537         // TODO: do it in one pass
538         // translate so that the pivot is in 0,0
539         addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
540         // scale
541         double rad = Math.toRadians(degrees);
542         float cos = (float)Math.cos(rad);
543         float sin = (float)Math.sin(rad);
544         addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
545         // translate back the pivot
546         addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
547 
548         return true;
549     }
550 
551     /**
552      * Postconcats the matrix with the specified rotation.
553      * M' = R(degrees) * M
554      */
555     @Override
postRotate(float degrees)556     public boolean postRotate(float degrees) {
557         double rad = Math.toRadians(degrees);
558         float cos = (float)Math.cos(rad);
559         float sin = (float)Math.sin(rad);
560         addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
561 
562         return true;
563     }
564 
565     /**
566      * Postconcats the matrix with the specified skew.
567      * M' = K(kx, ky, px, py) * M
568      */
569     @Override
postSkew(float kx, float ky, float px, float py)570     public boolean postSkew(float kx, float ky, float px, float py) {
571         // TODO: do it in one pass
572         // translate so that the pivot is in 0,0
573         addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
574         // scale
575         addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
576         // translate back the pivot
577         addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
578 
579         return true;
580     }
581 
582     /**
583      * Postconcats the matrix with the specified skew.
584      * M' = K(kx, ky) * M
585      */
586     @Override
postSkew(float kx, float ky)587     public boolean postSkew(float kx, float ky) {
588         addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
589 
590         return true;
591     }
592 
593     /**
594      * Postconcats the matrix with the specified matrix.
595      * M' = other * M
596      */
postConcat(Matrix other)597     public boolean postConcat(Matrix other) {
598         addTransform(other.mValues);
599 
600         return true;
601     }
602 
603     @Override
postConcat(_Original_Matrix other)604     public boolean postConcat(_Original_Matrix other) {
605         throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
606     }
607 
608     /** Controlls how the src rect should align into the dst rect for
609         setRectToRect().
610     */
611     public enum ScaleToFit {
612         /**
613          * Scale in X and Y independently, so that src matches dst exactly.
614          * This may change the aspect ratio of the src.
615          */
616         FILL    (0),
617         /**
618          * Compute a scale that will maintain the original src aspect ratio,
619          * but will also ensure that src fits entirely inside dst. At least one
620          * axis (X or Y) will fit exactly. START aligns the result to the
621          * left and top edges of dst.
622          */
623         START   (1),
624         /**
625          * Compute a scale that will maintain the original src aspect ratio,
626          * but will also ensure that src fits entirely inside dst. At least one
627          * axis (X or Y) will fit exactly. The result is centered inside dst.
628          */
629         CENTER  (2),
630         /**
631          * Compute a scale that will maintain the original src aspect ratio,
632          * but will also ensure that src fits entirely inside dst. At least one
633          * axis (X or Y) will fit exactly. END aligns the result to the
634          * right and bottom edges of dst.
635          */
636         END     (3);
637 
638         // the native values must match those in SkMatrix.h
ScaleToFit(int nativeInt)639         ScaleToFit(int nativeInt) {
640             this.nativeInt = nativeInt;
641         }
642         final int nativeInt;
643     }
644 
645     /**
646      * Set the matrix to the scale and translate values that map the source
647      * rectangle to the destination rectangle, returning true if the result
648      * can be represented.
649      *
650      * @param src the source rectangle to map from.
651      * @param dst the destination rectangle to map to.
652      * @param stf the ScaleToFit option
653      * @return true if the matrix can be represented by the rectangle mapping.
654      */
setRectToRect(RectF src, RectF dst, ScaleToFit stf)655     public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) {
656         if (dst == null || src == null) {
657             throw new NullPointerException();
658         }
659 
660         if (src.isEmpty()) {
661             reset();
662             return false;
663         }
664 
665         if (dst.isEmpty()) {
666             mValues[0] = mValues[1] = mValues[2] = mValues[3] = mValues[4] = mValues[5]
667                = mValues[6] = mValues[7] = 0;
668             mValues[8] = 1;
669         } else {
670             float    tx, sx = dst.width() / src.width();
671             float    ty, sy = dst.height() / src.height();
672             boolean  xLarger = false;
673 
674             if (stf != ScaleToFit.FILL) {
675                 if (sx > sy) {
676                     xLarger = true;
677                     sx = sy;
678                 } else {
679                     sy = sx;
680                 }
681             }
682 
683             tx = dst.left - src.left * sx;
684             ty = dst.top - src.top * sy;
685             if (stf == ScaleToFit.CENTER || stf == ScaleToFit.END) {
686                 float diff;
687 
688                 if (xLarger) {
689                     diff = dst.width() - src.width() * sy;
690                 } else {
691                     diff = dst.height() - src.height() * sy;
692                 }
693 
694                 if (stf == ScaleToFit.CENTER) {
695                     diff = diff / 2;
696                 }
697 
698                 if (xLarger) {
699                     tx += diff;
700                 } else {
701                     ty += diff;
702                 }
703             }
704 
705             mValues[0] = sx;
706             mValues[4] = sy;
707             mValues[2] = tx;
708             mValues[5] = ty;
709             mValues[1]  = mValues[3] = mValues[6] = mValues[7] = 0;
710 
711         }
712         // shared cleanup
713         mValues[8] = 1;
714         return true;
715     }
716 
717     @Override
setRectToRect(RectF src, RectF dst, _Original_Matrix.ScaleToFit stf)718     public boolean setRectToRect(RectF src, RectF dst, _Original_Matrix.ScaleToFit stf) {
719         throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
720     }
721 
722     /**
723      * Set the matrix such that the specified src points would map to the
724      * specified dst points. The "points" are represented as an array of floats,
725      * order [x0, y0, x1, y1, ...], where each "point" is 2 float values.
726      *
727      * @param src   The array of src [x,y] pairs (points)
728      * @param srcIndex Index of the first pair of src values
729      * @param dst   The array of dst [x,y] pairs (points)
730      * @param dstIndex Index of the first pair of dst values
731      * @param pointCount The number of pairs/points to be used. Must be [0..4]
732      * @return true if the matrix was set to the specified transformation
733      */
734     @Override
setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount)735     public boolean setPolyToPoly(float[] src, int srcIndex,
736                                  float[] dst, int dstIndex,
737                                  int pointCount) {
738         if (pointCount > 4) {
739             throw new IllegalArgumentException();
740         }
741         checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
742         throw new UnsupportedOperationException("STUB NEEDED");
743     }
744 
745     /**
746      * If this matrix can be inverted, return true and if inverse is not null,
747      * set inverse to be the inverse of this matrix. If this matrix cannot be
748      * inverted, ignore inverse and return false.
749      */
invert(Matrix inverse)750     public boolean invert(Matrix inverse) {
751         if (inverse == null) {
752             return false;
753         }
754 
755         try {
756             AffineTransform affineTransform = getTransform();
757             AffineTransform inverseTransform = affineTransform.createInverse();
758             inverse.mValues[0] = (float)inverseTransform.getScaleX();
759             inverse.mValues[1] = (float)inverseTransform.getShearX();
760             inverse.mValues[2] = (float)inverseTransform.getTranslateX();
761             inverse.mValues[3] = (float)inverseTransform.getScaleX();
762             inverse.mValues[4] = (float)inverseTransform.getShearY();
763             inverse.mValues[5] = (float)inverseTransform.getTranslateY();
764 
765             return true;
766         } catch (NoninvertibleTransformException e) {
767             return false;
768         }
769     }
770 
771     @Override
invert(_Original_Matrix inverse)772     public boolean invert(_Original_Matrix inverse) {
773         throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
774     }
775 
776     /**
777     * Apply this matrix to the array of 2D points specified by src, and write
778      * the transformed points into the array of points specified by dst. The
779      * two arrays represent their "points" as pairs of floats [x, y].
780      *
781      * @param dst   The array of dst points (x,y pairs)
782      * @param dstIndex The index of the first [x,y] pair of dst floats
783      * @param src   The array of src points (x,y pairs)
784      * @param srcIndex The index of the first [x,y] pair of src floats
785      * @param pointCount The number of points (x,y pairs) to transform
786      */
787     @Override
mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, int pointCount)788     public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
789                           int pointCount) {
790         checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
791 
792         for (int i = 0 ; i < pointCount ; i++) {
793             // just in case we are doing in place, we better put this in temp vars
794             float x = mValues[0] * src[i + srcIndex] +
795                       mValues[1] * src[i + srcIndex + 1] +
796                       mValues[2];
797             float y = mValues[3] * src[i + srcIndex] +
798                       mValues[4] * src[i + srcIndex + 1] +
799                       mValues[5];
800 
801             dst[i + dstIndex]     = x;
802             dst[i + dstIndex + 1] = y;
803         }
804     }
805 
806     /**
807     * Apply this matrix to the array of 2D vectors specified by src, and write
808      * the transformed vectors into the array of vectors specified by dst. The
809      * two arrays represent their "vectors" as pairs of floats [x, y].
810      *
811      * @param dst   The array of dst vectors (x,y pairs)
812      * @param dstIndex The index of the first [x,y] pair of dst floats
813      * @param src   The array of src vectors (x,y pairs)
814      * @param srcIndex The index of the first [x,y] pair of src floats
815      * @param vectorCount The number of vectors (x,y pairs) to transform
816      */
817     @Override
mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int vectorCount)818     public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,
819                           int vectorCount) {
820         checkPointArrays(src, srcIndex, dst, dstIndex, vectorCount);
821         throw new UnsupportedOperationException("STUB NEEDED");
822     }
823 
824     /**
825      * Apply this matrix to the array of 2D points specified by src, and write
826      * the transformed points into the array of points specified by dst. The
827      * two arrays represent their "points" as pairs of floats [x, y].
828      *
829      * @param dst   The array of dst points (x,y pairs)
830      * @param src   The array of src points (x,y pairs)
831      */
832     @Override
mapPoints(float[] dst, float[] src)833     public void mapPoints(float[] dst, float[] src) {
834         if (dst.length != src.length) {
835             throw new ArrayIndexOutOfBoundsException();
836         }
837         mapPoints(dst, 0, src, 0, dst.length >> 1);
838     }
839 
840     /**
841      * Apply this matrix to the array of 2D vectors specified by src, and write
842      * the transformed vectors into the array of vectors specified by dst. The
843      * two arrays represent their "vectors" as pairs of floats [x, y].
844      *
845      * @param dst   The array of dst vectors (x,y pairs)
846      * @param src   The array of src vectors (x,y pairs)
847      */
848     @Override
mapVectors(float[] dst, float[] src)849     public void mapVectors(float[] dst, float[] src) {
850         if (dst.length != src.length) {
851             throw new ArrayIndexOutOfBoundsException();
852         }
853         mapVectors(dst, 0, src, 0, dst.length >> 1);
854     }
855 
856     /**
857      * Apply this matrix to the array of 2D points, and write the transformed
858      * points back into the array
859      *
860      * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
861      */
862     @Override
mapPoints(float[] pts)863     public void mapPoints(float[] pts) {
864         mapPoints(pts, 0, pts, 0, pts.length >> 1);
865     }
866 
867     /**
868      * Apply this matrix to the array of 2D vectors, and write the transformed
869      * vectors back into the array.
870      * @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform.
871      */
872     @Override
mapVectors(float[] vecs)873     public void mapVectors(float[] vecs) {
874         mapVectors(vecs, 0, vecs, 0, vecs.length >> 1);
875     }
876 
877     /**
878      * Apply this matrix to the src rectangle, and write the transformed
879      * rectangle into dst. This is accomplished by transforming the 4 corners of
880      * src, and then setting dst to the bounds of those points.
881      *
882      * @param dst Where the transformed rectangle is written.
883      * @param src The original rectangle to be transformed.
884      * @return the result of calling rectStaysRect()
885      */
886     @Override
mapRect(RectF dst, RectF src)887     public boolean mapRect(RectF dst, RectF src) {
888         if (dst == null || src == null) {
889             throw new NullPointerException();
890         }
891 
892         // array with 4 corners
893         float[] corners = new float[] {
894                 src.left, src.top,
895                 src.right, src.top,
896                 src.right, src.bottom,
897                 src.left, src.bottom,
898         };
899 
900         // apply the transform to them.
901         mapPoints(corners);
902 
903         // now put the result in the rect. We take the min/max of Xs and min/max of Ys
904         dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
905         dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
906 
907         dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
908         dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
909 
910         return rectStaysRect();
911     }
912 
913     /**
914      * Apply this matrix to the rectangle, and write the transformed rectangle
915      * back into it. This is accomplished by transforming the 4 corners of rect,
916      * and then setting it to the bounds of those points
917      *
918      * @param rect The rectangle to transform.
919      * @return the result of calling rectStaysRect()
920      */
921     @Override
mapRect(RectF rect)922     public boolean mapRect(RectF rect) {
923         return mapRect(rect, rect);
924     }
925 
926     /**
927      * Return the mean radius of a circle after it has been mapped by
928      * this matrix. NOTE: in perspective this value assumes the circle
929      * has its center at the origin.
930      */
931     @Override
mapRadius(float radius)932     public float mapRadius(float radius) {
933         throw new UnsupportedOperationException("STUB NEEDED");
934     }
935 
936     /** Copy 9 values from the matrix into the array.
937     */
938     @Override
getValues(float[] values)939     public void getValues(float[] values) {
940         if (values.length < 9) {
941             throw new ArrayIndexOutOfBoundsException();
942         }
943         System.arraycopy(mValues, 0, values, 0, mValues.length);
944     }
945 
946     /** Copy 9 values from the array into the matrix.
947         Depending on the implementation of Matrix, these may be
948         transformed into 16.16 integers in the Matrix, such that
949         a subsequent call to getValues() will not yield exactly
950         the same values.
951     */
952     @Override
setValues(float[] values)953     public void setValues(float[] values) {
954         if (values.length < 9) {
955             throw new ArrayIndexOutOfBoundsException();
956         }
957         System.arraycopy(values, 0, mValues, 0, mValues.length);
958     }
959 
960     @SuppressWarnings("unused")
961     private final static int kIdentity_Mask      = 0;
962     private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
963     private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
964     private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
965     private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
966     private final static int kRectStaysRect_Mask = 0x10;
967     @SuppressWarnings("unused")
968     private final static int kUnknown_Mask       = 0x80;
969 
970     @SuppressWarnings("unused")
971     private final static int kAllMasks           = kTranslate_Mask |
972                                                      kScale_Mask |
973                                                      kAffine_Mask |
974                                                      kPerspective_Mask |
975                                                      kRectStaysRect_Mask;
976 
977     // these guys align with the masks, so we can compute a mask from a variable 0/1
978     @SuppressWarnings("unused")
979     private final static int kTranslate_Shift = 0;
980     @SuppressWarnings("unused")
981     private final static int kScale_Shift = 1;
982     @SuppressWarnings("unused")
983     private final static int kAffine_Shift = 2;
984     @SuppressWarnings("unused")
985     private final static int kPerspective_Shift = 3;
986     private final static int kRectStaysRect_Shift = 4;
987 
computeTypeMask()988     private int computeTypeMask() {
989         int mask = 0;
990 
991         if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
992             mask |= kPerspective_Mask;
993         }
994 
995         if (mValues[2] != 0. || mValues[5] != 0.) {
996             mask |= kTranslate_Mask;
997         }
998 
999         float m00 = mValues[0];
1000         float m01 = mValues[1];
1001         float m10 = mValues[3];
1002         float m11 = mValues[4];
1003 
1004         if (m01 != 0. || m10 != 0.) {
1005             mask |= kAffine_Mask;
1006         }
1007 
1008         if (m00 != 1. || m11 != 1.) {
1009             mask |= kScale_Mask;
1010         }
1011 
1012         if ((mask & kPerspective_Mask) == 0) {
1013             // map non-zero to 1
1014             int im00 = m00 != 0 ? 1 : 0;
1015             int im01 = m01 != 0 ? 1 : 0;
1016             int im10 = m10 != 0 ? 1 : 0;
1017             int im11 = m11 != 0 ? 1 : 0;
1018 
1019             // record if the (p)rimary and (s)econdary diagonals are all 0 or
1020             // all non-zero (answer is 0 or 1)
1021             int dp0 = (im00 | im11) ^ 1;  // true if both are 0
1022             int dp1 = im00 & im11;        // true if both are 1
1023             int ds0 = (im01 | im10) ^ 1;  // true if both are 0
1024             int ds1 = im01 & im10;        // true if both are 1
1025 
1026             // return 1 if primary is 1 and secondary is 0 or
1027             // primary is 0 and secondary is 1
1028             mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
1029         }
1030 
1031         return mask;
1032     }
1033 }
1034