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