Pencil2D  ff90c0872e88be3bf81c548cd60f01983012ec49
Pencil2D is an animation software for both bitmap and vector graphics. It is free, multi-platform, and open source.
 All Classes Functions
colorwheel.cpp
1 /*
2 
3 Pencil - Traditional Animation Software
4 Copyright (C) 2013-2017 Matt Chiawen Chang
5 
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; version 2 of the License.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 */
16 
17 #include <cmath>
18 #include <QPainter>
19 #include <QResizeEvent>
20 #include <QStyleOption>
21 #include <QtCore/qmath.h>
22 #include <QRect>
23 #include <QDebug>
24 
25 #include "colorwheel.h"
26 
27 ColorWheel::ColorWheel(QWidget *parent) : QWidget(parent),
28  m_initSize(200, 200),
29  m_wheelThickness(20),
30  m_currentColor(Qt::red),
31  m_isInWheel(false),
32  m_isInSquare(false)
33 
34 {
35  m_currentColor = m_currentColor.toHsv();
36 }
37 
38 QColor ColorWheel::color()
39 {
40  return m_currentColor;
41 }
42 
43 void ColorWheel::changeColor(const QColor &color)
44 {
45  if (color == m_currentColor)
46  {
47  return;
48  }
49  if (color.hue() != m_currentColor.hue())
50  {
51  hueChanged(color.hue());
52  }
53 
54  if (color.saturation() != m_currentColor.saturation() ||
55  color.value() != m_currentColor.value() )
56  {
57  svChanged(color);
58  }
59 
60  if(color.alpha() != m_currentColor.alpha())
61  {
62  alphaChanged(color.alpha());
63  }
64  //emit colorChanged(color);
65  update();
66 }
67 
68 void ColorWheel::setColor(const QColor &color)
69 {
70  if (color == m_currentColor)
71  {
72  return;
73  }
74  if (color.hue() != m_currentColor.hue())
75  {
76  hueChanged(color.hue());
77  }
78 
79  if (color.saturation() != m_currentColor.saturation() ||
80  color.value() != m_currentColor.value() )
81  {
82  svChanged(color);
83  }
84 
85  if ( color.alpha() != m_currentColor.alpha() )
86  {
87  alphaChanged(color.alpha());
88  }
89 
90  update();
91  emit colorSelected(color);
92 }
93 
94 QColor ColorWheel::pickColor(const QPoint& point)
95 {
96  if ( ! m_wheelPixmap.rect().contains(point) )
97  {
98  return QColor();
99  }
100  if (m_isInWheel)
101  {
102  qreal hue = 0;
103  int r = qMin(width(), height()) / 2;
104  QString strDebug = "";
105  strDebug += QString("Radius=%1").arg(r);
106 
107  QPoint center(width() / 2, height() / 2);
108 
109  QPoint diff = point - center;
110  strDebug += QString(" Atan2=%1").arg(qAtan2(diff.y(), diff.x()));
111 
112  hue = qAtan2( -diff.y(), diff.x() ) / M_PI * 180;
113 
114  hue = fmod((hue + 360), 360); // shift -180~180 to 0~360
115 
116  strDebug += QString(" Hue=%1").arg(hue);
117  //qDebug() << strDebug;
118 
119  hue = (hue > 359) ? 359 : hue;
120  hue = (hue < 0) ? 0 : hue;
121 
122  return QColor::fromHsv(hue,
123  m_currentColor.saturation(),
124  m_currentColor.value());
125  }
126  else if (m_isInSquare)
127  {
128  QRect rect = m_squareRegion.boundingRect();
129  QPoint p = point - rect.topLeft();
130  //qDebug("TopRight(%d, %d) Point(%d, %d)", rect.topRight().x(), rect.topRight().y(), point.x(), point.y());
131  QSizeF regionSize = rect.size() - QSizeF(1, 1);
132 
133  //qDebug("p(%d, %d), Region(%.1f, %.1f)", p.x(), p.y(), regionSize.width(), regionSize.height());
134  return QColor::fromHsvF( m_currentColor.hueF(),
135  p.x() / regionSize.width(),
136  1.0 - (p.y() / regionSize.height()));
137  }
138  return QColor();
139 }
140 
141 QSize ColorWheel::sizeHint () const
142 {
143  return m_initSize;
144 }
145 
146 QSize ColorWheel::minimumSizeHint () const
147 {
148  return m_initSize;
149 }
150 
151 void ColorWheel::mousePressEvent(QMouseEvent *event)
152 {
153  QPoint lastPos = event->pos();
154  if (m_squareRegion.contains(lastPos))
155  {
156  m_isInWheel = false;
157  m_isInSquare = true;
158  QColor color = pickColor(lastPos);
159  svChanged(color);
160  }
161  else if (m_wheelRegion.contains(lastPos))
162  {
163  m_isInWheel = true;
164  m_isInSquare = false;
165  QColor color = pickColor(lastPos);
166  hueChanged(color.hue());
167  }
168 }
169 
170 void ColorWheel::mouseMoveEvent(QMouseEvent* event)
171 {
172  QPoint lastPos = event->pos();
173  if ( event->buttons() == Qt::NoButton )
174  {
175  return;
176  }
177  if (m_isInSquare)
178  {
179  QRect rect = m_squareRegion.boundingRect();
180 
181  if ( lastPos.x() < rect.topLeft().x() )
182  {
183  lastPos.setX( rect.topLeft().x() );
184  }
185  else if ( lastPos.x() > rect.bottomRight().x() )
186  {
187  lastPos.setX( rect.bottomRight().x() );
188  }
189 
190  if ( lastPos.y() < rect.topLeft().y() )
191  {
192  lastPos.setY( rect.topLeft().y() );
193  }
194  else if ( lastPos.y() > rect.bottomRight().y() )
195  {
196  lastPos.setY( rect.bottomRight().y() );
197  }
198 
199  QColor color = pickColor(lastPos);
200  svChanged(color);
201  }
202  else if (m_wheelRegion.contains(lastPos) && m_isInWheel)
203  {
204  QColor color = pickColor(lastPos);
205  hueChanged(color.hue());
206  }
207 }
208 
209 void ColorWheel::mouseReleaseEvent(QMouseEvent *)
210 {
211  m_isInWheel = false;
212  m_isInSquare = false;
213  emit colorSelected(m_currentColor);
214 }
215 
216 void ColorWheel::resizeEvent(QResizeEvent *event)
217 {
218  m_wheelPixmap = QPixmap(event->size());
219  m_wheelPixmap.fill(palette().background().color());
220  drawWheelImage(event->size());
221  drawSquareImage(m_currentColor.hue());
222  update();
223 }
224 
225 void ColorWheel::paintEvent(QPaintEvent *)
226 {
227  QPainter painter(this);
228  QStyleOption opt;
229  opt.initFrom(this);
230  composeWheel(m_wheelPixmap);
231  painter.translate(width() / 2, height() / 2);
232  painter.translate(-m_wheelPixmap.width() / 2,-m_wheelPixmap.height() / 2);
233  painter.drawPixmap(0, 0, m_wheelPixmap);
234  style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
235 }
236 
237 void ColorWheel::drawWheelImage(const QSize &newSize)
238 {
239  int r = qMin(newSize.width(), newSize.height());
240 
241  QStyleOption option;
242  option.initFrom(this);
243 
244  QBrush backgroundBrush = option.palette.window();
245 
246  m_wheelImage = QImage(newSize, QImage::Format_ARGB32_Premultiplied);
247 
248  //m_wheelImage.fill(background.color()); // Only in 4.8
249 
250  QPainter painter(&m_wheelImage);
251  painter.setRenderHint(QPainter::Antialiasing);
252 
253  painter.fillRect(m_wheelImage.rect(), backgroundBrush);
254 
255  QConicalGradient conicalGradient(0, 0, 0);
256 
257  for (int hue = 0; hue < 360; hue +=1)
258  {
259  conicalGradient.setColorAt( hue / 360.0, QColor::fromHsv(hue, 255, 255));
260  }
261 
262  qreal ir = r - m_wheelThickness;
263 
264  /* outer circle */
265  painter.translate(width() / 2, height() / 2);
266 
267  QBrush brush(conicalGradient);
268  painter.setPen(Qt::NoPen);
269  painter.setBrush(brush);
270  painter.drawEllipse(QPoint(0, 0), r/2, r/2);
271 
272  /* inner circle */
273  painter.setBrush(backgroundBrush);
274  painter.drawEllipse(QPoint(0, 0), r/2 - m_wheelThickness, r/2 - m_wheelThickness);
275 
276  // Center of wheel
277  qreal m1 = (m_wheelPixmap.width() / 2) - (ir / qSqrt(2));
278  qreal m2 = (m_wheelPixmap.height() / 2) - (ir / qSqrt(2));
279 
280  // Calculate size of wheel width
281  qreal wheelWidth = 2 * ir / qSqrt(2);
282 
283  // Caculate wheel region
284  m_wheelRegion = QRegion(m1, m2, wheelWidth, wheelWidth);
285 }
286 
287 void ColorWheel::drawSquareImage(const int &hue)
288 {
289  // region of the widget
290  int w = qMin(width(), height());
291  // radius of outer circle
292  qreal r = w / 2.0;
293  // radius of inner circle
294  qreal ir = r - m_wheelThickness;
295 
296  // center of square
297  qreal m1 = (width() / 2) - (ir / qSqrt(2));
298  qreal m2 = (height() / 2) - (ir / qSqrt(2));
299 
300  QImage square(255, 255, QImage::Format_ARGB32_Premultiplied);
301  QColor color;
302 
303  for (int i = 0; i < 255; ++i)
304  {
305  for (int j = 0; j < 255; ++j)
306  {
307  color = QColor::fromHsv(hue, i, 255 - j);
308  QRgb rgb = qRgb(color.red(), color.green(), color.blue());
309  square.setPixel(i, j, rgb);
310  }
311  }
312 
313  qreal SquareWidth = 2 * ir / qSqrt(2.1);
314  m_squareImage = square.scaled(SquareWidth, SquareWidth);
315  m_squareRegion = QRegion(m1, m2, SquareWidth, SquareWidth);
316 }
317 
318 void ColorWheel::drawHueIndicator(const int &hue)
319 {
320  QPainter painter(&m_wheelPixmap);
321  painter.setRenderHint(QPainter::Antialiasing);
322  if(hue > 20 && hue < 200)
323  {
324  painter.setPen(Qt::black);
325  }
326  else
327  {
328  painter.setPen(Qt::white);
329  }
330  painter.setBrush(Qt::NoBrush);
331 
332  QPen pen = painter.pen();
333  pen.setWidth(3);
334  painter.setPen(pen);
335  qreal r = qMin(height(), width());
336  painter.translate(width() / 2, height() / 2);
337  painter.rotate( -hue );
338 
339  r = r / 2.0 - m_wheelThickness/2;
340  painter.drawEllipse(QPointF(r, 0), 7, 7);
341 }
342 
343 void ColorWheel::drawPicker(const QColor &color)
344 {
345  QPainter painter(&m_wheelPixmap);
346  painter.setRenderHint(QPainter::Antialiasing);
347 
348  QPoint squareTopLeft = m_squareRegion.boundingRect().topLeft();
349 
350  painter.translate(squareTopLeft.x(), squareTopLeft.y());
351 
352  QSize squareSize = m_squareRegion.boundingRect().size();
353 
354  qreal S = color.saturationF() * squareSize.width();
355  qreal V = squareSize.height() - (color.valueF() * squareSize.height());
356 
357  QPen pen;
358  pen.setWidth(3);
359  if (color.saturation() > 30 || color.value() < 50)
360  {
361  pen.setColor(Qt::white);
362  }
363  painter.setPen(pen);
364 
365  painter.drawEllipse(S - 2, V - 2, 10, 10);
366 }
367 
368 void ColorWheel::composeWheel( QPixmap& pixmap )
369 {
370  QPainter composePainter(&pixmap);
371  composePainter.drawImage(0, 0, m_wheelImage);
372  composePainter.translate(width() / 2, height() / 2); //Move to center of widget
373  composePainter.translate(-m_squareImage.width() / 2, -m_squareImage.height() / 2); //move to center of image
374  composePainter.drawImage(0, 0, m_squareImage);
375  composePainter.end();
376  drawHueIndicator(m_currentColor.hue());
377  drawPicker(m_currentColor);
378 }
379 
380 void ColorWheel::hueChanged(const int &hue)
381 {
382  if ( hue < 0 || hue > 359)
383  {
384  return;
385  }
386  int s = m_currentColor.saturation();
387  int v = m_currentColor.value();
388  int a = m_currentColor.alpha();
389 
390  m_currentColor.setHsv(hue, s, v, a);
391 
392  if(!isVisible())
393  {
394  return;
395  }
396 
397  drawSquareImage(hue);
398 
399  repaint();
400  emit colorChanged(m_currentColor);
401 }
402 
403 void ColorWheel::svChanged(const QColor &newcolor)
404 {
405  int hue = m_currentColor.hue();
406  int alpha = m_currentColor.alpha();
407  m_currentColor.setHsv(hue,
408  newcolor.saturation(),
409  newcolor.value(),
410  alpha);
411  if( !isVisible() )
412  {
413  return;
414  }
415 
416  repaint();
417  emit colorChanged(m_currentColor);
418 }
419 
420 void ColorWheel::alphaChanged(const int &alpha)
421 {
422  m_currentColor.setAlpha(alpha);
423 
424  if(!isVisible())
425  {
426  return;
427  }
428 
429  emit colorChanged(m_currentColor);
430 }