• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 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 import android.annotation.FlaggedApi;
20 import android.annotation.IntRange;
21 import android.annotation.NonNull;
22 import android.ravenwood.annotation.RavenwoodKeepWholeClass;
23 
24 import com.android.graphics.hwui.flags.Flags;
25 
26 import java.util.Arrays;
27 
28 /**
29  * The Matrix44 class holds a 4x4 matrix for transforming coordinates. It is similar to
30  * {@link Matrix}, and should be used when you want to manipulate the canvas in 3D. Values are kept
31  * in row-major order. The values and operations are treated as column vectors.
32  */
33 @FlaggedApi(Flags.FLAG_MATRIX_44)
34 @RavenwoodKeepWholeClass
35 public class Matrix44 {
36     final float[] mBackingArray;
37     /**
38      * The default Matrix44 constructor will instantiate an identity matrix.
39      */
40     @FlaggedApi(Flags.FLAG_MATRIX_44)
Matrix44()41     public Matrix44() {
42         mBackingArray = new float[]{1.0f, 0.0f, 0.0f, 0.0f,
43                                     0.0f, 1.0f, 0.0f, 0.0f,
44                                     0.0f, 0.0f, 1.0f, 0.0f,
45                                     0.0f, 0.0f, 0.0f, 1.0f};
46     }
47 
48     /**
49      * Creates and returns a Matrix44 by taking the 3x3 Matrix and placing it on the 0 of the z-axis
50      * by setting row {@code 2} and column {@code 2} to the identity as seen in the following
51      * operation:
52      * <pre class="prettyprint">
53      * [ a b c ]      [ a b 0 c ]
54      * [ d e f ]  ->  [ d e 0 f ]
55      * [ g h i ]      [ 0 0 1 0 ]
56      *                [ g h 0 i ]
57      * </pre>
58      *
59      * @param mat A 3x3 Matrix to be converted (original Matrix will not be changed)
60      */
61     @FlaggedApi(Flags.FLAG_MATRIX_44)
Matrix44(@onNull Matrix mat)62     public Matrix44(@NonNull Matrix mat) {
63         float[] m = new float[9];
64         mat.getValues(m);
65         mBackingArray = new float[]{m[0], m[1], 0.0f, m[2],
66                                     m[3], m[4], 0.0f, m[5],
67                                     0.0f, 0.0f, 1.0f, 0.0f,
68                                     m[6], m[7], 0.0f, m[8]};
69     }
70 
71     /**
72      * Copies matrix values into the provided array in row-major order.
73      *
74      * @param dst The float array where values will be copied, must be of length 16
75      * @throws IllegalArgumentException if the destination float array is not of length 16
76      */
77     @FlaggedApi(Flags.FLAG_MATRIX_44)
getValues(@onNull float [] dst)78     public void getValues(@NonNull float [] dst) {
79         if (dst.length == 16) {
80             System.arraycopy(mBackingArray, 0, dst, 0, mBackingArray.length);
81         } else {
82             throw new IllegalArgumentException("Dst array must be of length 16");
83         }
84     }
85 
86     /**
87      * Replaces the Matrix's values with the values in the provided array.
88      *
89      * @param src A float array of length 16. Floats are treated in row-major order
90      * @throws IllegalArgumentException if the destination float array is not of length 16
91      */
92     @FlaggedApi(Flags.FLAG_MATRIX_44)
setValues(@onNull float[] src)93     public void setValues(@NonNull float[] src) {
94         if (src.length == 16) {
95             System.arraycopy(src, 0, mBackingArray, 0, mBackingArray.length);
96         } else {
97             throw new IllegalArgumentException("Src array must be of length 16");
98         }
99     }
100 
101     /**
102      * Gets the value at the matrix's row and column.
103      *
104      * @param row An integer from 0 to 3 indicating the row of the value to get
105      * @param col An integer from 0 to 3 indicating the column of the value to get
106      */
107     @FlaggedApi(Flags.FLAG_MATRIX_44)
get(@ntRangefrom = 0, to = 3) int row, @IntRange(from = 0, to = 3) int col)108     public float get(@IntRange(from = 0, to = 3) int row, @IntRange(from = 0, to = 3) int col) {
109         if (row >= 0 && row < 4 && col >= 0 && col < 4) {
110             return mBackingArray[row * 4 + col];
111         }
112         throw new IllegalArgumentException("invalid row and column values");
113     }
114 
115     /**
116      * Sets the value at the matrix's row and column to the provided value.
117      *
118      * @param row An integer from 0 to 3 indicating the row of the value to change
119      * @param col An integer from 0 to 3 indicating the column of the value to change
120      * @param val The value the element at the specified index will be set to
121      */
122     @FlaggedApi(Flags.FLAG_MATRIX_44)
set(@ntRangefrom = 0, to = 3) int row, @IntRange(from = 0, to = 3) int col, float val)123     public void set(@IntRange(from = 0, to = 3) int row, @IntRange(from = 0, to = 3) int col,
124             float val) {
125         if (row >= 0 && row < 4 && col >= 0 && col < 4) {
126             mBackingArray[row * 4 + col] = val;
127         } else {
128             throw new IllegalArgumentException("invalid row and column values");
129         }
130     }
131 
132     /**
133      * Sets the Matrix44 to the identity matrix.
134      */
135     @FlaggedApi(Flags.FLAG_MATRIX_44)
reset()136     public void reset() {
137         for (int i = 0; i < mBackingArray.length; i++) {
138             mBackingArray[i] = (i % 4 == i / 4) ? 1.0f : 0.0f;
139         }
140     }
141 
142     /**
143      * Inverts the Matrix44, then return true if successful, false if unable to invert.
144      *
145      * @return {@code true} on success, {@code false} otherwise
146      */
147     @FlaggedApi(Flags.FLAG_MATRIX_44)
invert()148     public boolean invert() {
149         float a00 = mBackingArray[0];
150         float a01 = mBackingArray[1];
151         float a02 = mBackingArray[2];
152         float a03 = mBackingArray[3];
153         float a10 = mBackingArray[4];
154         float a11 = mBackingArray[5];
155         float a12 = mBackingArray[6];
156         float a13 = mBackingArray[7];
157         float a20 = mBackingArray[8];
158         float a21 = mBackingArray[9];
159         float a22 = mBackingArray[10];
160         float a23 = mBackingArray[11];
161         float a30 = mBackingArray[12];
162         float a31 = mBackingArray[13];
163         float a32 = mBackingArray[14];
164         float a33 = mBackingArray[15];
165         float b00 = a00 * a11 - a01 * a10;
166         float b01 = a00 * a12 - a02 * a10;
167         float b02 = a00 * a13 - a03 * a10;
168         float b03 = a01 * a12 - a02 * a11;
169         float b04 = a01 * a13 - a03 * a11;
170         float b05 = a02 * a13 - a03 * a12;
171         float b06 = a20 * a31 - a21 * a30;
172         float b07 = a20 * a32 - a22 * a30;
173         float b08 = a20 * a33 - a23 * a30;
174         float b09 = a21 * a32 - a22 * a31;
175         float b10 = a21 * a33 - a23 * a31;
176         float b11 = a22 * a33 - a23 * a32;
177         float det = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06);
178         if (det == 0.0f) {
179             return false;
180         }
181         float invDet = 1.0f / det;
182         mBackingArray[0] = ((a11 * b11 - a12 * b10 + a13 * b09) * invDet);
183         mBackingArray[1] = ((-a01 * b11 + a02 * b10 - a03 * b09) * invDet);
184         mBackingArray[2] = ((a31 * b05 - a32 * b04 + a33 * b03) * invDet);
185         mBackingArray[3] = ((-a21 * b05 + a22 * b04 - a23 * b03) * invDet);
186         mBackingArray[4] = ((-a10 * b11 + a12 * b08 - a13 * b07) * invDet);
187         mBackingArray[5] = ((a00 * b11 - a02 * b08 + a03 * b07) * invDet);
188         mBackingArray[6] = ((-a30 * b05 + a32 * b02 - a33 * b01) * invDet);
189         mBackingArray[7] = ((a20 * b05 - a22 * b02 + a23 * b01) * invDet);
190         mBackingArray[8] = ((a10 * b10 - a11 * b08 + a13 * b06) * invDet);
191         mBackingArray[9] = ((-a00 * b10 + a01 * b08 - a03 * b06) * invDet);
192         mBackingArray[10] = ((a30 * b04 - a31 * b02 + a33 * b00) * invDet);
193         mBackingArray[11] = ((-a20 * b04 + a21 * b02 - a23 * b00) * invDet);
194         mBackingArray[12] = ((-a10 * b09 + a11 * b07 - a12 * b06) * invDet);
195         mBackingArray[13] = ((a00 * b09 - a01 * b07 + a02 * b06) * invDet);
196         mBackingArray[14] = ((-a30 * b03 + a31 * b01 - a32 * b00) * invDet);
197         mBackingArray[15] = ((a20 * b03 - a21 * b01 + a22 * b00) * invDet);
198         return true;
199     }
200 
201     /**
202      * Returns true if Matrix44 is equal to identity matrix.
203      */
204     @FlaggedApi(Flags.FLAG_MATRIX_44)
isIdentity()205     public boolean isIdentity() {
206         for (int i = 0; i < mBackingArray.length; i++) {
207             float expected = (i % 4 == i / 4) ? 1.0f : 0.0f;
208             if (expected != mBackingArray[i]) return false;
209         }
210         return true;
211     }
212 
213     @FlaggedApi(Flags.FLAG_MATRIX_44)
dot(Matrix44 a, Matrix44 b, int row, int col)214     private static float dot(Matrix44 a, Matrix44 b, int row, int col) {
215         return (a.get(row, 0) * b.get(0, col))
216                 + (a.get(row, 1) * b.get(1, col))
217                 + (a.get(row, 2) * b.get(2, col))
218                 + (a.get(row, 3) * b.get(3, col));
219     }
220 
221     @FlaggedApi(Flags.FLAG_MATRIX_44)
dot(float r0, float r1, float r2, float r3, float c0, float c1, float c2, float c3)222     private static float dot(float r0, float r1, float r2, float r3,
223                              float c0, float c1, float c2, float c3) {
224         return (r0 * c0) + (r1 * c1) + (r2 * c2) + (r3 * c3);
225     }
226 
227     /**
228      * Multiplies (x, y, z, w) vector by the Matrix44, then returns the new (x, y, z, w). Users
229      * should set {@code w} to 1 to indicate the coordinates are normalized.
230      *
231      * @return An array of length 4 that represents the x, y, z, w (where w is perspective) value
232      * after multiplying x, y, z, 1 by the matrix
233      */
234     @FlaggedApi(Flags.FLAG_MATRIX_44)
map(float x, float y, float z, float w)235     public @NonNull float[] map(float x, float y, float z, float w) {
236         float[] dst = new float[4];
237         this.map(x, y, z, w, dst);
238         return dst;
239     }
240 
241     /**
242      * Multiplies (x, y, z, w) vector by the Matrix44, then returns the new (x, y, z, w). Users
243      * should set {@code w} to 1 to indicate the coordinates are normalized.
244      */
245     @FlaggedApi(Flags.FLAG_MATRIX_44)
map(float x, float y, float z, float w, @NonNull float[] dst)246     public void map(float x, float y, float z, float w, @NonNull float[] dst) {
247         if (dst.length != 4) {
248             throw new IllegalArgumentException("Dst array must be of length 4");
249         }
250         dst[0] = x * mBackingArray[0] + y * mBackingArray[1]
251                 + z * mBackingArray[2] + w * mBackingArray[3];
252         dst[1] = x * mBackingArray[4] + y * mBackingArray[5]
253                 + z * mBackingArray[6] + w * mBackingArray[7];
254         dst[2] = x * mBackingArray[8] + y * mBackingArray[9]
255                 + z * mBackingArray[10] + w * mBackingArray[11];
256         dst[3] = x * mBackingArray[12] + y * mBackingArray[13]
257                 + z * mBackingArray[14] + w * mBackingArray[15];
258     }
259 
260     /**
261      * Multiplies `this` matrix (A) and provided Matrix (B) in the order of A * B.
262      * The result is saved in `this` Matrix.
263      *
264      * @param b The second Matrix in the concatenation operation
265      * @return A reference to this Matrix, which can be used to chain Matrix operations
266      */
267     @FlaggedApi(Flags.FLAG_MATRIX_44)
concat(@onNull Matrix44 b)268     public @NonNull Matrix44 concat(@NonNull Matrix44 b) {
269         float val00 = dot(this, b, 0, 0);
270         float val01 = dot(this, b, 0, 1);
271         float val02 = dot(this, b, 0, 2);
272         float val03 = dot(this, b, 0, 3);
273         float val10 = dot(this, b, 1, 0);
274         float val11 = dot(this, b, 1, 1);
275         float val12 = dot(this, b, 1, 2);
276         float val13 = dot(this, b, 1, 3);
277         float val20 = dot(this, b, 2, 0);
278         float val21 = dot(this, b, 2, 1);
279         float val22 = dot(this, b, 2, 2);
280         float val23 = dot(this, b, 2, 3);
281         float val30 = dot(this, b, 3, 0);
282         float val31 = dot(this, b, 3, 1);
283         float val32 = dot(this, b, 3, 2);
284         float val33 = dot(this, b, 3, 3);
285 
286         mBackingArray[0] = val00;
287         mBackingArray[1] = val01;
288         mBackingArray[2] = val02;
289         mBackingArray[3] = val03;
290         mBackingArray[4] = val10;
291         mBackingArray[5] = val11;
292         mBackingArray[6] = val12;
293         mBackingArray[7] = val13;
294         mBackingArray[8] = val20;
295         mBackingArray[9] = val21;
296         mBackingArray[10] = val22;
297         mBackingArray[11] = val23;
298         mBackingArray[12] = val30;
299         mBackingArray[13] = val31;
300         mBackingArray[14] = val32;
301         mBackingArray[15] = val33;
302 
303         return this;
304     }
305 
306     /**
307      * Applies a rotation around a given axis, then returns self.
308      * {@code x}, {@code y}, {@code z} represent the axis by which to rotate around.
309      * For example, pass in {@code 1, 0, 0} to rotate around the x-axis.
310      * The axis provided will be normalized.
311      *
312      * @param deg Amount in degrees to rotate the matrix about the x-axis
313      * @param xComp X component of the rotation axis
314      * @param yComp Y component of the rotation axis
315      * @param zComp Z component of the rotation axis
316      * @return A reference to this Matrix, which can be used to chain Matrix operations
317      */
318     @FlaggedApi(Flags.FLAG_MATRIX_44)
rotate(float deg, float xComp, float yComp, float zComp)319     public @NonNull Matrix44 rotate(float deg, float xComp, float yComp, float zComp) {
320         float sum = xComp + yComp + zComp;
321         float x = xComp / sum;
322         float y = yComp / sum;
323         float z = zComp / sum;
324 
325         float c = (float) Math.cos(deg * Math.PI / 180.0f);
326         float s = (float) Math.sin(deg * Math.PI / 180.0f);
327         float t = 1 - c;
328 
329         float rotVals00 = t * x * x + c;
330         float rotVals01 = t * x * y - s * z;
331         float rotVals02 = t * x * z + s * y;
332         float rotVals10 = t * x * y + s * z;
333         float rotVals11 = t * y * y + c;
334         float rotVals12 = t * y * z - s * x;
335         float rotVals20 = t * x * z - s * y;
336         float rotVals21 = t * y * z + s * x;
337         float rotVals22 = t * z * z + c;
338 
339         float v00 = dot(mBackingArray[0], mBackingArray[1], mBackingArray[2], mBackingArray[3],
340                 rotVals00, rotVals10, rotVals20, 0);
341         float v01 = dot(mBackingArray[0], mBackingArray[1], mBackingArray[2], mBackingArray[3],
342                 rotVals01, rotVals11, rotVals21, 0);
343         float v02 = dot(mBackingArray[0], mBackingArray[1], mBackingArray[2], mBackingArray[3],
344                 rotVals02, rotVals12, rotVals22, 0);
345         float v03 = dot(mBackingArray[0], mBackingArray[1], mBackingArray[2], mBackingArray[3],
346                 0, 0, 0, 1);
347         float v10 = dot(mBackingArray[4], mBackingArray[5], mBackingArray[6], mBackingArray[7],
348                 rotVals00, rotVals10, rotVals20, 0);
349         float v11 = dot(mBackingArray[4], mBackingArray[5], mBackingArray[6], mBackingArray[7],
350                 rotVals01, rotVals11, rotVals21, 0);
351         float v12 = dot(mBackingArray[4], mBackingArray[5], mBackingArray[6], mBackingArray[7],
352                 rotVals02, rotVals12, rotVals22, 0);
353         float v13 = dot(mBackingArray[4], mBackingArray[5], mBackingArray[6], mBackingArray[7],
354                 0, 0, 0, 1);
355         float v20 = dot(mBackingArray[8], mBackingArray[9], mBackingArray[10], mBackingArray[11],
356                 rotVals00, rotVals10, rotVals20, 0);
357         float v21 = dot(mBackingArray[8], mBackingArray[9], mBackingArray[10], mBackingArray[11],
358                 rotVals01, rotVals11, rotVals21, 0);
359         float v22 = dot(mBackingArray[8], mBackingArray[9], mBackingArray[10], mBackingArray[11],
360                 rotVals02, rotVals12, rotVals22, 0);
361         float v23 = dot(mBackingArray[8], mBackingArray[9], mBackingArray[10], mBackingArray[11],
362                 0, 0, 0, 1);
363         float v30 = dot(mBackingArray[12], mBackingArray[13], mBackingArray[14], mBackingArray[15],
364                 rotVals00, rotVals10, rotVals20, 0);
365         float v31 = dot(mBackingArray[12], mBackingArray[13], mBackingArray[14], mBackingArray[15],
366                 rotVals01, rotVals11, rotVals21, 0);
367         float v32 = dot(mBackingArray[12], mBackingArray[13], mBackingArray[14], mBackingArray[15],
368                 rotVals02, rotVals12, rotVals22, 0);
369         float v33 = dot(mBackingArray[12], mBackingArray[13], mBackingArray[14], mBackingArray[15],
370                 0, 0, 0, 1);
371 
372         mBackingArray[0] = v00;
373         mBackingArray[1] = v01;
374         mBackingArray[2] = v02;
375         mBackingArray[3] = v03;
376         mBackingArray[4] = v10;
377         mBackingArray[5] = v11;
378         mBackingArray[6] = v12;
379         mBackingArray[7] = v13;
380         mBackingArray[8] = v20;
381         mBackingArray[9] = v21;
382         mBackingArray[10] = v22;
383         mBackingArray[11] = v23;
384         mBackingArray[12] = v30;
385         mBackingArray[13] = v31;
386         mBackingArray[14] = v32;
387         mBackingArray[15] = v33;
388 
389         return this;
390     }
391 
392     /**
393      * Applies scaling factors to `this` Matrix44, then returns self. Pass 1s for no change.
394      *
395      * @param x Scaling factor for the x-axis
396      * @param y Scaling factor for the y-axis
397      * @param z Scaling factor for the z-axis
398      * @return A reference to this Matrix, which can be used to chain Matrix operations
399      */
400     @FlaggedApi(Flags.FLAG_MATRIX_44)
scale(float x, float y, float z)401     public @NonNull Matrix44 scale(float x, float y, float z) {
402         mBackingArray[0] *= x;
403         mBackingArray[4] *= x;
404         mBackingArray[8] *= x;
405         mBackingArray[12] *= x;
406         mBackingArray[1] *= y;
407         mBackingArray[5] *= y;
408         mBackingArray[9] *= y;
409         mBackingArray[13] *= y;
410         mBackingArray[2] *= z;
411         mBackingArray[6] *= z;
412         mBackingArray[10] *= z;
413         mBackingArray[14] *= z;
414 
415         return this;
416     }
417 
418     /**
419      * Applies a translation to `this` Matrix44, then returns self.
420      *
421      * @param x Translation for the x-axis
422      * @param y Translation for the y-axis
423      * @param z Translation for the z-axis
424      * @return A reference to this Matrix, which can be used to chain Matrix operations
425      */
426     @FlaggedApi(Flags.FLAG_MATRIX_44)
translate(float x, float y, float z)427     public @NonNull Matrix44 translate(float x, float y, float z) {
428         float newX = x * mBackingArray[0] + y * mBackingArray[1]
429                 + z * mBackingArray[2] + mBackingArray[3];
430         float newY = x * mBackingArray[4] + y * mBackingArray[5]
431                 + z * mBackingArray[6] + mBackingArray[7];
432         float newZ = x * mBackingArray[8] + y * mBackingArray[9]
433                 + z * mBackingArray[10] + mBackingArray[11];
434         float newW = x * mBackingArray[12] + y * mBackingArray[13]
435                 + z * mBackingArray[14] + mBackingArray[15];
436 
437         mBackingArray[3] = newX;
438         mBackingArray[7] = newY;
439         mBackingArray[11] = newZ;
440         mBackingArray[15] = newW;
441 
442         return this;
443     }
444 
445     @Override
toString()446     public String toString() {
447         return String.format("""
448                         | %f %f %f %f |
449                         | %f %f %f %f |
450                         | %f %f %f %f |
451                         | %f %f %f %f |
452                         """, mBackingArray[0], mBackingArray[1], mBackingArray[2], mBackingArray[3],
453                 mBackingArray[4], mBackingArray[5], mBackingArray[6], mBackingArray[7],
454                 mBackingArray[8], mBackingArray[9], mBackingArray[10], mBackingArray[11],
455                 mBackingArray[12], mBackingArray[13], mBackingArray[14], mBackingArray[15]);
456     }
457 
458     @Override
equals(Object obj)459     public boolean equals(Object obj) {
460         if (obj instanceof Matrix44) {
461             return Arrays.equals(mBackingArray, ((Matrix44) obj).mBackingArray);
462         }
463         return false;
464     }
465 
466     @Override
hashCode()467     public int hashCode() {
468         return (int) mBackingArray[0] + (int) mBackingArray[1] + (int) mBackingArray[2]
469                 + (int) mBackingArray[3] + (int) mBackingArray[4] + (int) mBackingArray[5]
470                 + (int) mBackingArray[6] + (int) mBackingArray[7] + (int) mBackingArray[8]
471                 + (int) mBackingArray[9] + (int) mBackingArray[10] + (int) mBackingArray[11]
472                 + (int) mBackingArray[12] + (int) mBackingArray[13] + (int) mBackingArray[14]
473                 + (int) mBackingArray[15];
474     }
475 
476 }
477