1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 1997-02-20
7 * Description : color chooser widgets
8 *
9 * Copyright (C) 1997 by Martin Jones (mjones at kde dot org)
10 * Copyright (C) 2015-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
11 *
12 * This program is free software; you can redistribute it
13 * and/or modify it under the terms of the GNU General
14 * Public License as published by the Free Software Foundation;
15 * either version 2, or (at your option)
16 * any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * ============================================================ */
24
25 #include "dhuesaturationselect.h"
26
27 // Qt includes
28
29 #include <QStyle>
30 #include <QPainter>
31 #include <QStyleOptionFrame>
32 #include <QMouseEvent>
33
34 // Local includes
35
36 #include "digikam_debug.h"
37 #include "dcolorchoosermode_p.h"
38
39 namespace Digikam
40 {
41
42 class Q_DECL_HIDDEN DPointSelect::Private
43 {
44 public:
45
Private(DPointSelect * const q)46 explicit Private(DPointSelect* const q):
47 q(q),
48 px(0),
49 py(0),
50 xPos(0),
51 yPos(0),
52 minX(0),
53 maxX(100),
54 minY(0),
55 maxY(100),
56 m_markerColor(Qt::white)
57 {
58 }
59
60 void setValues(int _xPos, int _yPos);
61
62 public:
63
64 DPointSelect* q;
65 int px;
66 int py;
67 int xPos;
68 int yPos;
69 int minX;
70 int maxX;
71 int minY;
72 int maxY;
73 QColor m_markerColor;
74 };
75
setValues(int _xPos,int _yPos)76 void DPointSelect::Private::setValues(int _xPos, int _yPos)
77 {
78 int w = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
79 xPos = _xPos;
80 yPos = _yPos;
81
82 if (xPos > maxX)
83 {
84 xPos = maxX;
85 }
86 else if (xPos < minX)
87 {
88 xPos = minX;
89 }
90
91 if (yPos > maxY)
92 {
93 yPos = maxY;
94 }
95 else if (yPos < minY)
96 {
97 yPos = minY;
98 }
99
100 Q_ASSERT(maxX != minX);
101 int xp = w + (q->width() - 2 * w) * xPos / (maxX - minX);
102
103 Q_ASSERT(maxY != minY);
104 int yp = q->height() - w - (q->height() - 2 * w) * yPos / (maxY - minY);
105
106 q->setPosition(xp, yp);
107 }
108
DPointSelect(QWidget * const parent)109 DPointSelect::DPointSelect(QWidget* const parent)
110 : QWidget(parent),
111 d(new Private(this))
112 {
113 }
114
~DPointSelect()115 DPointSelect::~DPointSelect()
116 {
117 delete d;
118 }
119
xValue() const120 int DPointSelect::xValue() const
121 {
122 return d->xPos;
123 }
124
yValue() const125 int DPointSelect::yValue() const
126 {
127 return d->yPos;
128 }
129
setRange(int _minX,int _minY,int _maxX,int _maxY)130 void DPointSelect::setRange(int _minX, int _minY, int _maxX, int _maxY)
131 {
132 if (_maxX == _minX)
133 {
134 qCWarning(DIGIKAM_GENERAL_LOG) << "DPointSelect::setRange invalid range: " << _maxX << " == " << _minX << " (for X) ";
135 return;
136 }
137
138 if (_maxY == _minY)
139 {
140 qCWarning(DIGIKAM_GENERAL_LOG) << "DPointSelect::setRange invalid range: " << _maxY << " == " << _minY << " (for Y) ";
141 return;
142 }
143
144 int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
145 d->px = w;
146 d->py = w;
147 d->minX = _minX;
148 d->minY = _minY;
149 d->maxX = _maxX;
150 d->maxY = _maxY;
151 }
152
setXValue(int _xPos)153 void DPointSelect::setXValue(int _xPos)
154 {
155 setValues(_xPos, d->yPos);
156 }
157
setYValue(int _yPos)158 void DPointSelect::setYValue(int _yPos)
159 {
160 setValues(d->xPos, _yPos);
161 }
162
setValues(int _xPos,int _yPos)163 void DPointSelect::setValues(int _xPos, int _yPos)
164 {
165 d->setValues(_xPos, _yPos);
166 }
167
setMarkerColor(const QColor & col)168 void DPointSelect::setMarkerColor(const QColor &col)
169 {
170 d->m_markerColor = col;
171 }
172
contentsRect() const173 QRect DPointSelect::contentsRect() const
174 {
175 int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
176
177 return rect().adjusted(w, w, -w, -w);
178 }
179
minimumSizeHint() const180 QSize DPointSelect::minimumSizeHint() const
181 {
182 int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
183
184 return QSize(2 * w, 2 * w);
185 }
186
paintEvent(QPaintEvent *)187 void DPointSelect::paintEvent(QPaintEvent * /* ev */)
188 {
189 QStyleOptionFrame opt;
190 opt.initFrom(this);
191
192 QPainter painter;
193 painter.begin(this);
194
195 drawContents(&painter);
196 drawMarker(&painter, d->px, d->py);
197
198 style()->drawPrimitive(QStyle::PE_Frame, &opt, &painter, this);
199
200 painter.end();
201 }
202
mousePressEvent(QMouseEvent * e)203 void DPointSelect::mousePressEvent(QMouseEvent* e)
204 {
205 mouseMoveEvent(e);
206 }
207
mouseMoveEvent(QMouseEvent * e)208 void DPointSelect::mouseMoveEvent(QMouseEvent* e)
209 {
210 int xVal, yVal;
211 int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
212 valuesFromPosition(e->pos().x() - w, e->pos().y() - w, xVal, yVal);
213 setValues(xVal, yVal);
214
215 emit valueChanged(d->xPos, d->yPos);
216 }
217
wheelEvent(QWheelEvent * e)218 void DPointSelect::wheelEvent(QWheelEvent* e)
219 {
220 QPoint delta = e->angleDelta();
221
222 if (delta.x() != 0)
223 {
224 setValues(xValue() + delta.x() / 120, yValue());
225 }
226 else if (delta.y() != 0)
227 {
228 setValues(xValue(), yValue() + delta.y() / 120);
229 }
230
231 emit valueChanged(d->xPos, d->yPos);
232 }
233
valuesFromPosition(int x,int y,int & xVal,int & yVal) const234 void DPointSelect::valuesFromPosition(int x, int y, int& xVal, int& yVal) const
235 {
236 int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
237 xVal = ((d->maxX - d->minX) * (x - w)) / (width() - 2 * w);
238 yVal = d->maxY - (((d->maxY - d->minY) * (y - w)) / (height() - 2 * w));
239
240 if (xVal > d->maxX)
241 {
242 xVal = d->maxX;
243 }
244 else if (xVal < d->minX)
245 {
246 xVal = d->minX;
247 }
248
249 if (yVal > d->maxY)
250 {
251 yVal = d->maxY;
252 }
253 else if (yVal < d->minY)
254 {
255 yVal = d->minY;
256 }
257 }
258
setPosition(int xp,int yp)259 void DPointSelect::setPosition(int xp, int yp)
260 {
261 int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
262
263 if (xp < w)
264 {
265 xp = w;
266 }
267 else if (xp > width() - w)
268 {
269 xp = width() - w;
270 }
271
272 if (yp < w)
273 {
274 yp = w;
275 }
276 else if (yp > height() - w)
277 {
278 yp = height() - w;
279 }
280
281 d->px = xp;
282 d->py = yp;
283
284 update();
285 }
286
drawMarker(QPainter * p,int xp,int yp)287 void DPointSelect::drawMarker(QPainter* p, int xp, int yp)
288 {
289 QPen pen(d->m_markerColor);
290 p->setPen(pen);
291 p->drawEllipse(xp - 4, yp - 4, 8, 8);
292 }
293
294 // --------------------------------------------------------------------------------------------------------
295
296 class Q_DECL_HIDDEN DHueSaturationSelector::Private
297 {
298 public:
299
Private(DHueSaturationSelector * const q)300 explicit Private(DHueSaturationSelector* const q)
301 : q(q),
302 mode(ChooserClassic),
303 hue(0),
304 saturation(0),
305 color(0)
306 {
307 }
308
309 DHueSaturationSelector* q;
310 QPixmap pixmap;
311
312 /**
313 * Stores the chooser mode
314 */
315 DColorChooserMode mode;
316
317 /**
318 * Stores the values for hue, saturation and luminosity
319 */
320 int hue;
321 int saturation;
322 int color;
323 };
324
DHueSaturationSelector(QWidget * const parent)325 DHueSaturationSelector::DHueSaturationSelector(QWidget* const parent)
326 : DPointSelect(parent),
327 d(new Private(this))
328 {
329 setChooserMode(ChooserClassic);
330 }
331
~DHueSaturationSelector()332 DHueSaturationSelector::~DHueSaturationSelector()
333 {
334 delete d;
335 }
336
chooserMode() const337 DColorChooserMode DHueSaturationSelector::chooserMode() const
338 {
339 return d->mode;
340 }
341
setChooserMode(DColorChooserMode chooserMode)342 void DHueSaturationSelector::setChooserMode(DColorChooserMode chooserMode)
343 {
344 int x = 0;
345 int y = 255;
346
347 switch (chooserMode)
348 {
349 case ChooserSaturation:
350 case ChooserValue:
351 x = 359;
352 break;
353
354 default:
355 x = 255;
356 break;
357 }
358
359 setRange(0, 0, x, y);
360 d->mode = chooserMode;
361 }
362
hue() const363 int DHueSaturationSelector::hue() const
364 {
365 return d->hue;
366 }
367
setHue(int hue)368 void DHueSaturationSelector::setHue(int hue)
369 {
370 d->hue = hue;
371 }
372
saturation() const373 int DHueSaturationSelector::saturation() const
374 {
375 return d->saturation;
376 }
377
setSaturation(int saturation)378 void DHueSaturationSelector::setSaturation(int saturation)
379 {
380 d->saturation = saturation;
381 }
382
colorValue() const383 int DHueSaturationSelector::colorValue() const
384 {
385 return d->color;
386 }
387
setColorValue(int color)388 void DHueSaturationSelector::setColorValue(int color)
389 {
390 d->color = color;
391 }
392
updateContents()393 void DHueSaturationSelector::updateContents()
394 {
395 drawPalette(&d->pixmap);
396 }
397
resizeEvent(QResizeEvent *)398 void DHueSaturationSelector::resizeEvent(QResizeEvent*)
399 {
400 updateContents();
401 }
402
drawContents(QPainter * painter)403 void DHueSaturationSelector::drawContents(QPainter* painter)
404 {
405 painter->drawPixmap(contentsRect().x(), contentsRect().y(), d->pixmap);
406 }
407
drawPalette(QPixmap * pixmap)408 void DHueSaturationSelector::drawPalette(QPixmap* pixmap)
409 {
410 int xSteps = componentXSteps(chooserMode());
411 int ySteps = componentYSteps(chooserMode());
412
413 QColor color;
414 color.setHsv(hue(), saturation(), (chooserMode() == ChooserClassic) ? 192 : colorValue());
415
416 QImage image(QSize(xSteps + 1, ySteps + 1), QImage::Format_RGB32);
417
418 for (int y = 0 ; y <= ySteps ; ++y)
419 {
420 setComponentY(color, chooserMode(), y * (1.0 / ySteps));
421
422 for (int x = 0 ; x <= xSteps ; ++x)
423 {
424 setComponentX(color, chooserMode(), x * (1.0 / xSteps));
425 image.setPixel(x, ySteps - y, color.rgb());
426 }
427 }
428
429 QPixmap pix(contentsRect().size());
430 QPainter painter(&pix);
431
432 // Bilinear filtering
433
434 painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
435 QRectF srcRect(0.5, 0.5, xSteps, ySteps);
436 QRectF destRect(QPointF(0, 0), contentsRect().size());
437 painter.drawImage(destRect, image, srcRect);
438 painter.end();
439
440 *pixmap = pix;
441 }
442
443 } // namespace Digikam
444