Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_color_selector.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include <QPaintDevice>
8#include <QPainter>
9#include <QColor>
10#include <QBrush>
11#include <QPen>
12#include <QRadialGradient>
13#include <QConicalGradient>
14#include <QMouseEvent>
15#include <QResizeEvent>
16#include <QTransform>
17#include <cmath>
18
19#include <kis_config.h>
20#include <kis_arcs_constants.h>
22//#include <KisGamutMaskViewConverter.h>
23
24#include "kis_color_selector.h"
25
26//#define DEBUG_ARC_SELECTOR
27
29 : QWidget(parent)
30 , m_colorConverter(KisDisplayColorConverter::dumbConverterInstance())
31 , m_colorSpace(type)
32 , m_inverseSaturation(false)
33 , m_selectedColor(m_colorConverter)
34 , m_fgColor(m_colorConverter)
35 , m_bgColor(m_colorConverter)
36 , m_clickedRing(-1)
37 , m_gamutMaskOn(false)
38 , m_currentGamutMask(nullptr)
39 , m_maskPreviewActive(true)
40 , m_gamutMaskViewTransform(QTransform())
41 , m_widgetUpdatesSelf(false)
42 , m_isDirtyWheel(false)
43 , m_isDirtyLightStrip(false)
44 , m_isDirtyGamutMask(false)
45 , m_isDirtyColorPreview(false)
46{
47// m_viewConverter = new KisGamutMaskViewConverter();
48
50
54
55 using namespace std::placeholders; // For _1 placeholder
56 auto function = std::bind(&KisColorSelector::slotUpdateColorAndPreview, this, _1);
57 m_updateColorCompressor.reset(new ColorCompressorType(25 /* ms */, function));
58}
59
61{
62 m_colorSpace = type;
64
66 m_isDirtyWheel = true;
67
68#ifdef DEBUG_ARC_SELECTOR
69 dbgPlugins << "KisColorSelector::setColorSpace: set to:" << m_colorSpace;
70#endif
71
72 update();
73}
74
85
87{
89
90 recalculateAreas(quint8(num));
91
92 if (m_selectedLightPiece >= 0)
94
95 update();
96}
97
99{
100 num = qBound(MIN_NUM_HUE_PIECES, num, MAX_NUM_HUE_PIECES);
101
102 recalculateRings(quint8(getNumRings()), quint8(num));
103
104 if (m_selectedPiece >= 0)
106
107 update();
108}
109
111{
113
114 recalculateRings(quint8(num), quint8(getNumPieces()));
115
116 if (m_selectedRing >= 0)
118
119 update();
120}
121
130
132{
133 if (!m_widgetUpdatesSelf) {
136
137 m_isDirtyWheel = true;
138 m_isDirtyLightStrip = true;
140#ifdef DEBUG_ARC_SELECTOR
141 dbgPlugins << "KisColorSelector::setFgColor: m_fgColor set to:"
142 << "H:" << m_fgColor.getH()
143 << "S:" << m_fgColor.getS()
144 << "X:" << m_fgColor.getX();
145
146 dbgPlugins << "KisColorSelector::setFgColor: m_selectedColor set to:"
147 << "H:" << m_selectedColor.getH()
148 << "S:" << m_selectedColor.getS()
149 << "X:" << m_selectedColor.getX();
150#endif
151 update();
152 }
153}
154
156{
157 if (!m_widgetUpdatesSelf) {
159
161
162#ifdef DEBUG_ARC_SELECTOR
163 dbgPlugins << "KisColorSelector::setBgColor: m_bgColor set to:"
164 << "H:" << m_bgColor.getH()
165 << "S:" << m_bgColor.getS()
166 << "X:" << m_bgColor.getX();
167#endif
168 update();
169 }
170}
171
173{
174 m_selectedColor.setX(qBound(0.0, light, 1.0));
176 m_isDirtyLightStrip = true;
177 update();
178}
179
180void KisColorSelector::setLumaCoefficients(qreal lR, qreal lG, qreal lB, qreal lGamma)
181{
182 m_lumaR = lR;
183 m_lumaG = lG;
184 m_lumaB = lB;
185 m_lumaGamma = lGamma;
186
188
189 m_isDirtyLightStrip = true;
190 m_isDirtyWheel = true;
191
192#ifdef DEBUG_ARC_SELECTOR
193 dbgPlugins << "KisColorSelector::setLumaCoefficients: " << m_lumaR
194 << " " << m_lumaG << " " << m_lumaB << " " << m_lumaGamma;
195#endif
196
197 update();
198}
199
201{
202 if (m_inverseSaturation != inverse) {
204 m_inverseSaturation = inverse;
205 recalculateRings(quint8(getNumRings()), quint8(getNumPieces()));
206 update();
207 }
208}
209
211{
212 if (!gamutMask) {
213 return;
214 }
215
217 m_gamutMaskViewTransform = m_currentGamutMask->maskToViewTransform(m_renderArea.width());
218
219 if (m_enforceGamutMask) {
220 m_isDirtyWheel = true;
221 } else {
222 m_isDirtyGamutMask = true;
223 }
224
225 update();
226}
227
229{
230 m_isDirtyWheel = true;
231 m_isDirtyLightStrip = true;
232 m_isDirtyGamutMask = true;
234
235 update();
236}
237
242
244{
245 return m_gamutMaskOn;
246}
247
248
250{
251 if (m_currentGamutMask) {
253
254 if (m_enforceGamutMask) {
255 m_isDirtyWheel = true;
256 } else {
257 m_isDirtyGamutMask = true;
258 }
259
260 update();
261 }
262}
263
265{
266 m_enforceGamutMask = enforce;
267 m_isDirtyGamutMask = true;
268 m_isDirtyWheel = true;
269 update();
270}
271
272QPointF KisColorSelector::mapCoordToView(const QPointF& pt, const QRectF& viewRect) const
273{
274 qreal w = viewRect.width() / 2.0;
275 qreal h = viewRect.height() / 2.0;
276
277 qreal x = pt.x() + 1.0;
278 qreal y = (pt.y()) + 1.0;
279
280 return QPointF(x*w, y*h);
281}
282
283QPointF KisColorSelector::mapCoordToUnit(const QPointF& pt, const QRectF& viewRect) const
284{
285 qreal w = viewRect.width() / 2.0;
286 qreal h = viewRect.height() / 2.0;
287 qreal x = pt.x() - (viewRect.x() + w);
288 qreal y = pt.y() - (viewRect.y() + h);
289 return QPointF(x/w, y/h);
290}
291
292QPointF KisColorSelector::mapColorToUnit(const KisColor& color, bool invertSaturation) const
293{
294 qreal radius;
295 if (invertSaturation && m_inverseSaturation) {
296 radius = 1.0 - color.getS();
297 } else {
298 radius = color.getS();
299 }
300
301 QPointF hueCoord = mapHueToAngle(color.getH());
302 qreal x = hueCoord.x()*radius;
303 qreal y = hueCoord.y()*radius;
304
305 return QPointF(x,y);
306}
307
309{
310 qreal angle = std::atan2(-y, -x);
311
312#ifdef DEBUG_ARC_SELECTOR
313 dbgPlugins << "KisColorSelector::mapCoordToAngle: "
314 << "X:" << x
315 << "Y:" << y
316 << "angle:" << angle;
317#endif
318
319 return angle;
320}
321
322QPointF KisColorSelector::mapHueToAngle(qreal hue) const
323{
324 qreal angle = hue * 2.0 * M_PI - M_PI;
325 qreal x = std::cos(angle);
326 qreal y = std::sin(angle);
327
328 return QPointF(x,y);
329}
330
331
332qint8 KisColorSelector::getLightIndex(const QPointF& pt) const
333{
334 if (m_lightStripArea.contains(pt.toPoint(), true)) {
335 qreal t = (pt.y() - m_lightStripArea.y()) / qreal(m_lightStripArea.height());
336 return qint8(t * getNumLightPieces());
337 }
338
339 return -1;
340}
341
342qint8 KisColorSelector::getLightIndex(qreal light) const
343{
344 light = qreal(1) - qBound(qreal(0), light, qreal(1));
345 return qint8(qRound(light * (getNumLightPieces()-1)));
346}
347
348qreal KisColorSelector::getLight(const QPointF& pt) const
349{
350 qint8 clickedLightPiece = getLightIndex(pt);
351
352 if (clickedLightPiece >= 0) {
353 if (getNumLightPieces() > 1) {
354 return 1.0 - (qreal(clickedLightPiece) / qreal(getNumLightPieces()-1));
355 }
356 return 1.0 - (qreal(pt.y()) / qreal(m_lightStripArea.height()));
357 }
358
359 return qreal(0);
360}
361
363{
364 qreal partSize = 1.0 / qreal(getNumPieces());
365 return qint8(qRound(hue.scaled(0.0, 1.0) / partSize) % getNumPieces());
366}
367
368qreal KisColorSelector::getHue(int hueIdx, Radian shift) const
369{
370 Radian hue = (qreal(hueIdx) / qreal(getNumPieces())) * PI2;
371 hue += shift;
372 return hue.scaled(0.0, 1.0);
373}
374
375qint8 KisColorSelector::getSaturationIndex(qreal saturation) const
376{
377 saturation = qBound(qreal(0), saturation, qreal(1));
378 saturation = m_inverseSaturation ? (qreal(1) - saturation) : saturation;
379 return qint8(saturation * qreal(getNumRings() - 1));
380}
381
382qint8 KisColorSelector::getSaturationIndex(const QPointF& pt) const
383{
384 qreal length = std::sqrt(pt.x()*pt.x() + pt.y()*pt.y());
385
386 for(int i=0; i<m_colorRings.size(); ++i) {
387 if (length >= m_colorRings[i].innerRadius && length < m_colorRings[i].outerRadius)
388 return qint8(i);
389 }
390
391 return -1;
392}
393
394qreal KisColorSelector::getSaturation(int saturationIdx) const
395{
396 qreal sat = qreal(saturationIdx) / qreal(getNumRings()-1);
397 return m_inverseSaturation ? (1.0 - sat) : sat;
398}
399
400void KisColorSelector::recalculateAreas(quint8 numLightPieces)
401{
402 qreal LIGHT_STRIP_RATIO = 0.075;
403
405 LIGHT_STRIP_RATIO = 0.25;
406 }
407
408 int width = QWidget::width();
409 int height = QWidget::height();
410 int size = qMin(width, height);
411 int stripThick = int(size * LIGHT_STRIP_RATIO);
412
413 width -= stripThick;
414
415 size = qMin(width, height);
416
417 int x = (width - size) / 2;
418 int y = (height - size) / 2;
419
420 m_widgetArea = QRect(0, 0, QWidget::width(), QWidget::height());
421 m_renderArea = QRect(x+stripThick, y, size, size);
422 m_lightStripArea = QRect(0, 0, stripThick, QWidget::height());
423
424 m_renderBuffer = QImage(size*devicePixelRatioF(), size*devicePixelRatioF(), QImage::Format_ARGB32_Premultiplied);
425 m_colorPreviewBuffer = QImage(QWidget::width()*devicePixelRatioF(), QWidget::height()*devicePixelRatioF(), QImage::Format_ARGB32_Premultiplied);
426 m_maskBuffer = QImage(size*devicePixelRatioF(), size*devicePixelRatioF(), QImage::Format_ARGB32_Premultiplied);
427 m_lightStripBuffer = QImage(stripThick*devicePixelRatioF(), QWidget::height()*devicePixelRatioF(), QImage::Format_ARGB32_Premultiplied);
428
429 m_renderBuffer.setDevicePixelRatio(devicePixelRatioF());
430 m_colorPreviewBuffer.setDevicePixelRatio(devicePixelRatioF());
431 m_maskBuffer.setDevicePixelRatio(devicePixelRatioF());
432 m_lightStripBuffer.setDevicePixelRatio(devicePixelRatioF());
433
434 m_numLightPieces = numLightPieces;
435
436 if (m_currentGamutMask) {
437 m_gamutMaskViewTransform = m_currentGamutMask->maskToViewTransform(m_renderArea.width());
438 }
439
440 m_isDirtyGamutMask = true;
441 m_isDirtyLightStrip = true;
442 m_isDirtyWheel = true;
444}
445
446void KisColorSelector::recalculateRings(quint8 numRings, quint8 numPieces)
447{
448 m_colorRings.resize(numRings);
449 m_numPieces = numPieces;
450
451 for(int i=0; i<numRings; ++i) {
452 qreal innerRadius = qreal(i) / qreal(numRings);
453 qreal outerRadius = qreal(i+1) / qreal(numRings);
454 qreal saturation = qreal(i) / qreal(numRings-1);
455
456 createRing(m_colorRings[i], numPieces, innerRadius, outerRadius+0.001);
457 m_colorRings[i].saturation = m_inverseSaturation ? (1.0 - saturation) : saturation;
458 }
459
460 m_isDirtyWheel = true;
461}
462
463void KisColorSelector::createRing(ColorRing& ring, quint8 numPieces, qreal innerRadius, qreal outerRadius)
464{
465 int numParts = qMax<int>(numPieces, 1);
466
467 ring.innerRadius = innerRadius;
468 ring.outerRadius = outerRadius;
469 ring.pieced.resize(numParts);
470
471 qreal partSize = 360.0 / qreal(numParts);
472 QRectF outerRect(-outerRadius, -outerRadius, outerRadius*2.0, outerRadius*2.0);
473 QRectF innerRect(-innerRadius, -innerRadius, innerRadius*2.0, innerRadius*2.0);
474
475 for(int i=0; i<numParts; ++i) {
476 qreal aBeg = partSize*i;
477 qreal aEnd = aBeg + partSize;
478
479 aBeg -= partSize / 2.0;
480 aEnd -= partSize / 2.0;
481
482 ring.pieced[i] = QPainterPath();
483 ring.pieced[i].arcMoveTo(innerRect, aBeg);
484 ring.pieced[i].arcTo(outerRect, aBeg, partSize);
485 ring.pieced[i].arcTo(innerRect, aEnd,-partSize);
486 }
487}
488
490{
492
493 QPointF colorCoord = mapCoordToView(mapColorToUnit(color, false), m_renderArea);
494
495 QPointF translatedPoint = m_currentGamutMask->viewToMaskTransform(m_renderArea.width()).map(colorCoord);
496 bool isClear = m_currentGamutMask->coordIsClear(translatedPoint, m_maskPreviewActive);
497
498 if (isClear) {
499 return true;
500 } else {
501 return false;
502 }
503
504 } else {
505 return true;
506 }
507
508 return false;
509}
510
511
513{
514#ifdef DEBUG_ARC_SELECTOR
515 dbgPlugins << "KisColorSelector::requestUpdateColorAndPreview: requesting update to: "
516 << "H:" << color.getH()
517 << "S:" << color.getS()
518 << "X:" << color.getX();
519#endif
520
521 m_updateColorCompressor->start(qMakePair(color, role));
522}
523
524void KisColorSelector::slotUpdateColorAndPreview(QPair<KisColor, Acs::ColorRole> color)
525{
526 const bool selectAsFgColor = color.second == Acs::Foreground;
527
528 if (selectAsFgColor) {
530 } else {
532 }
533
535
536 m_isDirtyLightStrip = true;
538 m_isDirtyWheel = true;
539
540#ifdef DEBUG_ARC_SELECTOR
541 dbgPlugins << "KisColorSelector::slotUpdateColorAndPreview: m_selectedColor set to:"
542 << "H:" << m_selectedColor.getH()
543 << "S:" << m_selectedColor.getS()
544 << "X:" << m_selectedColor.getX();
545#endif
546
547 if (selectAsFgColor) { Q_EMIT sigFgColorChanged(m_selectedColor); }
548 else { Q_EMIT sigBgColorChanged(m_selectedColor); }
549}
550
551void KisColorSelector::drawRing(QPainter& painter, KisColorSelector::ColorRing& ring, const QRect& rect)
552{
553 painter.save();
554 painter.setRenderHint(QPainter::Antialiasing, true);
555 painter.resetTransform();
556 painter.translate(rect.width()/2, rect.height()/2);
557
558 if (ring.pieced.size() > 1) {
559 QTransform mirror;
560 mirror.rotate(180, Qt::YAxis);
561 painter.setTransform(mirror, true);
562 painter.scale(rect.width()/2, rect.height()/2);
563 QPen normalPen = QPen(QBrush(COLOR_NORMAL_OUTLINE), 0.005);
564 QPen clearMaskPen = QPen(QBrush(COLOR_MASK_CLEAR), 0.005);
565 QBrush brush(Qt::SolidPattern);
566
567 for(int i=0; i<ring.pieced.size(); ++i) {
568 qreal hue = qreal(i) / qreal(ring.pieced.size());
569 hue = (hue >= 1.0) ? (hue - 1.0) : hue;
570 hue = (hue < 0.0) ? (hue + 1.0) : hue;
571
573 color.setS(ring.saturation);
574 color.setX(m_selectedColor.getX());
575
577 painter.setPen(clearMaskPen);
578 } else {
579 painter.setPen(normalPen);
580 }
581
582 if ((m_enforceGamutMask) && (!colorIsClear(color))) {
583 brush.setColor(COLOR_MASK_FILL);
584 } else {
585 brush.setColor(color.toQColor());
586 }
587 painter.setBrush(brush);
588
589 painter.drawPath(ring.pieced[i]);
590 }
591 }
592 else {
593 KisColor colors[7] = {
601 };
602
603 QConicalGradient gradient(0, 0, 0);
604
605 for(int i=0; i<=6; ++i) {
606 qreal hue = qreal(i) / 6.0;
607 colors[i].setS(ring.saturation);
608 colors[i].setX(m_selectedColor.getX());
609 gradient.setColorAt(hue, colors[i].toQColor());
610 }
611
612 painter.scale(rect.width()/2, rect.height()/2);
613 painter.fillPath(ring.pieced[0], QBrush(gradient));
614 }
615
616 painter.restore();
617}
618
619void KisColorSelector::drawOutline(QPainter& painter, const QRect& rect)
620{
621 painter.save();
622
623 painter.setRenderHint(QPainter::Antialiasing, true);
624 painter.resetTransform();
625 painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2);
626 painter.scale(rect.width()/2, rect.height()/2);
627
628 QPen normalPen = QPen(QBrush(COLOR_NORMAL_OUTLINE), 0.005);
629 QPen selectedPen;
630
631 painter.setPen(normalPen);
632
633 if (getNumPieces() > 1) {
634 if (m_selectedRing >= 0 && m_selectedPiece >= 0) {
635 painter.resetTransform();
636 painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2);
637 QTransform mirror;
638 mirror.rotate(180, Qt::YAxis);
639 painter.setTransform(mirror, true);
640 painter.scale(rect.width()/2, rect.height()/2);
641
642 if (m_selectedColor.getX() < 0.55) {
643 selectedPen = QPen(QBrush(COLOR_SELECTED_LIGHT), 0.007);
644 } else {
645 selectedPen = QPen(QBrush(COLOR_SELECTED_DARK), 0.007);
646 }
647
648 painter.setPen(selectedPen);
649 painter.drawPath(m_colorRings[m_selectedRing].pieced[m_selectedPiece]);
650 }
651 }
652 else {
653 for(int i=0; i<getNumRings(); ++i) {
654 qreal rad = m_colorRings[i].outerRadius;
655 painter.drawEllipse(QRectF(-rad, -rad, rad*2.0, rad*2.0));
656 }
657
658 if (m_selectedRing >= 0) {
659 qreal iRad = m_colorRings[m_selectedRing].innerRadius;
660 qreal oRad = m_colorRings[m_selectedRing].outerRadius;
661
662 if (m_selectedColor.getX() < 0.55) {
663 selectedPen = QPen(QBrush(COLOR_SELECTED_LIGHT), 0.005);
664 } else {
665 selectedPen = QPen(QBrush(COLOR_SELECTED_DARK), 0.005);
666 }
667
668 painter.setPen(selectedPen);
669 painter.drawEllipse(QRectF(-iRad, -iRad, iRad*2.0, iRad*2.0));
670 painter.drawEllipse(QRectF(-oRad, -oRad, oRad*2.0, oRad*2.0));
671
672 QPointF lineCoords = mapHueToAngle(m_selectedColor.getH());
673 painter.drawLine(QPointF(lineCoords.x()*iRad, lineCoords.y()*iRad), QPointF(lineCoords.x()*oRad, lineCoords.y()*oRad));
674 }
675 }
676
677 painter.restore();
678}
679
680void KisColorSelector::drawLightStrip(QPainter& painter, const QRect& rect)
681{
682 qreal penSize = qreal(qMin(QWidget::width(), QWidget::height())) / 200.0;
683 qreal penSizeSmall = penSize / 1.2;
684 QPen selectedPen;
685
688 int rectSize = rect.height();
689
690 painter.save();
691 painter.resetTransform();
692 painter.setRenderHint(QPainter::Antialiasing, true);
693
694 QTransform matrix;
695 matrix.translate(rect.x(), rect.y());
696 matrix.scale(rect.width(), rect.height());
697
698 qreal rectColorLeftX;
699 qreal rectColorWidth;
700
702 rectColorLeftX = 0.6;
703 rectColorWidth = 0.4;
704 } else {
705 rectColorLeftX = 0.0;
706 rectColorWidth = 1.0;
707 }
708
709 if (getNumLightPieces() > 1) {
710 for(int i=0; i<getNumLightPieces(); ++i) {
711 qreal t1 = qreal(i) / qreal(getNumLightPieces());
712 qreal t2 = qreal(i+1) / qreal(getNumLightPieces());
713 qreal light = 1.0 - (qreal(i) / qreal(getNumLightPieces()-1));
714 qreal diff = t2 - t1;// + 0.001;
715
716 QRectF rectColor = QRectF(rectColorLeftX, t1, rectColorWidth, diff);
717 rectColor = matrix.mapRect(rectColor);
718
719 valueScaleColor.setX(light);
720 painter.fillRect(rectColor, valueScaleColor.toQColor());
721
722 if (i == m_selectedLightPiece) {
723 if (light < 0.55) {
724 selectedPen = QPen(QBrush(COLOR_SELECTED_LIGHT), penSize);
725 } else {
726 selectedPen = QPen(QBrush(COLOR_SELECTED_DARK), penSize);
727 }
728
729 painter.setPen(selectedPen);
730 painter.drawRect(rectColor);
731 }
732 }
733 } else {
734 painter.setRenderHint(QPainter::Antialiasing, false);
735
736 for(int i=0; i<rectSize; ++i) {
737 int y = rect.y() + i;
738 qreal light = 1.0 - (qreal(i) / qreal(rectSize-1));
739 valueScaleColor.setX(light);
740 painter.setPen(QPen(QBrush(valueScaleColor.toQColor()), penSize));
741 painter.drawLine(rect.left(), y, rect.right(), y);
742 }
743 }
744
745 // draw color blip
746 painter.setRenderHint(QPainter::Antialiasing, false);
747 // draw position of fg color value on the strip
748 qreal fgColorValue = 1.0 - m_fgColor.getX();
749
750 int y = rect.y() + int(rectSize * fgColorValue);
751 painter.setPen(QPen(QBrush(COLOR_SELECTED_LIGHT), penSizeSmall));
752 painter.drawLine(rect.left(), y, rect.right(), y);
753 painter.setPen(QPen(QBrush(COLOR_SELECTED_DARK), penSizeSmall));
754 painter.drawLine(rect.left(), y+2*penSizeSmall, rect.right(), y+2*penSizeSmall);
755 // draw color blip
756
758 painter.setRenderHint(QPainter::Antialiasing, true);
759
760 int valueScalePieces = getNumLightPieces();
761 if (getNumLightPieces() == 1) {
762 valueScalePieces = 11;
763 }
764
765 QFont font = painter.font();
766 QFontMetrics fm = painter.fontMetrics();
767 int retries = 10; // limit number of font size searches to prevent endless cycles
768 while ((retries > 0) && (fm.boundingRect("100%").width() > rect.width()*rectColorLeftX)) {
769 font.setPointSize(font.pointSize() - 1);
770 painter.setFont(font);
771 fm = painter.fontMetrics();
772 retries--;
773 }
774
775 for(int i=0; i<valueScalePieces; ++i) {
776 qreal t1 = qreal(i) / qreal(valueScalePieces);
777 qreal t2 = qreal(i+1) / qreal(valueScalePieces);
778 qreal light = 1.0 - (qreal(i) / qreal(valueScalePieces-1));
779 qreal diff = t2 - t1;// + 0.001;
780
781 grayScaleColor.setX(light);
782
783 QRectF rectValue = QRectF(0.0, t1, rectColorLeftX, diff);
784 rectValue = matrix.mapRect(rectValue);
785
786 painter.fillRect(rectValue, grayScaleColor.toQColor());
787
788 // if the right font size was not found in time,
789 // skip drawing the numbers
790 if (retries > 0) {
791 int valueNumber = 0;
793 valueNumber = 100 - round(pow(pow(grayScaleColor.getX(), m_lumaGamma), 1.0/2.2)*100);
794 } else {
795 valueNumber = 100 - grayScaleColor.getX()*100;
796 }
797
798 if (valueNumber < 55) {
799 painter.setPen(QPen(QBrush(COLOR_DARK), penSize));
800 } else {
801 painter.setPen(QPen(QBrush(COLOR_LIGHT), penSize));
802 }
803
804 painter.drawText(rectValue, Qt::AlignRight|Qt::AlignBottom, QString("%1%").arg(QString::number(valueNumber)));
805 }
806 }
807 }
808
809 painter.restore();
810}
811
812void KisColorSelector::drawBlip(QPainter& painter, const QRect& rect)
813{
814 painter.save();
815
816 painter.setRenderHint(QPainter::Antialiasing, true);
817 painter.resetTransform();
818 painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2);
819 painter.scale(rect.width()/2, rect.height()/2);
820
821 QPointF fgColorPos = mapColorToUnit(m_fgColor);
822
823#ifdef DEBUG_ARC_SELECTOR
824 dbgPlugins << "KisColorSelector::drawBlip: "
825 << "colorPoint H:" << m_fgColor.getH() << " S:" << m_fgColor.getS()
826 << "-> coord X:" << fgColorPos.x() << " Y:" << fgColorPos.y();
827#endif
828
829 painter.setPen(QPen(QBrush(COLOR_SELECTED_DARK), 0.01));
830 painter.drawEllipse(fgColorPos, 0.05, 0.05);
831
832 painter.setPen(QPen(QBrush(COLOR_SELECTED_LIGHT), 0.01));
833 painter.drawEllipse(fgColorPos, 0.04, 0.04);
834
835 painter.restore();
836}
837
838void KisColorSelector::drawGamutMaskShape(QPainter &painter, const QRect &rect)
839{
840 painter.save();
841 painter.setRenderHint(QPainter::Antialiasing, true);
842
843 painter.resetTransform();
844 painter.translate(rect.width()/2, rect.height()/2);
845 painter.scale(rect.width()/2, rect.height()/2);
846
847 painter.setPen(Qt::NoPen);
848 painter.setBrush(COLOR_MASK_FILL);
849
850 painter.drawEllipse(QPointF(0,0), 1.0, 1.0);
851
852 painter.setWorldTransform(m_gamutMaskViewTransform);
853
854 painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
856
857 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
858 m_currentGamutMask->paintStroke(painter, m_maskPreviewActive);
859
860 painter.restore();
861}
862
863void KisColorSelector::drawColorPreview(QPainter &painter, const QRect &rect)
864{
865 painter.save();
866 painter.setRenderHint(QPainter::Antialiasing, true);
867
868 painter.fillRect(rect, m_fgColor.toQColor());
869
870 int bgSide = qMin(rect.width()*0.15,rect.height()*0.15);
871
872 if (m_showBgColor) {
873 QPointF bgPolyPoints[3] = {
874 QPointF(rect.width(), rect.height()),
875 QPointF(rect.width()-bgSide, rect.height()),
876 QPointF(rect.width(), rect.height()-bgSide)
877 };
878
879 painter.setBrush(m_bgColor.toQColor());
880 painter.setPen(m_bgColor.toQColor());
881 painter.drawPolygon(bgPolyPoints, 3);
882 }
883
884 painter.restore();
885}
886
887void KisColorSelector::paintEvent(QPaintEvent* /*event*/)
888{
889 QPainter wdgPainter(this);
890
891 // draw the fg and bg color previews
893 m_colorPreviewBuffer.fill(Qt::transparent);
894 QPainter colorPreviewPainter(&m_colorPreviewBuffer);
895 drawColorPreview(colorPreviewPainter, m_widgetArea);
896
897 m_isDirtyColorPreview = false;
898 }
899 wdgPainter.drawImage(m_widgetArea, m_colorPreviewBuffer);
900 // draw the fg and bg color previews
901
902 // draw the wheel
903 if (m_isDirtyWheel) {
904 m_renderBuffer.fill(Qt::transparent);
905 QPainter wheelPainter(&m_renderBuffer);
906
907 for(int i=0; i<m_colorRings.size(); ++i)
908 drawRing(wheelPainter, m_colorRings[i], m_renderArea);
909
910 m_isDirtyWheel = false;
911 }
912 wdgPainter.drawImage(m_renderArea, m_renderBuffer);
913 // draw the wheel
914
915 // draw the mask either in continuous mode or in discrete mode when enforcing is turned off
916 // if enforcing is turned on in discrete mode,
917 // drawRing function takes care of delineating the mask swatches
918 if (m_gamutMaskOn
919 && ((getNumPieces() == 1) || (!m_enforceGamutMask))) {
920
921 if (m_isDirtyGamutMask) {
922 m_maskBuffer.fill(Qt::transparent);
923 QPainter maskPainter(&m_maskBuffer);
924 drawGamutMaskShape(maskPainter, m_renderArea);
925
926 m_isDirtyGamutMask = false;
927 }
928 wdgPainter.drawImage(m_renderArea, m_maskBuffer);
929 }
930 // draw gamut mask
931
932 drawOutline(wdgPainter, m_renderArea);
933
934 // draw light strip
936 m_lightStripBuffer.fill(Qt::transparent);
937 QPainter lightStripPainter(&m_lightStripBuffer);
938
939 drawLightStrip(lightStripPainter, m_lightStripArea);
940 m_isDirtyLightStrip = false;
941 }
942 wdgPainter.drawImage(m_lightStripArea, m_lightStripBuffer);
943 // draw light strip
944
945 drawBlip(wdgPainter, m_renderArea);
946}
947
948void KisColorSelector::mousePressEvent(QMouseEvent* event)
949{
950 m_widgetUpdatesSelf = true;
951#ifdef DEBUG_ARC_SELECTOR
952 dbgPlugins << "KisColorSelector::mousePressEvent: m_widgetUpdatesSelf = true";
953#endif
954
955 m_clickPos = mapCoordToUnit(event->localPos(), m_renderArea);
956 m_mouseMoved = false;
957 m_pressedButtons = event->buttons();
959 Acs::ColorRole colorRole = Acs::buttonsToRole(Qt::NoButton, m_pressedButtons);
960
961 qint8 clickedLightPiece = getLightIndex(event->localPos());
962
963 if (clickedLightPiece >= 0) {
964 setLight(getLight(event->localPos()));
965 m_selectedLightPiece = clickedLightPiece;
967 m_mouseMoved = true;
968 }
969 else if (m_clickedRing >= 0) {
970 if (getNumPieces() == 1) {
972
974 color.setHSX(angle.scaled(0.0, 1.0)
977 );
978
979#ifdef DEBUG_ARC_SELECTOR
980 dbgPlugins << "KisColorSelector::mousePressEvent: picked color: "
981 << "H:" << color.getH()
982 << "S:" << color.getS()
983 << "X:" << color.getX();
984#endif
985
986 if ((!m_enforceGamutMask) || colorIsClear(color)) {
987 m_selectedColor.setHSX(color.getH(), color.getS(), color.getX());
990 m_mouseMoved = true;
991 update();
992 }
993 }
994 }
995}
996
997void KisColorSelector::mouseMoveEvent(QMouseEvent* event)
998{
999 QPointF dragPos = mapCoordToUnit(event->localPos(), m_renderArea);
1000 qint8 clickedLightPiece = getLightIndex(event->localPos());
1001 Acs::ColorRole colorRole = Acs::buttonsToRole(Qt::NoButton, m_pressedButtons);
1002
1003 if (clickedLightPiece >= 0) {
1004 setLight(getLight(event->localPos()));
1005 m_selectedLightPiece = clickedLightPiece;
1007 }
1008
1009 if (m_clickedRing < 0)
1010 return;
1011
1012 if (getNumPieces() == 1) {
1013 Radian angle = mapCoordToAngle(dragPos.x(), dragPos.y());
1015 color.setHSX(angle.scaled(0.0, 1.0)
1018 );
1019
1020 if ((!m_enforceGamutMask) || colorIsClear(color)) {
1021 m_selectedColor.setHSX(color.getH(), color.getS(), color.getX());
1023 }
1024 }
1025
1026 update();
1027}
1028
1029void KisColorSelector::mouseReleaseEvent(QMouseEvent* /*event*/)
1030{
1031 Acs::ColorRole colorRole = Acs::buttonsToRole(Qt::NoButton, m_pressedButtons);
1032
1033 if (!m_mouseMoved && m_clickedRing >= 0) {
1034 Radian angle = mapCoordToAngle(m_clickPos.x(), m_clickPos.y());
1036
1037 qint8 hueIndex = getHueIndex(angle);
1038
1039 if (getNumPieces() > 1) {
1040 color.setH(getHue(hueIndex));
1041 } else {
1042 color.setH(angle.scaled(0.0, 1.0));
1043 }
1044
1045 color.setS(getSaturation(m_clickedRing));
1046 color.setX(m_selectedColor.getX());
1047
1048 if ((!m_enforceGamutMask) || colorIsClear(color)) {
1049 m_selectedColor.setHSX(color.getH(), color.getS(), color.getX());
1050 m_selectedPiece = hueIndex;
1053 }
1054 }
1055 else if (m_mouseMoved)
1057
1058 m_clickedRing = -1;
1059
1060 m_widgetUpdatesSelf = false;
1061#ifdef DEBUG_ARC_SELECTOR
1062 dbgPlugins << "KisColorSelector::ReleasePressEvent: m_widgetUpdatesSelf = false";
1063#endif
1064
1065 update();
1066}
1067
1068void KisColorSelector::resizeEvent(QResizeEvent* /*event*/)
1069{
1071}
1072
1074{
1075 m_widgetUpdatesSelf = false;
1076#ifdef DEBUG_ARC_SELECTOR
1077 dbgPlugins << "KisColorSelector::leaveEvent: m_widgetUpdatesSelf = false";
1078#endif
1079}
1080
1082{
1083 KisConfig cfg(false);
1084 cfg.writeEntry("ArtColorSel.ColorSpace" , qint32(m_colorSpace));
1085
1086 cfg.writeEntry("ArtColorSel.lumaR", qreal(m_lumaR));
1087 cfg.writeEntry("ArtColorSel.lumaG", qreal(m_lumaG));
1088 cfg.writeEntry("ArtColorSel.lumaB", qreal(m_lumaB));
1089 cfg.writeEntry("ArtColorSel.lumaGamma", qreal(m_lumaGamma));
1090
1091 cfg.writeEntry("ArtColorSel.NumRings" , m_colorRings.size());
1092 cfg.writeEntry("ArtColorSel.RingPieces" , qint32(m_numPieces));
1093 cfg.writeEntry("ArtColorSel.LightPieces", qint32(m_numLightPieces));
1094
1095 cfg.writeEntry("ArtColorSel.InversedSaturation", m_inverseSaturation);
1096 cfg.writeEntry("ArtColorSel.Light" , m_selectedColor.getX());
1097
1098 cfg.writeEntry("ArtColorSel.SelColorH", m_selectedColor.getH());
1099 cfg.writeEntry("ArtColorSel.SelColorS", m_selectedColor.getS());
1100 cfg.writeEntry("ArtColorSel.SelColorX", m_selectedColor.getX());
1101
1102 cfg.writeEntry("ArtColorSel.defaultHueSteps", quint32(m_defaultHueSteps));
1103 cfg.writeEntry("ArtColorSel.defaultSaturationSteps", quint32(m_defaultSaturationSteps));
1104 cfg.writeEntry("ArtColorSel.defaultValueScaleSteps", quint32(m_defaultValueScaleSteps));
1105
1106 cfg.writeEntry("ArtColorSel.showBgColor", m_showBgColor);
1107 cfg.writeEntry("ArtColorSel.showValueScale", m_showValueScaleNumbers);
1108 cfg.writeEntry("ArtColorSel.enforceGamutMask", m_enforceGamutMask);
1109}
1110
1112{
1113 KisConfig cfg(true);
1114
1115 m_defaultHueSteps = cfg.readEntry("ArtColorSel.defaultHueSteps", DEFAULT_HUE_STEPS);
1116 m_defaultSaturationSteps = cfg.readEntry("ArtColorSel.defaultSaturationSteps", DEFAULT_SATURATION_STEPS);
1117 m_defaultValueScaleSteps = cfg.readEntry("ArtColorSel.defaultValueScaleSteps", DEFAULT_VALUE_SCALE_STEPS);
1118
1119 setNumLightPieces(cfg.readEntry("ArtColorSel.LightPieces", DEFAULT_VALUE_SCALE_STEPS));
1120
1121 KisColor::Type colorSpace = KisColor::Type(cfg.readEntry<qint32>("ArtColorSel.ColorSpace" , KisColor::HSY));
1122
1124
1125 setLumaCoefficients(cfg.readEntry("ArtColorSel.lumaR", DEFAULT_LUMA_R),
1126 cfg.readEntry("ArtColorSel.lumaG", DEFAULT_LUMA_G),
1127 cfg.readEntry("ArtColorSel.lumaB", DEFAULT_LUMA_B),
1128 cfg.readEntry("ArtColorSel.lumaGamma", DEFAULT_LUMA_GAMMA));
1129
1130 m_selectedColor.setH(cfg.readEntry<qreal>("ArtColorSel.SelColorH", 0.0));
1131 m_selectedColor.setS(cfg.readEntry<qreal>("ArtColorSel.SelColorS", 0.0));
1132 m_selectedColor.setX(cfg.readEntry<qreal>("ArtColorSel.SelColorX", 0.0));
1133
1134 setInverseSaturation(cfg.readEntry<bool>("ArtColorSel.InversedSaturation", false));
1135 setLight(cfg.readEntry<qreal>("ArtColorSel.Light", 0.5f));
1136
1137 setNumRings(cfg.readEntry("ArtColorSel.NumRings", DEFAULT_SATURATION_STEPS));
1138 setNumPieces(cfg.readEntry("ArtColorSel.RingPieces", DEFAULT_HUE_STEPS));
1139
1140 m_showBgColor = cfg.readEntry("ArtColorSel.showBgColor", true);
1141 m_showValueScaleNumbers = cfg.readEntry("ArtColorSel.showValueScale", false);
1142 m_enforceGamutMask = cfg.readEntry("ArtColorSel.enforceGamutMask", false);
1143
1145 update();
1146}
1147
1149{
1150 num = qBound(MIN_NUM_HUE_PIECES, num, MAX_NUM_HUE_PIECES);
1151 m_defaultHueSteps = num;
1152}
1153
1159
1165
1167{
1169 m_isDirtyColorPreview = true;
1170 update();
1171}
1172
1179
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
float value(const T *src, size_t ch)
const KoColorSpace * colorSpace() const
qreal getLight(const QPointF &pt) const
void setColorConverter(KisDisplayColorConverter *colorConverter)
qint8 getLightIndex(const QPointF &pt) const
QPointF mapCoordToUnit(const QPointF &pt, const QRectF &viewRect) const
qint8 getSaturationIndex(const QPointF &pt) const
KisSignalCompressorWithParam< QPair< KisColor, Acs::ColorRole > > ColorCompressorType
void setDefaultSaturationSteps(int num)
void setNumLightPieces(int num)
void drawRing(QPainter &painter, ColorRing &wheel, const QRect &rect)
Radian mapCoordToAngle(qreal x, qreal y) const
qint32 getNumLightPieces() const
void setColorSpace(KisColor::Type type)
QVector< ColorRing > m_colorRings
void drawBlip(QPainter &painter, const QRect &rect)
QPointF mapCoordToView(const QPointF &pt, const QRectF &viewRect) const
void sigFgColorChanged(const KisColor &color)
void mouseMoveEvent(QMouseEvent *) override
void recalculateRings(quint8 numRings, quint8 numPieces)
void setInverseSaturation(bool inverse)
KoGamutMaskSP gamutMask()
KoGamutMaskSP m_currentGamutMask
Qt::MouseButtons m_pressedButtons
qreal getSaturation(int saturationIdx) const
KisColorSelector(KisColorSelectorConfiguration conf, QWidget *parent=0)
void recalculateAreas(quint8 numLightPieces)
void leaveEvent(QEvent *e) override
void setEnforceGamutMask(bool enforce)
void setGamutMaskOn(bool gamutMaskOn)
qreal getHue(int hueIdx, Radian shift=0.0f) const
void resizeEvent(QResizeEvent *) override
QScopedPointer< ColorCompressorType > m_updateColorCompressor
void setDefaultHueSteps(int num)
void setDefaultValueScaleSteps(int num)
void requestUpdateColorAndPreview(const KisColor &color, Acs::ColorRole role)
QPointF mapHueToAngle(qreal hue) const
void sigBgColorChanged(const KisColor &color)
QTransform m_gamutMaskViewTransform
void setNumPieces(int num)
void setLumaCoefficients(qreal lR, qreal lG, qreal lB, qreal lGamma)
qint32 getNumRings() const
KisColor::Type m_colorSpace
void mousePressEvent(QMouseEvent *) override
bool colorIsClear(const KisColor &color)
void selectColor(const KisColor &color)
void setFgColor(const KoColor &fgColor)
void slotUpdateColorAndPreview(QPair< KisColor, Acs::ColorRole > color)
void drawGamutMaskShape(QPainter &painter, const QRect &rect)
QPointF mapColorToUnit(const KisColor &color, bool invertSaturation=true) const
void setBgColor(const KoColor &bgColor)
KisDisplayColorConverter * m_colorConverter
void mouseReleaseEvent(QMouseEvent *) override
void setGamutMask(KoGamutMaskSP gamutMask)
void setShowValueScaleNumbers(bool value)
void drawOutline(QPainter &painter, const QRect &rect)
void setLight(qreal light=0.0f)
void paintEvent(QPaintEvent *) override
void setShowBgColor(bool value)
void createRing(ColorRing &wheel, quint8 numPieces, qreal innerRadius, qreal outerRadius)
qint8 getHueIndex(Radian hue) const
void drawColorPreview(QPainter &painter, const QRect &rect)
void drawLightStrip(QPainter &painter, const QRect &rect)
qint32 getNumPieces() const
KisRadian< qreal > Radian
void setS(qreal v)
Definition kis_color.h:58
void setHSX(qreal h, qreal s, qreal x)
Definition kis_color.h:65
qreal getS() const
Definition kis_color.h:50
void setH(qreal v)
Definition kis_color.h:57
QColor toQColor() const
Definition kis_color.cpp:95
void setX(qreal v)
Definition kis_color.h:59
qreal getH() const
Definition kis_color.h:49
qreal getX() const
Definition kis_color.h:51
void writeEntry(const QString &name, const T &value)
Definition kis_config.h:779
T readEntry(const QString &name, const T &defaultValue=T())
Definition kis_config.h:789
TReal scaled(const TReal &min, const TReal &max) const
Definition kis_radian.h:88
static const int MIN_NUM_SATURATION_RINGS
static const qreal DEFAULT_LUMA_R
static const int MAX_NUM_HUE_PIECES
static const int DEFAULT_VALUE_SCALE_STEPS
static const QColor COLOR_DARK
static const int MAX_NUM_LIGHT_PIECES
static const int MIN_NUM_HUE_PIECES
static const QColor COLOR_MASK_CLEAR
static const int DEFAULT_SATURATION_STEPS
static const QColor COLOR_SELECTED_LIGHT
static const QColor COLOR_LIGHT
static const int MAX_NUM_SATURATION_RINGS
static const qreal DEFAULT_LUMA_GAMMA
static const qreal DEFAULT_LUMA_B
static const int DEFAULT_HUE_STEPS
static const QColor COLOR_MASK_FILL
static const int MIN_NUM_LIGHT_PIECES
static const QColor COLOR_NORMAL_OUTLINE
static const qreal DEFAULT_LUMA_G
static const QColor COLOR_SELECTED_DARK
#define dbgPlugins
Definition kis_debug.h:51
#define M_PI
Definition kis_global.h:111
#define PI2
Definition kis_radian.h:13
ColorRole buttonsToRole(Qt::MouseButton button, Qt::MouseButtons buttons)
@ Foreground
bool update(QSpinBox *spinBox)
QVector< QPainterPath > pieced