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