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