Color.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /********************************************************************************
  2. * https://github.com/RoboticsBrno/SmartLeds
  3. *
  4. * MIT License
  5. *
  6. * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in all
  16. * copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  24. * SOFTWARE.
  25. *******************************************************************************/
  26. #include "Color.h"
  27. #include <algorithm>
  28. #include <cassert>
  29. #include <cmath>
  30. namespace {
  31. // Int -> fixed point
  32. int up(int x) { return x * 255; }
  33. } // namespace
  34. int iRgbSqrt(int num) {
  35. // https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_.28base_2.29
  36. assert("sqrt input should be non-negative" && num >= 0);
  37. assert("sqrt input should no exceed 16 bits" && num <= 0xFFFF);
  38. int res = 0;
  39. int bit = 1 << 16;
  40. while (bit > num)
  41. bit >>= 2;
  42. while (bit != 0) {
  43. if (num >= res + bit) {
  44. num -= res + bit;
  45. res = (res >> 1) + bit;
  46. } else
  47. res >>= 1;
  48. bit >>= 2;
  49. }
  50. return res;
  51. }
  52. Rgb::Rgb(const Hsv& y) {
  53. // https://stackoverflow.com/questions/24152553/hsv-to-rgb-and-back-without-floating-point-math-in-python
  54. // greyscale
  55. if (y.s == 0) {
  56. r = g = b = y.v;
  57. return;
  58. }
  59. const int region = y.h / 43;
  60. const int remainder = (y.h - (region * 43)) * 6;
  61. const int p = (y.v * (255 - y.s)) >> 8;
  62. const int q = (y.v * (255 - ((y.s * remainder) >> 8))) >> 8;
  63. const int t = (y.v * (255 - ((y.s * (255 - remainder)) >> 8))) >> 8;
  64. switch (region) {
  65. case 0:
  66. r = y.v;
  67. g = t;
  68. b = p;
  69. break;
  70. case 1:
  71. r = q;
  72. g = y.v;
  73. b = p;
  74. break;
  75. case 2:
  76. r = p;
  77. g = y.v;
  78. b = t;
  79. break;
  80. case 3:
  81. r = p;
  82. g = q;
  83. b = y.v;
  84. break;
  85. case 4:
  86. r = t;
  87. g = p;
  88. b = y.v;
  89. break;
  90. case 5:
  91. r = y.v;
  92. g = p;
  93. b = q;
  94. break;
  95. default:
  96. __builtin_trap();
  97. }
  98. a = y.a;
  99. }
  100. Rgb& Rgb::operator=(const Hsv& hsv) {
  101. Rgb r { hsv };
  102. swap(r);
  103. return *this;
  104. }
  105. Rgb Rgb::operator+(const Rgb& in) const {
  106. auto copy = *this;
  107. copy += in;
  108. return copy;
  109. }
  110. Rgb& Rgb::operator+=(const Rgb& in) {
  111. unsigned int red = r + in.r;
  112. r = (red < 255) ? red : 255;
  113. unsigned int green = g + in.g;
  114. g = (green < 255) ? green : 255;
  115. unsigned int blue = b + in.b;
  116. b = (blue < 255) ? blue : 255;
  117. return *this;
  118. }
  119. Rgb Rgb::operator-(const Rgb& in) const {
  120. auto copy = *this;
  121. copy -= in;
  122. return copy;
  123. }
  124. Rgb& Rgb::operator-=(const Rgb& in) {
  125. r = (in.r > r) ? 0 : r - in.r;
  126. g = (in.g > g) ? 0 : g - in.g;
  127. b = (in.b > b) ? 0 : b - in.b;
  128. return *this;
  129. }
  130. Rgb& Rgb::blend(const Rgb& in) {
  131. unsigned int inAlpha = in.a * (255 - a);
  132. unsigned int alpha = a + inAlpha;
  133. r = iRgbSqrt(((r * r * a) + (in.r * in.r * inAlpha)) / alpha);
  134. g = iRgbSqrt(((g * g * a) + (in.g * in.g * inAlpha)) / alpha);
  135. b = iRgbSqrt(((b * b * a) + (in.b * in.b * inAlpha)) / alpha);
  136. a = alpha;
  137. return *this;
  138. }
  139. Hsv::Hsv(const Rgb& r) {
  140. int min = std::min(r.r, std::min(r.g, r.b));
  141. int max = std::max(r.r, std::max(r.g, r.b));
  142. int chroma = max - min;
  143. v = max;
  144. if (chroma == 0) {
  145. h = s = 0;
  146. return;
  147. }
  148. s = up(chroma) / max;
  149. int hh;
  150. if (max == r.r)
  151. hh = (up(int(r.g) - int(r.b))) / chroma / 6;
  152. else if (max == r.g)
  153. hh = 255 / 3 + (up(int(r.b) - int(r.r))) / chroma / 6;
  154. else
  155. hh = 2 * 255 / 3 + (up(int(r.r) - int(r.g))) / chroma / 6;
  156. if (hh < 0)
  157. hh += 255;
  158. h = hh;
  159. a = r.a;
  160. }
  161. Hsv& Hsv::operator=(const Rgb& rgb) {
  162. Hsv h { rgb };
  163. swap(h);
  164. return *this;
  165. }