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 = (float) Math.hypot(src[0], src[1]);
    665         float l2 = (float) Math.hypot(src[2], src[3]);
    666         return (float) Math.sqrt(l1 * l2);
    667     }
    668 
    669     @LayoutlibDelegate
    670     /*package*/ static void native_getValues(long native_object, float[] values) {
    671         Matrix_Delegate d = sManager.getDelegate(native_object);
    672         if (d == null) {
    673             return;
    674         }
    675 
    676         System.arraycopy(d.mValues, 0, values, 0, MATRIX_SIZE);
    677     }
    678 
    679     @LayoutlibDelegate
    680     /*package*/ static void native_setValues(long native_object, float[] values) {
    681         Matrix_Delegate d = sManager.getDelegate(native_object);
    682         if (d == null) {
    683             return;
    684         }
    685 
    686         System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE);
    687     }
    688 
    689     @LayoutlibDelegate
    690     /*package*/ static boolean native_equals(long native_a, long native_b) {
    691         Matrix_Delegate a = sManager.getDelegate(native_a);
    692         if (a == null) {
    693             return false;
    694         }
    695 
    696         Matrix_Delegate b = sManager.getDelegate(native_b);
    697         if (b == null) {
    698             return false;
    699         }
    700 
    701         for (int i = 0 ; i < MATRIX_SIZE ; i++) {
    702             if (a.mValues[i] != b.mValues[i]) {
    703                 return false;
    704             }
    705         }
    706 
    707         return true;
    708     }
    709 
    710     @LayoutlibDelegate
    711     /*package*/ static void finalizer(long native_instance) {
    712         sManager.removeJavaReferenceFor(native_instance);
    713     }
    714 
    715     // ---- Private helper methods ----
    716 
    717     /*package*/ static AffineTransform getAffineTransform(float[] matrix) {
    718         // the AffineTransform constructor takes the value in a different order
    719         // for a matrix [ 0 1 2 ]
    720         //              [ 3 4 5 ]
    721         // the order is 0, 3, 1, 4, 2, 5...
    722         return new AffineTransform(
    723                 matrix[0], matrix[3], matrix[1],
    724                 matrix[4], matrix[2], matrix[5]);
    725     }
    726 
    727     /**
    728      * Reset a matrix to the identity
    729      */
    730     private static void reset(float[] mtx) {
    731         for (int i = 0, k = 0; i < 3; i++) {
    732             for (int j = 0; j < 3; j++, k++) {
    733                 mtx[k] = ((i==j) ? 1 : 0);
    734             }
    735         }
    736     }
    737 
    738     @SuppressWarnings("unused")
    739     private final static int kIdentity_Mask      = 0;
    740     private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
    741     private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
    742     private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
    743     private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
    744     private final static int kRectStaysRect_Mask = 0x10;
    745     @SuppressWarnings("unused")
    746     private final static int kUnknown_Mask       = 0x80;
    747 
    748     @SuppressWarnings("unused")
    749     private final static int kAllMasks           = kTranslate_Mask |
    750                                                    kScale_Mask |
    751                                                    kAffine_Mask |
    752                                                    kPerspective_Mask |
    753                                                    kRectStaysRect_Mask;
    754 
    755     // these guys align with the masks, so we can compute a mask from a variable 0/1
    756     @SuppressWarnings("unused")
    757     private final static int kTranslate_Shift = 0;
    758     @SuppressWarnings("unused")
    759     private final static int kScale_Shift = 1;
    760     @SuppressWarnings("unused")
    761     private final static int kAffine_Shift = 2;
    762     @SuppressWarnings("unused")
    763     private final static int kPerspective_Shift = 3;
    764     private final static int kRectStaysRect_Shift = 4;
    765 
    766     private int computeTypeMask() {
    767         int mask = 0;
    768 
    769         if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
    770             mask |= kPerspective_Mask;
    771         }
    772 
    773         if (mValues[2] != 0. || mValues[5] != 0.) {
    774             mask |= kTranslate_Mask;
    775         }
    776 
    777         float m00 = mValues[0];
    778         float m01 = mValues[1];
    779         float m10 = mValues[3];
    780         float m11 = mValues[4];
    781 
    782         if (m01 != 0. || m10 != 0.) {
    783             mask |= kAffine_Mask;
    784         }
    785 
    786         if (m00 != 1. || m11 != 1.) {
    787             mask |= kScale_Mask;
    788         }
    789 
    790         if ((mask & kPerspective_Mask) == 0) {
    791             // map non-zero to 1
    792             int im00 = m00 != 0 ? 1 : 0;
    793             int im01 = m01 != 0 ? 1 : 0;
    794             int im10 = m10 != 0 ? 1 : 0;
    795             int im11 = m11 != 0 ? 1 : 0;
    796 
    797             // record if the (p)rimary and (s)econdary diagonals are all 0 or
    798             // all non-zero (answer is 0 or 1)
    799             int dp0 = (im00 | im11) ^ 1;  // true if both are 0
    800             int dp1 = im00 & im11;        // true if both are 1
    801             int ds0 = (im01 | im10) ^ 1;  // true if both are 0
    802             int ds1 = im01 & im10;        // true if both are 1
    803 
    804             // return 1 if primary is 1 and secondary is 0 or
    805             // primary is 0 and secondary is 1
    806             mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
    807         }
    808 
    809         return mask;
    810     }
    811 
    812     private Matrix_Delegate() {
    813         reset();
    814     }
    815 
    816     private Matrix_Delegate(float[] values) {
    817         System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE);
    818     }
    819 
    820     /**
    821      * Adds the given transformation to the current Matrix
    822      * <p/>This in effect does this = this*matrix
    823      * @param matrix
    824      */
    825     private void postTransform(float[] matrix) {
    826         float[] tmp = new float[9];
    827         multiply(tmp, mValues, matrix);
    828         mValues = tmp;
    829     }
    830 
    831     /**
    832      * Adds the given transformation to the current Matrix
    833      * <p/>This in effect does this = matrix*this
    834      * @param matrix
    835      */
    836     private void preTransform(float[] matrix) {
    837         float[] tmp = new float[9];
    838         multiply(tmp, matrix, mValues);
    839         mValues = tmp;
    840     }
    841 
    842     /**
    843      * Apply this matrix to the array of 2D points specified by src, and write
    844       * the transformed points into the array of points specified by dst. The
    845       * two arrays represent their "points" as pairs of floats [x, y].
    846       *
    847       * @param dst   The array of dst points (x,y pairs)
    848       * @param dstIndex The index of the first [x,y] pair of dst floats
    849       * @param src   The array of src points (x,y pairs)
    850       * @param srcIndex The index of the first [x,y] pair of src floats
    851       * @param pointCount The number of points (x,y pairs) to transform
    852       */
    853 
    854      private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
    855                            int pointCount) {
    856          final int count = pointCount * 2;
    857 
    858          float[] tmpDest = dst;
    859          boolean inPlace = dst == src;
    860          if (inPlace) {
    861              tmpDest = new float[dstIndex + count];
    862          }
    863 
    864          for (int i = 0 ; i < count ; i += 2) {
    865              // just in case we are doing in place, we better put this in temp vars
    866              float x = mValues[0] * src[i + srcIndex] +
    867                        mValues[1] * src[i + srcIndex + 1] +
    868                        mValues[2];
    869              float y = mValues[3] * src[i + srcIndex] +
    870                        mValues[4] * src[i + srcIndex + 1] +
    871                        mValues[5];
    872 
    873              tmpDest[i + dstIndex]     = x;
    874              tmpDest[i + dstIndex + 1] = y;
    875          }
    876 
    877          if (inPlace) {
    878              System.arraycopy(tmpDest, dstIndex, dst, dstIndex, count);
    879          }
    880      }
    881 
    882      /**
    883       * Apply this matrix to the array of 2D points, and write the transformed
    884       * points back into the array
    885       *
    886       * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
    887       */
    888 
    889      private void mapPoints(float[] pts) {
    890          mapPoints(pts, 0, pts, 0, pts.length >> 1);
    891      }
    892 
    893      private void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount) {
    894          if (hasPerspective()) {
    895              // transform the (0,0) point
    896              float[] origin = new float[] { 0.f, 0.f};
    897              mapPoints(origin);
    898 
    899              // translate the vector data as points
    900              mapPoints(dst, dstIndex, src, srcIndex, ptCount);
    901 
    902              // then substract the transformed origin.
    903              final int count = ptCount * 2;
    904              for (int i = 0 ; i < count ; i += 2) {
    905                  dst[dstIndex + i] = dst[dstIndex + i] - origin[0];
    906                  dst[dstIndex + i + 1] = dst[dstIndex + i + 1] - origin[1];
    907              }
    908          } else {
    909              // make a copy of the matrix
    910              Matrix_Delegate copy = new Matrix_Delegate(mValues);
    911 
    912              // remove the translation
    913              setTranslate(copy.mValues, 0, 0);
    914 
    915              // map the content as points.
    916              copy.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
    917          }
    918      }
    919 
    920     /**
    921      * multiply two matrices and store them in a 3rd.
    922      * <p/>This in effect does dest = a*b
    923      * dest cannot be the same as a or b.
    924      */
    925      /*package*/ static void multiply(float dest[], float[] a, float[] b) {
    926         // first row
    927         dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6];
    928         dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7];
    929         dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8];
    930 
    931         // 2nd row
    932         dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6];
    933         dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7];
    934         dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8];
    935 
    936         // 3rd row
    937         dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6];
    938         dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7];
    939         dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8];
    940     }
    941 
    942     /**
    943      * Returns a matrix that represents a given translate
    944      * @param dx
    945      * @param dy
    946      * @return
    947      */
    948     /*package*/ static float[] getTranslate(float dx, float dy) {
    949         return setTranslate(new float[9], dx, dy);
    950     }
    951 
    952     /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) {
    953         dest[0] = 1;
    954         dest[1] = 0;
    955         dest[2] = dx;
    956         dest[3] = 0;
    957         dest[4] = 1;
    958         dest[5] = dy;
    959         dest[6] = 0;
    960         dest[7] = 0;
    961         dest[8] = 1;
    962         return dest;
    963     }
    964 
    965     /*package*/ static float[] getScale(float sx, float sy) {
    966         return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
    967     }
    968 
    969     /**
    970      * Returns a matrix that represents the given scale info.
    971      * @param sx
    972      * @param sy
    973      * @param px
    974      * @param py
    975      */
    976     /*package*/ static float[] getScale(float sx, float sy, float px, float py) {
    977         float[] tmp = new float[9];
    978         float[] tmp2 = new float[9];
    979 
    980         // TODO: do it in one pass
    981 
    982         // translate tmp so that the pivot is in 0,0
    983         setTranslate(tmp, -px, -py);
    984 
    985         // scale into tmp2
    986         multiply(tmp2, tmp, getScale(sx, sy));
    987 
    988         // translate back the pivot back into tmp
    989         multiply(tmp, tmp2, getTranslate(px, py));
    990 
    991         return tmp;
    992     }
    993 
    994 
    995     /*package*/ static float[] getRotate(float degrees) {
    996         double rad = Math.toRadians(degrees);
    997         float sin = (float)Math.sin(rad);
    998         float cos = (float)Math.cos(rad);
    999 
   1000         return getRotate(sin, cos);
   1001     }
   1002 
   1003     /*package*/ static float[] getRotate(float sin, float cos) {
   1004         return setRotate(new float[9], sin, cos);
   1005     }
   1006 
   1007     /*package*/ static float[] setRotate(float[] dest, float degrees) {
   1008         double rad = Math.toRadians(degrees);
   1009         float sin = (float)Math.sin(rad);
   1010         float cos = (float)Math.cos(rad);
   1011 
   1012         return setRotate(dest, sin, cos);
   1013     }
   1014 
   1015     /*package*/ static float[] setRotate(float[] dest, float sin, float cos) {
   1016         dest[0] = cos;
   1017         dest[1] = -sin;
   1018         dest[2] = 0;
   1019         dest[3] = sin;
   1020         dest[4] = cos;
   1021         dest[5] = 0;
   1022         dest[6] = 0;
   1023         dest[7] = 0;
   1024         dest[8] = 1;
   1025         return dest;
   1026     }
   1027 
   1028     /*package*/ static float[] getRotate(float degrees, float px, float py) {
   1029         float[] tmp = new float[9];
   1030         float[] tmp2 = new float[9];
   1031 
   1032         // TODO: do it in one pass
   1033 
   1034         // translate so that the pivot is in 0,0
   1035         setTranslate(tmp, -px, -py);
   1036 
   1037         // rotate into tmp2
   1038         double rad = Math.toRadians(degrees);
   1039         float cos = (float)Math.cos(rad);
   1040         float sin = (float)Math.sin(rad);
   1041         multiply(tmp2, tmp, getRotate(sin, cos));
   1042 
   1043         // translate back the pivot back into tmp
   1044         multiply(tmp, tmp2, getTranslate(px, py));
   1045 
   1046         return tmp;
   1047     }
   1048 
   1049     /*package*/ static float[] getSkew(float kx, float ky) {
   1050         return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
   1051     }
   1052 
   1053     /*package*/ static float[] getSkew(float kx, float ky, float px, float py) {
   1054         float[] tmp = new float[9];
   1055         float[] tmp2 = new float[9];
   1056 
   1057         // TODO: do it in one pass
   1058 
   1059         // translate so that the pivot is in 0,0
   1060         setTranslate(tmp, -px, -py);
   1061 
   1062         // skew into tmp2
   1063         multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
   1064         // translate back the pivot back into tmp
   1065         multiply(tmp, tmp2, getTranslate(px, py));
   1066 
   1067         return tmp;
   1068     }
   1069 }
   1070