reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    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));
}