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