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