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