xref: /aosp_15_r20/development/tools/winscope/src/parsers/surface_flinger/transform_utils.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 {assertDefined} from 'common/assert_utils';
18import {
19  IDENTITY_MATRIX,
20  TransformMatrix,
21} from 'common/geometry/transform_matrix';
22import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
23
24export enum TransformTypeFlags {
25  EMPTY = 0x0,
26  TRANSLATE_VAL = 0x0001,
27  ROTATE_VAL = 0x0002,
28  SCALE_VAL = 0x0004,
29  FLIP_H_VAL = 0x0100,
30  FLIP_V_VAL = 0x0200,
31  ROT_90_VAL = 0x0400,
32  ROT_INVALID_VAL = 0x8000,
33}
34
35export class Transform {
36  static EMPTY = new Transform(TransformTypeFlags.EMPTY, IDENTITY_MATRIX);
37
38  constructor(
39    public type: TransformTypeFlags,
40    public matrix: TransformMatrix,
41  ) {}
42
43  static from(
44    transformNode: PropertyTreeNode,
45    position?: PropertyTreeNode,
46  ): Transform {
47    if (transformNode.getAllChildren().length === 0) return Transform.EMPTY;
48
49    const transformType = transformNode.getChildByName('type')?.getValue() ?? 0;
50    const matrixNode = transformNode.getChildByName('matrix');
51
52    if (matrixNode) {
53      return new Transform(
54        transformType,
55        TransformMatrix.from({
56          dsdx: assertDefined(matrixNode.getChildByName('dsdx')).getValue(),
57          dtdx: assertDefined(matrixNode.getChildByName('dtdx')).getValue(),
58          tx: assertDefined(matrixNode.getChildByName('tx')).getValue(),
59          dtdy: assertDefined(matrixNode.getChildByName('dtdy')).getValue(),
60          dsdy: assertDefined(matrixNode.getChildByName('dsdy')).getValue(),
61          ty: assertDefined(matrixNode.getChildByName('ty')).getValue(),
62        }),
63      );
64    }
65
66    const x = position?.getChildByName('x')?.getValue() ?? 0;
67    const y = position?.getChildByName('y')?.getValue() ?? 0;
68
69    if (TransformType.isSimpleTransform(transformType)) {
70      return TransformType.getDefaultTransform(transformType, x, y);
71    }
72
73    return new Transform(
74      transformType,
75      TransformMatrix.from({
76        dsdx: transformNode.getChildByName('dsdx')?.getValue() ?? 0,
77        dtdx: transformNode.getChildByName('dtdx')?.getValue() ?? 0,
78        tx: x,
79        dtdy: transformNode.getChildByName('dtdy')?.getValue() ?? 0,
80        dsdy: transformNode.getChildByName('dsdy')?.getValue() ?? 0,
81        ty: y,
82      }),
83    );
84  }
85}
86
87export class TransformType {
88  static isSimpleRotation(type: TransformTypeFlags | undefined): boolean {
89    return !(type
90      ? TransformType.isFlagSet(type, TransformTypeFlags.ROT_INVALID_VAL)
91      : false);
92  }
93
94  static getTypeFlags(type: TransformTypeFlags): string {
95    const typeFlags: string[] = [];
96
97    if (
98      TransformType.isFlagClear(
99        type,
100        TransformTypeFlags.SCALE_VAL |
101          TransformTypeFlags.ROTATE_VAL |
102          TransformTypeFlags.TRANSLATE_VAL,
103      )
104    ) {
105      typeFlags.push('IDENTITY');
106    }
107
108    if (TransformType.isFlagSet(type, TransformTypeFlags.SCALE_VAL)) {
109      typeFlags.push('SCALE');
110    }
111
112    if (TransformType.isFlagSet(type, TransformTypeFlags.TRANSLATE_VAL)) {
113      typeFlags.push('TRANSLATE');
114    }
115
116    if (TransformType.isFlagSet(type, TransformTypeFlags.ROT_INVALID_VAL)) {
117      typeFlags.push('ROT_INVALID');
118    } else if (
119      TransformType.isFlagSet(
120        type,
121        TransformTypeFlags.ROT_90_VAL |
122          TransformTypeFlags.FLIP_V_VAL |
123          TransformTypeFlags.FLIP_H_VAL,
124      )
125    ) {
126      typeFlags.push('ROT_270');
127    } else if (
128      TransformType.isFlagSet(
129        type,
130        TransformTypeFlags.FLIP_V_VAL | TransformTypeFlags.FLIP_H_VAL,
131      )
132    ) {
133      typeFlags.push('ROT_180');
134    } else {
135      if (TransformType.isFlagSet(type, TransformTypeFlags.ROT_90_VAL)) {
136        typeFlags.push('ROT_90');
137      }
138      if (TransformType.isFlagSet(type, TransformTypeFlags.FLIP_V_VAL)) {
139        typeFlags.push('FLIP_V');
140      }
141      if (TransformType.isFlagSet(type, TransformTypeFlags.FLIP_H_VAL)) {
142        typeFlags.push('FLIP_H');
143      }
144    }
145
146    if (typeFlags.length === 0) {
147      throw TransformType.makeUnknownTransformTypeError(type);
148    }
149    return typeFlags.join('|');
150  }
151
152  static getDefaultTransform(
153    type: TransformTypeFlags,
154    x: number,
155    y: number,
156  ): Transform {
157    // IDENTITY
158    if (!type) {
159      return new Transform(
160        type,
161        TransformMatrix.from({
162          dsdx: 1,
163          dtdx: 0,
164          tx: x,
165          dtdy: 0,
166          dsdy: 1,
167          ty: y,
168        }),
169      );
170    }
171
172    // ROT_270 = ROT_90|FLIP_H|FLIP_V
173    if (
174      TransformType.isFlagSet(
175        type,
176        TransformTypeFlags.ROT_90_VAL |
177          TransformTypeFlags.FLIP_V_VAL |
178          TransformTypeFlags.FLIP_H_VAL,
179      )
180    ) {
181      return new Transform(
182        type,
183        TransformMatrix.from({
184          dsdx: 0,
185          dtdx: -1,
186          tx: x,
187          dtdy: 1,
188          dsdy: 0,
189          ty: y,
190        }),
191      );
192    }
193
194    // ROT_180 = FLIP_H|FLIP_V
195    if (
196      TransformType.isFlagSet(
197        type,
198        TransformTypeFlags.FLIP_V_VAL | TransformTypeFlags.FLIP_H_VAL,
199      )
200    ) {
201      return new Transform(
202        type,
203        TransformMatrix.from({
204          dsdx: -1,
205          dtdx: 0,
206          tx: x,
207          dtdy: 0,
208          dsdy: -1,
209          ty: y,
210        }),
211      );
212    }
213
214    // ROT_90
215    if (TransformType.isFlagSet(type, TransformTypeFlags.ROT_90_VAL)) {
216      return new Transform(
217        type,
218        TransformMatrix.from({
219          dsdx: 0,
220          dtdx: 1,
221          tx: x,
222          dtdy: -1,
223          dsdy: 0,
224          ty: y,
225        }),
226      );
227    }
228
229    // IDENTITY
230    if (
231      TransformType.isFlagClear(
232        type,
233        TransformTypeFlags.SCALE_VAL | TransformTypeFlags.ROTATE_VAL,
234      )
235    ) {
236      return new Transform(
237        type,
238        TransformMatrix.from({
239          dsdx: 1,
240          dtdx: 0,
241          tx: x,
242          dtdy: 0,
243          dsdy: 1,
244          ty: y,
245        }),
246      );
247    }
248
249    throw TransformType.makeUnknownTransformTypeError(type);
250  }
251
252  static makeUnknownTransformTypeError(type: TransformTypeFlags): Error {
253    return new Error(`Unknown transform type ${type} found in SF trace entry`);
254  }
255
256  static isSimpleTransform(type: TransformTypeFlags): boolean {
257    return TransformType.isFlagClear(
258      type,
259      TransformTypeFlags.ROT_INVALID_VAL | TransformTypeFlags.SCALE_VAL,
260    );
261  }
262
263  private static isFlagSet(type: TransformTypeFlags, bits: number): boolean {
264    type = type || 0;
265    return (type & bits) === bits;
266  }
267
268  private static isFlagClear(type: TransformTypeFlags, bits: number): boolean {
269    return (type & bits) === 0;
270  }
271}
272