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(int 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 int native_create(int 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(int 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_rectStaysRect(int native_object) {
    207         Matrix_Delegate d = sManager.getDelegate(native_object);
    208         if (d == null) {
    209             return true;
    210         }
    211 
    212         return (d.computeTypeMask() & kRectStaysRect_Mask) != 0;
    213     }
    214 
    215     @LayoutlibDelegate
    216     /*package*/ static void native_reset(int native_object) {
    217         Matrix_Delegate d = sManager.getDelegate(native_object);
    218         if (d == null) {
    219             return;
    220         }
    221 
    222         reset(d.mValues);
    223     }
    224 
    225     @LayoutlibDelegate
    226     /*package*/ static void native_set(int native_object, int other) {
    227         Matrix_Delegate d = sManager.getDelegate(native_object);
    228         if (d == null) {
    229             return;
    230         }
    231 
    232         Matrix_Delegate src = sManager.getDelegate(other);
    233         if (src == null) {
    234             return;
    235         }
    236 
    237         System.arraycopy(src.mValues, 0, d.mValues, 0, MATRIX_SIZE);
    238     }
    239 
    240     @LayoutlibDelegate
    241     /*package*/ static void native_setTranslate(int native_object, float dx, float dy) {
    242         Matrix_Delegate d = sManager.getDelegate(native_object);
    243         if (d == null) {
    244             return;
    245         }
    246 
    247         setTranslate(d.mValues, dx, dy);
    248     }
    249 
    250     @LayoutlibDelegate
    251     /*package*/ static void native_setScale(int native_object, float sx, float sy,
    252             float px, float py) {
    253         Matrix_Delegate d = sManager.getDelegate(native_object);
    254         if (d == null) {
    255             return;
    256         }
    257 
    258         d.mValues = getScale(sx, sy, px, py);
    259     }
    260 
    261     @LayoutlibDelegate
    262     /*package*/ static void native_setScale(int native_object, float sx, float sy) {
    263         Matrix_Delegate d = sManager.getDelegate(native_object);
    264         if (d == null) {
    265             return;
    266         }
    267 
    268         d.mValues[0] = sx;
    269         d.mValues[1] = 0;
    270         d.mValues[2] = 0;
    271         d.mValues[3] = 0;
    272         d.mValues[4] = sy;
    273         d.mValues[5] = 0;
    274         d.mValues[6] = 0;
    275         d.mValues[7] = 0;
    276         d.mValues[8] = 1;
    277     }
    278 
    279     @LayoutlibDelegate
    280     /*package*/ static void native_setRotate(int native_object, float degrees, float px, float py) {
    281         Matrix_Delegate d = sManager.getDelegate(native_object);
    282         if (d == null) {
    283             return;
    284         }
    285 
    286         d.mValues = getRotate(degrees, px, py);
    287     }
    288 
    289     @LayoutlibDelegate
    290     /*package*/ static void native_setRotate(int native_object, float degrees) {
    291         Matrix_Delegate d = sManager.getDelegate(native_object);
    292         if (d == null) {
    293             return;
    294         }
    295 
    296         setRotate(d.mValues, degrees);
    297     }
    298 
    299     @LayoutlibDelegate
    300     /*package*/ static void native_setSinCos(int native_object, float sinValue, float cosValue,
    301             float px, float py) {
    302         Matrix_Delegate d = sManager.getDelegate(native_object);
    303         if (d == null) {
    304             return;
    305         }
    306 
    307         // TODO: do it in one pass
    308 
    309         // translate so that the pivot is in 0,0
    310         setTranslate(d.mValues, -px, -py);
    311 
    312         // scale
    313         d.postTransform(getRotate(sinValue, cosValue));
    314         // translate back the pivot
    315         d.postTransform(getTranslate(px, py));
    316     }
    317 
    318     @LayoutlibDelegate
    319     /*package*/ static void native_setSinCos(int native_object, float sinValue, float cosValue) {
    320         Matrix_Delegate d = sManager.getDelegate(native_object);
    321         if (d == null) {
    322             return;
    323         }
    324 
    325         setRotate(d.mValues, sinValue, cosValue);
    326     }
    327 
    328     @LayoutlibDelegate
    329     /*package*/ static void native_setSkew(int native_object, float kx, float ky,
    330             float px, float py) {
    331         Matrix_Delegate d = sManager.getDelegate(native_object);
    332         if (d == null) {
    333             return;
    334         }
    335 
    336         d.mValues = getSkew(kx, ky, px, py);
    337     }
    338 
    339     @LayoutlibDelegate
    340     /*package*/ static void native_setSkew(int native_object, float kx, float ky) {
    341         Matrix_Delegate d = sManager.getDelegate(native_object);
    342         if (d == null) {
    343             return;
    344         }
    345 
    346         d.mValues[0] = 1;
    347         d.mValues[1] = kx;
    348         d.mValues[2] = -0;
    349         d.mValues[3] = ky;
    350         d.mValues[4] = 1;
    351         d.mValues[5] = 0;
    352         d.mValues[6] = 0;
    353         d.mValues[7] = 0;
    354         d.mValues[8] = 1;
    355     }
    356 
    357     @LayoutlibDelegate
    358     /*package*/ static boolean native_setConcat(int native_object, int a, int b) {
    359         if (a == native_object) {
    360             return native_preConcat(native_object, b);
    361         } else if (b == native_object) {
    362             return native_postConcat(native_object, a);
    363         }
    364 
    365         Matrix_Delegate d = sManager.getDelegate(native_object);
    366         if (d == null) {
    367             return false;
    368         }
    369 
    370         Matrix_Delegate a_mtx = sManager.getDelegate(a);
    371         if (a_mtx == null) {
    372             return false;
    373         }
    374 
    375         Matrix_Delegate b_mtx = sManager.getDelegate(b);
    376         if (b_mtx == null) {
    377             return false;
    378         }
    379 
    380         multiply(d.mValues, a_mtx.mValues, b_mtx.mValues);
    381 
    382         return true;
    383     }
    384 
    385     @LayoutlibDelegate
    386     /*package*/ static boolean native_preTranslate(int native_object, float dx, float dy) {
    387         Matrix_Delegate d = sManager.getDelegate(native_object);
    388         if (d == null) {
    389             return false;
    390         }
    391 
    392         d.preTransform(getTranslate(dx, dy));
    393         return true;
    394     }
    395 
    396     @LayoutlibDelegate
    397     /*package*/ static boolean native_preScale(int native_object, float sx, float sy,
    398             float px, float py) {
    399         Matrix_Delegate d = sManager.getDelegate(native_object);
    400         if (d == null) {
    401             return false;
    402         }
    403 
    404         d.preTransform(getScale(sx, sy, px, py));
    405         return true;
    406     }
    407 
    408     @LayoutlibDelegate
    409     /*package*/ static boolean native_preScale(int native_object, float sx, float sy) {
    410         Matrix_Delegate d = sManager.getDelegate(native_object);
    411         if (d == null) {
    412             return false;
    413         }
    414 
    415         d.preTransform(getScale(sx, sy));
    416         return true;
    417     }
    418 
    419     @LayoutlibDelegate
    420     /*package*/ static boolean native_preRotate(int native_object, float degrees,
    421             float px, float py) {
    422         Matrix_Delegate d = sManager.getDelegate(native_object);
    423         if (d == null) {
    424             return false;
    425         }
    426 
    427         d.preTransform(getRotate(degrees, px, py));
    428         return true;
    429     }
    430 
    431     @LayoutlibDelegate
    432     /*package*/ static boolean native_preRotate(int native_object, float degrees) {
    433         Matrix_Delegate d = sManager.getDelegate(native_object);
    434         if (d == null) {
    435             return false;
    436         }
    437 
    438         double rad = Math.toRadians(degrees);
    439         float sin = (float)Math.sin(rad);
    440         float cos = (float)Math.cos(rad);
    441 
    442         d.preTransform(getRotate(sin, cos));
    443         return true;
    444     }
    445 
    446     @LayoutlibDelegate
    447     /*package*/ static boolean native_preSkew(int native_object, float kx, float ky,
    448             float px, float py) {
    449         Matrix_Delegate d = sManager.getDelegate(native_object);
    450         if (d == null) {
    451             return false;
    452         }
    453 
    454         d.preTransform(getSkew(kx, ky, px, py));
    455         return true;
    456     }
    457 
    458     @LayoutlibDelegate
    459     /*package*/ static boolean native_preSkew(int native_object, float kx, float ky) {
    460         Matrix_Delegate d = sManager.getDelegate(native_object);
    461         if (d == null) {
    462             return false;
    463         }
    464 
    465         d.preTransform(getSkew(kx, ky));
    466         return true;
    467     }
    468 
    469     @LayoutlibDelegate
    470     /*package*/ static boolean native_preConcat(int native_object, int other_matrix) {
    471         Matrix_Delegate d = sManager.getDelegate(native_object);
    472         if (d == null) {
    473             return false;
    474         }
    475 
    476         Matrix_Delegate other = sManager.getDelegate(other_matrix);
    477         if (d == null) {
    478             return false;
    479         }
    480 
    481         d.preTransform(other.mValues);
    482         return true;
    483     }
    484 
    485     @LayoutlibDelegate
    486     /*package*/ static boolean native_postTranslate(int native_object, float dx, float dy) {
    487         Matrix_Delegate d = sManager.getDelegate(native_object);
    488         if (d == null) {
    489             return false;
    490         }
    491 
    492         d.postTransform(getTranslate(dx, dy));
    493         return true;
    494     }
    495 
    496     @LayoutlibDelegate
    497     /*package*/ static boolean native_postScale(int native_object, float sx, float sy,
    498             float px, float py) {
    499         Matrix_Delegate d = sManager.getDelegate(native_object);
    500         if (d == null) {
    501             return false;
    502         }
    503 
    504         d.postTransform(getScale(sx, sy, px, py));
    505         return true;
    506     }
    507 
    508     @LayoutlibDelegate
    509     /*package*/ static boolean native_postScale(int native_object, float sx, float sy) {
    510         Matrix_Delegate d = sManager.getDelegate(native_object);
    511         if (d == null) {
    512             return false;
    513         }
    514 
    515         d.postTransform(getScale(sx, sy));
    516         return true;
    517     }
    518 
    519     @LayoutlibDelegate
    520     /*package*/ static boolean native_postRotate(int native_object, float degrees,
    521             float px, float py) {
    522         Matrix_Delegate d = sManager.getDelegate(native_object);
    523         if (d == null) {
    524             return false;
    525         }
    526 
    527         d.postTransform(getRotate(degrees, px, py));
    528         return true;
    529     }
    530 
    531     @LayoutlibDelegate
    532     /*package*/ static boolean native_postRotate(int native_object, float degrees) {
    533         Matrix_Delegate d = sManager.getDelegate(native_object);
    534         if (d == null) {
    535             return false;
    536         }
    537 
    538         d.postTransform(getRotate(degrees));
    539         return true;
    540     }
    541 
    542     @LayoutlibDelegate
    543     /*package*/ static boolean native_postSkew(int native_object, float kx, float ky,
    544             float px, float py) {
    545         Matrix_Delegate d = sManager.getDelegate(native_object);
    546         if (d == null) {
    547             return false;
    548         }
    549 
    550         d.postTransform(getSkew(kx, ky, px, py));
    551         return true;
    552     }
    553 
    554     @LayoutlibDelegate
    555     /*package*/ static boolean native_postSkew(int native_object, float kx, float ky) {
    556         Matrix_Delegate d = sManager.getDelegate(native_object);
    557         if (d == null) {
    558             return false;
    559         }
    560 
    561         d.postTransform(getSkew(kx, ky));
    562         return true;
    563     }
    564 
    565     @LayoutlibDelegate
    566     /*package*/ static boolean native_postConcat(int native_object, int other_matrix) {
    567         Matrix_Delegate d = sManager.getDelegate(native_object);
    568         if (d == null) {
    569             return false;
    570         }
    571 
    572         Matrix_Delegate other = sManager.getDelegate(other_matrix);
    573         if (d == null) {
    574             return false;
    575         }
    576 
    577         d.postTransform(other.mValues);
    578         return true;
    579     }
    580 
    581     @LayoutlibDelegate
    582     /*package*/ static boolean native_setRectToRect(int native_object, RectF src,
    583             RectF dst, int stf) {
    584         Matrix_Delegate d = sManager.getDelegate(native_object);
    585         if (d == null) {
    586             return false;
    587         }
    588 
    589         if (src.isEmpty()) {
    590             reset(d.mValues);
    591             return false;
    592         }
    593 
    594         if (dst.isEmpty()) {
    595             d.mValues[0] = d.mValues[1] = d.mValues[2] = d.mValues[3] = d.mValues[4] = d.mValues[5]
    596                = d.mValues[6] = d.mValues[7] = 0;
    597             d.mValues[8] = 1;
    598         } else {
    599             float    tx, sx = dst.width() / src.width();
    600             float    ty, sy = dst.height() / src.height();
    601             boolean  xLarger = false;
    602 
    603             if (stf != ScaleToFit.FILL.nativeInt) {
    604                 if (sx > sy) {
    605                     xLarger = true;
    606                     sx = sy;
    607                 } else {
    608                     sy = sx;
    609                 }
    610             }
    611 
    612             tx = dst.left - src.left * sx;
    613             ty = dst.top - src.top * sy;
    614             if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) {
    615                 float diff;
    616 
    617                 if (xLarger) {
    618                     diff = dst.width() - src.width() * sy;
    619                 } else {
    620                     diff = dst.height() - src.height() * sy;
    621                 }
    622 
    623                 if (stf == ScaleToFit.CENTER.nativeInt) {
    624                     diff = diff / 2;
    625                 }
    626 
    627                 if (xLarger) {
    628                     tx += diff;
    629                 } else {
    630                     ty += diff;
    631                 }
    632             }
    633 
    634             d.mValues[0] = sx;
    635             d.mValues[4] = sy;
    636             d.mValues[2] = tx;
    637             d.mValues[5] = ty;
    638             d.mValues[1]  = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0;
    639 
    640         }
    641         // shared cleanup
    642         d.mValues[8] = 1;
    643         return true;
    644     }
    645 
    646     @LayoutlibDelegate
    647     /*package*/ static boolean native_setPolyToPoly(int native_object, float[] src, int srcIndex,
    648             float[] dst, int dstIndex, int pointCount) {
    649         // FIXME
    650         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
    651                 "Matrix.setPolyToPoly is not supported.",
    652                 null, null /*data*/);
    653         return false;
    654     }
    655 
    656     @LayoutlibDelegate
    657     /*package*/ static boolean native_invert(int native_object, int inverse) {
    658         Matrix_Delegate d = sManager.getDelegate(native_object);
    659         if (d == null) {
    660             return false;
    661         }
    662 
    663         Matrix_Delegate inv_mtx = sManager.getDelegate(inverse);
    664         if (inv_mtx == null) {
    665             return false;
    666         }
    667 
    668         try {
    669             AffineTransform affineTransform = d.getAffineTransform();
    670             AffineTransform inverseTransform = affineTransform.createInverse();
    671             inv_mtx.mValues[0] = (float)inverseTransform.getScaleX();
    672             inv_mtx.mValues[1] = (float)inverseTransform.getShearX();
    673             inv_mtx.mValues[2] = (float)inverseTransform.getTranslateX();
    674             inv_mtx.mValues[3] = (float)inverseTransform.getScaleX();
    675             inv_mtx.mValues[4] = (float)inverseTransform.getShearY();
    676             inv_mtx.mValues[5] = (float)inverseTransform.getTranslateY();
    677 
    678             return true;
    679         } catch (NoninvertibleTransformException e) {
    680             return false;
    681         }
    682     }
    683 
    684     @LayoutlibDelegate
    685     /*package*/ static void native_mapPoints(int native_object, float[] dst, int dstIndex,
    686             float[] src, int srcIndex, int ptCount, boolean isPts) {
    687         Matrix_Delegate d = sManager.getDelegate(native_object);
    688         if (d == null) {
    689             return;
    690         }
    691 
    692         if (isPts) {
    693             d.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
    694         } else {
    695             d.mapVectors(dst, dstIndex, src, srcIndex, ptCount);
    696         }
    697     }
    698 
    699     @LayoutlibDelegate
    700     /*package*/ static boolean native_mapRect(int native_object, RectF dst, RectF src) {
    701         Matrix_Delegate d = sManager.getDelegate(native_object);
    702         if (d == null) {
    703             return false;
    704         }
    705 
    706         return d.mapRect(dst, src);
    707     }
    708 
    709     @LayoutlibDelegate
    710     /*package*/ static float native_mapRadius(int native_object, float radius) {
    711         Matrix_Delegate d = sManager.getDelegate(native_object);
    712         if (d == null) {
    713             return 0.f;
    714         }
    715 
    716         float[] src = new float[] { radius, 0.f, 0.f, radius };
    717         d.mapVectors(src, 0, src, 0, 2);
    718 
    719         float l1 = getPointLength(src, 0);
    720         float l2 = getPointLength(src, 2);
    721 
    722         return (float) Math.sqrt(l1 * l2);
    723     }
    724 
    725     @LayoutlibDelegate
    726     /*package*/ static void native_getValues(int native_object, float[] values) {
    727         Matrix_Delegate d = sManager.getDelegate(native_object);
    728         if (d == null) {
    729             return;
    730         }
    731 
    732         System.arraycopy(d.mValues, 0, d.mValues, 0, MATRIX_SIZE);
    733     }
    734 
    735     @LayoutlibDelegate
    736     /*package*/ static void native_setValues(int native_object, float[] values) {
    737         Matrix_Delegate d = sManager.getDelegate(native_object);
    738         if (d == null) {
    739             return;
    740         }
    741 
    742         System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE);
    743     }
    744 
    745     @LayoutlibDelegate
    746     /*package*/ static boolean native_equals(int native_a, int native_b) {
    747         Matrix_Delegate a = sManager.getDelegate(native_a);
    748         if (a == null) {
    749             return false;
    750         }
    751 
    752         Matrix_Delegate b = sManager.getDelegate(native_b);
    753         if (b == null) {
    754             return false;
    755         }
    756 
    757         for (int i = 0 ; i < MATRIX_SIZE ; i++) {
    758             if (a.mValues[i] != b.mValues[i]) {
    759                 return false;
    760             }
    761         }
    762 
    763         return true;
    764     }
    765 
    766     @LayoutlibDelegate
    767     /*package*/ static void finalizer(int native_instance) {
    768         sManager.removeJavaReferenceFor(native_instance);
    769     }
    770 
    771     // ---- Private helper methods ----
    772 
    773     /*package*/ static AffineTransform getAffineTransform(float[] matrix) {
    774         // the AffineTransform constructor takes the value in a different order
    775         // for a matrix [ 0 1 2 ]
    776         //              [ 3 4 5 ]
    777         // the order is 0, 3, 1, 4, 2, 5...
    778         return new AffineTransform(
    779                 matrix[0], matrix[3], matrix[1],
    780                 matrix[4], matrix[2], matrix[5]);
    781     }
    782 
    783     /**
    784      * Reset a matrix to the identity
    785      */
    786     private static void reset(float[] mtx) {
    787         for (int i = 0, k = 0; i < 3; i++) {
    788             for (int j = 0; j < 3; j++, k++) {
    789                 mtx[k] = ((i==j) ? 1 : 0);
    790             }
    791         }
    792     }
    793 
    794     @SuppressWarnings("unused")
    795     private final static int kIdentity_Mask      = 0;
    796     private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
    797     private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
    798     private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
    799     private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
    800     private final static int kRectStaysRect_Mask = 0x10;
    801     @SuppressWarnings("unused")
    802     private final static int kUnknown_Mask       = 0x80;
    803 
    804     @SuppressWarnings("unused")
    805     private final static int kAllMasks           = kTranslate_Mask |
    806                                                    kScale_Mask |
    807                                                    kAffine_Mask |
    808                                                    kPerspective_Mask |
    809                                                    kRectStaysRect_Mask;
    810 
    811     // these guys align with the masks, so we can compute a mask from a variable 0/1
    812     @SuppressWarnings("unused")
    813     private final static int kTranslate_Shift = 0;
    814     @SuppressWarnings("unused")
    815     private final static int kScale_Shift = 1;
    816     @SuppressWarnings("unused")
    817     private final static int kAffine_Shift = 2;
    818     @SuppressWarnings("unused")
    819     private final static int kPerspective_Shift = 3;
    820     private final static int kRectStaysRect_Shift = 4;
    821 
    822     private int computeTypeMask() {
    823         int mask = 0;
    824 
    825         if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
    826             mask |= kPerspective_Mask;
    827         }
    828 
    829         if (mValues[2] != 0. || mValues[5] != 0.) {
    830             mask |= kTranslate_Mask;
    831         }
    832 
    833         float m00 = mValues[0];
    834         float m01 = mValues[1];
    835         float m10 = mValues[3];
    836         float m11 = mValues[4];
    837 
    838         if (m01 != 0. || m10 != 0.) {
    839             mask |= kAffine_Mask;
    840         }
    841 
    842         if (m00 != 1. || m11 != 1.) {
    843             mask |= kScale_Mask;
    844         }
    845 
    846         if ((mask & kPerspective_Mask) == 0) {
    847             // map non-zero to 1
    848             int im00 = m00 != 0 ? 1 : 0;
    849             int im01 = m01 != 0 ? 1 : 0;
    850             int im10 = m10 != 0 ? 1 : 0;
    851             int im11 = m11 != 0 ? 1 : 0;
    852 
    853             // record if the (p)rimary and (s)econdary diagonals are all 0 or
    854             // all non-zero (answer is 0 or 1)
    855             int dp0 = (im00 | im11) ^ 1;  // true if both are 0
    856             int dp1 = im00 & im11;        // true if both are 1
    857             int ds0 = (im01 | im10) ^ 1;  // true if both are 0
    858             int ds1 = im01 & im10;        // true if both are 1
    859 
    860             // return 1 if primary is 1 and secondary is 0 or
    861             // primary is 0 and secondary is 1
    862             mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
    863         }
    864 
    865         return mask;
    866     }
    867 
    868     private Matrix_Delegate() {
    869         reset();
    870     }
    871 
    872     private Matrix_Delegate(float[] values) {
    873         System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE);
    874     }
    875 
    876     /**
    877      * Adds the given transformation to the current Matrix
    878      * <p/>This in effect does this = this*matrix
    879      * @param matrix
    880      */
    881     private void postTransform(float[] matrix) {
    882         float[] tmp = new float[9];
    883         multiply(tmp, mValues, matrix);
    884         mValues = tmp;
    885     }
    886 
    887     /**
    888      * Adds the given transformation to the current Matrix
    889      * <p/>This in effect does this = matrix*this
    890      * @param matrix
    891      */
    892     private void preTransform(float[] matrix) {
    893         float[] tmp = new float[9];
    894         multiply(tmp, matrix, mValues);
    895         mValues = tmp;
    896     }
    897 
    898     /**
    899      * Apply this matrix to the array of 2D points specified by src, and write
    900       * the transformed points into the array of points specified by dst. The
    901       * two arrays represent their "points" as pairs of floats [x, y].
    902       *
    903       * @param dst   The array of dst points (x,y pairs)
    904       * @param dstIndex The index of the first [x,y] pair of dst floats
    905       * @param src   The array of src points (x,y pairs)
    906       * @param srcIndex The index of the first [x,y] pair of src floats
    907       * @param pointCount The number of points (x,y pairs) to transform
    908       */
    909 
    910      private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
    911                            int pointCount) {
    912          final int count = pointCount * 2;
    913 
    914          float[] tmpDest = dst;
    915          boolean inPlace = dst == src;
    916          if (inPlace) {
    917              tmpDest = new float[dstIndex + count];
    918          }
    919 
    920          for (int i = 0 ; i < count ; i += 2) {
    921              // just in case we are doing in place, we better put this in temp vars
    922              float x = mValues[0] * src[i + srcIndex] +
    923                        mValues[1] * src[i + srcIndex + 1] +
    924                        mValues[2];
    925              float y = mValues[3] * src[i + srcIndex] +
    926                        mValues[4] * src[i + srcIndex + 1] +
    927                        mValues[5];
    928 
    929              tmpDest[i + dstIndex]     = x;
    930              tmpDest[i + dstIndex + 1] = y;
    931          }
    932 
    933          if (inPlace) {
    934              System.arraycopy(tmpDest, dstIndex, dst, dstIndex, count);
    935          }
    936      }
    937 
    938      /**
    939       * Apply this matrix to the array of 2D points, and write the transformed
    940       * points back into the array
    941       *
    942       * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
    943       */
    944 
    945      private void mapPoints(float[] pts) {
    946          mapPoints(pts, 0, pts, 0, pts.length >> 1);
    947      }
    948 
    949      private void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount) {
    950          if (hasPerspective()) {
    951              // transform the (0,0) point
    952              float[] origin = new float[] { 0.f, 0.f};
    953              mapPoints(origin);
    954 
    955              // translate the vector data as points
    956              mapPoints(dst, dstIndex, src, srcIndex, ptCount);
    957 
    958              // then substract the transformed origin.
    959              final int count = ptCount * 2;
    960              for (int i = 0 ; i < count ; i += 2) {
    961                  dst[dstIndex + i] = dst[dstIndex + i] - origin[0];
    962                  dst[dstIndex + i + 1] = dst[dstIndex + i + 1] - origin[1];
    963              }
    964          } else {
    965              // make a copy of the matrix
    966              Matrix_Delegate copy = new Matrix_Delegate(mValues);
    967 
    968              // remove the translation
    969              setTranslate(copy.mValues, 0, 0);
    970 
    971              // map the content as points.
    972              copy.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
    973          }
    974      }
    975 
    976      private static float getPointLength(float[] src, int index) {
    977          return (float) Math.sqrt(src[index] * src[index] + src[index + 1] * src[index + 1]);
    978      }
    979 
    980     /**
    981      * multiply two matrices and store them in a 3rd.
    982      * <p/>This in effect does dest = a*b
    983      * dest cannot be the same as a or b.
    984      */
    985      /*package*/ static void multiply(float dest[], float[] a, float[] b) {
    986         // first row
    987         dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6];
    988         dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7];
    989         dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8];
    990 
    991         // 2nd row
    992         dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6];
    993         dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7];
    994         dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8];
    995 
    996         // 3rd row
    997         dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6];
    998         dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7];
    999         dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8];
   1000     }
   1001 
   1002     /**
   1003      * Returns a matrix that represents a given translate
   1004      * @param dx
   1005      * @param dy
   1006      * @return
   1007      */
   1008     /*package*/ static float[] getTranslate(float dx, float dy) {
   1009         return setTranslate(new float[9], dx, dy);
   1010     }
   1011 
   1012     /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) {
   1013         dest[0] = 1;
   1014         dest[1] = 0;
   1015         dest[2] = dx;
   1016         dest[3] = 0;
   1017         dest[4] = 1;
   1018         dest[5] = dy;
   1019         dest[6] = 0;
   1020         dest[7] = 0;
   1021         dest[8] = 1;
   1022         return dest;
   1023     }
   1024 
   1025     /*package*/ static float[] getScale(float sx, float sy) {
   1026         return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
   1027     }
   1028 
   1029     /**
   1030      * Returns a matrix that represents the given scale info.
   1031      * @param sx
   1032      * @param sy
   1033      * @param px
   1034      * @param py
   1035      */
   1036     /*package*/ static float[] getScale(float sx, float sy, 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 tmp so that the pivot is in 0,0
   1043         setTranslate(tmp, -px, -py);
   1044 
   1045         // scale into tmp2
   1046         multiply(tmp2, tmp, getScale(sx, sy));
   1047 
   1048         // translate back the pivot back into tmp
   1049         multiply(tmp, tmp2, getTranslate(px, py));
   1050 
   1051         return tmp;
   1052     }
   1053 
   1054 
   1055     /*package*/ static float[] getRotate(float degrees) {
   1056         double rad = Math.toRadians(degrees);
   1057         float sin = (float)Math.sin(rad);
   1058         float cos = (float)Math.cos(rad);
   1059 
   1060         return getRotate(sin, cos);
   1061     }
   1062 
   1063     /*package*/ static float[] getRotate(float sin, float cos) {
   1064         return setRotate(new float[9], sin, cos);
   1065     }
   1066 
   1067     /*package*/ static float[] setRotate(float[] dest, float degrees) {
   1068         double rad = Math.toRadians(degrees);
   1069         float sin = (float)Math.sin(rad);
   1070         float cos = (float)Math.cos(rad);
   1071 
   1072         return setRotate(dest, sin, cos);
   1073     }
   1074 
   1075     /*package*/ static float[] setRotate(float[] dest, float sin, float cos) {
   1076         dest[0] = cos;
   1077         dest[1] = -sin;
   1078         dest[2] = 0;
   1079         dest[3] = sin;
   1080         dest[4] = cos;
   1081         dest[5] = 0;
   1082         dest[6] = 0;
   1083         dest[7] = 0;
   1084         dest[8] = 1;
   1085         return dest;
   1086     }
   1087 
   1088     /*package*/ static float[] getRotate(float degrees, float px, float py) {
   1089         float[] tmp = new float[9];
   1090         float[] tmp2 = new float[9];
   1091 
   1092         // TODO: do it in one pass
   1093 
   1094         // translate so that the pivot is in 0,0
   1095         setTranslate(tmp, -px, -py);
   1096 
   1097         // rotate into tmp2
   1098         double rad = Math.toRadians(degrees);
   1099         float cos = (float)Math.cos(rad);
   1100         float sin = (float)Math.sin(rad);
   1101         multiply(tmp2, tmp, getRotate(sin, cos));
   1102 
   1103         // translate back the pivot back into tmp
   1104         multiply(tmp, tmp2, getTranslate(px, py));
   1105 
   1106         return tmp;
   1107     }
   1108 
   1109     /*package*/ static float[] getSkew(float kx, float ky) {
   1110         return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
   1111     }
   1112 
   1113     /*package*/ static float[] getSkew(float kx, float ky, float px, float py) {
   1114         float[] tmp = new float[9];
   1115         float[] tmp2 = new float[9];
   1116 
   1117         // TODO: do it in one pass
   1118 
   1119         // translate so that the pivot is in 0,0
   1120         setTranslate(tmp, -px, -py);
   1121 
   1122         // skew into tmp2
   1123         multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
   1124         // translate back the pivot back into tmp
   1125         multiply(tmp, tmp2, getTranslate(px, py));
   1126 
   1127         return tmp;
   1128     }
   1129 }
   1130