Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2010 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 
     20 import com.android.ide.common.rendering.api.LayoutLog;
     21 import com.android.layoutlib.bridge.Bridge;
     22 import com.android.layoutlib.bridge.impl.DelegateManager;
     23 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
     24 
     25 import android.graphics.Matrix.ScaleToFit;
     26 
     27 import java.awt.geom.AffineTransform;
     28 import java.awt.geom.NoninvertibleTransformException;
     29 
     30 /**
     31  * Delegate implementing the native methods of android.graphics.Matrix
     32  *
     33  * Through the layoutlib_create tool, the original native methods of Matrix have been replaced
     34  * by calls to methods of the same name in this delegate class.
     35  *
     36  * This class behaves like the original native implementation, but in Java, keeping previously
     37  * native data into its own objects and mapping them to int that are sent back and forth between
     38  * it and the original Matrix class.
     39  *
     40  * @see DelegateManager
     41  *
     42  */
     43 public final class Matrix_Delegate {
     44 
     45     private final static int MATRIX_SIZE = 9;
     46 
     47     // ---- delegate manager ----
     48     private static final DelegateManager<Matrix_Delegate> sManager =
     49             new DelegateManager<Matrix_Delegate>(Matrix_Delegate.class);
     50 
     51     // ---- delegate data ----
     52     private float mValues[] = new float[MATRIX_SIZE];
     53 
     54     // ---- Public Helper methods ----
     55 
     56     public static Matrix_Delegate getDelegate(long native_instance) {
     57         return sManager.getDelegate(native_instance);
     58     }
     59 
     60     /**
     61      * Returns an {@link AffineTransform} matching the given Matrix.
     62      */
     63     public static AffineTransform getAffineTransform(Matrix m) {
     64         Matrix_Delegate delegate = sManager.getDelegate(m.native_instance);
     65         if (delegate == null) {
     66             return null;
     67         }
     68 
     69         return delegate.getAffineTransform();
     70     }
     71 
     72     public static boolean hasPerspective(Matrix m) {
     73         Matrix_Delegate delegate = sManager.getDelegate(m.native_instance);
     74         if (delegate == null) {
     75             return false;
     76         }
     77 
     78         return delegate.hasPerspective();
     79     }
     80 
     81     /**
     82      * Sets the content of the matrix with the content of another matrix.
     83      */
     84     public void set(Matrix_Delegate matrix) {
     85         System.arraycopy(matrix.mValues, 0, mValues, 0, MATRIX_SIZE);
     86     }
     87 
     88     /**
     89      * Sets the content of the matrix with the content of another matrix represented as an array
     90      * of values.
     91      */
     92     public void set(float[] values) {
     93         System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE);
     94     }
     95 
     96     /**
     97      * Resets the matrix to be the identity matrix.
     98      */
     99     public void reset() {
    100         reset(mValues);
    101     }
    102 
    103     /**
    104      * Returns whether or not the matrix is identity.
    105      */
    106     public boolean isIdentity() {
    107         for (int i = 0, k = 0; i < 3; i++) {
    108             for (int j = 0; j < 3; j++, k++) {
    109                 if (mValues[k] != ((i==j) ? 1 : 0)) {
    110                     return false;
    111                 }
    112             }
    113         }
    114 
    115         return true;
    116     }
    117 
    118     public static float[] makeValues(AffineTransform matrix) {
    119         float[] values = new float[MATRIX_SIZE];
    120         values[0] = (float) matrix.getScaleX();
    121         values[1] = (float) matrix.getShearX();
    122         values[2] = (float) matrix.getTranslateX();
    123         values[3] = (float) matrix.getShearY();
    124         values[4] = (float) matrix.getScaleY();
    125         values[5] = (float) matrix.getTranslateY();
    126         values[6] = 0.f;
    127         values[7] = 0.f;
    128         values[8] = 1.f;
    129 
    130         return values;
    131     }
    132 
    133     public static Matrix_Delegate make(AffineTransform matrix) {
    134         return new Matrix_Delegate(makeValues(matrix));
    135     }
    136 
    137     public boolean mapRect(RectF dst, RectF src) {
    138         // array with 4 corners
    139         float[] corners = new float[] {
    140                 src.left, src.top,
    141                 src.right, src.top,
    142                 src.right, src.bottom,
    143                 src.left, src.bottom,
    144         };
    145 
    146         // apply the transform to them.
    147         mapPoints(corners);
    148 
    149         // now put the result in the rect. We take the min/max of Xs and min/max of Ys
    150         dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
    151         dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
    152 
    153         dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
    154         dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
    155 
    156 
    157         return (computeTypeMask() & kRectStaysRect_Mask) != 0;
    158     }
    159 
    160 
    161     /**
    162      * Returns an {@link AffineTransform} matching the matrix.
    163      */
    164     public AffineTransform getAffineTransform() {
    165         return getAffineTransform(mValues);
    166     }
    167 
    168     public boolean hasPerspective() {
    169         return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1);
    170     }
    171 
    172 
    173 
    174     // ---- native methods ----
    175 
    176     @LayoutlibDelegate
    177     /*package*/ static long native_create(long native_src_or_zero) {
    178         // create the delegate
    179         Matrix_Delegate newDelegate = new Matrix_Delegate();
    180 
    181         // copy from values if needed.
    182         if (native_src_or_zero > 0) {
    183             Matrix_Delegate oldDelegate = sManager.getDelegate(native_src_or_zero);
    184             if (oldDelegate != null) {
    185                 System.arraycopy(
    186                         oldDelegate.mValues, 0,
    187                         newDelegate.mValues, 0,
    188                         MATRIX_SIZE);
    189             }
    190         }
    191 
    192         return sManager.addNewDelegate(newDelegate);
    193     }
    194 
    195     @LayoutlibDelegate
    196     /*package*/ static boolean native_isIdentity(long native_object) {
    197         Matrix_Delegate d = sManager.getDelegate(native_object);
    198         if (d == null) {
    199             return false;
    200         }
    201 
    202         return d.isIdentity();
    203     }
    204 
    205     @LayoutlibDelegate
    206     /*package*/ static boolean native_isAffine(long native_object) {
    207         Matrix_Delegate d = sManager.getDelegate(native_object);
    208         if (d == null) {
    209             return true;
    210         }
    211 
    212         return (d.computeTypeMask() & kPerspective_Mask) == 0;
    213     }
    214 
    215     @LayoutlibDelegate
    216     /*package*/ static boolean native_rectStaysRect(long native_object) {
    217         Matrix_Delegate d = sManager.getDelegate(native_object);
    218         if (d == null) {
    219             return true;
    220         }
    221 
    222         return (d.computeTypeMask() & kRectStaysRect_Mask) != 0;
    223     }
    224 
    225     @LayoutlibDelegate
    226     /*package*/ static void native_reset(long native_object) {
    227         Matrix_Delegate d = sManager.getDelegate(native_object);
    228         if (d == null) {
    229             return;
    230         }
    231 
    232         reset(d.mValues);
    233     }
    234 
    235     @LayoutlibDelegate
    236     /*package*/ static void native_set(long native_object, long other) {
    237         Matrix_Delegate d = sManager.getDelegate(native_object);
    238         if (d == null) {
    239             return;
    240         }
    241 
    242         Matrix_Delegate src = sManager.getDelegate(other);
    243         if (src == null) {
    244             return;
    245         }
    246 
    247         System.arraycopy(src.mValues, 0, d.mValues, 0, MATRIX_SIZE);
    248     }
    249 
    250     @LayoutlibDelegate
    251     /*package*/ static void native_setTranslate(long native_object, float dx, float dy) {
    252         Matrix_Delegate d = sManager.getDelegate(native_object);
    253         if (d == null) {
    254             return;
    255         }
    256 
    257         setTranslate(d.mValues, dx, dy);
    258     }
    259 
    260     @LayoutlibDelegate
    261     /*package*/ static void native_setScale(long native_object, float sx, float sy,
    262             float px, float py) {
    263         Matrix_Delegate d = sManager.getDelegate(native_object);
    264         if (d == null) {
    265             return;
    266         }
    267 
    268         d.mValues = getScale(sx, sy, px, py);
    269     }
    270 
    271     @LayoutlibDelegate
    272     /*package*/ static void native_setScale(long native_object, float sx, float sy) {
    273         Matrix_Delegate d = sManager.getDelegate(native_object);
    274         if (d == null) {
    275             return;
    276         }
    277 
    278         d.mValues[0] = sx;
    279         d.mValues[1] = 0;
    280         d.mValues[2] = 0;
    281         d.mValues[3] = 0;
    282         d.mValues[4] = sy;
    283         d.mValues[5] = 0;
    284         d.mValues[6] = 0;
    285         d.mValues[7] = 0;
    286         d.mValues[8] = 1;
    287     }
    288 
    289     @LayoutlibDelegate
    290     /*package*/ static void native_setRotate(long native_object, float degrees, float px, float py) {
    291         Matrix_Delegate d = sManager.getDelegate(native_object);
    292         if (d == null) {
    293             return;
    294         }
    295 
    296         d.mValues = getRotate(degrees, px, py);
    297     }
    298 
    299     @LayoutlibDelegate
    300     /*package*/ static void native_setRotate(long native_object, float degrees) {
    301         Matrix_Delegate d = sManager.getDelegate(native_object);
    302         if (d == null) {
    303             return;
    304         }
    305 
    306         setRotate(d.mValues, degrees);
    307     }
    308 
    309     @LayoutlibDelegate
    310     /*package*/ static void native_setSinCos(long native_object, float sinValue, float cosValue,
    311             float px, float py) {
    312         Matrix_Delegate d = sManager.getDelegate(native_object);
    313         if (d == null) {
    314             return;
    315         }
    316 
    317         // TODO: do it in one pass
    318 
    319         // translate so that the pivot is in 0,0
    320         setTranslate(d.mValues, -px, -py);
    321 
    322         // scale
    323         d.postTransform(getRotate(sinValue, cosValue));
    324         // translate back the pivot
    325         d.postTransform(getTranslate(px, py));
    326     }
    327 
    328     @LayoutlibDelegate
    329     /*package*/ static void native_setSinCos(long native_object, float sinValue, float cosValue) {
    330         Matrix_Delegate d = sManager.getDelegate(native_object);
    331         if (d == null) {
    332             return;
    333         }
    334 
    335         setRotate(d.mValues, sinValue, cosValue);
    336     }
    337 
    338     @LayoutlibDelegate
    339     /*package*/ static void native_setSkew(long native_object, float kx, float ky,
    340             float px, float py) {
    341         Matrix_Delegate d = sManager.getDelegate(native_object);
    342         if (d == null) {
    343             return;
    344         }
    345 
    346         d.mValues = getSkew(kx, ky, px, py);
    347     }
    348 
    349     @LayoutlibDelegate
    350     /*package*/ static void native_setSkew(long native_object, float kx, float ky) {
    351         Matrix_Delegate d = sManager.getDelegate(native_object);
    352         if (d == null) {
    353             return;
    354         }
    355 
    356         d.mValues[0] = 1;
    357         d.mValues[1] = kx;
    358         d.mValues[2] = -0;
    359         d.mValues[3] = ky;
    360         d.mValues[4] = 1;
    361         d.mValues[5] = 0;
    362         d.mValues[6] = 0;
    363         d.mValues[7] = 0;
    364         d.mValues[8] = 1;
    365     }
    366 
    367     @LayoutlibDelegate
    368     /*package*/ static void native_setConcat(long native_object, long a, long b) {
    369         if (a == native_object) {
    370             native_preConcat(native_object, b);
    371             return;
    372         } else if (b == native_object) {
    373             native_postConcat(native_object, a);
    374             return;
    375         }
    376 
    377         Matrix_Delegate d = sManager.getDelegate(native_object);
    378         Matrix_Delegate a_mtx = sManager.getDelegate(a);
    379         Matrix_Delegate b_mtx = sManager.getDelegate(b);
    380         if (d != null && a_mtx != null && b_mtx != null) {
    381             multiply(d.mValues, a_mtx.mValues, b_mtx.mValues);
    382         }
    383     }
    384 
    385     @LayoutlibDelegate
    386     /*package*/ static void native_preTranslate(long native_object, float dx, float dy) {
    387         Matrix_Delegate d = sManager.getDelegate(native_object);
    388         if (d != null) {
    389             d.preTransform(getTranslate(dx, dy));
    390         }
    391     }
    392 
    393     @LayoutlibDelegate
    394     /*package*/ static void native_preScale(long native_object, float sx, float sy,
    395             float px, float py) {
    396         Matrix_Delegate d = sManager.getDelegate(native_object);
    397         if (d != null) {
    398             d.preTransform(getScale(sx, sy, px, py));
    399         }
    400     }
    401 
    402     @LayoutlibDelegate
    403     /*package*/ static void native_preScale(long native_object, float sx, float sy) {
    404         Matrix_Delegate d = sManager.getDelegate(native_object);
    405         if (d != null) {
    406             d.preTransform(getScale(sx, sy));
    407         }
    408     }
    409 
    410     @LayoutlibDelegate
    411     /*package*/ static void native_preRotate(long native_object, float degrees,
    412             float px, float py) {
    413         Matrix_Delegate d = sManager.getDelegate(native_object);
    414         if (d != null) {
    415             d.preTransform(getRotate(degrees, px, py));
    416         }
    417     }
    418 
    419     @LayoutlibDelegate
    420     /*package*/ static void native_preRotate(long native_object, float degrees) {
    421         Matrix_Delegate d = sManager.getDelegate(native_object);
    422         if (d != null) {
    423 
    424             double rad = Math.toRadians(degrees);
    425             float sin = (float) Math.sin(rad);
    426             float cos = (float) Math.cos(rad);
    427 
    428             d.preTransform(getRotate(sin, cos));
    429         }
    430     }
    431 
    432     @LayoutlibDelegate
    433     /*package*/ static void native_preSkew(long native_object, float kx, float ky,
    434             float px, float py) {
    435         Matrix_Delegate d = sManager.getDelegate(native_object);
    436         if (d != null) {
    437             d.preTransform(getSkew(kx, ky, px, py));
    438         }
    439     }
    440 
    441     @LayoutlibDelegate
    442     /*package*/ static void native_preSkew(long native_object, float kx, float ky) {
    443         Matrix_Delegate d = sManager.getDelegate(native_object);
    444         if (d != null) {
    445             d.preTransform(getSkew(kx, ky));
    446         }
    447     }
    448 
    449     @LayoutlibDelegate
    450     /*package*/ static void native_preConcat(long native_object, long other_matrix) {
    451         Matrix_Delegate d = sManager.getDelegate(native_object);
    452         Matrix_Delegate other = sManager.getDelegate(other_matrix);
    453         if (d != null && other != null) {
    454             d.preTransform(other.mValues);
    455         }
    456     }
    457 
    458     @LayoutlibDelegate
    459     /*package*/ static void native_postTranslate(long native_object, float dx, float dy) {
    460         Matrix_Delegate d = sManager.getDelegate(native_object);
    461         if (d != null) {
    462             d.postTransform(getTranslate(dx, dy));
    463         }
    464     }
    465 
    466     @LayoutlibDelegate
    467     /*package*/ static void native_postScale(long native_object, float sx, float sy,
    468             float px, float py) {
    469         Matrix_Delegate d = sManager.getDelegate(native_object);
    470         if (d != null) {
    471             d.postTransform(getScale(sx, sy, px, py));
    472         }
    473     }
    474 
    475     @LayoutlibDelegate
    476     /*package*/ static void native_postScale(long native_object, float sx, float sy) {
    477         Matrix_Delegate d = sManager.getDelegate(native_object);
    478         if (d != null) {
    479             d.postTransform(getScale(sx, sy));
    480         }
    481     }
    482 
    483     @LayoutlibDelegate
    484     /*package*/ static void native_postRotate(long native_object, float degrees,
    485             float px, float py) {
    486         Matrix_Delegate d = sManager.getDelegate(native_object);
    487         if (d != null) {
    488             d.postTransform(getRotate(degrees, px, py));
    489         }
    490     }
    491 
    492     @LayoutlibDelegate
    493     /*package*/ static void native_postRotate(long native_object, float degrees) {
    494         Matrix_Delegate d = sManager.getDelegate(native_object);
    495         if (d != null) {
    496             d.postTransform(getRotate(degrees));
    497         }
    498     }
    499 
    500     @LayoutlibDelegate
    501     /*package*/ static void native_postSkew(long native_object, float kx, float ky,
    502             float px, float py) {
    503         Matrix_Delegate d = sManager.getDelegate(native_object);
    504         if (d != null) {
    505             d.postTransform(getSkew(kx, ky, px, py));
    506         }
    507     }
    508 
    509     @LayoutlibDelegate
    510     /*package*/ static void native_postSkew(long native_object, float kx, float ky) {
    511         Matrix_Delegate d = sManager.getDelegate(native_object);
    512         if (d != null) {
    513             d.postTransform(getSkew(kx, ky));
    514         }
    515     }
    516 
    517     @LayoutlibDelegate
    518     /*package*/ static void native_postConcat(long native_object, long other_matrix) {
    519         Matrix_Delegate d = sManager.getDelegate(native_object);
    520         Matrix_Delegate other = sManager.getDelegate(other_matrix);
    521         if (d != null && other != null) {
    522             d.postTransform(other.mValues);
    523         }
    524     }
    525 
    526     @LayoutlibDelegate
    527     /*package*/ static boolean native_setRectToRect(long native_object, RectF src,
    528             RectF dst, int stf) {
    529         Matrix_Delegate d = sManager.getDelegate(native_object);
    530         if (d == null) {
    531             return false;
    532         }
    533 
    534         if (src.isEmpty()) {
    535             reset(d.mValues);
    536             return false;
    537         }
    538 
    539         if (dst.isEmpty()) {
    540             d.mValues[0] = d.mValues[1] = d.mValues[2] = d.mValues[3] = d.mValues[4] = d.mValues[5]
    541                = d.mValues[6] = d.mValues[7] = 0;
    542             d.mValues[8] = 1;
    543         } else {
    544             float    tx, sx = dst.width() / src.width();
    545             float    ty, sy = dst.height() / src.height();
    546             boolean  xLarger = false;
    547 
    548             if (stf != ScaleToFit.FILL.nativeInt) {
    549                 if (sx > sy) {
    550                     xLarger = true;
    551                     sx = sy;
    552                 } else {
    553                     sy = sx;
    554                 }
    555             }
    556 
    557             tx = dst.left - src.left * sx;
    558             ty = dst.top - src.top * sy;
    559             if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) {
    560                 float diff;
    561 
    562                 if (xLarger) {
    563                     diff = dst.width() - src.width() * sy;
    564                 } else {
    565                     diff = dst.height() - src.height() * sy;
    566                 }
    567 
    568                 if (stf == ScaleToFit.CENTER.nativeInt) {
    569                     diff = diff / 2;
    570                 }
    571 
    572                 if (xLarger) {
    573                     tx += diff;
    574                 } else {
    575                     ty += diff;
    576                 }
    577             }
    578 
    579             d.mValues[0] = sx;
    580             d.mValues[4] = sy;
    581             d.mValues[2] = tx;
    582             d.mValues[5] = ty;
    583             d.mValues[1]  = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0;
    584 
    585         }
    586         // shared cleanup
    587         d.mValues[8] = 1;
    588         return true;
    589     }
    590 
    591     @LayoutlibDelegate
    592     /*package*/ static boolean native_setPolyToPoly(long native_object, float[] src, int srcIndex,
    593             float[] dst, int dstIndex, int pointCount) {
    594         // FIXME
    595         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
    596                 "Matrix.setPolyToPoly is not supported.",
    597                 null, null /*data*/);
    598         return false;
    599     }
    600 
    601     @LayoutlibDelegate
    602     /*package*/ static boolean native_invert(long native_object, long inverse) {
    603         Matrix_Delegate d = sManager.getDelegate(native_object);
    604         if (d == null) {
    605             return false;
    606         }
    607 
    608         Matrix_Delegate inv_mtx = sManager.getDelegate(inverse);
    609         if (inv_mtx == null) {
    610             return false;
    611         }
    612 
    613         try {
    614             AffineTransform affineTransform = d.getAffineTransform();
    615             AffineTransform inverseTransform = affineTransform.createInverse();
    616             inv_mtx.mValues[0] = (float)inverseTransform.getScaleX();
    617             inv_mtx.mValues[1] = (float)inverseTransform.getShearX();
    618             inv_mtx.mValues[2] = (float)inverseTransform.getTranslateX();
    619             inv_mtx.mValues[3] = (float)inverseTransform.getScaleX();
    620             inv_mtx.mValues[4] = (float)inverseTransform.getShearY();
    621             inv_mtx.mValues[5] = (float)inverseTransform.getTranslateY();
    622 
    623             return true;
    624         } catch (NoninvertibleTransformException e) {
    625             return false;
    626         }
    627     }
    628 
    629     @LayoutlibDelegate
    630     /*package*/ static void native_mapPoints(long native_object, float[] dst, int dstIndex,
    631             float[] src, int srcIndex, int ptCount, boolean isPts) {
    632         Matrix_Delegate d = sManager.getDelegate(native_object);
    633         if (d == null) {
    634             return;
    635         }
    636 
    637         if (isPts) {
    638             d.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
    639         } else {
    640             d.mapVectors(dst, dstIndex, src, srcIndex, ptCount);
    641         }
    642     }
    643 
    644     @LayoutlibDelegate
    645     /*package*/ static boolean native_mapRect(long native_object, RectF dst, RectF src) {
    646         Matrix_Delegate d = sManager.getDelegate(native_object);
    647         if (d == null) {
    648             return false;
    649         }
    650 
    651         return d.mapRect(dst, src);
    652     }
    653 
    654     @LayoutlibDelegate
    655     /*package*/ static float native_mapRadius(long native_object, float radius) {
    656         Matrix_Delegate d = sManager.getDelegate(native_object);
    657         if (d == null) {
    658             return 0.f;
    659         }
    660 
    661         float[] src = new float[] { radius, 0.f, 0.f, radius };
    662         d.mapVectors(src, 0, src, 0, 2);
    663 
    664         float l1 = getPointLength(src, 0);
    665         float l2 = getPointLength(src, 2);
    666 
    667         return (float) Math.sqrt(l1 * l2);
    668     }
    669 
    670     @LayoutlibDelegate
    671     /*package*/ static void native_getValues(long native_object, float[] values) {
    672         Matrix_Delegate d = sManager.getDelegate(native_object);
    673         if (d == null) {
    674             return;
    675         }
    676 
    677         System.arraycopy(d.mValues, 0, d.mValues, 0, MATRIX_SIZE);
    678     }
    679 
    680     @LayoutlibDelegate
    681     /*package*/ static void native_setValues(long native_object, float[] values) {
    682         Matrix_Delegate d = sManager.getDelegate(native_object);
    683         if (d == null) {
    684             return;
    685         }
    686 
    687         System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE);
    688     }
    689 
    690     @LayoutlibDelegate
    691     /*package*/ static boolean native_equals(long native_a, long native_b) {
    692         Matrix_Delegate a = sManager.getDelegate(native_a);
    693         if (a == null) {
    694             return false;
    695         }
    696 
    697         Matrix_Delegate b = sManager.getDelegate(native_b);
    698         if (b == null) {
    699             return false;
    700         }
    701 
    702         for (int i = 0 ; i < MATRIX_SIZE ; i++) {
    703             if (a.mValues[i] != b.mValues[i]) {
    704                 return false;
    705             }
    706         }
    707 
    708         return true;
    709     }
    710 
    711     @LayoutlibDelegate
    712     /*package*/ static void finalizer(long native_instance) {
    713         sManager.removeJavaReferenceFor(native_instance);
    714     }
    715 
    716     // ---- Private helper methods ----
    717 
    718     /*package*/ static AffineTransform getAffineTransform(float[] matrix) {
    719         // the AffineTransform constructor takes the value in a different order
    720         // for a matrix [ 0 1 2 ]
    721         //              [ 3 4 5 ]
    722         // the order is 0, 3, 1, 4, 2, 5...
    723         return new AffineTransform(
    724                 matrix[0], matrix[3], matrix[1],
    725                 matrix[4], matrix[2], matrix[5]);
    726     }
    727 
    728     /**
    729      * Reset a matrix to the identity
    730      */
    731     private static void reset(float[] mtx) {
    732         for (int i = 0, k = 0; i < 3; i++) {
    733             for (int j = 0; j < 3; j++, k++) {
    734                 mtx[k] = ((i==j) ? 1 : 0);
    735             }
    736         }
    737     }
    738 
    739     @SuppressWarnings("unused")
    740     private final static int kIdentity_Mask      = 0;
    741     private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
    742     private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
    743     private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
    744     private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
    745     private final static int kRectStaysRect_Mask = 0x10;
    746     @SuppressWarnings("unused")
    747     private final static int kUnknown_Mask       = 0x80;
    748 
    749     @SuppressWarnings("unused")
    750     private final static int kAllMasks           = kTranslate_Mask |
    751                                                    kScale_Mask |
    752                                                    kAffine_Mask |
    753                                                    kPerspective_Mask |
    754                                                    kRectStaysRect_Mask;
    755 
    756     // these guys align with the masks, so we can compute a mask from a variable 0/1
    757     @SuppressWarnings("unused")
    758     private final static int kTranslate_Shift = 0;
    759     @SuppressWarnings("unused")
    760     private final static int kScale_Shift = 1;
    761     @SuppressWarnings("unused")
    762     private final static int kAffine_Shift = 2;
    763     @SuppressWarnings("unused")
    764     private final static int kPerspective_Shift = 3;
    765     private final static int kRectStaysRect_Shift = 4;
    766 
    767     private int computeTypeMask() {
    768         int mask = 0;
    769 
    770         if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
    771             mask |= kPerspective_Mask;
    772         }
    773 
    774         if (mValues[2] != 0. || mValues[5] != 0.) {
    775             mask |= kTranslate_Mask;
    776         }
    777 
    778         float m00 = mValues[0];
    779         float m01 = mValues[1];
    780         float m10 = mValues[3];
    781         float m11 = mValues[4];
    782 
    783         if (m01 != 0. || m10 != 0.) {
    784             mask |= kAffine_Mask;
    785         }
    786 
    787         if (m00 != 1. || m11 != 1.) {
    788             mask |= kScale_Mask;
    789         }
    790 
    791         if ((mask & kPerspective_Mask) == 0) {
    792             // map non-zero to 1
    793             int im00 = m00 != 0 ? 1 : 0;
    794             int im01 = m01 != 0 ? 1 : 0;
    795             int im10 = m10 != 0 ? 1 : 0;
    796             int im11 = m11 != 0 ? 1 : 0;
    797 
    798             // record if the (p)rimary and (s)econdary diagonals are all 0 or
    799             // all non-zero (answer is 0 or 1)
    800             int dp0 = (im00 | im11) ^ 1;  // true if both are 0
    801             int dp1 = im00 & im11;        // true if both are 1
    802             int ds0 = (im01 | im10) ^ 1;  // true if both are 0
    803             int ds1 = im01 & im10;        // true if both are 1
    804 
    805             // return 1 if primary is 1 and secondary is 0 or
    806             // primary is 0 and secondary is 1
    807             mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
    808         }
    809 
    810         return mask;
    811     }
    812 
    813     private Matrix_Delegate() {
    814         reset();
    815     }
    816 
    817     private Matrix_Delegate(float[] values) {
    818         System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE);
    819     }
    820 
    821     /**
    822      * Adds the given transformation to the current Matrix
    823      * <p/>This in effect does this = this*matrix
    824      * @param matrix
    825      */
    826     private void postTransform(float[] matrix) {
    827         float[] tmp = new float[9];
    828         multiply(tmp, mValues, matrix);
    829         mValues = tmp;
    830     }
    831 
    832     /**
    833      * Adds the given transformation to the current Matrix
    834      * <p/>This in effect does this = matrix*this
    835      * @param matrix
    836      */
    837     private void preTransform(float[] matrix) {
    838         float[] tmp = new float[9];
    839         multiply(tmp, matrix, mValues);
    840         mValues = tmp;
    841     }
    842 
    843     /**
    844      * Apply this matrix to the array of 2D points specified by src, and write
    845       * the transformed points into the array of points specified by dst. The
    846       * two arrays represent their "points" as pairs of floats [x, y].
    847       *
    848       * @param dst   The array of dst points (x,y pairs)
    849       * @param dstIndex The index of the first [x,y] pair of dst floats
    850       * @param src   The array of src points (x,y pairs)
    851       * @param srcIndex The index of the first [x,y] pair of src floats
    852       * @param pointCount The number of points (x,y pairs) to transform
    853       */
    854 
    855      private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
    856                            int pointCount) {
    857          final int count = pointCount * 2;
    858 
    859          float[] tmpDest = dst;
    860          boolean inPlace = dst == src;
    861          if (inPlace) {
    862              tmpDest = new float[dstIndex + count];
    863          }
    864 
    865          for (int i = 0 ; i < count ; i += 2) {
    866              // just in case we are doing in place, we better put this in temp vars
    867              float x = mValues[0] * src[i + srcIndex] +
    868                        mValues[1] * src[i + srcIndex + 1] +
    869                        mValues[2];
    870              float y = mValues[3] * src[i + srcIndex] +
    871                        mValues[4] * src[i + srcIndex + 1] +
    872                        mValues[5];
    873 
    874              tmpDest[i + dstIndex]     = x;
    875              tmpDest[i + dstIndex + 1] = y;
    876          }
    877 
    878          if (inPlace) {
    879              System.arraycopy(tmpDest, dstIndex, dst, dstIndex, count);
    880          }
    881      }
    882 
    883      /**
    884       * Apply this matrix to the array of 2D points, and write the transformed
    885       * points back into the array
    886       *
    887       * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
    888       */
    889 
    890      private void mapPoints(float[] pts) {
    891          mapPoints(pts, 0, pts, 0, pts.length >> 1);
    892      }
    893 
    894      private void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount) {
    895          if (hasPerspective()) {
    896              // transform the (0,0) point
    897              float[] origin = new float[] { 0.f, 0.f};
    898              mapPoints(origin);
    899 
    900              // translate the vector data as points
    901              mapPoints(dst, dstIndex, src, srcIndex, ptCount);
    902 
    903              // then substract the transformed origin.
    904              final int count = ptCount * 2;
    905              for (int i = 0 ; i < count ; i += 2) {
    906                  dst[dstIndex + i] = dst[dstIndex + i] - origin[0];
    907                  dst[dstIndex + i + 1] = dst[dstIndex + i + 1] - origin[1];
    908              }
    909          } else {
    910              // make a copy of the matrix
    911              Matrix_Delegate copy = new Matrix_Delegate(mValues);
    912 
    913              // remove the translation
    914              setTranslate(copy.mValues, 0, 0);
    915 
    916              // map the content as points.
    917              copy.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
    918          }
    919      }
    920 
    921      private static float getPointLength(float[] src, int index) {
    922          return (float) Math.sqrt(src[index] * src[index] + src[index + 1] * src[index + 1]);
    923      }
    924 
    925     /**
    926      * multiply two matrices and store them in a 3rd.
    927      * <p/>This in effect does dest = a*b
    928      * dest cannot be the same as a or b.
    929      */
    930      /*package*/ static void multiply(float dest[], float[] a, float[] b) {
    931         // first row
    932         dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6];
    933         dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7];
    934         dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8];
    935 
    936         // 2nd row
    937         dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6];
    938         dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7];
    939         dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8];
    940 
    941         // 3rd row
    942         dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6];
    943         dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7];
    944         dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8];
    945     }
    946 
    947     /**
    948      * Returns a matrix that represents a given translate
    949      * @param dx
    950      * @param dy
    951      * @return
    952      */
    953     /*package*/ static float[] getTranslate(float dx, float dy) {
    954         return setTranslate(new float[9], dx, dy);
    955     }
    956 
    957     /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) {
    958         dest[0] = 1;
    959         dest[1] = 0;
    960         dest[2] = dx;
    961         dest[3] = 0;
    962         dest[4] = 1;
    963         dest[5] = dy;
    964         dest[6] = 0;
    965         dest[7] = 0;
    966         dest[8] = 1;
    967         return dest;
    968     }
    969 
    970     /*package*/ static float[] getScale(float sx, float sy) {
    971         return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
    972     }
    973 
    974     /**
    975      * Returns a matrix that represents the given scale info.
    976      * @param sx
    977      * @param sy
    978      * @param px
    979      * @param py
    980      */
    981     /*package*/ static float[] getScale(float sx, float sy, float px, float py) {
    982         float[] tmp = new float[9];
    983         float[] tmp2 = new float[9];
    984 
    985         // TODO: do it in one pass
    986 
    987         // translate tmp so that the pivot is in 0,0
    988         setTranslate(tmp, -px, -py);
    989 
    990         // scale into tmp2
    991         multiply(tmp2, tmp, getScale(sx, sy));
    992 
    993         // translate back the pivot back into tmp
    994         multiply(tmp, tmp2, getTranslate(px, py));
    995 
    996         return tmp;
    997     }
    998 
    999 
   1000     /*package*/ static float[] getRotate(float degrees) {
   1001         double rad = Math.toRadians(degrees);
   1002         float sin = (float)Math.sin(rad);
   1003         float cos = (float)Math.cos(rad);
   1004 
   1005         return getRotate(sin, cos);
   1006     }
   1007 
   1008     /*package*/ static float[] getRotate(float sin, float cos) {
   1009         return setRotate(new float[9], sin, cos);
   1010     }
   1011 
   1012     /*package*/ static float[] setRotate(float[] dest, float degrees) {
   1013         double rad = Math.toRadians(degrees);
   1014         float sin = (float)Math.sin(rad);
   1015         float cos = (float)Math.cos(rad);
   1016 
   1017         return setRotate(dest, sin, cos);
   1018     }
   1019 
   1020     /*package*/ static float[] setRotate(float[] dest, float sin, float cos) {
   1021         dest[0] = cos;
   1022         dest[1] = -sin;
   1023         dest[2] = 0;
   1024         dest[3] = sin;
   1025         dest[4] = cos;
   1026         dest[5] = 0;
   1027         dest[6] = 0;
   1028         dest[7] = 0;
   1029         dest[8] = 1;
   1030         return dest;
   1031     }
   1032 
   1033     /*package*/ static float[] getRotate(float degrees, float px, float py) {
   1034         float[] tmp = new float[9];
   1035         float[] tmp2 = new float[9];
   1036 
   1037         // TODO: do it in one pass
   1038 
   1039         // translate so that the pivot is in 0,0
   1040         setTranslate(tmp, -px, -py);
   1041 
   1042         // rotate into tmp2
   1043         double rad = Math.toRadians(degrees);
   1044         float cos = (float)Math.cos(rad);
   1045         float sin = (float)Math.sin(rad);
   1046         multiply(tmp2, tmp, getRotate(sin, cos));
   1047 
   1048         // translate back the pivot back into tmp
   1049         multiply(tmp, tmp2, getTranslate(px, py));
   1050 
   1051         return tmp;
   1052     }
   1053 
   1054     /*package*/ static float[] getSkew(float kx, float ky) {
   1055         return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
   1056     }
   1057 
   1058     /*package*/ static float[] getSkew(float kx, float ky, float px, float py) {
   1059         float[] tmp = new float[9];
   1060         float[] tmp2 = new float[9];
   1061 
   1062         // TODO: do it in one pass
   1063 
   1064         // translate so that the pivot is in 0,0
   1065         setTranslate(tmp, -px, -py);
   1066 
   1067         // skew into tmp2
   1068         multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
   1069         // translate back the pivot back into tmp
   1070         multiply(tmp, tmp2, getTranslate(px, py));
   1071 
   1072         return tmp;
   1073     }
   1074 }
   1075