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