xref: /aosp_15_r20/development/tools/winscope/src/trace/tree_node/formatters.ts (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
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