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