1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
| //===-- xray-graph.cpp: XRay Function Call Graph Renderer -----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// A class to get a color from a specified gradient.
//
//===----------------------------------------------------------------------===//
#include "xray-color-helper.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace xray;
// Sequential ColorMaps, which are used to represent information
// from some minimum to some maximum.
static const std::tuple<uint8_t, uint8_t, uint8_t> SequentialMaps[][9] = {
{// The greys color scheme from http://colorbrewer2.org/
std::make_tuple(255, 255, 255), std::make_tuple(240, 240, 240),
std::make_tuple(217, 217, 217), std::make_tuple(189, 189, 189),
std::make_tuple(150, 150, 150), std::make_tuple(115, 115, 115),
std::make_tuple(82, 82, 82), std::make_tuple(37, 37, 37),
std::make_tuple(0, 0, 0)},
{// The OrRd color scheme from http://colorbrewer2.org/
std::make_tuple(255, 247, 236), std::make_tuple(254, 232, 200),
std::make_tuple(253, 212, 158), std::make_tuple(253, 187, 132),
std::make_tuple(252, 141, 89), std::make_tuple(239, 101, 72),
std::make_tuple(215, 48, 31), std::make_tuple(179, 0, 0),
std::make_tuple(127, 0, 0)},
{// The PuBu color scheme from http://colorbrewer2.org/
std::make_tuple(255, 247, 251), std::make_tuple(236, 231, 242),
std::make_tuple(208, 209, 230), std::make_tuple(166, 189, 219),
std::make_tuple(116, 169, 207), std::make_tuple(54, 144, 192),
std::make_tuple(5, 112, 176), std::make_tuple(4, 90, 141),
std::make_tuple(2, 56, 88)}};
// Sequential Maps extend the last colors given out of range inputs.
static const std::tuple<uint8_t, uint8_t, uint8_t> SequentialBounds[][2] = {
{// The Bounds for the greys color scheme
std::make_tuple(255, 255, 255), std::make_tuple(0, 0, 0)},
{// The Bounds for the OrRd color Scheme
std::make_tuple(255, 247, 236), std::make_tuple(127, 0, 0)},
{// The Bounds for the PuBu color Scheme
std::make_tuple(255, 247, 251), std::make_tuple(2, 56, 88)}};
ColorHelper::ColorHelper(ColorHelper::SequentialScheme S)
: MinIn(0.0), MaxIn(1.0), ColorMap(SequentialMaps[static_cast<int>(S)]),
BoundMap(SequentialBounds[static_cast<int>(S)]) {}
// Diverging ColorMaps, which are used to represent information
// representing differenes, or a range that goes from negative to positive.
// These take an input in the range [-1,1].
static const std::tuple<uint8_t, uint8_t, uint8_t> DivergingCoeffs[][11] = {
{// The PiYG color scheme from http://colorbrewer2.org/
std::make_tuple(142, 1, 82), std::make_tuple(197, 27, 125),
std::make_tuple(222, 119, 174), std::make_tuple(241, 182, 218),
std::make_tuple(253, 224, 239), std::make_tuple(247, 247, 247),
std::make_tuple(230, 245, 208), std::make_tuple(184, 225, 134),
std::make_tuple(127, 188, 65), std::make_tuple(77, 146, 33),
std::make_tuple(39, 100, 25)}};
// Diverging maps use out of bounds ranges to show missing data. Missing Right
// Being below min, and missing left being above max.
static const std::tuple<uint8_t, uint8_t, uint8_t> DivergingBounds[][2] = {
{// The PiYG color scheme has green and red for missing right and left
// respectively.
std::make_tuple(255, 0, 0), std::make_tuple(0, 255, 0)}};
ColorHelper::ColorHelper(ColorHelper::DivergingScheme S)
: MinIn(-1.0), MaxIn(1.0), ColorMap(DivergingCoeffs[static_cast<int>(S)]),
BoundMap(DivergingBounds[static_cast<int>(S)]) {}
// Takes a tuple of uint8_ts representing a color in RGB and converts them to
// HSV represented by a tuple of doubles
static std::tuple<double, double, double>
convertToHSV(const std::tuple<uint8_t, uint8_t, uint8_t> &Color) {
double Scaled[3] = {std::get<0>(Color) / 255.0, std::get<1>(Color) / 255.0,
std::get<2>(Color) / 255.0};
int Min = 0;
int Max = 0;
for (int i = 1; i < 3; ++i) {
if (Scaled[i] < Scaled[Min])
Min = i;
if (Scaled[i] > Scaled[Max])
Max = i;
}
double C = Scaled[Max] - Scaled[Min];
double HPrime =
(C == 0) ? 0 : (Scaled[(Max + 1) % 3] - Scaled[(Max + 2) % 3]) / C;
HPrime = HPrime + 2.0 * Max;
double H = (HPrime < 0) ? (HPrime + 6.0) * 60
: HPrime * 60; // Scale to between 0 and 360
double V = Scaled[Max];
double S = (V == 0.0) ? 0.0 : C / V;
return std::make_tuple(H, S, V);
}
// Takes a double precision number, clips it between 0 and 1 and then converts
// that to an integer between 0x00 and 0xFF with proxpper rounding.
static uint8_t unitIntervalTo8BitChar(double B) {
double n = std::max(std::min(B, 1.0), 0.0);
return static_cast<uint8_t>(255 * n + 0.5);
}
// Takes a typle of doubles representing a color in HSV and converts them to
// RGB represented as a tuple of uint8_ts
static std::tuple<uint8_t, uint8_t, uint8_t>
convertToRGB(const std::tuple<double, double, double> &Color) {
const double &H = std::get<0>(Color);
const double &S = std::get<1>(Color);
const double &V = std::get<2>(Color);
double C = V * S;
double HPrime = H / 60;
double X = C * (1 - std::abs(std::fmod(HPrime, 2.0) - 1));
double RGB1[3];
int HPrimeInt = static_cast<int>(HPrime);
if (HPrimeInt % 2 == 0) {
RGB1[(HPrimeInt / 2) % 3] = C;
RGB1[(HPrimeInt / 2 + 1) % 3] = X;
RGB1[(HPrimeInt / 2 + 2) % 3] = 0.0;
} else {
RGB1[(HPrimeInt / 2) % 3] = X;
RGB1[(HPrimeInt / 2 + 1) % 3] = C;
RGB1[(HPrimeInt / 2 + 2) % 3] = 0.0;
}
double Min = V - C;
double RGB2[3] = {RGB1[0] + Min, RGB1[1] + Min, RGB1[2] + Min};
return std::make_tuple(unitIntervalTo8BitChar(RGB2[0]),
unitIntervalTo8BitChar(RGB2[1]),
unitIntervalTo8BitChar(RGB2[2]));
}
// The Hue component of the HSV interpolation Routine
static double interpolateHue(double H0, double H1, double T) {
double D = H1 - H0;
if (H0 > H1) {
std::swap(H0, H1);
D = -D;
T = 1 - T;
}
if (D <= 180) {
return H0 + T * (H1 - H0);
} else {
H0 = H0 + 360;
return std::fmod(H0 + T * (H1 - H0) + 720, 360);
}
}
// Interpolates between two HSV Colors both represented as a tuple of doubles
// Returns an HSV Color represented as a tuple of doubles
static std::tuple<double, double, double>
interpolateHSV(const std::tuple<double, double, double> &C0,
const std::tuple<double, double, double> &C1, double T) {
double H = interpolateHue(std::get<0>(C0), std::get<0>(C1), T);
double S = std::get<1>(C0) + T * (std::get<1>(C1) - std::get<1>(C0));
double V = std::get<2>(C0) + T * (std::get<2>(C1) - std::get<2>(C0));
return std::make_tuple(H, S, V);
}
// Get the Color as a tuple of uint8_ts
std::tuple<uint8_t, uint8_t, uint8_t>
ColorHelper::getColorTuple(double Point) const {
assert(!ColorMap.empty() && "ColorMap must not be empty!");
assert(!BoundMap.empty() && "BoundMap must not be empty!");
if (Point < MinIn)
return BoundMap[0];
if (Point > MaxIn)
return BoundMap[1];
size_t MaxIndex = ColorMap.size() - 1;
double IntervalWidth = MaxIn - MinIn;
double OffsetP = Point - MinIn;
double SectionWidth = IntervalWidth / static_cast<double>(MaxIndex);
size_t SectionNo = std::floor(OffsetP / SectionWidth);
double T = (OffsetP - SectionNo * SectionWidth) / SectionWidth;
auto &RGBColor0 = ColorMap[SectionNo];
auto &RGBColor1 = ColorMap[std::min(SectionNo + 1, MaxIndex)];
auto HSVColor0 = convertToHSV(RGBColor0);
auto HSVColor1 = convertToHSV(RGBColor1);
auto InterpolatedHSVColor = interpolateHSV(HSVColor0, HSVColor1, T);
return convertToRGB(InterpolatedHSVColor);
}
// A helper method to convert a color represented as tuple of uint8s to a hex
// string.
std::string
ColorHelper::getColorString(std::tuple<uint8_t, uint8_t, uint8_t> t) {
return llvm::formatv("#{0:X-2}{1:X-2}{2:X-2}", std::get<0>(t), std::get<1>(t),
std::get<2>(t));
}
// Gets a color in a gradient given a number in the interval [0,1], it does this
// by evaluating a polynomial which maps [0, 1] -> [0, 1] for each of the R G
// and B values in the color. It then converts this [0,1] colors to a 24 bit
// color as a hex string.
std::string ColorHelper::getColorString(double Point) const {
return getColorString(getColorTuple(Point));
}
|