1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { ColorUtils } from '../../component/trace/base/ColorUtils';
17import { TraceRow } from '../../component/trace/base/TraceRow';
18import {
19  BaseStruct,
20  drawLoadingFrame,
21  drawString,
22  isFrameContainPoint,
23  ns2x,
24  Rect,
25  Render,
26} from './ProcedureWorkerCommon';
27import { SpSystemTrace } from '../../component/SpSystemTrace';
28
29export class FrameAnimationRender extends Render {
30  renderMainThread(
31    req: {
32      useCache: boolean;
33      context: CanvasRenderingContext2D;
34      type: string;
35    },
36    row: TraceRow<FrameAnimationStruct>
37  ): void {
38    let frameAnimationList: FrameAnimationStruct[] = row.dataList;
39    let frameAnimationFilter: FrameAnimationStruct[] = row.dataListCache;
40    this.frameAnimation(
41      frameAnimationList,
42      frameAnimationFilter,
43      TraceRow.range!.startNS,
44      TraceRow.range!.endNS,
45      TraceRow.range!.totalNS, // @ts-ignore
46      row.frame,
47      req.useCache || !TraceRow.range!.refresh
48    );
49    drawLoadingFrame(req.context, row.dataListCache, row);
50    req.context.beginPath();
51    let find: boolean = false;
52    for (let index: number = 0; index < frameAnimationFilter.length; index++) {
53      let currentAnimationStruct: FrameAnimationStruct = frameAnimationFilter[index];
54      FrameAnimationStruct.draw(req.context, currentAnimationStruct, row);
55      if (
56        row.isHover &&
57        currentAnimationStruct.frame &&
58        isFrameContainPoint(currentAnimationStruct.frame, row.hoverX, row.hoverY)
59      ) {
60        FrameAnimationStruct.hoverFrameAnimationStruct = currentAnimationStruct;
61        find = true;
62      }
63    }
64    if (!find && row.isHover) {
65      FrameAnimationStruct.hoverFrameAnimationStruct = undefined;
66    }
67    req.context.closePath();
68  }
69
70  private frameAnimation(
71    frameAnimationList: FrameAnimationStruct[],
72    frameAnimationFilter: FrameAnimationStruct[],
73    startNS: number = 0,
74    endNS: number = 0,
75    totalNS: number,
76    frame: Rect,
77    use: boolean
78  ): void {
79    if (use && frameAnimationFilter.length > 0) {
80      for (let index: number = 0; index < frameAnimationFilter.length; index++) {
81        let frameAnimationNode: FrameAnimationStruct = frameAnimationFilter[index];
82        frameAnimationNode.frame = undefined;
83        FrameAnimationStruct.setFrameAnimation(frameAnimationNode, padding, startNS, endNS, totalNS, frame);
84      }
85      return;
86    }
87    frameAnimationFilter.length = 0;
88    if (frameAnimationList) {
89      for (let index: number = 0; index < frameAnimationList.length; index++) {
90        let currentFrameAnimation: FrameAnimationStruct = frameAnimationList[index];
91        if (
92          (currentFrameAnimation.startTs || 0) + (currentFrameAnimation.dur || 0) > startNS &&
93          (currentFrameAnimation.startTs || 0) < endNS
94        ) {
95          FrameAnimationStruct.setFrameAnimation(
96            currentFrameAnimation,
97            padding,
98            startNS,
99            endNS || 0,
100            totalNS || 0,
101            frame
102          );
103          frameAnimationFilter.push(currentFrameAnimation);
104        }
105      }
106    }
107  }
108}
109export function FrameAnimationStructOnClick(
110  clickRowType: string,
111  sp: SpSystemTrace,
112  scrollToFuncHandler: Function,
113  row: TraceRow<FrameAnimationStruct>,
114  entry?: FrameAnimationStruct,
115): Promise<unknown> {
116  return new Promise((resolve, reject) => {
117    if (clickRowType === TraceRow.ROW_TYPE_FRAME_ANIMATION) {
118      FrameAnimationStruct.selectFrameAnimationStruct =
119        FrameAnimationStruct.hoverFrameAnimationStruct || row.getHoverStruct();
120      if (FrameAnimationStruct.selectFrameAnimationStruct || entry) {
121        let data = entry || FrameAnimationStruct.selectFrameAnimationStruct;
122        sp.traceSheetEL?.displayFrameAnimationData(
123          data!,
124          scrollToFuncHandler
125        );
126        sp.timerShaftEL?.modifyFlagList(undefined);
127      }
128      reject(new Error());
129    } else {
130      resolve(null);
131    }
132  });
133}
134export class FrameAnimationStruct extends BaseStruct {
135  static hoverFrameAnimationStruct: FrameAnimationStruct | undefined;
136  static selectFrameAnimationStruct: FrameAnimationStruct | undefined;
137  dur: number = 0;
138  status: string = '';
139  animationId: number | undefined;
140  fps: number | undefined;
141  depth: number = 0;
142  startTs: number = 0;
143  endTs: number = 0;
144  frameInfo: string | undefined;
145  name: string | undefined;
146  inputTime: number = 0;
147  endTime: number = 0;
148
149  static setFrameAnimation(
150    animationNode: FrameAnimationStruct,
151    padding: number,
152    startNS: number,
153    endNS: number,
154    totalNS: number,
155    frame: Rect
156  ): void {
157    let stateStartPointX: number;
158    let stateEndPointX: number;
159    if ((animationNode.startTs || 0) < startNS) {
160      stateStartPointX = 0;
161    } else {
162      stateStartPointX = ns2x(animationNode.startTs || 0, startNS, endNS, totalNS, frame);
163    }
164    if ((animationNode.startTs || 0) + (animationNode.dur || 0) > endNS) {
165      stateEndPointX = frame.width;
166    } else {
167      stateEndPointX = ns2x((animationNode.startTs || 0) + (animationNode.dur || 0), startNS, endNS, totalNS, frame);
168    }
169    let frameWidth: number =
170      stateEndPointX - stateStartPointX <= unitIndex ? unitIndex : stateEndPointX - stateStartPointX;
171    if (!animationNode.frame) {
172      animationNode.frame = new Rect(0, 0, 0, 0);
173    }
174    animationNode.frame.x = Math.floor(stateStartPointX);
175    animationNode.frame.y = frame.y + animationNode.depth * 20 + padding;
176    animationNode.frame.width = Math.ceil(frameWidth);
177    animationNode.frame.height = 20 - multiple * padding;
178  }
179
180  static draw(
181    ctx: CanvasRenderingContext2D,
182    frameAnimationNode: FrameAnimationStruct,
183    row: TraceRow<FrameAnimationStruct>
184  ): void {
185    let tsFixed: number = 6;
186    let isHover: boolean = row.isHover;
187    let frame = frameAnimationNode.frame;
188    if (frame) {
189      let nsToMillisecond = 1000_000;
190      ctx.globalAlpha = 1.0;
191      ctx.lineWidth = 1;
192      ctx.lineJoin = 'round';
193      ctx.fillStyle = ColorUtils.ANIMATION_COLOR[6];
194      ctx.fillRect(frame.x, frame.y, frame.width, frame.height);
195      ctx.fillStyle = ColorUtils.ANIMATION_COLOR[3];
196      ctx.textBaseline = 'middle';
197      ctx.font = '8px sans-serif';
198      drawString(
199        ctx,
200        `${frameAnimationNode.status} (${(frameAnimationNode.dur / nsToMillisecond).toFixed(tsFixed)} ms)`,
201        textPadding,
202        frame,
203        frameAnimationNode
204      );
205      ctx.lineWidth = 2;
206      if (
207        (frameAnimationNode === FrameAnimationStruct.hoverFrameAnimationStruct && isHover) ||
208        frameAnimationNode === FrameAnimationStruct.selectFrameAnimationStruct
209      ) {
210        ctx.globalAlpha = 0.8;
211        ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[3];
212
213        ctx.strokeRect(frame.x + padding, frame.y, frame.width - padding, frame.height);
214      } else {
215        ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[2];
216        ctx.strokeRect(frame.x + padding, frame.y, frame.width - padding, frame.height);
217      }
218    }
219  }
220}
221
222const padding: number = 2;
223const multiple: number = 2;
224const unitIndex: number = 1;
225const textPadding: number = 5;
226