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