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
30import 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 */
45public 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