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