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