color_wheel_private.hpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /**
  2. * \file
  3. *
  4. * \author Mattia Basaglia
  5. *
  6. * \copyright Copyright (C) 2013-2020 Mattia Basaglia
  7. * \copyright Copyright (C) 2017 caryoscelus
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Lesser General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. */
  23. #include "QtColorWidgets/color_wheel.hpp"
  24. #include "QtColorWidgets/color_utils.hpp"
  25. #include <QPainter>
  26. #include <QPainterPath>
  27. #include <QMouseEvent>
  28. namespace color_widgets {
  29. enum MouseStatus
  30. {
  31. Nothing,
  32. DragCircle,
  33. DragSquare
  34. };
  35. class ColorWheel::Private
  36. {
  37. private:
  38. ColorWheel * const w;
  39. public:
  40. qreal hue, sat, val;
  41. bool backgroundIsDark;
  42. unsigned int wheel_width;
  43. MouseStatus mouse_status;
  44. QPixmap hue_ring;
  45. QImage inner_selector;
  46. std::vector<uint32_t> inner_selector_buffer;
  47. ColorSpaceEnum color_space = ColorHSV;
  48. bool rotating_selector = true;
  49. ShapeEnum selector_shape = ShapeTriangle;
  50. QColor (*color_from)(qreal,qreal,qreal,qreal);
  51. QColor (*rainbow_from_hue)(qreal);
  52. int max_size = 128;
  53. Private(ColorWheel *widget)
  54. : w(widget), hue(0), sat(0), val(0),
  55. wheel_width(20), mouse_status(Nothing),
  56. color_from(&QColor::fromHsvF), rainbow_from_hue(&utils::rainbow_hsv)
  57. {
  58. }
  59. void setup()
  60. {
  61. qreal backgroundValue = w->palette().window().color().valueF();
  62. backgroundIsDark = backgroundValue < 0.5;
  63. }
  64. virtual ~Private(){}
  65. /// Calculate outer wheel radius from idget center
  66. qreal outer_radius() const
  67. {
  68. return qMin(w->geometry().width(), w->geometry().height())/2;
  69. }
  70. /// Calculate inner wheel radius from idget center
  71. qreal inner_radius() const
  72. {
  73. return outer_radius()-wheel_width;
  74. }
  75. /// Calculate the edge length of the inner square
  76. qreal square_size() const
  77. {
  78. return inner_radius()*qSqrt(2);
  79. }
  80. /// Calculate the height of the inner triangle
  81. qreal triangle_height() const
  82. {
  83. return inner_radius()*3/2;
  84. }
  85. /// Calculate the side of the inner triangle
  86. qreal triangle_side() const
  87. {
  88. return inner_radius()*qSqrt(3);
  89. }
  90. /// return line from center to given point
  91. QLineF line_to_point(const QPoint &p) const
  92. {
  93. return QLineF (w->geometry().width()/2, w->geometry().height()/2, p.x(), p.y());
  94. }
  95. /**
  96. * Ensures the internal image buffer is the correct size
  97. * and that the QImage is associated to it
  98. */
  99. void init_buffer(QSize size)
  100. {
  101. std::size_t linear_size = size.width() * size.height();
  102. if ( inner_selector_buffer.size() == linear_size )
  103. return;
  104. inner_selector_buffer.resize(linear_size);
  105. inner_selector = QImage(
  106. reinterpret_cast<uchar*>(inner_selector_buffer.data()),
  107. size.width(),
  108. size.height(),
  109. QImage::Format_RGB32
  110. );
  111. }
  112. void render_square()
  113. {
  114. int width = qMin<int>(square_size(), max_size);
  115. init_buffer(QSize(width, width));
  116. for ( int y = 0; y < width; ++y )
  117. {
  118. for ( int x = 0; x < width; ++x )
  119. {
  120. QRgb color = color_from(hue,double(x)/width,double(y)/width,1).rgb();
  121. inner_selector_buffer[width * y + x] = color;
  122. }
  123. }
  124. }
  125. /**
  126. * \brief renders the selector as a triangle
  127. * \note It's the same as a square with the edge with value=0 collapsed to a single point
  128. */
  129. void render_triangle()
  130. {
  131. QSizeF size = selector_size();
  132. if ( size.height() > max_size )
  133. size *= max_size / size.height();
  134. qreal ycenter = size.height()/2;
  135. QSize isize = size.toSize();
  136. init_buffer(isize);
  137. for (int x = 0; x < isize.width(); x++ )
  138. {
  139. qreal pval = x / size.height();
  140. qreal slice_h = size.height() * pval;
  141. for (int y = 0; y < isize.height(); y++ )
  142. {
  143. qreal ymin = ycenter-slice_h/2;
  144. qreal psat = qBound(0.0,(y-ymin)/slice_h,1.0);
  145. QRgb color = color_from(hue,psat,pval,1).rgb();
  146. inner_selector_buffer[isize.width() * y + x] = color;
  147. }
  148. }
  149. }
  150. /// Updates the inner image that displays the saturation-value selector
  151. void render_inner_selector()
  152. {
  153. if ( selector_shape == ShapeTriangle )
  154. render_triangle();
  155. else
  156. render_square();
  157. }
  158. /// Offset of the selector image
  159. QPointF selector_image_offset()
  160. {
  161. if ( selector_shape == ShapeTriangle )
  162. return QPointF(-inner_radius(),-triangle_side()/2);
  163. return QPointF(-square_size()/2,-square_size()/2);
  164. }
  165. /**
  166. * \brief Size of the selector when rendered to the screen
  167. */
  168. QSizeF selector_size()
  169. {
  170. if ( selector_shape == ShapeTriangle )
  171. return QSizeF(triangle_height(), triangle_side());
  172. return QSizeF(square_size(), square_size());
  173. }
  174. /// Rotation of the selector image
  175. qreal selector_image_angle()
  176. {
  177. if ( selector_shape == ShapeTriangle )
  178. {
  179. if ( rotating_selector )
  180. return -hue*360-60;
  181. return -150;
  182. }
  183. else
  184. {
  185. if ( rotating_selector )
  186. return -hue*360-45;
  187. else
  188. return 180;
  189. }
  190. }
  191. /// Updates the outer ring that displays the hue selector
  192. void render_ring()
  193. {
  194. hue_ring = QPixmap(outer_radius()*2,outer_radius()*2);
  195. hue_ring.fill(Qt::transparent);
  196. QPainter painter(&hue_ring);
  197. painter.setRenderHint(QPainter::Antialiasing);
  198. painter.setCompositionMode(QPainter::CompositionMode_Source);
  199. const int hue_stops = 24;
  200. QConicalGradient gradient_hue(0, 0, 0);
  201. if ( gradient_hue.stops().size() < hue_stops )
  202. {
  203. for ( double a = 0; a < 1.0; a+=1.0/(hue_stops-1) )
  204. {
  205. gradient_hue.setColorAt(a,rainbow_from_hue(a));
  206. }
  207. gradient_hue.setColorAt(1,rainbow_from_hue(0));
  208. }
  209. painter.translate(outer_radius(),outer_radius());
  210. painter.setPen(Qt::NoPen);
  211. painter.setBrush(QBrush(gradient_hue));
  212. painter.drawEllipse(QPointF(0,0),outer_radius(),outer_radius());
  213. painter.setBrush(Qt::transparent);//palette().background());
  214. painter.drawEllipse(QPointF(0,0),inner_radius(),inner_radius());
  215. }
  216. void set_color(const QColor& c)
  217. {
  218. float nice_hue = c.hsvHueF();
  219. if ( nice_hue < 0 )
  220. nice_hue = c.hslHueF();
  221. if ( nice_hue < 0 )
  222. nice_hue = hue;
  223. switch ( color_space )
  224. {
  225. case ColorHSV:
  226. hue = nice_hue;
  227. sat = c.hsvSaturationF();
  228. val = c.valueF();
  229. break;
  230. case ColorHSL:
  231. hue = nice_hue;
  232. sat = utils::color_HSL_saturationF(c);
  233. val = utils::color_lightnessF(c);
  234. break;
  235. case ColorLCH:
  236. hue = nice_hue;
  237. sat = utils::color_chromaF(c);
  238. val = utils::color_lumaF(c);
  239. break;
  240. }
  241. }
  242. void draw_ring_editor(double editor_hue, QPainter& painter, QColor color) {
  243. painter.setPen(QPen(color,3));
  244. painter.setBrush(Qt::NoBrush);
  245. QLineF ray(0, 0, outer_radius(), 0);
  246. ray.setAngle(editor_hue*360);
  247. QPointF h1 = ray.p2();
  248. ray.setLength(inner_radius());
  249. QPointF h2 = ray.p2();
  250. painter.drawLine(h1,h2);
  251. }
  252. };
  253. } // namespace color_widgets