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 { SpApplication } from "../SpApplication.js"; 17import { BaseStruct } from "./BaseStruct.js"; 18import { Rect } from "../component/trace/timer-shaft/Rect.js"; 19import { info } from "../../log/Log.js"; 20 21const padding: number = 1; 22 23export class ChartStruct extends BaseStruct { 24 static hoverFuncStruct: ChartStruct | undefined; 25 static selectFuncStruct: ChartStruct | undefined; 26 static lastSelectFuncStruct: ChartStruct | undefined; 27 needShow = false; 28 depth: number = 0; 29 symbol: string = ''; 30 size: number = 0; 31 count: number = 0; 32 dur: number = 0; 33 type: ChartMode = ChartMode.Call; 34 parent: ChartStruct | undefined; 35 children: Array<ChartStruct> = []; 36} 37 38export enum ChartMode { 39 Call, 40 Byte, 41 Count, 42 Duration, 43} 44 45export function setFuncFrame(node: ChartStruct, canvas_frame: Rect, total: number, mode: ChartMode) { 46 if (!node.frame) { 47 node.frame = new Rect(0, 0, 0, 0); 48 } 49 // filter depth is 0 50 if (node.parent) { 51 let idx = node.parent.children.indexOf(node); 52 if (idx == 0) { 53 node.frame!.x = node.parent.frame!.x; 54 } else { 55 // set x by left frame. left frame is parent.children[idx - 1] 56 node.frame.x = node.parent.children[idx - 1].frame!.x + node.parent.children[idx - 1].frame!.width 57 } 58 switch (mode) { 59 case ChartMode.Byte: 60 node.frame!.width = Math.floor(node.size / total * canvas_frame.width); 61 break; 62 case ChartMode.Count: 63 node.frame!.width = Math.floor(node.count / total * canvas_frame.width); 64 break; 65 case ChartMode.Duration: 66 node.frame!.width = Math.floor(node.dur / total * canvas_frame.width); 67 break; 68 default: 69 info('not match ChartMode'); 70 } 71 node.frame!.y = node.parent.frame!.y + 20; 72 node.frame!.height = 20; 73 } 74} 75 76 77/** 78 * draw rect 79 * @param ctx CanvasRenderingContext2D 80 * @param data rect which is need draw 81 * @param percent function size or count / total size or count 82 */ 83export function draw(ctx: CanvasRenderingContext2D, data: ChartStruct, percent: number) { 84 let spApplication = <SpApplication>document.getElementsByTagName("sp-application")[0] 85 if (data.frame) { 86 // draw rect 87 let miniHeight = 20; 88 if (isSelected(data)) { 89 ctx.fillStyle = `rgba(${82}, ${145}, ${255}, 0.9)`; 90 } else { 91 let color = getHeatColor(percent); 92 ctx.fillStyle = `rgba(${color.r}, ${color.g}, ${color.b}, 0.9)`; 93 } 94 ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, miniHeight - padding * 2); 95 //draw border 96 if (isHover(data)) { 97 if (spApplication.dark) { 98 ctx.strokeStyle = "#fff"; 99 } else { 100 ctx.strokeStyle = "#000"; 101 } 102 } else { 103 if (spApplication.dark) { 104 ctx.strokeStyle = "#000"; 105 } else { 106 ctx.strokeStyle = "#fff"; 107 } 108 } 109 ctx.lineWidth = 0.4; 110 ctx.strokeRect(data.frame.x, data.frame.y, data.frame.width, miniHeight - padding * 2); 111 112 //draw symbol name 113 if (data.frame.width > 10) { 114 if (percent > 0.6 || isSelected(data)) { 115 ctx.fillStyle = "#fff"; 116 } else { 117 ctx.fillStyle = "#000"; 118 } 119 drawString(ctx, data.symbol || '', 5, data.frame); 120 } 121 } 122} 123 124/** 125 * get framechart color by percent 126 * @param widthPercentage proportion of function 127 * @returns rbg 128 */ 129function getHeatColor(widthPercentage: number) { 130 return { 131 r: Math.floor(245 + 10 * (1 - widthPercentage)), 132 g: Math.floor(110 + 105 * (1 - widthPercentage)), 133 b: 100, 134 }; 135} 136 137/** 138 * draw function string in rect 139 * @param ctx CanvasRenderingContext2D 140 * @param str function Name 141 * @param textPadding textPadding 142 * @param frame canvas area 143 * @returns is draw 144 */ 145function drawString(ctx: CanvasRenderingContext2D, str: string, textPadding: number, frame: Rect): boolean { 146 let textMetrics = ctx.measureText(str); 147 let charWidth = Math.round(textMetrics.width / str.length) 148 if (textMetrics.width < frame.width - textPadding * 2) { 149 let x2 = Math.floor(frame.width / 2 - textMetrics.width / 2 + frame.x + textPadding) 150 ctx.fillText(str, x2, Math.floor(frame.y + frame.height / 2 + 2), frame.width - textPadding * 2) 151 return true; 152 } 153 if (frame.width - textPadding * 2 > charWidth * 4) { 154 let chatNum = (frame.width - textPadding * 2) / charWidth; 155 let x1 = frame.x + textPadding 156 ctx.fillText(str.substring(0, chatNum - 4) + '...', x1, Math.floor(frame.y + frame.height / 2 + 2), frame.width - textPadding * 2) 157 return true; 158 } 159 return false; 160} 161 162function isHover(data: ChartStruct): boolean { 163 return ChartStruct.hoverFuncStruct == data; 164} 165 166function isSelected(data: ChartStruct): boolean { 167 return ChartStruct.lastSelectFuncStruct == data; 168} 169