• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
17import {hash} from '../common/hash';
18import {cachedHsluvToHex} from '../frontend/hsluv_cache';
19
20export interface Color {
21  c: string;
22  h: number;
23  s: number;
24  l: number;
25  a?: number;
26}
27
28const MD_PALETTE: Color[] = [
29  {c: 'red', h: 4, s: 90, l: 58},
30  {c: 'pink', h: 340, s: 82, l: 52},
31  {c: 'purple', h: 291, s: 64, l: 42},
32  {c: 'deep purple', h: 262, s: 52, l: 47},
33  {c: 'indigo', h: 231, s: 48, l: 48},
34  {c: 'blue', h: 207, s: 90, l: 54},
35  {c: 'light blue', h: 199, s: 98, l: 48},
36  {c: 'cyan', h: 187, s: 100, l: 42},
37  {c: 'teal', h: 174, s: 100, l: 29},
38  {c: 'green', h: 122, s: 39, l: 49},
39  {c: 'light green', h: 88, s: 50, l: 53},
40  {c: 'lime', h: 66, s: 70, l: 54},
41  {c: 'amber', h: 45, s: 100, l: 51},
42  {c: 'orange', h: 36, s: 100, l: 50},
43  {c: 'deep orange', h: 14, s: 100, l: 57},
44  {c: 'brown', h: 16, s: 25, l: 38},
45  {c: 'blue gray', h: 200, s: 18, l: 46},
46  {c: 'yellow', h: 54, s: 100, l: 62},
47];
48
49export const GRAY_COLOR: Color = {
50  c: 'grey',
51  h: 0,
52  s: 0,
53  l: 62,
54};
55
56// A piece of wisdom from a long forgotten blog post: "Don't make
57// colors you want to change something normal like grey."
58export const UNEXPECTED_PINK_COLOR: Color = {
59  c: '#ff69b4',
60  h: 330,
61  s: 1.0,
62  l: 0.706,
63};
64
65export function hueForCpu(cpu: number): number {
66  return (128 + (32 * cpu)) % 256;
67}
68
69const DESAT_RED: Color = {
70  c: 'desat red',
71  h: 3,
72  s: 30,
73  l: 49,
74};
75const DARK_GREEN: Color = {
76  c: 'dark green',
77  h: 120,
78  s: 44,
79  l: 34,
80};
81const LIME_GREEN: Color = {
82  c: 'lime green',
83  h: 75,
84  s: 55,
85  l: 47,
86};
87const TRANSPARENT_WHITE: Color = {
88  c: 'white',
89  h: 0,
90  s: 1,
91  l: 97,
92  a: 0.55,
93};
94const ORANGE: Color = {
95  c: 'orange',
96  h: 36,
97  s: 100,
98  l: 50,
99};
100const INDIGO: Color = {
101  c: 'indigo',
102  h: 231,
103  s: 48,
104  l: 48,
105};
106
107export function colorForState(state: string): Readonly<Color> {
108  if (state === 'Running') {
109    return DARK_GREEN;
110  } else if (state.startsWith('Runnable')) {
111    return LIME_GREEN;
112  } else if (state.includes('Uninterruptible Sleep')) {
113    if (state.includes('non-IO')) {
114      return DESAT_RED;
115    }
116    return ORANGE;
117  } else if (state.includes('Sleeping') || state.includes('Idle')) {
118    return TRANSPARENT_WHITE;
119  }
120  return INDIGO;
121}
122
123export function textColorForState(stateCode: string): string {
124  const background = colorForState(stateCode);
125  return background.l > 80 ? '#404040' : '#fff';
126}
127
128export function colorForString(identifier: string): Color {
129  const colorIdx = hash(identifier, MD_PALETTE.length);
130  return Object.assign({}, MD_PALETTE[colorIdx]);
131}
132
133export function colorForTid(tid: number): Color {
134  return colorForString(tid.toString());
135}
136
137export function colorForThread(thread?: {pid?: number, tid: number}): Color {
138  if (thread === undefined) {
139    return Object.assign({}, GRAY_COLOR);
140  }
141  const tid = thread.pid ? thread.pid : thread.tid;
142  return colorForTid(tid);
143}
144
145// 40 different random hues 9 degrees apart.
146export function randomColor(): string {
147  const hue = Math.floor(Math.random() * 40) * 9;
148  return '#' + hsl.hex([hue, 90, 30]);
149}
150
151// Chooses a color uniform at random based on hash(sliceName).  Returns [hue,
152// saturation, lightness].
153//
154// Prefer converting this to an RGB color using hsluv, not the browser's
155// built-in vanilla HSL handling.  This is because this function chooses
156// hue/lightness uniform at random, but HSL is not perceptually uniform.  See
157// https://www.boronine.com/2012/03/26/Color-Spaces-for-Human-Beings/.
158//
159// If isSelected, the color will be particularly dark, making it stand out.
160export function hslForSlice(
161    sliceName: string, isSelected: boolean|null): [number, number, number] {
162  const hue = hash(sliceName, 360);
163  // Saturation 100 would give the most differentiation between colors, but it's
164  // garish.
165  const saturation = 80;
166  const lightness = isSelected ? 30 : hash(sliceName + 'x', 40) + 40;
167  return [hue, saturation, lightness];
168}
169
170// Lightens the color for thread slices to represent wall time.
171export function colorForThreadIdleSlice(
172    hue: number,
173    saturation: number,
174    lightness: number,
175    isSelected: boolean|null): string {
176  // Increase lightness by 80% when selected and 40% otherwise,
177  // without exceeding 88.
178  let newLightness = isSelected ? lightness * 1.8 : lightness * 1.4;
179  newLightness = Math.min(newLightness, 88);
180  return cachedHsluvToHex(hue, saturation, newLightness);
181}
182
183export function colorToStr(color: Color) {
184  if (color.a !== undefined) {
185    return `hsla(${color.h}, ${color.s}%, ${color.l}%, ${color.a})`;
186  }
187  return `hsl(${color.h}, ${color.s}%, ${color.l}%)`;
188}
189
190export function colorCompare(x: Color, y: Color) {
191  return (x.h - y.h) || (x.s - y.s) || (x.l - y.l);
192}
193