Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • app
  • src
colorwheel.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2012-2020 Matthew Chiawen Chang
5
6This program is free software; you can redistribute it and/or
7modify it under the terms of the GNU General Public License
8as published by the Free Software Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15*/
16
17#include <QVBoxLayout>
18#include <QtMath>
19#include <QPainter>
20#include <QResizeEvent>
21#include <QStyleOption>
22#include <QRect>
23#include <QDebug>
24#include "pencildef.h"
25
26#include "colorwheel.h"
27
28ColorWheel::ColorWheel(QWidget* parent) : QWidget(parent)
29{
30 setWindowTitle(tr("Color Wheel", "Color Wheel's window title"));
31 mCurrentColor = mCurrentColor.toHsv();
32 setMinimumHeight(100);
33}
34
35QColor ColorWheel::color()
36{
37 return mCurrentColor;
38}
39
40void ColorWheel::setColor(QColor color)
41{
42 // this is a UI updating function, never emit any signals
43 // and don't call any functions that will emit signals
44
45 color = color.toHsv();
46
47 if (color == mCurrentColor)
48 {
49 return;
50 }
51
52 if (color.hue() == -1) // grayscale color, keep the current hue
53 {
54 color.setHsv(mCurrentColor.hue(), color.saturation(), color.value(), color.alpha());
55 }
56
57 mCurrentColor = color;
58
59 drawSquareImage(color.hue());
60 update();
61}
62
63QColor ColorWheel::pickColor(const QPoint& point)
64{
65 if (!mWheelPixmap.rect().contains(point))
66 {
67 return QColor();
68 }
69 if (mIsInWheel)
70 {
71 qreal hue = 0;
72 QPoint center(width() / 2, height() / 2);
73 QPoint diff = point - center;
74
75 hue = qAtan2(-diff.y(), diff.x()) / M_PI * 180;
76 hue = fmod((hue + 360), 360); // shift -180~180 to 0~360
77
78 //QString strDebug = "";
79 //strDebug += QString("Radius=%1").arg(r);
80 //strDebug += QString(" Atan2=%1").arg(qAtan2(diff.y(), diff.x()));
81 //strDebug += QString(" Hue=%1").arg(hue);
82 //qDebug() << strDebug;
83
84 hue = (hue > 359) ? 359 : hue;
85 hue = (hue < 0) ? 0 : hue;
86
87 return QColor::fromHsv(static_cast<int>(hue),
88 mCurrentColor.saturation(),
89 mCurrentColor.value());
90 }
91 else if (mIsInSquare)
92 {
93 QPointF p = point - mSquareRect.topLeft();
94 //qDebug("TopRight(%d, %d) Point(%d, %d)", rect.topRight().x(), rect.topRight().y(), point.x(), point.y());
95
96 //qDebug("p(%d, %d), Region(%.1f, %.1f)", p.x(), p.y(), regionSize.width(), regionSize.height());
97 return QColor::fromHsvF(mCurrentColor.hueF(),
98 p.x() / (mSquareRect.width() - 1),
99 1.0 - (p.y() / (mSquareRect.height()-1)));
100 }
101 return QColor();
102}
103
104void ColorWheel::mousePressEvent(QMouseEvent *event)
105{
106 QPoint lastPos = event->pos();
107 if (mSquareRect.contains(lastPos))
108 {
109 mIsInWheel = false;
110 mIsInSquare = true;
111 QColor color = pickColor(lastPos);
112 saturationChanged(color.saturation());
113 valueChanged(color.value());
114
115 }
116 else if (mWheelRect.contains(lastPos))
117 {
118 mIsInWheel = true;
119 mIsInSquare = false;
120 QColor color = pickColor(lastPos);
121 hueChanged(color.hue());
122 }
123}
124
125void ColorWheel::mouseMoveEvent(QMouseEvent* event)
126{
127 QPoint lastPos = event->pos();
128 if (event->buttons() == Qt::NoButton)
129 {
130 return;
131 }
132 if (mIsInSquare)
133 {
134 if (lastPos.x() < mSquareRect.topLeft().x())
135 {
136 lastPos.setX(mSquareRect.topLeft().x());
137 }
138 else if (lastPos.x() > mSquareRect.bottomRight().x())
139 {
140 lastPos.setX(mSquareRect.bottomRight().x());
141 }
142
143 if (lastPos.y() < mSquareRect.topLeft().y())
144 {
145 lastPos.setY(mSquareRect.topLeft().y());
146 }
147 else if (lastPos.y() > mSquareRect.bottomRight().y())
148 {
149 lastPos.setY(mSquareRect.bottomRight().y());
150 }
151
152 QColor color = pickColor(lastPos);
153 saturationChanged(color.saturation());
154 valueChanged(color.value());
155 }
156 else if (mWheelRect.contains(lastPos) && mIsInWheel)
157 {
158 QColor color = pickColor(lastPos);
159 hueChanged(color.hue());
160 }
161}
162
163void ColorWheel::mouseReleaseEvent(QMouseEvent *)
164{
165 mIsInWheel = false;
166 mIsInSquare = false;
167 emit colorSelected(mCurrentColor);
168}
169
170void ColorWheel::resizeEvent(QResizeEvent* event)
171{
172 mWheelPixmap = QPixmap(event->size());
173 mWheelPixmap.fill(palette().window().color());
174 drawWheelImage(event->size());
175 drawSquareImage(mCurrentColor.hue());
176
177 update();
178}
179
180void ColorWheel::paintEvent(QPaintEvent*)
181{
182 QPainter painter;
183
184 painter.begin(this);
185 QStyleOption opt;
186 opt.initFrom(this);
187
188 composeWheel(mWheelPixmap);
189 painter.translate(width() / 2, height() / 2);
190 painter.translate(-mWheelPixmap.width() / 2, -mWheelPixmap.height() / 2);
191 painter.drawPixmap(0, 0, mWheelPixmap);
192
193 style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
194}
195
196void ColorWheel::drawWheelImage(const QSize &newSize)
197{
198 int r = qMin(newSize.width(), newSize.height());
199
200 QStyleOption option;
201 option.initFrom(this);
202
203 QBrush backgroundBrush = option.palette.window();
204
205 mWheelImage = QImage(newSize, QImage::Format_ARGB32_Premultiplied);
206
207 QPainter painter(&mWheelImage);
208 painter.setRenderHint(QPainter::Antialiasing);
209
210 painter.fillRect(mWheelImage.rect(), backgroundBrush);
211
212 QConicalGradient conicalGradient(0, 0, 0);
213
214 for (int hue = 0; hue < 360; hue += 1)
215 {
216 conicalGradient.setColorAt(hue / 360.0, QColor::fromHsv(hue, 255, 255));
217 }
218
219 qreal ir = r - mWheelThickness;
220
221 /* outer circle */
222 painter.translate(width() / 2, height() / 2);
223
224 QBrush brush(conicalGradient);
225 painter.setPen(Qt::NoPen);
226 painter.setBrush(brush);
227 painter.drawEllipse(QPoint(0, 0), r / 2, r / 2);
228
229 /* inner circle */
230 painter.setBrush(backgroundBrush);
231 painter.drawEllipse(QPoint(0, 0), r / 2 - mWheelThickness, r / 2 - mWheelThickness);
232
233 // Center of wheel
234 qreal m1 = (mWheelPixmap.width() / 2) - (ir / qSqrt(2));
235 qreal m2 = (mWheelPixmap.height() / 2) - (ir / qSqrt(2));
236
237 // Calculate size of wheel width
238 qreal wheelWidth = 2 * ir / qSqrt(2);
239
240 // Calculate wheel region
241 mWheelRect = QRectF(m1, m2, wheelWidth, wheelWidth).toAlignedRect();
242}
243
244void ColorWheel::drawSquareImage(const int &hue)
245{
246 // region of the widget
247 int w = qMin(width(), height());
248 // radius of outer circle
249 qreal r = w / 2.0;
250 // radius of inner circle
251 qreal ir = r - mWheelThickness;
252
253 // top left of square
254 qreal m1 = (width() / 2) - (ir / qSqrt(2.1));
255 qreal m2 = (height() / 2) - (ir / qSqrt(2.1));
256
257 QImage square(255, 255, QImage::Format_ARGB32);
258
259 QLinearGradient colorGradient = QLinearGradient(0, 0, square.width(), 0);
260 colorGradient.setColorAt(0, QColor(255,255,255));
261
262 // color square should always use full value and saturation
263 colorGradient.setColorAt(1, QColor::fromHsv(hue, 255, 255));
264
265 QLinearGradient blackGradient = QLinearGradient(0, 0, 0, square.height());
266 blackGradient.setColorAt(0, QColor(0,0,0,0));
267 blackGradient.setColorAt(1, QColor(0,0,0,255));
268
269 QBrush colorGradiantBrush = QBrush(colorGradient);
270 QBrush blackGradiantBrush = QBrush(blackGradient);
271
272 QPainter painter(&square);
273
274 painter.fillRect(square.rect(), colorGradiantBrush);
275 painter.fillRect(square.rect(), blackGradiantBrush);
276
277 qreal SquareWidth = 2 * ir / qSqrt(2.1);
278 mSquareRect = QRectF(m1, m2, SquareWidth, SquareWidth).toAlignedRect();
279 mSquareImage = square.scaled(mSquareRect.size());
280
281}
282
283void ColorWheel::drawHueIndicator(const int &hue)
284{
285 QPainter painter(&mWheelPixmap);
286 painter.setRenderHint(QPainter::Antialiasing);
287 if (hue > 20 && hue < 200)
288 {
289 painter.setPen(Qt::black);
290 }
291 else
292 {
293 painter.setPen(Qt::white);
294 }
295 painter.setBrush(Qt::NoBrush);
296
297 QPen pen = painter.pen();
298 pen.setWidth(3);
299 painter.setPen(pen);
300 qreal r = qMin(height(), width());
301 painter.translate(width() / 2, height() / 2);
302 painter.rotate(-hue);
303
304 r = r / 2.0 - mWheelThickness / 2;
305 painter.drawEllipse(QPointF(r, 0), 7, 7);
306}
307
308void ColorWheel::drawPicker(const QColor& color)
309{
310 QPainter painter(&mWheelPixmap);
311 painter.setRenderHint(QPainter::Antialiasing);
312 int ellipseSize = 9;
313
314 QPoint squareTopLeft = mSquareRect.topLeft();
315 QSize squareSize = mSquareRect.size();
316
317 qreal S = color.hsvSaturationF() * (squareSize.width()-1);
318 qreal V = (squareSize.height() - (color.valueF() * squareSize.height()-1));
319
320 QPen pen;
321 pen.setWidth(1);
322 if (color.hsvSaturation() > 30 || color.value() < 50)
323 {
324 pen.setColor(Qt::white);
325 }
326 painter.setPen(pen);
327
328 QTransform transform;
329 transform.translate(-ellipseSize/2,-ellipseSize/2);
330 transform.translate(squareTopLeft.x(),squareTopLeft.y()-1);
331 painter.setTransform(transform);
332 painter.drawEllipse(static_cast<int>(S), static_cast<int>(V), ellipseSize, ellipseSize);
333}
334
335void ColorWheel::composeWheel(QPixmap& pixmap)
336{
337 QPainter composePainter(&pixmap);
338 composePainter.drawImage(0, 0, mWheelImage);
339 composePainter.drawImage(mSquareRect, mSquareImage);
340 composePainter.end();
341 drawHueIndicator(mCurrentColor.hsvHue());
342 drawPicker(mCurrentColor);
343}
344
345void ColorWheel::hueChanged(const int &hue)
346{
347 if (hue < 0 || hue > 359)
348 {
349 return;
350 }
351 int s = mCurrentColor.hsvSaturation();
352 int v = mCurrentColor.value();
353 int a = mCurrentColor.alpha();
354
355 mCurrentColor.setHsv(hue, s, v, a);
356
357 if (!isVisible())
358 {
359 return;
360 }
361
362 drawSquareImage(hue);
363
364 update();
365 emit colorChanged(mCurrentColor);
366}
367
368void ColorWheel::saturationChanged(const int &sat)
369{
370 int hue = mCurrentColor.hsvHue();
371 int value = mCurrentColor.value();
372 int alpha = mCurrentColor.alpha();
373
374 mCurrentColor.setHsv(hue, sat, value, alpha);
375
376 update();
377 emit colorChanged(mCurrentColor);
378}
379
380void ColorWheel::valueChanged(const int &value)
381{
382 int hue = mCurrentColor.hsvHue();
383 int sat = mCurrentColor.hsvSaturation();
384 int alpha = mCurrentColor.alpha();
385 mCurrentColor.setHsv(hue, sat, value, alpha);
386
387 update();
388 emit colorChanged(mCurrentColor);
389}
QBrush
QColor
QColor::alpha
int alpha() const const
QColor::fromHsv
QColor fromHsv(int h, int s, int v, int a)
QColor::fromHsvF
QColor fromHsvF(qreal h, qreal s, qreal v, qreal a)
QColor::hsvHue
int hsvHue() const const
QColor::hsvSaturation
int hsvSaturation() const const
QColor::hsvSaturationF
qreal hsvSaturationF() const const
QColor::hue
int hue() const const
QColor::hueF
qreal hueF() const const
QColor::saturation
int saturation() const const
QColor::setHsv
void setHsv(int h, int s, int v, int a)
QColor::toHsv
QColor toHsv() const const
QColor::value
int value() const const
QColor::valueF
qreal valueF() const const
QConicalGradient
QGradient::setColorAt
void setColorAt(qreal position, const QColor &color)
QImage
QImage::Format_ARGB32_Premultiplied
Format_ARGB32_Premultiplied
QImage::rect
QRect rect() const const
QImage::scaled
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
QLinearGradient
QMouseEvent
QPainter
QPainter::Antialiasing
Antialiasing
QPainter::begin
bool begin(QPaintDevice *device)
QPainter::drawEllipse
void drawEllipse(const QRectF &rectangle)
QPainter::drawPixmap
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
QPainter::fillRect
void fillRect(const QRectF &rectangle, const QBrush &brush)
QPainter::pen
const QPen & pen() const const
QPainter::rotate
void rotate(qreal angle)
QPainter::setBrush
void setBrush(const QBrush &brush)
QPainter::setPen
void setPen(const QColor &color)
QPainter::setRenderHint
void setRenderHint(QPainter::RenderHint hint, bool on)
QPainter::setTransform
void setTransform(const QTransform &transform, bool combine)
QPainter::translate
void translate(const QPointF &offset)
QPaintEvent
QPen
QPen::setColor
void setColor(const QColor &color)
QPen::setWidth
void setWidth(int width)
QPixmap
QPixmap::fill
void fill(const QColor &color)
QPixmap::height
int height() const const
QPixmap::rect
QRect rect() const const
QPixmap::width
int width() const const
QPoint
QPoint::setX
void setX(int x)
QPoint::setY
void setY(int y)
QPoint::x
int x() const const
QPoint::y
int y() const const
QPointF
QPointF::x
qreal x() const const
QPointF::y
qreal y() const const
QRect::bottomRight
QPoint bottomRight() const const
QRect::contains
bool contains(const QRect &rectangle, bool proper) const const
QRect::height
int height() const const
QRect::size
QSize size() const const
QRect::topLeft
QPoint topLeft() const const
QRect::width
int width() const const
QRectF
QRectF::toAlignedRect
QRect toAlignedRect() const const
QResizeEvent
QSize
QSize::height
int height() const const
QSize::width
int width() const const
QStyle::PE_Widget
PE_Widget
QStyle::drawPrimitive
virtual void drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
QStyleOption
QStyleOption::initFrom
void initFrom(const QWidget *widget)
Qt::NoBrush
NoBrush
Qt::black
black
Qt::NoButton
NoButton
Qt::NoPen
NoPen
Qt::center
QTextStream & center(QTextStream &stream)
QTransform
QTransform::translate
QTransform & translate(qreal dx, qreal dy)
QWidget
QWidget::event
virtual bool event(QEvent *event) override
QWidget::height
height
QWidget::palette
palette
QWidget::style
QStyle * style() const const
QWidget::update
void update()
QWidget::isVisible
bool isVisible() const const
QWidget::width
width
QWidget::window
QWidget * window() const const
Generated on Thu May 8 2025 04:47:53 for Pencil2D by doxygen 1.9.6 based on revision 4513250b1d5b1a3676ec0e67b06b7a885ceaae39