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