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