• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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