1// Copyright (C) 2019 The Android Open Source Project 2// 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 15import {hsl} from 'color-convert'; 16 17export interface Color { 18 c: string; 19 h: number; 20 s: number; 21 l: number; 22 a?: number; 23} 24 25const MD_PALETTE: Color[] = [ 26 {c: 'red', h: 4, s: 90, l: 58}, 27 {c: 'pink', h: 340, s: 82, l: 52}, 28 {c: 'purple', h: 291, s: 64, l: 42}, 29 {c: 'deep purple', h: 262, s: 52, l: 47}, 30 {c: 'indigo', h: 231, s: 48, l: 48}, 31 {c: 'blue', h: 207, s: 90, l: 54}, 32 {c: 'light blue', h: 199, s: 98, l: 48}, 33 {c: 'cyan', h: 187, s: 100, l: 42}, 34 {c: 'teal', h: 174, s: 100, l: 29}, 35 {c: 'green', h: 122, s: 39, l: 49}, 36 {c: 'light green', h: 88, s: 50, l: 53}, 37 {c: 'lime', h: 66, s: 70, l: 54}, 38 {c: 'amber', h: 45, s: 100, l: 51}, 39 {c: 'orange', h: 36, s: 100, l: 50}, 40 {c: 'deep orange', h: 14, s: 100, l: 57}, 41 {c: 'brown', h: 16, s: 25, l: 38}, 42 {c: 'blue gray', h: 200, s: 18, l: 46}, 43 {c: 'yellow', h: 54, s: 100, l: 62}, 44]; 45 46const GREY_COLOR: Color = { 47 c: 'grey', 48 h: 0, 49 s: 0, 50 l: 62 51}; 52 53function hash(s: string, max: number): number { 54 let hash = 0x811c9dc5 & 0xfffffff; 55 for (let i = 0; i < s.length; i++) { 56 hash ^= s.charCodeAt(i); 57 hash = (hash * 16777619) & 0xffffffff; 58 } 59 return Math.abs(hash) % max; 60} 61 62export function hueForCpu(cpu: number): number { 63 return (128 + (32 * cpu)) % 256; 64} 65 66const DESAT_RED: Color = { 67 c: 'desat red', 68 h: 3, 69 s: 30, 70 l: 49 71}; 72const DARK_GREEN: Color = { 73 c: 'dark green', 74 h: 120, 75 s: 44, 76 l: 34 77}; 78const LIME_GREEN: Color = { 79 c: 'lime green', 80 h: 75, 81 s: 55, 82 l: 47 83}; 84const TRANSPARENT_WHITE: Color = { 85 c: 'white', 86 h: 0, 87 s: 1, 88 l: 97, 89 a: 0.55, 90}; 91const ORANGE: Color = { 92 c: 'orange', 93 h: 36, 94 s: 100, 95 l: 50 96}; 97const INDIGO: Color = { 98 c: 'indigo', 99 h: 231, 100 s: 48, 101 l: 48 102}; 103 104export function colorForState(state: string): Readonly<Color> { 105 if (state === 'Running') { 106 return DARK_GREEN; 107 } else if (state.startsWith('Runnable')) { 108 return LIME_GREEN; 109 } else if (state.includes('Uninterruptible Sleep')) { 110 if (state.includes('non-IO')) { 111 return DESAT_RED; 112 } 113 return ORANGE; 114 } else if (state.includes('Sleeping')) { 115 return TRANSPARENT_WHITE; 116 } 117 return INDIGO; 118} 119 120export function textColorForState(stateCode: string): string { 121 const background = colorForState(stateCode); 122 return background.l > 80 ? '#404040' : '#fff'; 123} 124 125export function colorForTid(tid: number): Color { 126 const colorIdx = hash(tid.toString(), MD_PALETTE.length); 127 return Object.assign({}, MD_PALETTE[colorIdx]); 128} 129 130export function colorForThread(thread?: {pid?: number, tid: number}): Color { 131 if (thread === undefined) { 132 return Object.assign({}, GREY_COLOR); 133 } 134 const tid = thread.pid ? thread.pid : thread.tid; 135 return colorForTid(tid); 136} 137 138// 40 different random hues 9 degrees apart. 139export function randomColor(): string { 140 const hue = Math.floor(Math.random() * 40) * 9; 141 return '#' + hsl.hex([hue, 90, 30]); 142} 143 144// Chooses a color uniform at random based on hash(sliceName). Returns [hue, 145// saturation, lightness]. 146// 147// Prefer converting this to an RGB color using hsluv, not the browser's 148// built-in vanilla HSL handling. This is because this function chooses 149// hue/lightness uniform at random, but HSL is not perceptually uniform. See 150// https://www.boronine.com/2012/03/26/Color-Spaces-for-Human-Beings/. 151// 152// If isSelected, the color will be particularly dark, making it stand out. 153export function hslForSlice( 154 sliceName: string, isSelected: boolean|null): [number, number, number] { 155 const hue = hash(sliceName, 360); 156 // Saturation 100 would give the most differentiation between colors, but it's 157 // garish. 158 const saturation = 80; 159 const lightness = isSelected ? 30 : hash(sliceName + 'x', 40) + 40; 160 return [hue, saturation, lightness]; 161} 162