• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics;
18 
19 
20 import com.android.ide.common.rendering.api.LayoutLog;
21 import com.android.layoutlib.bridge.Bridge;
22 import com.android.layoutlib.bridge.impl.DelegateManager;
23 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
24 
25 import android.graphics.Matrix.ScaleToFit;
26 
27 import java.awt.geom.AffineTransform;
28 import java.awt.geom.NoninvertibleTransformException;
29 
30 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         try {
620             AffineTransform affineTransform = d.getAffineTransform();
621             AffineTransform inverseTransform = affineTransform.createInverse();
622             setValues(inverseTransform, inv_mtx.mValues);
623 
624             return true;
625         } catch (NoninvertibleTransformException e) {
626             return false;
627         }
628     }
629 
630     @LayoutlibDelegate
nMapPoints(long native_object, float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount, boolean isPts)631     /*package*/ static void nMapPoints(long native_object, float[] dst, int dstIndex,
632             float[] src, int srcIndex, int ptCount, boolean isPts) {
633         Matrix_Delegate d = sManager.getDelegate(native_object);
634         if (d == null) {
635             return;
636         }
637 
638         if (isPts) {
639             d.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
640         } else {
641             d.mapVectors(dst, dstIndex, src, srcIndex, ptCount);
642         }
643     }
644 
645     @LayoutlibDelegate
nMapRect(long native_object, RectF dst, RectF src)646     /*package*/ static boolean nMapRect(long native_object, RectF dst, RectF src) {
647         Matrix_Delegate d = sManager.getDelegate(native_object);
648         if (d == null) {
649             return false;
650         }
651 
652         return d.mapRect(dst, src);
653     }
654 
655     @LayoutlibDelegate
nMapRadius(long native_object, float radius)656     /*package*/ static float nMapRadius(long native_object, float radius) {
657         Matrix_Delegate d = sManager.getDelegate(native_object);
658         if (d == null) {
659             return 0.f;
660         }
661 
662         float[] src = new float[] { radius, 0.f, 0.f, radius };
663         d.mapVectors(src, 0, src, 0, 2);
664 
665         float l1 = (float) Math.hypot(src[0], src[1]);
666         float l2 = (float) Math.hypot(src[2], src[3]);
667         return (float) Math.sqrt(l1 * l2);
668     }
669 
670     @LayoutlibDelegate
nGetValues(long native_object, float[] values)671     /*package*/ static void nGetValues(long native_object, float[] values) {
672         Matrix_Delegate d = sManager.getDelegate(native_object);
673         if (d == null) {
674             return;
675         }
676 
677         System.arraycopy(d.mValues, 0, values, 0, MATRIX_SIZE);
678     }
679 
680     @LayoutlibDelegate
nSetValues(long native_object, float[] values)681     /*package*/ static void nSetValues(long native_object, float[] values) {
682         Matrix_Delegate d = sManager.getDelegate(native_object);
683         if (d == null) {
684             return;
685         }
686 
687         System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE);
688     }
689 
690     @LayoutlibDelegate
nEquals(long native_a, long native_b)691     /*package*/ static boolean nEquals(long native_a, long native_b) {
692         Matrix_Delegate a = sManager.getDelegate(native_a);
693         if (a == null) {
694             return false;
695         }
696 
697         Matrix_Delegate b = sManager.getDelegate(native_b);
698         if (b == null) {
699             return false;
700         }
701 
702         for (int i = 0 ; i < MATRIX_SIZE ; i++) {
703             if (a.mValues[i] != b.mValues[i]) {
704                 return false;
705             }
706         }
707 
708         return true;
709     }
710 
711     @LayoutlibDelegate
nGetNativeFinalizer()712     /*package*/ static long nGetNativeFinalizer() {
713         synchronized (Matrix_Delegate.class) {
714             if (sFinalizer == -1) {
715                 sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
716             }
717         }
718         return sFinalizer;
719     }
720 
721     // ---- Private helper methods ----
722 
getAffineTransform(float[] matrix)723     /*package*/ static AffineTransform getAffineTransform(float[] matrix) {
724         // the AffineTransform constructor takes the value in a different order
725         // for a matrix [ 0 1 2 ]
726         //              [ 3 4 5 ]
727         // the order is 0, 3, 1, 4, 2, 5...
728         return new AffineTransform(
729                 matrix[0], matrix[3], matrix[1],
730                 matrix[4], matrix[2], matrix[5]);
731     }
732 
733     /**
734      * Reset a matrix to the identity
735      */
reset(float[] mtx)736     private static void reset(float[] mtx) {
737         for (int i = 0, k = 0; i < 3; i++) {
738             for (int j = 0; j < 3; j++, k++) {
739                 mtx[k] = ((i==j) ? 1 : 0);
740             }
741         }
742     }
743 
744     @SuppressWarnings("unused")
745     private final static int kIdentity_Mask      = 0;
746     private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
747     private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
748     private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
749     private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
750     private final static int kRectStaysRect_Mask = 0x10;
751     @SuppressWarnings("unused")
752     private final static int kUnknown_Mask       = 0x80;
753 
754     @SuppressWarnings("unused")
755     private final static int kAllMasks           = kTranslate_Mask |
756                                                    kScale_Mask |
757                                                    kAffine_Mask |
758                                                    kPerspective_Mask |
759                                                    kRectStaysRect_Mask;
760 
761     // these guys align with the masks, so we can compute a mask from a variable 0/1
762     @SuppressWarnings("unused")
763     private final static int kTranslate_Shift = 0;
764     @SuppressWarnings("unused")
765     private final static int kScale_Shift = 1;
766     @SuppressWarnings("unused")
767     private final static int kAffine_Shift = 2;
768     @SuppressWarnings("unused")
769     private final static int kPerspective_Shift = 3;
770     private final static int kRectStaysRect_Shift = 4;
771 
computeTypeMask()772     private int computeTypeMask() {
773         int mask = 0;
774 
775         if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
776             mask |= kPerspective_Mask;
777         }
778 
779         if (mValues[2] != 0. || mValues[5] != 0.) {
780             mask |= kTranslate_Mask;
781         }
782 
783         float m00 = mValues[0];
784         float m01 = mValues[1];
785         float m10 = mValues[3];
786         float m11 = mValues[4];
787 
788         if (m01 != 0. || m10 != 0.) {
789             mask |= kAffine_Mask;
790         }
791 
792         if (m00 != 1. || m11 != 1.) {
793             mask |= kScale_Mask;
794         }
795 
796         if ((mask & kPerspective_Mask) == 0) {
797             // map non-zero to 1
798             int im00 = m00 != 0 ? 1 : 0;
799             int im01 = m01 != 0 ? 1 : 0;
800             int im10 = m10 != 0 ? 1 : 0;
801             int im11 = m11 != 0 ? 1 : 0;
802 
803             // record if the (p)rimary and (s)econdary diagonals are all 0 or
804             // all non-zero (answer is 0 or 1)
805             int dp0 = (im00 | im11) ^ 1;  // true if both are 0
806             int dp1 = im00 & im11;        // true if both are 1
807             int ds0 = (im01 | im10) ^ 1;  // true if both are 0
808             int ds1 = im01 & im10;        // true if both are 1
809 
810             // return 1 if primary is 1 and secondary is 0 or
811             // primary is 0 and secondary is 1
812             mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
813         }
814 
815         return mask;
816     }
817 
Matrix_Delegate()818     private Matrix_Delegate() {
819         reset();
820     }
821 
Matrix_Delegate(float[] values)822     private Matrix_Delegate(float[] values) {
823         System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE);
824     }
825 
826     /**
827      * Adds the given transformation to the current Matrix
828      * <p/>This in effect does this = this*matrix
829      * @param matrix
830      */
postTransform(float[] matrix)831     private void postTransform(float[] matrix) {
832         float[] tmp = new float[9];
833         multiply(tmp, mValues, matrix);
834         mValues = tmp;
835     }
836 
837     /**
838      * Adds the given transformation to the current Matrix
839      * <p/>This in effect does this = matrix*this
840      * @param matrix
841      */
preTransform(float[] matrix)842     private void preTransform(float[] matrix) {
843         float[] tmp = new float[9];
844         multiply(tmp, matrix, mValues);
845         mValues = tmp;
846     }
847 
848     /**
849      * Apply this matrix to the array of 2D points specified by src, and write
850       * the transformed points into the array of points specified by dst. The
851       * two arrays represent their "points" as pairs of floats [x, y].
852       *
853       * @param dst   The array of dst points (x,y pairs)
854       * @param dstIndex The index of the first [x,y] pair of dst floats
855       * @param src   The array of src points (x,y pairs)
856       * @param srcIndex The index of the first [x,y] pair of src floats
857       * @param pointCount The number of points (x,y pairs) to transform
858       */
859 
mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, int pointCount)860      private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
861                            int pointCount) {
862          final int count = pointCount * 2;
863 
864          float[] tmpDest = dst;
865          boolean inPlace = dst == src;
866          if (inPlace) {
867              tmpDest = new float[dstIndex + count];
868          }
869 
870          for (int i = 0 ; i < count ; i += 2) {
871              // just in case we are doing in place, we better put this in temp vars
872              float x = mValues[0] * src[i + srcIndex] +
873                        mValues[1] * src[i + srcIndex + 1] +
874                        mValues[2];
875              float y = mValues[3] * src[i + srcIndex] +
876                        mValues[4] * src[i + srcIndex + 1] +
877                        mValues[5];
878 
879              tmpDest[i + dstIndex]     = x;
880              tmpDest[i + dstIndex + 1] = y;
881          }
882 
883          if (inPlace) {
884              System.arraycopy(tmpDest, dstIndex, dst, dstIndex, count);
885          }
886      }
887 
888      /**
889       * Apply this matrix to the array of 2D points, and write the transformed
890       * points back into the array
891       *
892       * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
893       */
894 
mapPoints(float[] pts)895      private void mapPoints(float[] pts) {
896          mapPoints(pts, 0, pts, 0, pts.length >> 1);
897      }
898 
mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount)899      private void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount) {
900          if (hasPerspective()) {
901              // transform the (0,0) point
902              float[] origin = new float[] { 0.f, 0.f};
903              mapPoints(origin);
904 
905              // translate the vector data as points
906              mapPoints(dst, dstIndex, src, srcIndex, ptCount);
907 
908              // then substract the transformed origin.
909              final int count = ptCount * 2;
910              for (int i = 0 ; i < count ; i += 2) {
911                  dst[dstIndex + i] = dst[dstIndex + i] - origin[0];
912                  dst[dstIndex + i + 1] = dst[dstIndex + i + 1] - origin[1];
913              }
914          } else {
915              // make a copy of the matrix
916              Matrix_Delegate copy = new Matrix_Delegate(mValues);
917 
918              // remove the translation
919              setTranslate(copy.mValues, 0, 0);
920 
921              // map the content as points.
922              copy.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
923          }
924      }
925 
926     /**
927      * multiply two matrices and store them in a 3rd.
928      * <p/>This in effect does dest = a*b
929      * dest cannot be the same as a or b.
930      */
multiply(float dest[], float[] a, float[] b)931      /*package*/ static void multiply(float dest[], float[] a, float[] b) {
932         // first row
933         dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6];
934         dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7];
935         dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8];
936 
937         // 2nd row
938         dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6];
939         dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7];
940         dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8];
941 
942         // 3rd row
943         dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6];
944         dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7];
945         dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8];
946     }
947 
948     /**
949      * Returns a matrix that represents a given translate
950      * @param dx
951      * @param dy
952      * @return
953      */
getTranslate(float dx, float dy)954     /*package*/ static float[] getTranslate(float dx, float dy) {
955         return setTranslate(new float[9], dx, dy);
956     }
957 
setTranslate(float[] dest, float dx, float dy)958     /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) {
959         dest[0] = 1;
960         dest[1] = 0;
961         dest[2] = dx;
962         dest[3] = 0;
963         dest[4] = 1;
964         dest[5] = dy;
965         dest[6] = 0;
966         dest[7] = 0;
967         dest[8] = 1;
968         return dest;
969     }
970 
getScale(float sx, float sy)971     /*package*/ static float[] getScale(float sx, float sy) {
972         return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
973     }
974 
975     /**
976      * Returns a matrix that represents the given scale info.
977      * @param sx
978      * @param sy
979      * @param px
980      * @param py
981      */
getScale(float sx, float sy, float px, float py)982     /*package*/ static float[] getScale(float sx, float sy, float px, float py) {
983         float[] tmp = new float[9];
984         float[] tmp2 = new float[9];
985 
986         // TODO: do it in one pass
987 
988         // translate tmp so that the pivot is in 0,0
989         setTranslate(tmp, -px, -py);
990 
991         // scale into tmp2
992         multiply(tmp2, tmp, getScale(sx, sy));
993 
994         // translate back the pivot back into tmp
995         multiply(tmp, tmp2, getTranslate(px, py));
996 
997         return tmp;
998     }
999 
1000 
getRotate(float degrees)1001     /*package*/ static float[] getRotate(float degrees) {
1002         double rad = Math.toRadians(degrees);
1003         float sin = (float)Math.sin(rad);
1004         float cos = (float)Math.cos(rad);
1005 
1006         return getRotate(sin, cos);
1007     }
1008 
getRotate(float sin, float cos)1009     /*package*/ static float[] getRotate(float sin, float cos) {
1010         return setRotate(new float[9], sin, cos);
1011     }
1012 
setRotate(float[] dest, float degrees)1013     /*package*/ static float[] setRotate(float[] dest, float degrees) {
1014         double rad = Math.toRadians(degrees);
1015         float sin = (float)Math.sin(rad);
1016         float cos = (float)Math.cos(rad);
1017 
1018         return setRotate(dest, sin, cos);
1019     }
1020 
setRotate(float[] dest, float sin, float cos)1021     /*package*/ static float[] setRotate(float[] dest, float sin, float cos) {
1022         dest[0] = cos;
1023         dest[1] = -sin;
1024         dest[2] = 0;
1025         dest[3] = sin;
1026         dest[4] = cos;
1027         dest[5] = 0;
1028         dest[6] = 0;
1029         dest[7] = 0;
1030         dest[8] = 1;
1031         return dest;
1032     }
1033 
getRotate(float degrees, float px, float py)1034     /*package*/ static float[] getRotate(float degrees, float px, float py) {
1035         float[] tmp = new float[9];
1036         float[] tmp2 = new float[9];
1037 
1038         // TODO: do it in one pass
1039 
1040         // translate so that the pivot is in 0,0
1041         setTranslate(tmp, -px, -py);
1042 
1043         // rotate into tmp2
1044         double rad = Math.toRadians(degrees);
1045         float cos = (float)Math.cos(rad);
1046         float sin = (float)Math.sin(rad);
1047         multiply(tmp2, tmp, getRotate(sin, cos));
1048 
1049         // translate back the pivot back into tmp
1050         multiply(tmp, tmp2, getTranslate(px, py));
1051 
1052         return tmp;
1053     }
1054 
getSkew(float kx, float ky)1055     /*package*/ static float[] getSkew(float kx, float ky) {
1056         return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
1057     }
1058 
getSkew(float kx, float ky, float px, float py)1059     /*package*/ static float[] getSkew(float kx, float ky, float px, float py) {
1060         float[] tmp = new float[9];
1061         float[] tmp2 = new float[9];
1062 
1063         // TODO: do it in one pass
1064 
1065         // translate so that the pivot is in 0,0
1066         setTranslate(tmp, -px, -py);
1067 
1068         // skew into tmp2
1069         multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
1070         // translate back the pivot back into tmp
1071         multiply(tmp, tmp2, getTranslate(px, py));
1072 
1073         return tmp;
1074     }
1075 }
1076