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