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 = (float) Math.hypot(src[0], src[1]);
665         float l2 = (float) Math.hypot(src[2], src[3]);
666         return (float) Math.sqrt(l1 * l2);
667     }
668 
669     @LayoutlibDelegate
native_getValues(long native_object, float[] values)670     /*package*/ static void native_getValues(long native_object, float[] values) {
671         Matrix_Delegate d = sManager.getDelegate(native_object);
672         if (d == null) {
673             return;
674         }
675 
676         System.arraycopy(d.mValues, 0, values, 0, MATRIX_SIZE);
677     }
678 
679     @LayoutlibDelegate
native_setValues(long native_object, float[] values)680     /*package*/ static void native_setValues(long native_object, float[] values) {
681         Matrix_Delegate d = sManager.getDelegate(native_object);
682         if (d == null) {
683             return;
684         }
685 
686         System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE);
687     }
688 
689     @LayoutlibDelegate
native_equals(long native_a, long native_b)690     /*package*/ static boolean native_equals(long native_a, long native_b) {
691         Matrix_Delegate a = sManager.getDelegate(native_a);
692         if (a == null) {
693             return false;
694         }
695 
696         Matrix_Delegate b = sManager.getDelegate(native_b);
697         if (b == null) {
698             return false;
699         }
700 
701         for (int i = 0 ; i < MATRIX_SIZE ; i++) {
702             if (a.mValues[i] != b.mValues[i]) {
703                 return false;
704             }
705         }
706 
707         return true;
708     }
709 
710     @LayoutlibDelegate
finalizer(long native_instance)711     /*package*/ static void finalizer(long native_instance) {
712         sManager.removeJavaReferenceFor(native_instance);
713     }
714 
715     // ---- Private helper methods ----
716 
getAffineTransform(float[] matrix)717     /*package*/ static AffineTransform getAffineTransform(float[] matrix) {
718         // the AffineTransform constructor takes the value in a different order
719         // for a matrix [ 0 1 2 ]
720         //              [ 3 4 5 ]
721         // the order is 0, 3, 1, 4, 2, 5...
722         return new AffineTransform(
723                 matrix[0], matrix[3], matrix[1],
724                 matrix[4], matrix[2], matrix[5]);
725     }
726 
727     /**
728      * Reset a matrix to the identity
729      */
reset(float[] mtx)730     private static void reset(float[] mtx) {
731         for (int i = 0, k = 0; i < 3; i++) {
732             for (int j = 0; j < 3; j++, k++) {
733                 mtx[k] = ((i==j) ? 1 : 0);
734             }
735         }
736     }
737 
738     @SuppressWarnings("unused")
739     private final static int kIdentity_Mask      = 0;
740     private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
741     private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
742     private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
743     private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
744     private final static int kRectStaysRect_Mask = 0x10;
745     @SuppressWarnings("unused")
746     private final static int kUnknown_Mask       = 0x80;
747 
748     @SuppressWarnings("unused")
749     private final static int kAllMasks           = kTranslate_Mask |
750                                                    kScale_Mask |
751                                                    kAffine_Mask |
752                                                    kPerspective_Mask |
753                                                    kRectStaysRect_Mask;
754 
755     // these guys align with the masks, so we can compute a mask from a variable 0/1
756     @SuppressWarnings("unused")
757     private final static int kTranslate_Shift = 0;
758     @SuppressWarnings("unused")
759     private final static int kScale_Shift = 1;
760     @SuppressWarnings("unused")
761     private final static int kAffine_Shift = 2;
762     @SuppressWarnings("unused")
763     private final static int kPerspective_Shift = 3;
764     private final static int kRectStaysRect_Shift = 4;
765 
computeTypeMask()766     private int computeTypeMask() {
767         int mask = 0;
768 
769         if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
770             mask |= kPerspective_Mask;
771         }
772 
773         if (mValues[2] != 0. || mValues[5] != 0.) {
774             mask |= kTranslate_Mask;
775         }
776 
777         float m00 = mValues[0];
778         float m01 = mValues[1];
779         float m10 = mValues[3];
780         float m11 = mValues[4];
781 
782         if (m01 != 0. || m10 != 0.) {
783             mask |= kAffine_Mask;
784         }
785 
786         if (m00 != 1. || m11 != 1.) {
787             mask |= kScale_Mask;
788         }
789 
790         if ((mask & kPerspective_Mask) == 0) {
791             // map non-zero to 1
792             int im00 = m00 != 0 ? 1 : 0;
793             int im01 = m01 != 0 ? 1 : 0;
794             int im10 = m10 != 0 ? 1 : 0;
795             int im11 = m11 != 0 ? 1 : 0;
796 
797             // record if the (p)rimary and (s)econdary diagonals are all 0 or
798             // all non-zero (answer is 0 or 1)
799             int dp0 = (im00 | im11) ^ 1;  // true if both are 0
800             int dp1 = im00 & im11;        // true if both are 1
801             int ds0 = (im01 | im10) ^ 1;  // true if both are 0
802             int ds1 = im01 & im10;        // true if both are 1
803 
804             // return 1 if primary is 1 and secondary is 0 or
805             // primary is 0 and secondary is 1
806             mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
807         }
808 
809         return mask;
810     }
811 
Matrix_Delegate()812     private Matrix_Delegate() {
813         reset();
814     }
815 
Matrix_Delegate(float[] values)816     private Matrix_Delegate(float[] values) {
817         System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE);
818     }
819 
820     /**
821      * Adds the given transformation to the current Matrix
822      * <p/>This in effect does this = this*matrix
823      * @param matrix
824      */
postTransform(float[] matrix)825     private void postTransform(float[] matrix) {
826         float[] tmp = new float[9];
827         multiply(tmp, mValues, matrix);
828         mValues = tmp;
829     }
830 
831     /**
832      * Adds the given transformation to the current Matrix
833      * <p/>This in effect does this = matrix*this
834      * @param matrix
835      */
preTransform(float[] matrix)836     private void preTransform(float[] matrix) {
837         float[] tmp = new float[9];
838         multiply(tmp, matrix, mValues);
839         mValues = tmp;
840     }
841 
842     /**
843      * Apply this matrix to the array of 2D points specified by src, and write
844       * the transformed points into the array of points specified by dst. The
845       * two arrays represent their "points" as pairs of floats [x, y].
846       *
847       * @param dst   The array of dst points (x,y pairs)
848       * @param dstIndex The index of the first [x,y] pair of dst floats
849       * @param src   The array of src points (x,y pairs)
850       * @param srcIndex The index of the first [x,y] pair of src floats
851       * @param pointCount The number of points (x,y pairs) to transform
852       */
853 
mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, int pointCount)854      private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
855                            int pointCount) {
856          final int count = pointCount * 2;
857 
858          float[] tmpDest = dst;
859          boolean inPlace = dst == src;
860          if (inPlace) {
861              tmpDest = new float[dstIndex + count];
862          }
863 
864          for (int i = 0 ; i < count ; i += 2) {
865              // just in case we are doing in place, we better put this in temp vars
866              float x = mValues[0] * src[i + srcIndex] +
867                        mValues[1] * src[i + srcIndex + 1] +
868                        mValues[2];
869              float y = mValues[3] * src[i + srcIndex] +
870                        mValues[4] * src[i + srcIndex + 1] +
871                        mValues[5];
872 
873              tmpDest[i + dstIndex]     = x;
874              tmpDest[i + dstIndex + 1] = y;
875          }
876 
877          if (inPlace) {
878              System.arraycopy(tmpDest, dstIndex, dst, dstIndex, count);
879          }
880      }
881 
882      /**
883       * Apply this matrix to the array of 2D points, and write the transformed
884       * points back into the array
885       *
886       * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
887       */
888 
mapPoints(float[] pts)889      private void mapPoints(float[] pts) {
890          mapPoints(pts, 0, pts, 0, pts.length >> 1);
891      }
892 
mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount)893      private void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount) {
894          if (hasPerspective()) {
895              // transform the (0,0) point
896              float[] origin = new float[] { 0.f, 0.f};
897              mapPoints(origin);
898 
899              // translate the vector data as points
900              mapPoints(dst, dstIndex, src, srcIndex, ptCount);
901 
902              // then substract the transformed origin.
903              final int count = ptCount * 2;
904              for (int i = 0 ; i < count ; i += 2) {
905                  dst[dstIndex + i] = dst[dstIndex + i] - origin[0];
906                  dst[dstIndex + i + 1] = dst[dstIndex + i + 1] - origin[1];
907              }
908          } else {
909              // make a copy of the matrix
910              Matrix_Delegate copy = new Matrix_Delegate(mValues);
911 
912              // remove the translation
913              setTranslate(copy.mValues, 0, 0);
914 
915              // map the content as points.
916              copy.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
917          }
918      }
919 
920     /**
921      * multiply two matrices and store them in a 3rd.
922      * <p/>This in effect does dest = a*b
923      * dest cannot be the same as a or b.
924      */
multiply(float dest[], float[] a, float[] b)925      /*package*/ static void multiply(float dest[], float[] a, float[] b) {
926         // first row
927         dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6];
928         dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7];
929         dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8];
930 
931         // 2nd row
932         dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6];
933         dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7];
934         dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8];
935 
936         // 3rd row
937         dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6];
938         dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7];
939         dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8];
940     }
941 
942     /**
943      * Returns a matrix that represents a given translate
944      * @param dx
945      * @param dy
946      * @return
947      */
getTranslate(float dx, float dy)948     /*package*/ static float[] getTranslate(float dx, float dy) {
949         return setTranslate(new float[9], dx, dy);
950     }
951 
setTranslate(float[] dest, float dx, float dy)952     /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) {
953         dest[0] = 1;
954         dest[1] = 0;
955         dest[2] = dx;
956         dest[3] = 0;
957         dest[4] = 1;
958         dest[5] = dy;
959         dest[6] = 0;
960         dest[7] = 0;
961         dest[8] = 1;
962         return dest;
963     }
964 
getScale(float sx, float sy)965     /*package*/ static float[] getScale(float sx, float sy) {
966         return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
967     }
968 
969     /**
970      * Returns a matrix that represents the given scale info.
971      * @param sx
972      * @param sy
973      * @param px
974      * @param py
975      */
getScale(float sx, float sy, float px, float py)976     /*package*/ static float[] getScale(float sx, float sy, float px, float py) {
977         float[] tmp = new float[9];
978         float[] tmp2 = new float[9];
979 
980         // TODO: do it in one pass
981 
982         // translate tmp so that the pivot is in 0,0
983         setTranslate(tmp, -px, -py);
984 
985         // scale into tmp2
986         multiply(tmp2, tmp, getScale(sx, sy));
987 
988         // translate back the pivot back into tmp
989         multiply(tmp, tmp2, getTranslate(px, py));
990 
991         return tmp;
992     }
993 
994 
getRotate(float degrees)995     /*package*/ static float[] getRotate(float degrees) {
996         double rad = Math.toRadians(degrees);
997         float sin = (float)Math.sin(rad);
998         float cos = (float)Math.cos(rad);
999 
1000         return getRotate(sin, cos);
1001     }
1002 
getRotate(float sin, float cos)1003     /*package*/ static float[] getRotate(float sin, float cos) {
1004         return setRotate(new float[9], sin, cos);
1005     }
1006 
setRotate(float[] dest, float degrees)1007     /*package*/ static float[] setRotate(float[] dest, float degrees) {
1008         double rad = Math.toRadians(degrees);
1009         float sin = (float)Math.sin(rad);
1010         float cos = (float)Math.cos(rad);
1011 
1012         return setRotate(dest, sin, cos);
1013     }
1014 
setRotate(float[] dest, float sin, float cos)1015     /*package*/ static float[] setRotate(float[] dest, float sin, float cos) {
1016         dest[0] = cos;
1017         dest[1] = -sin;
1018         dest[2] = 0;
1019         dest[3] = sin;
1020         dest[4] = cos;
1021         dest[5] = 0;
1022         dest[6] = 0;
1023         dest[7] = 0;
1024         dest[8] = 1;
1025         return dest;
1026     }
1027 
getRotate(float degrees, float px, float py)1028     /*package*/ static float[] getRotate(float degrees, float px, float py) {
1029         float[] tmp = new float[9];
1030         float[] tmp2 = new float[9];
1031 
1032         // TODO: do it in one pass
1033 
1034         // translate so that the pivot is in 0,0
1035         setTranslate(tmp, -px, -py);
1036 
1037         // rotate into tmp2
1038         double rad = Math.toRadians(degrees);
1039         float cos = (float)Math.cos(rad);
1040         float sin = (float)Math.sin(rad);
1041         multiply(tmp2, tmp, getRotate(sin, cos));
1042 
1043         // translate back the pivot back into tmp
1044         multiply(tmp, tmp2, getTranslate(px, py));
1045 
1046         return tmp;
1047     }
1048 
getSkew(float kx, float ky)1049     /*package*/ static float[] getSkew(float kx, float ky) {
1050         return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
1051     }
1052 
getSkew(float kx, float ky, float px, float py)1053     /*package*/ static float[] getSkew(float kx, float ky, float px, float py) {
1054         float[] tmp = new float[9];
1055         float[] tmp2 = new float[9];
1056 
1057         // TODO: do it in one pass
1058 
1059         // translate so that the pivot is in 0,0
1060         setTranslate(tmp, -px, -py);
1061 
1062         // skew into tmp2
1063         multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
1064         // translate back the pivot back into tmp
1065         multiply(tmp, tmp2, getTranslate(px, py));
1066 
1067         return tmp;
1068     }
1069 }
1070