blob: d0a0adc5e2c16b11d566330b55a5dbb8cb8721a9 [file] [log] [blame]
Adam Lesinskiab775ec2014-01-23 18:17:42 -08001/*
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
17package android.graphics;
18
19
Jerome Gaillardd831f2f2020-07-01 13:24:28 +010020import com.android.ide.common.rendering.api.ILayoutLog;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080021import com.android.layoutlib.bridge.Bridge;
22import com.android.layoutlib.bridge.impl.DelegateManager;
23import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
24
25import android.graphics.Matrix.ScaleToFit;
26
27import java.awt.geom.AffineTransform;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080028
Jerome Gaillard7fa71f52016-11-11 12:39:34 +000029import libcore.util.NativeAllocationRegistry_Delegate;
30
Adam Lesinskiab775ec2014-01-23 18:17:42 -080031/**
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 */
44public 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);
Jerome Gaillard7fa71f52016-11-11 12:39:34 +000051 private static long sFinalizer = -1;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080052
53 // ---- delegate data ----
54 private float mValues[] = new float[MATRIX_SIZE];
55
56 // ---- Public Helper methods ----
57
Narayan Kamathec411892014-01-27 14:24:16 +000058 public static Matrix_Delegate getDelegate(long native_instance) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -080059 return sManager.getDelegate(native_instance);
60 }
61
62 /**
63 * Returns an {@link AffineTransform} matching the given Matrix.
64 */
65 public static AffineTransform getAffineTransform(Matrix m) {
Leon Scroggins IIIe0003c52020-05-18 10:19:10 -040066 Matrix_Delegate delegate = sManager.getDelegate(m.ni());
Adam Lesinskiab775ec2014-01-23 18:17:42 -080067 if (delegate == null) {
68 return null;
69 }
70
71 return delegate.getAffineTransform();
72 }
73
74 public static boolean hasPerspective(Matrix m) {
Leon Scroggins IIIe0003c52020-05-18 10:19:10 -040075 Matrix_Delegate delegate = sManager.getDelegate(m.ni());
Adam Lesinskiab775ec2014-01-23 18:17:42 -080076 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 */
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 */
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 */
101 public void reset() {
102 reset(mValues);
103 }
104
105 /**
106 * Returns whether or not the matrix is identity.
107 */
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
Diego Perez9a92f182018-02-02 17:49:45 +0000120 private static float[] setValues(AffineTransform matrix, float[] values) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800121 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
Diego Perez9a92f182018-02-02 17:49:45 +0000134 public static float[] makeValues(AffineTransform matrix) {
135 return setValues(matrix, new float[MATRIX_SIZE]);
136 }
137
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800138 public static Matrix_Delegate make(AffineTransform matrix) {
139 return new Matrix_Delegate(makeValues(matrix));
140 }
141
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 */
169 public AffineTransform getAffineTransform() {
170 return getAffineTransform(mValues);
171 }
172
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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000182 /*package*/ static long nCreate(long native_src_or_zero) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800183 // 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000201 /*package*/ static boolean nIsIdentity(long native_object) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800202 Matrix_Delegate d = sManager.getDelegate(native_object);
203 if (d == null) {
204 return false;
205 }
206
207 return d.isIdentity();
208 }
209
210 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000211 /*package*/ static boolean nIsAffine(long native_object) {
Deepanshu Gupta017c0622014-05-19 16:14:23 -0700212 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000221 /*package*/ static boolean nRectStaysRect(long native_object) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800222 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000231 /*package*/ static void nReset(long native_object) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800232 Matrix_Delegate d = sManager.getDelegate(native_object);
233 if (d == null) {
234 return;
235 }
236
237 reset(d.mValues);
238 }
239
240 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000241 /*package*/ static void nSet(long native_object, long other) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800242 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000256 /*package*/ static void nSetTranslate(long native_object, float dx, float dy) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800257 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000266 /*package*/ static void nSetScale(long native_object, float sx, float sy,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800267 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000277 /*package*/ static void nSetScale(long native_object, float sx, float sy) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800278 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000295 /*package*/ static void nSetRotate(long native_object, float degrees, float px, float py) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800296 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000305 /*package*/ static void nSetRotate(long native_object, float degrees) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800306 Matrix_Delegate d = sManager.getDelegate(native_object);
307 if (d == null) {
308 return;
309 }
310
311 setRotate(d.mValues, degrees);
312 }
313
314 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000315 /*package*/ static void nSetSinCos(long native_object, float sinValue, float cosValue,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800316 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000334 /*package*/ static void nSetSinCos(long native_object, float sinValue, float cosValue) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800335 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000344 /*package*/ static void nSetSkew(long native_object, float kx, float ky,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800345 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000355 /*package*/ static void nSetSkew(long native_object, float kx, float ky) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800356 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000373 /*package*/ static void nSetConcat(long native_object, long a, long b) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800374 if (a == native_object) {
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000375 nPreConcat(native_object, b);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700376 return;
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800377 } else if (b == native_object) {
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000378 nPostConcat(native_object, a);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700379 return;
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800380 }
381
382 Matrix_Delegate d = sManager.getDelegate(native_object);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800383 Matrix_Delegate a_mtx = sManager.getDelegate(a);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800384 Matrix_Delegate b_mtx = sManager.getDelegate(b);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700385 if (d != null && a_mtx != null && b_mtx != null) {
386 multiply(d.mValues, a_mtx.mValues, b_mtx.mValues);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800387 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800388 }
389
390 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000391 /*package*/ static void nPreTranslate(long native_object, float dx, float dy) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800392 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700393 if (d != null) {
394 d.preTransform(getTranslate(dx, dy));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800395 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800396 }
397
398 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000399 /*package*/ static void nPreScale(long native_object, float sx, float sy,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800400 float px, float py) {
401 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700402 if (d != null) {
403 d.preTransform(getScale(sx, sy, px, py));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800404 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800405 }
406
407 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000408 /*package*/ static void nPreScale(long native_object, float sx, float sy) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800409 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700410 if (d != null) {
411 d.preTransform(getScale(sx, sy));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800412 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800413 }
414
415 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000416 /*package*/ static void nPreRotate(long native_object, float degrees,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800417 float px, float py) {
418 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700419 if (d != null) {
420 d.preTransform(getRotate(degrees, px, py));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800421 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800422 }
423
424 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000425 /*package*/ static void nPreRotate(long native_object, float degrees) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800426 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700427 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));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800434 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800435 }
436
437 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000438 /*package*/ static void nPreSkew(long native_object, float kx, float ky,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800439 float px, float py) {
440 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700441 if (d != null) {
442 d.preTransform(getSkew(kx, ky, px, py));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800443 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800444 }
445
446 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000447 /*package*/ static void nPreSkew(long native_object, float kx, float ky) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800448 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700449 if (d != null) {
450 d.preTransform(getSkew(kx, ky));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800451 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800452 }
453
454 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000455 /*package*/ static void nPreConcat(long native_object, long other_matrix) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800456 Matrix_Delegate d = sManager.getDelegate(native_object);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800457 Matrix_Delegate other = sManager.getDelegate(other_matrix);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700458 if (d != null && other != null) {
459 d.preTransform(other.mValues);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800460 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800461 }
462
463 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000464 /*package*/ static void nPostTranslate(long native_object, float dx, float dy) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800465 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700466 if (d != null) {
467 d.postTransform(getTranslate(dx, dy));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800468 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800469 }
470
471 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000472 /*package*/ static void nPostScale(long native_object, float sx, float sy,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800473 float px, float py) {
474 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700475 if (d != null) {
476 d.postTransform(getScale(sx, sy, px, py));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800477 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800478 }
479
480 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000481 /*package*/ static void nPostScale(long native_object, float sx, float sy) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800482 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700483 if (d != null) {
484 d.postTransform(getScale(sx, sy));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800485 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800486 }
487
488 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000489 /*package*/ static void nPostRotate(long native_object, float degrees,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800490 float px, float py) {
491 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700492 if (d != null) {
493 d.postTransform(getRotate(degrees, px, py));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800494 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800495 }
496
497 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000498 /*package*/ static void nPostRotate(long native_object, float degrees) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800499 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700500 if (d != null) {
501 d.postTransform(getRotate(degrees));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800502 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800503 }
504
505 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000506 /*package*/ static void nPostSkew(long native_object, float kx, float ky,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800507 float px, float py) {
508 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700509 if (d != null) {
510 d.postTransform(getSkew(kx, ky, px, py));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800511 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800512 }
513
514 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000515 /*package*/ static void nPostSkew(long native_object, float kx, float ky) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800516 Matrix_Delegate d = sManager.getDelegate(native_object);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700517 if (d != null) {
518 d.postTransform(getSkew(kx, ky));
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800519 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800520 }
521
522 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000523 /*package*/ static void nPostConcat(long native_object, long other_matrix) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800524 Matrix_Delegate d = sManager.getDelegate(native_object);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800525 Matrix_Delegate other = sManager.getDelegate(other_matrix);
Deepanshu Gupta678cd7f2014-04-21 14:28:56 -0700526 if (d != null && other != null) {
527 d.postTransform(other.mValues);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800528 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800529 }
530
531 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000532 /*package*/ static boolean nSetRectToRect(long native_object, RectF src,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800533 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000597 /*package*/ static boolean nSetPolyToPoly(long native_object, float[] src, int srcIndex,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800598 float[] dst, int dstIndex, int pointCount) {
599 // FIXME
Jerome Gaillardd831f2f2020-07-01 13:24:28 +0100600 Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800601 "Matrix.setPolyToPoly is not supported.",
Jerome Gaillardb484b7e2020-04-14 16:10:43 +0100602 null, null, null /*data*/);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800603 return false;
604 }
605
606 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000607 /*package*/ static boolean nInvert(long native_object, long inverse) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800608 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
Jerome Gaillard9baa3042018-09-04 23:50:09 +0100618 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]);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800621
Jerome Gaillard9baa3042018-09-04 23:50:09 +0100622 if (det == 0.0) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800623 return false;
624 }
Jerome Gaillard9baa3042018-09-04 23:50:09 +0100625
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;
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800637 }
638
639 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000640 /*package*/ static void nMapPoints(long native_object, float[] dst, int dstIndex,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800641 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000655 /*package*/ static boolean nMapRect(long native_object, RectF dst, RectF src) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800656 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000665 /*package*/ static float nMapRadius(long native_object, float radius) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800666 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
Neil Fuller2460c572014-10-01 11:55:10 +0100674 float l1 = (float) Math.hypot(src[0], src[1]);
675 float l2 = (float) Math.hypot(src[2], src[3]);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800676 return (float) Math.sqrt(l1 * l2);
677 }
678
679 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000680 /*package*/ static void nGetValues(long native_object, float[] values) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800681 Matrix_Delegate d = sManager.getDelegate(native_object);
682 if (d == null) {
683 return;
684 }
685
Deepanshu Guptae5581b52015-08-03 10:23:56 -0700686 System.arraycopy(d.mValues, 0, values, 0, MATRIX_SIZE);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800687 }
688
689 @LayoutlibDelegate
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000690 /*package*/ static void nSetValues(long native_object, float[] values) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800691 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000700 /*package*/ static boolean nEquals(long native_a, long native_b) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800701 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
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000721 /*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;
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800728 }
729
730 // ---- Private helper methods ----
731
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 */
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
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
827 private Matrix_Delegate() {
828 reset();
829 }
830
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 */
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 */
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
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
904 private void mapPoints(float[] pts) {
905 mapPoints(pts, 0, pts, 0, pts.length >> 1);
906 }
907
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
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800935 /**
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 */
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 */
963 /*package*/ static float[] getTranslate(float dx, float dy) {
964 return setTranslate(new float[9], dx, dy);
965 }
966
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
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 */
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
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
1018 /*package*/ static float[] getRotate(float sin, float cos) {
1019 return setRotate(new float[9], sin, cos);
1020 }
1021
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
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
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
1064 /*package*/ static float[] getSkew(float kx, float ky) {
1065 return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
1066 }
1067
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}