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