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