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(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 (other == 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 (other == 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