1 /*
2  * Copyright (C) 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 com.google.android.wallpaper.weathereffects.graphics.utils
18 
19 import android.graphics.Matrix
20 import android.util.SizeF
21 
22 /** Helper functions for matrix operations. */
23 object MatrixUtils {
24     // Member variables in this object should be only used as intermediate buffer
25     // Should not be used as any return value
26     private val inverseMatrix: Matrix = Matrix()
27     private val concatMatrix: Matrix = Matrix()
28     private val matrixValues = FloatArray(9)
29 
30     /** Returns a [Matrix] that crops the image and centers to the screen. */
centerCropMatrixnull31     fun centerCropMatrix(surfaceSize: SizeF, imageSize: SizeF): Matrix {
32         val widthScale = surfaceSize.width / imageSize.width
33         val heightScale = surfaceSize.height / imageSize.height
34         val scale = maxOf(widthScale, heightScale)
35 
36         return Matrix(Matrix.IDENTITY_MATRIX).apply {
37             // Move the origin of the image to its center.
38             postTranslate(-imageSize.width / 2f, -imageSize.height / 2f)
39             // Apply scale.
40             postScale(scale, scale)
41             // Translate back to the center of the screen.
42             postTranslate(surfaceSize.width / 2f, surfaceSize.height / 2f)
43         }
44     }
45 
46     // To apply parallax matrix to fragCoord, we need to invert and transpose the matrix
invertAndTransposeMatrixnull47     fun invertAndTransposeMatrix(matrix: Matrix, outArray: FloatArray): FloatArray {
48         matrix.invert(inverseMatrix)
49         inverseMatrix.getValues(matrixValues)
50         return transposeMatrixArray(matrixValues, outArray)
51     }
52 
getScalenull53     fun getScale(matrix: Matrix): Float {
54         matrix.getValues(matrixValues)
55         return matrixValues[0]
56     }
57 
58     /**
59      * Calculates the transformation matrix that, when applied to `originMatrix`, results in
60      * `targetMatrix`. Current use case: Calculating parallax effect for the homescreen compared
61      * with page 0.
62      *
63      * @param originMatrix The original transformation matrix.
64      * @param targetMatrix The target transformation matrix.
65      * @param outArray A pre-allocated FloatArray to store the result.
66      * @return The transformation difference matrix as a FloatArray.
67      */
calculateTransformDifferencenull68     fun calculateTransformDifference(
69         originMatrix: Matrix,
70         targetMatrix: Matrix,
71         outArray: FloatArray,
72     ): FloatArray {
73         targetMatrix.invert(inverseMatrix)
74         concatMatrix.set(originMatrix)
75         concatMatrix.postConcat(inverseMatrix)
76         concatMatrix.getValues(matrixValues)
77         return transposeMatrixArray(matrixValues, outArray)
78     }
79 
80     // Transpose 3x3 matrix values as a FloatArray[9], write results to outArray
transposeMatrixArraynull81     private fun transposeMatrixArray(inMatrixArray: FloatArray, outArray: FloatArray): FloatArray {
82         for (i in 0 until 3) {
83             for (j in 0 until 3) {
84                 outArray[j * 3 + i] = inMatrixArray[i * 3 + j]
85             }
86         }
87         return outArray
88     }
89 }
90