6#include "chromahuediagram.h"
8#include "chromahuediagram_p.h"
10#include "abstractdiagram.h"
11#include "asyncimageprovider.h"
12#include "chromahueimageparameters.h"
13#include "cielchd50values.h"
14#include "colorwheelimage.h"
15#include "constpropagatingrawpointer.h"
16#include "constpropagatinguniquepointer.h"
18#include "helperconstants.h"
19#include "helperconversion.h"
21#include "polarpointf.h"
22#include "rgbcolorspace.h"
28#include <qnamespace.h>
32#include <qsharedpointer.h>
44 , d_pointer(new ChromaHueDiagramPrivate(this, colorSpace))
48 d_pointer->m_rgbColorSpace = colorSpace;
66 connect(&d_pointer->m_chromaHueImage,
67 &AsyncImageProvider<ChromaHueImageParameters>::interlacingPassCompleted,
87 : m_currentColor{0, 0, 0}
88 , m_wheelImage(colorSpace)
115 const bool isWithinCircle =
116 d_pointer->isWidgetPixelPositionWithinMouseSensibleCircle(
event->pos());
117 if (isWithinCircle) {
126 d_pointer->m_isMouseEventActive =
true;
132 d_pointer->setColorFromWidgetPixelPosition(
event->pos());
167 if (d_pointer->m_isMouseEventActive) {
169 const cmsCIELab cielabD50 =
170 d_pointer->fromWidgetPixelPositionToLab(
event->pos());
171 const bool isWithinCircle =
172 d_pointer->isWidgetPixelPositionWithinMouseSensibleCircle(
174 if (isWithinCircle && d_pointer->m_rgbColorSpace->isCielabD50InGamut(cielabD50)) {
179 d_pointer->setColorFromWidgetPixelPosition(
event->pos());
212 if (d_pointer->m_isMouseEventActive) {
215 d_pointer->m_isMouseEventActive =
false;
216 d_pointer->setColorFromWidgetPixelPosition(
event->pos());
243 const bool isWithinCircle =
244 d_pointer->isWidgetPixelPositionWithinMouseSensibleCircle(
245 event->position().toPoint());
249 (!d_pointer->m_isMouseEventActive)
252 && (
event->angleDelta().y() != 0)
265 LchDouble newColor = d_pointer->m_currentColor;
266 newColor.
h += standardWheelStepCount(
event) * singleStepHue;
268 d_pointer->m_rgbColorSpace->reduceCielchD50ChromaToFitIntoGamut(newColor));
308 switch (
event->key()) {
310 newColor.
c += singleStepChroma;
313 newColor.
c -= singleStepChroma;
316 newColor.
h += singleStepHue;
319 newColor.
h -= singleStepHue;
322 newColor.
c += pageStepChroma;
325 newColor.
c -= pageStepChroma;
328 newColor.
h += pageStepHue;
331 newColor.
h -= pageStepHue;
350 if (newColor.
c < 0) {
356 newColor = d_pointer->m_rgbColorSpace->reduceCielchD50ChromaToFitIntoGamut(newColor);
388 return QSize(mySize, mySize);
395 return d_pointer->m_currentColor;
407 const LchDouble oldColor = d_pointer->m_currentColor;
409 d_pointer->m_currentColor = newCurrentColor;
412 if (d_pointer->m_currentColor.
l != oldColor.
l) {
413 const qreal temp = qBound(
static_cast<qreal
>(0),
414 d_pointer->m_currentColor.
l,
415 static_cast<qreal
>(100));
416 d_pointer->m_chromaHueImageParameters.lightness = temp;
438QPointF ChromaHueDiagramPrivate::diagramCenter()
const
440 const qreal tempOffset{diagramOffset()};
441 return QPointF(tempOffset, tempOffset);
453qreal ChromaHueDiagramPrivate::diagramOffset()
const
455 return q_pointer->maximumWidgetSquareSize() / 2.0;
469 d_pointer->m_chromaHueImageParameters.imageSizePhysical =
492QPointF ChromaHueDiagramPrivate::widgetCoordinatesFromCurrentColor()
const
494 const qreal scaleFactor =
495 (q_pointer->maximumWidgetSquareSize() - 2.0 * diagramBorder())
496 / (2.0 * m_rgbColorSpace->profileMaximumCielchD50Chroma());
498 PolarPointF(m_currentColor.
c, m_currentColor.
h).toCartesian();
501 currentColor.
x() * scaleFactor + diagramOffset(),
503 diagramOffset() - currentColor.
y() * scaleFactor);
516cmsCIELab ChromaHueDiagramPrivate::fromWidgetPixelPositionToLab(
const QPoint position)
const
518 const qreal scaleFactor =
519 (2.0 * m_rgbColorSpace->profileMaximumCielchD50Chroma())
520 / (q_pointer->maximumWidgetSquareSize() - 2.0 * diagramBorder());
525 constexpr qreal pixelValueShift = 0.5;
527 lab.L = m_currentColor.
l;
529 (position.
x() + pixelValueShift - diagramOffset()) * scaleFactor;
531 (position.
y() + pixelValueShift - diagramOffset()) * scaleFactor * (-1);
564void ChromaHueDiagramPrivate::setColorFromWidgetPixelPosition(
const QPoint position)
566 const cmsCIELab lab = fromWidgetPixelPositionToLab(position);
568 m_rgbColorSpace->reduceCielchD50ChromaToFitIntoGamut(toLchDouble(lab));
569 q_pointer->setCurrentColor(myColor);
582bool ChromaHueDiagramPrivate::isWidgetPixelPositionWithinMouseSensibleCircle(
const QPoint position)
const
584 const qreal radius = PolarPointF(
596 const qreal diagramCircleRadius =
597 q_pointer->maximumWidgetSquareSize() / 2.0 - diagramBorder();
599 return (radius <= diagramCircleRadius);
666 const QPointF widgetCoordinatesFromCurrentColor
667 {d_pointer->widgetCoordinatesFromCurrentColor()};
673 d_pointer->m_chromaHueImageParameters.borderPhysical =
678 d_pointer->m_chromaHueImageParameters.imageSizePhysical =
681 const qreal temp = qBound(
static_cast<qreal
>(0),
682 d_pointer->m_currentColor.
l,
683 static_cast<qreal
>(100));
684 d_pointer->m_chromaHueImageParameters.lightness = temp;
685 d_pointer->m_chromaHueImageParameters.devicePixelRatioF =
687 d_pointer->m_chromaHueImageParameters.rgbColorSpace =
688 d_pointer->m_rgbColorSpace;
689 d_pointer->m_chromaHueImage.setImageParameters(
690 d_pointer->m_chromaHueImageParameters);
691 d_pointer->m_chromaHueImage.refreshAsync();
692 const qreal circleRadius =
696 bufferPainter.
setBrush(d_pointer->m_chromaHueImage.getCache());
710 d_pointer->m_wheelImage.setBorder(
714 d_pointer->m_wheelImage.setWheelThickness(
718 d_pointer->m_wheelImage.getImage()
723 if (d_pointer->m_isMouseEventActive) {
729 d_pointer->m_currentColor.
h)
731 myHandleInner.
ry() *= -1;
732 myHandleInner += d_pointer->diagramCenter();
734 PolarPointF(radius, d_pointer->m_currentColor.
h).toCartesian();
735 myHandleOuter.
ry() *= -1;
736 myHandleOuter += d_pointer->diagramCenter();
745 bufferPainter.
setPen(pen);
747 bufferPainter.
drawLine(myHandleInner, myHandleOuter);
756 bufferPainter.
setPen(pen);
757 bufferPainter.
setBrush(transparentBrush);
758 bufferPainter.
drawEllipse(widgetCoordinatesFromCurrentColor,
762 const auto diagramOffset = d_pointer->diagramOffset();
763 const QPointF diagramCartesianCoordinatesFromCurrentColor(
765 widgetCoordinatesFromCurrentColor.x() - diagramOffset,
767 (widgetCoordinatesFromCurrentColor.y() - diagramOffset) * (-1));
768 PolarPointF diagramPolarCoordinatesFromCurrentColor(
769 diagramCartesianCoordinatesFromCurrentColor);
773 diagramPolarCoordinatesFromCurrentColor.radius() -
handleRadius();
774 if (lineRadius > 0) {
775 QPointF lineEndWidgetCoordinates =
780 diagramPolarCoordinatesFromCurrentColor.angleDegree()
783 lineEndWidgetCoordinates.
ry() *= (-1);
784 lineEndWidgetCoordinates += d_pointer->diagramCenter();
787 d_pointer->diagramCenter(),
789 lineEndWidgetCoordinates);
826 bufferPainter.
setPen(pen);
827 bufferPainter.
setBrush(transparentBrush);
830 d_pointer->diagramCenter(),
849int ChromaHueDiagramPrivate::diagramBorder()
const
853 q_pointer->spaceForFocusIndicator()
855 + q_pointer->gradientThickness()
857 + 2 * q_pointer->handleOutlineThickness();
Base class for LCH diagrams.
qreal handleRadius() const
The radius of a circular handle.
QColor handleColorFromBackgroundLightness(qreal lightness) const
An appropriate color for a handle, depending on the background lightness.
int gradientMinimumLength() const
The minimum length of a color gradient.
int spaceForFocusIndicator() const
The empty space around diagrams reserved for the focus indicator.
void callUpdate()
An alternative to QWidget::update().
int handleOutlineThickness() const
The outline thickness of a handle.
int gradientThickness() const
The thickness of a color gradient.
QColor focusIndicatorColor() const
The color for painting focus indicators.
int maximumPhysicalSquareSize() const
The maximum possible size of a square within the widget, measured in physical pixels.
qreal maximumWidgetSquareSize() const
The maximum possible size of a square within the widget, measured in device-independent pixels.
A widget for selecting chroma and hue in LCH color space.
virtual void mouseMoveEvent(QMouseEvent *event) override
React on a mouse move event.
virtual void wheelEvent(QWheelEvent *event) override
React on a mouse wheel event.
void setCurrentColor(const PerceptualColor::LchDouble &newCurrentColor)
Setter for the currentColor property.
LchDouble currentColor
Currently selected color.
virtual QSize minimumSizeHint() const override
Recommended minimum size for the widget.
void currentColorChanged(const PerceptualColor::LchDouble &newCurrentColor)
Notify signal for property currentColor.
virtual void keyPressEvent(QKeyEvent *event) override
React on key press events.
virtual ~ChromaHueDiagram() noexcept override
Default destructor.
virtual void paintEvent(QPaintEvent *event) override
Paint the widget.
virtual void mousePressEvent(QMouseEvent *event) override
React on a mouse press event.
virtual void mouseReleaseEvent(QMouseEvent *event) override
React on a mouse release event.
virtual void resizeEvent(QResizeEvent *event) override
React on a resize event.
Q_INVOKABLE ChromaHueDiagram(const QSharedPointer< PerceptualColor::RgbColorSpace > &colorSpace, QWidget *parent=nullptr)
The constructor.
virtual QSize sizeHint() const override
Recommended size for the widget.
The namespace of this library.
void fill(Qt::GlobalColor color)
void setDevicePixelRatio(qreal scaleFactor)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
qreal devicePixelRatioF() const const
void drawEllipse(const QPoint ¢er, int rx, int ry)
void drawImage(const QPoint &point, const QImage &image)
void drawLine(const QLine &line)
void setBrush(Qt::BrushStyle style)
void setPen(Qt::PenStyle style)
void setRenderHint(RenderHint hint, bool on)
void setCapStyle(Qt::PenCapStyle style)
void setColor(const QColor &color)
A LCH color (Oklch, CielchD50, CielchD65…)
bool hasSameCoordinates(const LchDouble &other) const
Compares coordinates with another object.
double l
Lightness, mesured in percent.
double h
Hue, measured in degree.