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