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