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