Gradient.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. #include "Gradient.h"
  2. #include <cmath>
  3. #include <algorithm>
  4. #include <QtXml/QDomDocument>
  5. #include <QFile>
  6. Gradient::Gradient(void) :
  7. max{ 1.0 }
  8. {
  9. }
  10. Gradient::Gradient(const std::vector<std::pair<RGBColor, float>>& colors, bool repeat, int precalcSteps) :
  11. repeat{ repeat }
  12. {
  13. if(colors.empty())
  14. return;
  15. /*std::sort(colors.begin(), colors.end(),
  16. [] (const std::pair<RGBColor, float>& a, const std::pair<RGBColor, float>& b) {
  17. return a.second < b.second;
  18. });*/
  19. max = colors.at(colors.size() - 1).second;
  20. for (int i = 0; i < precalcSteps; i++) {
  21. float position = i * max / precalcSteps;
  22. RGBColor left = RGBColor{ 0, 0, 0 };
  23. RGBColor right = RGBColor{ 0, 0, 0 };
  24. float lerp = 0.0f;
  25. RGBColor atPosition = RGBColor{ 0, 0, 0 };
  26. // slow, but not in any critical path
  27. for (auto it = colors.begin(); it != colors.end(); ++it) {
  28. if (it->second > position) {
  29. if (it == colors.begin()) {
  30. atPosition = it->first;
  31. break;
  32. }
  33. else {
  34. float lerp = (position - (it - 1)->second) / (it->second - (it - 1)->second);
  35. atPosition = lerpColors((it - 1)->first, it->first, lerp);
  36. break;
  37. }
  38. }
  39. }
  40. this->colors.push_back(atPosition);
  41. }
  42. }
  43. Gradient Gradient::defaultGradient(void)
  44. {
  45. QFile res(":/gradients/default");
  46. res.open(QIODevice::ReadOnly);
  47. QString str = QString::fromUtf8(res.readAll());
  48. return readXml(str);
  49. }
  50. Gradient Gradient::readXml(const QString& xml)
  51. {
  52. QDomDocument xsr;
  53. xsr.setContent(xml);
  54. auto elem = xsr.documentElement();
  55. auto repeatAttr = elem.attributeNode("repeat");
  56. bool repeat = !repeatAttr.isNull() && repeatAttr.value().toLower() == "true";
  57. auto colors = xsr.elementsByTagName("color");
  58. std::vector<std::pair<RGBColor, float>> colorArr;
  59. for (int i = 0; i < colors.length(); ++i) {
  60. auto child = colors.item(i).toElement();
  61. uint8_t r = child.attributeNode("r").value().toInt();
  62. uint8_t g = child.attributeNode("g").value().toInt();
  63. uint8_t b = child.attributeNode("b").value().toInt();
  64. float p = child.attributeNode("p").value().toInt();
  65. printf("rgb (%s): %d, %d, %d\n", child.text().toUtf8().data(), r, g, b);
  66. colorArr.push_back({ { r, g, b }, p });
  67. }
  68. return Gradient(colorArr, repeat);
  69. }
  70. RGBColor Gradient::get(float x) const
  71. {
  72. if (colors.empty())
  73. return RGBColor();
  74. /*const auto [left, right, lerp] = getNeighbors(x);
  75. RGBColor lerped = lerpColors(left, right, lerp);
  76. return lerped;*/
  77. if (x > this->max) {
  78. if (repeat)
  79. x = ::fmodf(x, this->max);
  80. }
  81. float pos = x * colors.size() / max;
  82. if (pos < 0) {
  83. pos = 0;
  84. }
  85. if (pos > colors.size() - 1) {
  86. pos = colors.size() - 1;
  87. }
  88. int left = int(pos);
  89. int right = int(pos + 1);
  90. float lerp = pos - left;
  91. if (lerp < 1e-5) {
  92. return colors.at(left);
  93. }
  94. else {
  95. return lerpColors(colors.at(left), colors.at(right), lerp);
  96. }
  97. }
  98. RGBColor Gradient::lerpColors(RGBColor a, RGBColor b, float val)
  99. {
  100. auto mklin = [] (double x) {
  101. return x * x;//::pow(x, 2.4);
  102. };
  103. auto unlin = [] (double x) {
  104. return ::sqrt(x);// ::pow(x, 1.0 / 2.4);
  105. };
  106. return RGBColor{
  107. uint8_t(unlin(mklin(b.r) * val + mklin(a.r) * (1 - val))),
  108. uint8_t(unlin(mklin(b.g) * val + mklin(a.g) * (1 - val))),
  109. uint8_t(unlin(mklin(b.b) * val + mklin(a.b) * (1 - val)))
  110. };
  111. }
  112. /*std::tuple<RGBColor, RGBColor, float> Gradient::getNeighbors(float x) const
  113. {
  114. for (auto it = colors.begin(); it != colors.end(); ++it) {
  115. if (it->second > x) {
  116. if (it == colors.begin()) {
  117. return { it->first, it->first, 0 };
  118. }
  119. else {
  120. float lerp = (x - (it - 1)->second) / (it->second - (it - 1)->second);
  121. return { (it - 1)->first, it->first, lerp };
  122. }
  123. }
  124. }
  125. return { (colors.end() - 1)->first, (colors.end() - 1)->first, 0 };
  126. }*/