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 {Timestamp} from 'common/time/time'; 18import {TimeDuration} from 'common/time/time_duration'; 19import {RawDataUtils} from 'parsers/raw_data_utils'; 20import {TransformType} from 'parsers/surface_flinger/transform_utils'; 21import {CujType} from 'trace/cuj_type'; 22import {PropertyTreeNode} from './property_tree_node'; 23 24const EMPTY_OBJ_STRING = '{empty}'; 25const EMPTY_ARRAY_STRING = '[empty]'; 26 27function formatAsDecimal(value: number): string { 28 if (!Number.isInteger(value)) { 29 return value.toFixed(3).toString(); 30 } 31 return value.toString(); 32} 33 34function formatAsHex(value: number, upperCase = false): string { 35 if (value < 0) { 36 value += Math.pow(2, 32); // convert to 2's complement 37 } 38 let hexValue = value.toString(16); 39 if (upperCase) { 40 hexValue = hexValue.toUpperCase(); 41 } 42 return '0x' + hexValue; 43} 44 45interface PropertyFormatter { 46 format(node: PropertyTreeNode): string; 47} 48 49class DefaultPropertyFormatter implements PropertyFormatter { 50 format(node: PropertyTreeNode): string { 51 const value = node.getValue(); 52 if (Array.isArray(value) && value.length === 0) { 53 return EMPTY_ARRAY_STRING; 54 } 55 56 if (typeof value === 'number') { 57 return formatAsDecimal(value); 58 } 59 60 if (value?.toString) return value.toString(); 61 62 return `${value}`; 63 } 64} 65const DEFAULT_PROPERTY_FORMATTER = new DefaultPropertyFormatter(); 66 67class ColorFormatter implements PropertyFormatter { 68 format(node: PropertyTreeNode): string { 69 const rNode = node.getChildByName('r'); 70 const gNode = node.getChildByName('g'); 71 const bNode = node.getChildByName('b'); 72 const alphaNode = node.getChildByName('a'); 73 74 const r = formatAsDecimal(rNode?.getValue() ?? 0); 75 const g = formatAsDecimal(gNode?.getValue() ?? 0); 76 const b = formatAsDecimal(bNode?.getValue() ?? 0); 77 const rgbString = `(${r}, ${g}, ${b})`; 78 if (rNode && gNode && bNode && !alphaNode) { 79 return rgbString; 80 } 81 82 const alpha = formatAsDecimal(alphaNode?.getValue() ?? 0); 83 const alphaString = `alpha: ${alpha}`; 84 if (RawDataUtils.isEmptyObj(node)) { 85 return `${EMPTY_OBJ_STRING}, ${alphaString}`; 86 } 87 return `${rgbString}, ${alphaString}`; 88 } 89} 90const COLOR_FORMATTER = new ColorFormatter(); 91 92class RectFormatter implements PropertyFormatter { 93 format(node: PropertyTreeNode): string { 94 if (!RawDataUtils.isRect(node) || RawDataUtils.isEmptyObj(node)) { 95 return EMPTY_OBJ_STRING; 96 } 97 const left = formatAsDecimal(node.getChildByName('left')?.getValue() ?? 0); 98 const top = formatAsDecimal(node.getChildByName('top')?.getValue() ?? 0); 99 const right = formatAsDecimal( 100 node.getChildByName('right')?.getValue() ?? 0, 101 ); 102 const bottom = formatAsDecimal( 103 node.getChildByName('bottom')?.getValue() ?? 0, 104 ); 105 106 return `(${left}, ${top}) - (${right}, ${bottom})`; 107 } 108} 109const RECT_FORMATTER = new RectFormatter(); 110 111class BufferFormatter implements PropertyFormatter { 112 format(node: PropertyTreeNode): string { 113 return `w: ${node.getChildByName('width')?.getValue() ?? 0}, h: ${ 114 node.getChildByName('height')?.getValue() ?? 0 115 }, stride: ${node.getChildByName('stride')?.getValue()}, format: ${node 116 .getChildByName('format') 117 ?.getValue()}`; 118 } 119} 120const BUFFER_FORMATTER = new BufferFormatter(); 121 122class LayerIdFormatter implements PropertyFormatter { 123 format(node: PropertyTreeNode): string { 124 const value = node.getValue(); 125 return value === -1 || value === 0 ? 'none' : `${value}`; 126 } 127} 128const LAYER_ID_FORMATTER = new LayerIdFormatter(); 129 130class MatrixFormatter implements PropertyFormatter { 131 format(node: PropertyTreeNode): string { 132 const dsdx = formatAsDecimal(node.getChildByName('dsdx')?.getValue() ?? 0); 133 const dtdx = formatAsDecimal(node.getChildByName('dtdx')?.getValue() ?? 0); 134 const dtdy = formatAsDecimal(node.getChildByName('dtdy')?.getValue() ?? 0); 135 const dsdy = formatAsDecimal(node.getChildByName('dsdy')?.getValue() ?? 0); 136 const tx = node.getChildByName('tx'); 137 const ty = node.getChildByName('ty'); 138 if ( 139 dsdx === '0' && 140 dtdx === '0' && 141 dsdy === '0' && 142 dtdy === '0' && 143 !tx && 144 !ty 145 ) { 146 return 'null'; 147 } 148 const matrix22 = `dsdx: ${dsdx}, dtdx: ${dtdx}, dtdy: ${dtdy}, dsdy: ${dsdy}`; 149 if (!tx && !ty) { 150 return matrix22; 151 } 152 return ( 153 matrix22 + 154 `, tx: ${formatAsDecimal(tx?.getValue() ?? 0)}, ty: ${formatAsDecimal( 155 ty?.getValue() ?? 0, 156 )}` 157 ); 158 } 159} 160const MATRIX_FORMATTER = new MatrixFormatter(); 161 162class TransformFormatter implements PropertyFormatter { 163 format(node: PropertyTreeNode): string { 164 const type = node.getChildByName('type'); 165 return type !== undefined 166 ? TransformType.getTypeFlags(type.getValue() ?? 0) 167 : 'null'; 168 } 169} 170const TRANSFORM_FORMATTER = new TransformFormatter(); 171 172class SizeFormatter implements PropertyFormatter { 173 format(node: PropertyTreeNode): string { 174 return `${node.getChildByName('w')?.getValue() ?? 0} x ${ 175 node.getChildByName('h')?.getValue() ?? 0 176 }`; 177 } 178} 179const SIZE_FORMATTER = new SizeFormatter(); 180 181class PositionFormatter implements PropertyFormatter { 182 format(node: PropertyTreeNode): string { 183 const x = formatAsDecimal(node.getChildByName('x')?.getValue() ?? 0); 184 const y = formatAsDecimal(node.getChildByName('y')?.getValue() ?? 0); 185 return `x: ${x}, y: ${y}`; 186 } 187} 188const POSITION_FORMATTER = new PositionFormatter(); 189 190class RegionFormatter implements PropertyFormatter { 191 format(node: PropertyTreeNode): string { 192 let res = 'SkRegion('; 193 node 194 .getChildByName('rect') 195 ?.getAllChildren() 196 .forEach((rectNode: PropertyTreeNode) => { 197 res += `(${rectNode.getChildByName('left')?.getValue() ?? 0}, ${ 198 rectNode.getChildByName('top')?.getValue() ?? 0 199 }, ${rectNode.getChildByName('right')?.getValue() ?? 0}, ${ 200 rectNode.getChildByName('bottom')?.getValue() ?? 0 201 })`; 202 }); 203 return res + ')'; 204 } 205} 206const REGION_FORMATTER = new RegionFormatter(); 207 208class EnumFormatter implements PropertyFormatter { 209 constructor(private readonly valuesById: {[key: number]: string}) {} 210 211 format(node: PropertyTreeNode): string { 212 const value = node.getValue(); 213 if (typeof value === 'number' && this.valuesById[value]) { 214 return this.valuesById[value]; 215 } 216 if (typeof value === 'bigint' && this.valuesById[Number(value)]) { 217 return this.valuesById[Number(value)]; 218 } 219 return `${value}`; 220 } 221} 222 223class FixedStringFormatter implements PropertyFormatter { 224 constructor(private readonly fixedStringValue: string) {} 225 226 format(node: PropertyTreeNode): string { 227 return this.fixedStringValue; 228 } 229} 230 231class TimestampNodeFormatter implements PropertyFormatter { 232 format(node: PropertyTreeNode): string { 233 const timestamp = node.getValue(); 234 if (timestamp instanceof Timestamp || timestamp instanceof TimeDuration) { 235 return timestamp.format(); 236 } 237 return 'null'; 238 } 239} 240const TIMESTAMP_NODE_FORMATTER = new TimestampNodeFormatter(); 241 242class CujTypeFormatter implements PropertyFormatter { 243 format(node: PropertyTreeNode): string { 244 const cujTypeId: string = `${node.getValue()}`; 245 let cujTypeString: string | undefined; 246 if (cujTypeId in CujType) { 247 cujTypeString = CujType[cujTypeId as keyof typeof CujType]; 248 } else { 249 cujTypeString = 'UNKNOWN'; 250 } 251 return `${cujTypeString} (${cujTypeId})`; 252 } 253} 254const CUJ_TYPE_FORMATTER = new CujTypeFormatter(); 255 256class HexFormatter implements PropertyFormatter { 257 format(node: PropertyTreeNode): string { 258 return formatAsHex(node.getValue() ?? 0); 259 } 260} 261const HEX_FORMATTER = new HexFormatter(); 262 263export { 264 EMPTY_OBJ_STRING, 265 EMPTY_ARRAY_STRING, 266 formatAsHex, 267 PropertyFormatter, 268 DEFAULT_PROPERTY_FORMATTER, 269 COLOR_FORMATTER, 270 RECT_FORMATTER, 271 BUFFER_FORMATTER, 272 LAYER_ID_FORMATTER, 273 TRANSFORM_FORMATTER, 274 SIZE_FORMATTER, 275 POSITION_FORMATTER, 276 REGION_FORMATTER, 277 EnumFormatter, 278 FixedStringFormatter, 279 TIMESTAMP_NODE_FORMATTER, 280 MATRIX_FORMATTER, 281 CUJ_TYPE_FORMATTER, 282 HEX_FORMATTER, 283}; 284