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