1/* 2 * Copyright (C) 2024 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 17import {assertDefined} from 'common/assert_utils'; 18import { 19 IDENTITY_MATRIX, 20 TransformMatrix, 21} from 'common/geometry/transform_matrix'; 22import {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 23 24export enum TransformTypeFlags { 25 EMPTY = 0x0, 26 TRANSLATE_VAL = 0x0001, 27 ROTATE_VAL = 0x0002, 28 SCALE_VAL = 0x0004, 29 FLIP_H_VAL = 0x0100, 30 FLIP_V_VAL = 0x0200, 31 ROT_90_VAL = 0x0400, 32 ROT_INVALID_VAL = 0x8000, 33} 34 35export class Transform { 36 static EMPTY = new Transform(TransformTypeFlags.EMPTY, IDENTITY_MATRIX); 37 38 constructor( 39 public type: TransformTypeFlags, 40 public matrix: TransformMatrix, 41 ) {} 42 43 static from( 44 transformNode: PropertyTreeNode, 45 position?: PropertyTreeNode, 46 ): Transform { 47 if (transformNode.getAllChildren().length === 0) return Transform.EMPTY; 48 49 const transformType = transformNode.getChildByName('type')?.getValue() ?? 0; 50 const matrixNode = transformNode.getChildByName('matrix'); 51 52 if (matrixNode) { 53 return new Transform( 54 transformType, 55 TransformMatrix.from({ 56 dsdx: assertDefined(matrixNode.getChildByName('dsdx')).getValue(), 57 dtdx: assertDefined(matrixNode.getChildByName('dtdx')).getValue(), 58 tx: assertDefined(matrixNode.getChildByName('tx')).getValue(), 59 dtdy: assertDefined(matrixNode.getChildByName('dtdy')).getValue(), 60 dsdy: assertDefined(matrixNode.getChildByName('dsdy')).getValue(), 61 ty: assertDefined(matrixNode.getChildByName('ty')).getValue(), 62 }), 63 ); 64 } 65 66 const x = position?.getChildByName('x')?.getValue() ?? 0; 67 const y = position?.getChildByName('y')?.getValue() ?? 0; 68 69 if (TransformType.isSimpleTransform(transformType)) { 70 return TransformType.getDefaultTransform(transformType, x, y); 71 } 72 73 return new Transform( 74 transformType, 75 TransformMatrix.from({ 76 dsdx: transformNode.getChildByName('dsdx')?.getValue() ?? 0, 77 dtdx: transformNode.getChildByName('dtdx')?.getValue() ?? 0, 78 tx: x, 79 dtdy: transformNode.getChildByName('dtdy')?.getValue() ?? 0, 80 dsdy: transformNode.getChildByName('dsdy')?.getValue() ?? 0, 81 ty: y, 82 }), 83 ); 84 } 85} 86 87export class TransformType { 88 static isSimpleRotation(type: TransformTypeFlags | undefined): boolean { 89 return !(type 90 ? TransformType.isFlagSet(type, TransformTypeFlags.ROT_INVALID_VAL) 91 : false); 92 } 93 94 static getTypeFlags(type: TransformTypeFlags): string { 95 const typeFlags: string[] = []; 96 97 if ( 98 TransformType.isFlagClear( 99 type, 100 TransformTypeFlags.SCALE_VAL | 101 TransformTypeFlags.ROTATE_VAL | 102 TransformTypeFlags.TRANSLATE_VAL, 103 ) 104 ) { 105 typeFlags.push('IDENTITY'); 106 } 107 108 if (TransformType.isFlagSet(type, TransformTypeFlags.SCALE_VAL)) { 109 typeFlags.push('SCALE'); 110 } 111 112 if (TransformType.isFlagSet(type, TransformTypeFlags.TRANSLATE_VAL)) { 113 typeFlags.push('TRANSLATE'); 114 } 115 116 if (TransformType.isFlagSet(type, TransformTypeFlags.ROT_INVALID_VAL)) { 117 typeFlags.push('ROT_INVALID'); 118 } else if ( 119 TransformType.isFlagSet( 120 type, 121 TransformTypeFlags.ROT_90_VAL | 122 TransformTypeFlags.FLIP_V_VAL | 123 TransformTypeFlags.FLIP_H_VAL, 124 ) 125 ) { 126 typeFlags.push('ROT_270'); 127 } else if ( 128 TransformType.isFlagSet( 129 type, 130 TransformTypeFlags.FLIP_V_VAL | TransformTypeFlags.FLIP_H_VAL, 131 ) 132 ) { 133 typeFlags.push('ROT_180'); 134 } else { 135 if (TransformType.isFlagSet(type, TransformTypeFlags.ROT_90_VAL)) { 136 typeFlags.push('ROT_90'); 137 } 138 if (TransformType.isFlagSet(type, TransformTypeFlags.FLIP_V_VAL)) { 139 typeFlags.push('FLIP_V'); 140 } 141 if (TransformType.isFlagSet(type, TransformTypeFlags.FLIP_H_VAL)) { 142 typeFlags.push('FLIP_H'); 143 } 144 } 145 146 if (typeFlags.length === 0) { 147 throw TransformType.makeUnknownTransformTypeError(type); 148 } 149 return typeFlags.join('|'); 150 } 151 152 static getDefaultTransform( 153 type: TransformTypeFlags, 154 x: number, 155 y: number, 156 ): Transform { 157 // IDENTITY 158 if (!type) { 159 return new Transform( 160 type, 161 TransformMatrix.from({ 162 dsdx: 1, 163 dtdx: 0, 164 tx: x, 165 dtdy: 0, 166 dsdy: 1, 167 ty: y, 168 }), 169 ); 170 } 171 172 // ROT_270 = ROT_90|FLIP_H|FLIP_V 173 if ( 174 TransformType.isFlagSet( 175 type, 176 TransformTypeFlags.ROT_90_VAL | 177 TransformTypeFlags.FLIP_V_VAL | 178 TransformTypeFlags.FLIP_H_VAL, 179 ) 180 ) { 181 return new Transform( 182 type, 183 TransformMatrix.from({ 184 dsdx: 0, 185 dtdx: -1, 186 tx: x, 187 dtdy: 1, 188 dsdy: 0, 189 ty: y, 190 }), 191 ); 192 } 193 194 // ROT_180 = FLIP_H|FLIP_V 195 if ( 196 TransformType.isFlagSet( 197 type, 198 TransformTypeFlags.FLIP_V_VAL | TransformTypeFlags.FLIP_H_VAL, 199 ) 200 ) { 201 return new Transform( 202 type, 203 TransformMatrix.from({ 204 dsdx: -1, 205 dtdx: 0, 206 tx: x, 207 dtdy: 0, 208 dsdy: -1, 209 ty: y, 210 }), 211 ); 212 } 213 214 // ROT_90 215 if (TransformType.isFlagSet(type, TransformTypeFlags.ROT_90_VAL)) { 216 return new Transform( 217 type, 218 TransformMatrix.from({ 219 dsdx: 0, 220 dtdx: 1, 221 tx: x, 222 dtdy: -1, 223 dsdy: 0, 224 ty: y, 225 }), 226 ); 227 } 228 229 // IDENTITY 230 if ( 231 TransformType.isFlagClear( 232 type, 233 TransformTypeFlags.SCALE_VAL | TransformTypeFlags.ROTATE_VAL, 234 ) 235 ) { 236 return new Transform( 237 type, 238 TransformMatrix.from({ 239 dsdx: 1, 240 dtdx: 0, 241 tx: x, 242 dtdy: 0, 243 dsdy: 1, 244 ty: y, 245 }), 246 ); 247 } 248 249 throw TransformType.makeUnknownTransformTypeError(type); 250 } 251 252 static makeUnknownTransformTypeError(type: TransformTypeFlags): Error { 253 return new Error(`Unknown transform type ${type} found in SF trace entry`); 254 } 255 256 static isSimpleTransform(type: TransformTypeFlags): boolean { 257 return TransformType.isFlagClear( 258 type, 259 TransformTypeFlags.ROT_INVALID_VAL | TransformTypeFlags.SCALE_VAL, 260 ); 261 } 262 263 private static isFlagSet(type: TransformTypeFlags, bits: number): boolean { 264 type = type || 0; 265 return (type & bits) === bits; 266 } 267 268 private static isFlagClear(type: TransformTypeFlags, bits: number): boolean { 269 return (type & bits) === 0; 270 } 271} 272