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