xref: /aosp_15_r20/platform_testing/libraries/flicker/utils/src/android/tools/traces/surfaceflinger/Transform.kt (revision dd0948b35e70be4c0246aabd6c72554a5eb8b22a)
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 android.tools.traces.surfaceflinger
18 
19 import android.graphics.RectF
20 import android.tools.Rotation
21 import android.tools.datatypes.Matrix33
22 import android.tools.withCache
23 
24 /**
25  * Wrapper for TransformProto (frameworks/native/services/surfaceflinger/layerproto/common.proto)
26  *
27  * This class is used by flicker and Winscope
28  */
29 class Transform private constructor(val type: Int?, val matrix: Matrix33) {
30 
31     /**
32      * Returns true if the applying the transform on an an axis aligned rectangle results in another
33      * axis aligned rectangle.
34      */
35     val isSimpleRotation: Boolean = !(type?.isFlagSet(ROT_INVALID_VAL) ?: false)
36 
37     /**
38      * The transformation matrix is defined as the product of: | cos(a) -sin(a) | \/ | X 0 | |
39      * sin(a) cos(a) | /\ | 0 Y |
40      *
41      * where a is a rotation angle, and X and Y are scaling factors. A transformation matrix is
42      * invalid when either X or Y is zero, as a rotation matrix is valid for any angle. When either
43      * X or Y is 0, then the scaling matrix is not invertible, which makes the transformation matrix
44      * not invertible as well. A 2D matrix with components | A B | is not invertible if and only if
45      * AD - BC = 0.
46      *
47      * ```
48      *            | C D |
49      * ```
50      *
51      * This check is included above.
52      */
53     val isValid: Boolean
54         get() {
55             // determinant of transform
56             return matrix.dsdx * matrix.dtdy != matrix.dtdx * matrix.dsdy
57         }
58 
59     val isScaling: Boolean
60         get() = type?.isFlagSet(SCALE_VAL) ?: false
61 
62     val isTranslating: Boolean
63         get() = type?.isFlagSet(TRANSLATE_VAL) ?: false
64 
65     val isRotating: Boolean
66         get() = type?.isFlagSet(ROTATE_VAL) ?: false
67 
getRotationnull68     fun getRotation(): Rotation {
69         if (type == null) {
70             return Rotation.ROTATION_0
71         }
72 
73         return when {
74             type.isFlagClear(SCALE_VAL or ROTATE_VAL or TRANSLATE_VAL) -> Rotation.ROTATION_0
75             type.isFlagSet(ROT_90_VAL) -> Rotation.ROTATION_90
76             type.isFlagSet(FLIP_V_VAL or FLIP_H_VAL) -> Rotation.ROTATION_180
77             type.isFlagSet(ROT_90_VAL or FLIP_V_VAL or FLIP_H_VAL) -> Rotation.ROTATION_270
78             else -> Rotation.ROTATION_0
79         }
80     }
81 
82     private val typeFlags: Collection<String>
83         get() {
84             if (type == null) {
85                 return listOf("IDENTITY")
86             }
87 
88             val result = mutableListOf<String>()
89 
90             if (type.isFlagClear(SCALE_VAL or ROTATE_VAL or TRANSLATE_VAL)) {
91                 result.add("IDENTITY")
92             }
93 
94             if (type.isFlagSet(SCALE_VAL)) {
95                 result.add("SCALE")
96             }
97 
98             if (type.isFlagSet(TRANSLATE_VAL)) {
99                 result.add("TRANSLATE")
100             }
101 
102             when {
103                 type.isFlagSet(ROT_INVALID_VAL) -> result.add("ROT_INVALID")
104                 type.isFlagSet(ROT_90_VAL or FLIP_V_VAL or FLIP_H_VAL) -> result.add("ROT_270")
105                 type.isFlagSet(FLIP_V_VAL or FLIP_H_VAL) -> result.add("ROT_180")
106                 else -> {
107                     if (type.isFlagSet(ROT_90_VAL)) {
108                         result.add("ROT_90")
109                     }
110                     if (type.isFlagSet(FLIP_V_VAL)) {
111                         result.add("FLIP_V")
112                     }
113                     if (type.isFlagSet(FLIP_H_VAL)) {
114                         result.add("FLIP_H")
115                     }
116                 }
117             }
118 
119             if (result.isEmpty()) {
120                 throw RuntimeException("Unknown transform type $type")
121             }
122 
123             return result
124         }
125 
toStringnull126     override fun toString(): String {
127         val transformType = typeFlags.joinToString("|")
128 
129         if (isSimpleTransform(type)) {
130             return transformType
131         }
132 
133         return "$transformType $matrix"
134     }
135 
applynull136     fun apply(bounds: RectF?): RectF {
137         return multiplyRect(matrix, bounds ?: RectF())
138     }
139 
140     private data class Vec2(val x: Float, val y: Float)
141 
multiplyRectnull142     private fun multiplyRect(matrix: Matrix33, rect: RectF): RectF {
143         //          |dsdx dsdy  tx|         | left, top         |
144         // matrix = |dtdx dtdy  ty|  rect = |                   |
145         //          |0    0     1 |         |     right, bottom |
146 
147         val leftTop = multiplyVec2(matrix, rect.left, rect.top)
148         val rightTop = multiplyVec2(matrix, rect.right, rect.top)
149         val leftBottom = multiplyVec2(matrix, rect.left, rect.bottom)
150         val rightBottom = multiplyVec2(matrix, rect.right, rect.bottom)
151 
152         return RectF(
153             /* left */ arrayOf(leftTop.x, rightTop.x, leftBottom.x, rightBottom.x).minOrNull()
154                 ?: 0f,
155             /* top */ arrayOf(leftTop.y, rightTop.y, leftBottom.y, rightBottom.y).minOrNull() ?: 0f,
156             /* right */ arrayOf(leftTop.x, rightTop.x, leftBottom.x, rightBottom.x).minOrNull()
157                 ?: 0f,
158             /* bottom */ arrayOf(leftTop.y, rightTop.y, leftBottom.y, rightBottom.y).minOrNull()
159                 ?: 0f,
160         )
161     }
162 
multiplyVec2null163     private fun multiplyVec2(matrix: Matrix33, x: Float, y: Float): Vec2 {
164         // |dsdx dsdy  tx|     | x |
165         // |dtdx dtdy  ty|  x  | y |
166         // |0    0     1 |     | 1 |
167         return Vec2(
168             matrix.dsdx * x + matrix.dsdy * y + matrix.tx,
169             matrix.dtdx * x + matrix.dtdy * y + matrix.ty,
170         )
171     }
172 
equalsnull173     override fun equals(other: Any?): Boolean {
174         if (this === other) return true
175         if (other !is Transform) return false
176 
177         if (type != other.type) return false
178         if (matrix != other.matrix) return false
179         if (isSimpleRotation != other.isSimpleRotation) return false
180 
181         return true
182     }
183 
hashCodenull184     override fun hashCode(): Int {
185         var result = type ?: 0
186         result = 31 * result + matrix.hashCode()
187         result = 31 * result + isSimpleRotation.hashCode()
188         return result
189     }
190 
191     companion object {
192         val EMPTY: Transform
<lambda>null193             get() = withCache { Transform(type = null, matrix = Matrix33.EMPTY) }
194 
195         /* transform type flags */
196         const val TRANSLATE_VAL = 0x0001
197         const val ROTATE_VAL = 0x0002
198         const val SCALE_VAL = 0x0004
199 
200         /* orientation flags */
201         const val FLIP_H_VAL = 0x0100 // (1 << 0 << 8)
202         const val FLIP_V_VAL = 0x0200 // (1 << 1 << 8)
203         const val ROT_90_VAL = 0x0400 // (1 << 2 << 8)
204         const val ROT_INVALID_VAL = 0x8000 // (0x80 << 8)
205 
206         @JvmStatic
isSimpleTransformnull207         fun isSimpleTransform(type: Int?): Boolean {
208             return type?.isFlagClear(ROT_INVALID_VAL or SCALE_VAL) ?: false
209         }
210 
isFlagClearnull211         fun Int.isFlagClear(bits: Int): Boolean {
212             return this and bits == 0
213         }
214 
isFlagSetnull215         fun Int.isFlagSet(bits: Int): Boolean {
216             return this and bits == bits
217         }
218 
219         @JvmStatic
<lambda>null220         fun from(type: Int?, matrix: Matrix33): Transform = withCache { Transform(type, matrix) }
221     }
222 }
223