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 { BaseStruct, isFrameContainPoint, ns2x, Rect, Render } from './ProcedureWorkerCommon';
17import { TraceRow } from '../../component/trace/base/TraceRow';
18
19export class SdkCounterRender extends Render {
20  renderMainThread(
21    req: {
22      context: CanvasRenderingContext2D;
23      useCache: boolean;
24      type: string;
25      maxName: string;
26      maxValue: number;
27    },
28    row: TraceRow<CounterStruct>
29  ): void {
30    let counterList = row.dataList;
31    let counterFilter = row.dataListCache;
32    let maxCounter = req.maxValue;
33    let maxCounterName = req.maxName;
34    this.counter(
35      counterList,
36      counterFilter,
37      TraceRow.range?.startNS ?? 0,
38      TraceRow.range?.endNS ?? 0,
39      TraceRow.range?.totalNS ?? 0,
40      row.frame,
41      req.useCache || (TraceRow.range?.refresh ?? false)
42    );
43    req.context.beginPath();
44    let sdkCounterFind = false;
45    for (let re of counterFilter) {
46      if (row.isHover && re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) {
47        CounterStruct.hoverCounterStruct = re;
48        sdkCounterFind = true;
49      }
50      CounterStruct.draw(req.context, re, maxCounter);
51    }
52    if (!sdkCounterFind && row.isHover) {
53      CounterStruct.hoverCounterStruct = undefined;
54    }
55    req.context.closePath();
56    let textMetrics = req.context.measureText(maxCounterName);
57    req.context.globalAlpha = 0.8;
58    req.context.fillStyle = '#f0f0f0';
59    req.context.fillRect(0, 5, textMetrics.width + 8, 18);
60    req.context.globalAlpha = 1;
61    req.context.fillStyle = '#333';
62    req.context.textBaseline = 'middle';
63    req.context.fillText(maxCounterName, 4, 5 + 9);
64  }
65
66  counter(
67    sdkCounterList: Array<unknown>,
68    sdkCounterFilters: Array<unknown>,
69    startNS: number,
70    endNS: number,
71    totalNS: number,
72    frame: Rect,
73    use: boolean
74  ): void {
75    if (use && sdkCounterFilters.length > 0) {
76      for (let index = 0; index < sdkCounterFilters.length; index++) {
77        let item = sdkCounterFilters[index];
78        //@ts-ignore
79        if ((item.ts || 0) + (item.dur || 0) > startNS && (item.ts || 0) < endNS) {
80          CounterStruct.setCounterFrame(sdkCounterFilters[index], 5, startNS, endNS, totalNS, frame);
81        } else {
82          //@ts-ignore
83          sdkCounterFilters[index].frame = null;
84        }
85      }
86      return;
87    }
88    sdkCounterFilters.length = 0;
89    setSdkCounterFilter(sdkCounterList, sdkCounterFilters, startNS, endNS, totalNS, frame);
90  }
91}
92function setSdkCounterFilter(
93  list: Array<unknown>,
94  sdkCounterFilters: Array<unknown>,
95  startNS: number,
96  endNS: number,
97  totalNS: number,
98  frame: Rect
99): void {
100  if (list) {
101    for (let index = 0; index < list.length; index++) {
102      let item = list[index];
103      //@ts-ignore
104      item.dur = index === list.length - 1 ? endNS - (item.ts || 0) : (list[index + 1].ts || 0) - (item.ts || 0);
105      //@ts-ignore
106      if ((item.ts || 0) + (item.dur || 0) > startNS && (item.ts || 0) < endNS) {
107        CounterStruct.setCounterFrame(list[index], 5, startNS, endNS, totalNS, frame);
108        if (
109          !(
110            index > 0 &&
111            //@ts-ignore
112            (list[index - 1].frame?.x || 0) === (list[index].frame?.x || 0) &&
113            //@ts-ignore
114            (list[index - 1].frame?.width || 0) === (list[index].frame?.width || 0)
115          )
116        ) {
117          sdkCounterFilters.push(item);
118        }
119      }
120    }
121  }
122}
123
124export class CounterStruct extends BaseStruct {
125  static maxCounter: number = 0;
126  static maxCounterName: string = '';
127  static hoverCounterStruct: CounterStruct | undefined;
128  static selectCounterStruct: CounterStruct | undefined;
129
130  value: number | undefined;
131  ts: number | undefined;
132  counter_id: number | undefined;
133
134  static draw(sdkCounterContext: CanvasRenderingContext2D, data: CounterStruct, maxCounter: number): void {
135    if (data.frame) {
136      let width = data.frame.width || 0;
137      sdkCounterContext.fillStyle = '#67B0FC';
138      sdkCounterContext.strokeStyle = '#67B0FC';
139      if (data.ts === CounterStruct.hoverCounterStruct?.ts) {
140        sdkCounterContext.lineWidth = 1;
141        let drawHeight: number = Math.floor(((data.value || 0) * (data.frame.height || 0) * 1.0) / maxCounter);
142        sdkCounterContext.fillRect(data.frame.x, data.frame.y + data.frame.height - drawHeight + 4, width, drawHeight);
143        sdkCounterContext.beginPath();
144        sdkCounterContext.arc(data.frame.x, data.frame.y + data.frame.height - drawHeight + 4, 3, 0, 2 * Math.PI, true);
145        sdkCounterContext.fill();
146        sdkCounterContext.globalAlpha = 1.0;
147        sdkCounterContext.stroke();
148        sdkCounterContext.beginPath();
149        sdkCounterContext.moveTo(data.frame.x + 3, data.frame.y + data.frame.height - drawHeight + 4);
150        sdkCounterContext.lineWidth = 3;
151        sdkCounterContext.lineTo(data.frame.x + width, data.frame.y + data.frame.height - drawHeight + 4);
152        sdkCounterContext.stroke();
153      } else {
154        sdkCounterContext.lineWidth = 1;
155        let drawHeight: number = Math.floor(((data.value || 0) * (data.frame.height || 0)) / maxCounter);
156        sdkCounterContext.fillRect(data.frame.x, data.frame.y + data.frame.height - drawHeight + 4, width, drawHeight);
157      }
158    }
159    sdkCounterContext.globalAlpha = 1.0;
160    sdkCounterContext.lineWidth = 1;
161  }
162
163  static setCounterFrame(
164    counterNode: unknown,
165    padding: number,
166    startNS: number,
167    endNS: number,
168    totalNS: number,
169    frame: Rect
170  ): void {
171    let sdkCounterStartPointX: number;
172    let sdkCountEndPointX: number;
173    //@ts-ignore
174    if ((counterNode.ts || 0) < startNS) {
175      sdkCounterStartPointX = 0;
176    } else {
177      //@ts-ignore
178      sdkCounterStartPointX = ns2x(counterNode.ts || 0, startNS, endNS, totalNS, frame);
179    }
180    //@ts-ignore
181    if ((counterNode.ts || 0) + (counterNode.dur || 0) > endNS) {
182      sdkCountEndPointX = frame.width;
183    } else {
184      //@ts-ignore
185      sdkCountEndPointX = ns2x((counterNode.ts || 0) + (counterNode.dur || 0), startNS, endNS, totalNS, frame);
186    }
187    let frameWidth: number =
188      sdkCountEndPointX - sdkCounterStartPointX <= 1 ? 1 : sdkCountEndPointX - sdkCounterStartPointX;
189    //@ts-ignore
190    if (!counterNode.frame) {
191      //@ts-ignore
192      counterNode.frame = {};
193    }
194    //@ts-ignore
195    counterNode.frame.x = Math.floor(sdkCounterStartPointX);
196    //@ts-ignore
197    counterNode.frame.y = frame.y + padding;
198    //@ts-ignore
199    counterNode.frame.width = Math.ceil(frameWidth);
200    //@ts-ignore
201    counterNode.frame.height = Math.floor(frame.height - padding * 2);
202  }
203}
204