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 222
|
//===-- 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"
#include <cmath>
using namespace llvm;
using namespace xray;
// Sequential ColorMaps, which are used to represent information
// from some minimum to some maximum.
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.
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].
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.
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 std::string(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));
}
|