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 val isTranslating: Boolean 62 get() = type?.isFlagSet(TRANSLATE_VAL) ?: false 63 val isRotating: Boolean 64 get() = type?.isFlagSet(ROTATE_VAL) ?: false 65 getRotationnull66 fun getRotation(): Rotation { 67 if (type == null) { 68 return Rotation.ROTATION_0 69 } 70 71 return when { 72 type.isFlagClear(SCALE_VAL or ROTATE_VAL or TRANSLATE_VAL) -> Rotation.ROTATION_0 73 type.isFlagSet(ROT_90_VAL) -> Rotation.ROTATION_90 74 type.isFlagSet(FLIP_V_VAL or FLIP_H_VAL) -> Rotation.ROTATION_180 75 type.isFlagSet(ROT_90_VAL or FLIP_V_VAL or FLIP_H_VAL) -> Rotation.ROTATION_270 76 else -> Rotation.ROTATION_0 77 } 78 } 79 80 private val typeFlags: Collection<String> 81 get() { 82 if (type == null) { 83 return listOf("IDENTITY") 84 } 85 86 val result = mutableListOf<String>() 87 88 if (type.isFlagClear(SCALE_VAL or ROTATE_VAL or TRANSLATE_VAL)) { 89 result.add("IDENTITY") 90 } 91 92 if (type.isFlagSet(SCALE_VAL)) { 93 result.add("SCALE") 94 } 95 96 if (type.isFlagSet(TRANSLATE_VAL)) { 97 result.add("TRANSLATE") 98 } 99 100 when { 101 type.isFlagSet(ROT_INVALID_VAL) -> result.add("ROT_INVALID") 102 type.isFlagSet(ROT_90_VAL or FLIP_V_VAL or FLIP_H_VAL) -> result.add("ROT_270") 103 type.isFlagSet(FLIP_V_VAL or FLIP_H_VAL) -> result.add("ROT_180") 104 else -> { 105 if (type.isFlagSet(ROT_90_VAL)) { 106 result.add("ROT_90") 107 } 108 if (type.isFlagSet(FLIP_V_VAL)) { 109 result.add("FLIP_V") 110 } 111 if (type.isFlagSet(FLIP_H_VAL)) { 112 result.add("FLIP_H") 113 } 114 } 115 } 116 117 if (result.isEmpty()) { 118 throw RuntimeException("Unknown transform type $type") 119 } 120 121 return result 122 } 123 toStringnull124 override fun toString(): String { 125 val transformType = typeFlags.joinToString("|") 126 127 if (isSimpleTransform(type)) { 128 return transformType 129 } 130 131 return "$transformType $matrix" 132 } 133 applynull134 fun apply(bounds: RectF?): RectF { 135 return multiplyRect(matrix, bounds ?: RectF()) 136 } 137 138 private data class Vec2(val x: Float, val y: Float) 139 multiplyRectnull140 private fun multiplyRect(matrix: Matrix33, rect: RectF): RectF { 141 // |dsdx dsdy tx| | left, top | 142 // matrix = |dtdx dtdy ty| rect = | | 143 // |0 0 1 | | right, bottom | 144 145 val leftTop = multiplyVec2(matrix, rect.left, rect.top) 146 val rightTop = multiplyVec2(matrix, rect.right, rect.top) 147 val leftBottom = multiplyVec2(matrix, rect.left, rect.bottom) 148 val rightBottom = multiplyVec2(matrix, rect.right, rect.bottom) 149 150 return RectF( 151 /* left */ arrayOf(leftTop.x, rightTop.x, leftBottom.x, rightBottom.x).minOrNull() 152 ?: 0f, 153 /* top */ arrayOf(leftTop.y, rightTop.y, leftBottom.y, rightBottom.y).minOrNull() ?: 0f, 154 /* right */ arrayOf(leftTop.x, rightTop.x, leftBottom.x, rightBottom.x).minOrNull() 155 ?: 0f, 156 /* bottom */ arrayOf(leftTop.y, rightTop.y, leftBottom.y, rightBottom.y).minOrNull() 157 ?: 0f 158 ) 159 } 160 multiplyVec2null161 private fun multiplyVec2(matrix: Matrix33, x: Float, y: Float): Vec2 { 162 // |dsdx dsdy tx| | x | 163 // |dtdx dtdy ty| x | y | 164 // |0 0 1 | | 1 | 165 return Vec2( 166 matrix.dsdx * x + matrix.dsdy * y + matrix.tx, 167 matrix.dtdx * x + matrix.dtdy * y + matrix.ty 168 ) 169 } 170 equalsnull171 override fun equals(other: Any?): Boolean { 172 if (this === other) return true 173 if (other !is Transform) return false 174 175 if (type != other.type) return false 176 if (matrix != other.matrix) return false 177 if (isSimpleRotation != other.isSimpleRotation) return false 178 179 return true 180 } 181 hashCodenull182 override fun hashCode(): Int { 183 var result = type ?: 0 184 result = 31 * result + matrix.hashCode() 185 result = 31 * result + isSimpleRotation.hashCode() 186 return result 187 } 188 189 companion object { 190 val EMPTY: Transform <lambda>null191 get() = withCache { Transform(type = null, matrix = Matrix33.EMPTY) } 192 193 /* transform type flags */ 194 const val TRANSLATE_VAL = 0x0001 195 const val ROTATE_VAL = 0x0002 196 const val SCALE_VAL = 0x0004 197 198 /* orientation flags */ 199 const val FLIP_H_VAL = 0x0100 // (1 << 0 << 8) 200 const val FLIP_V_VAL = 0x0200 // (1 << 1 << 8) 201 const val ROT_90_VAL = 0x0400 // (1 << 2 << 8) 202 const val ROT_INVALID_VAL = 0x8000 // (0x80 << 8) 203 isSimpleTransformnull204 fun isSimpleTransform(type: Int?): Boolean { 205 return type?.isFlagClear(ROT_INVALID_VAL or SCALE_VAL) ?: false 206 } 207 isFlagClearnull208 fun Int.isFlagClear(bits: Int): Boolean { 209 return this and bits == 0 210 } 211 isFlagSetnull212 fun Int.isFlagSet(bits: Int): Boolean { 213 return this and bits == bits 214 } 215 <lambda>null216 fun from(type: Int?, matrix: Matrix33): Transform = withCache { Transform(type, matrix) } 217 } 218 } 219