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