21 , m_constrainPositions(true)
22 , m_selectedHandle(-1)
25 setFocusPolicy(Qt::WheelFocus);
26 setMouseTracking(
true);
34 Q_ASSERT(handleIndex >= 0 && handleIndex <
m_handles.size());
41 Q_ASSERT(handleIndex >= 0 && handleIndex <
m_handles.size());
48 Q_ASSERT(handleIndex >= 0 && handleIndex <
m_handles.size());
58 newPosition = qBound(0.0, newPosition, 1.0);
61 if (newPosition ==
m_handles[handleIndex].position) {
65 m_handles[handleIndex].position = newPosition;
73 Q_ASSERT(handleIndex >= 0 && handleIndex <
m_handles.size());
75 if (newColor ==
m_handles[handleIndex].color) {
104 std::sort(sortedHandles_.begin(), sortedHandles_.end(),
107 return qFuzzyCompare(lhs.position, rhs.position) ? lhs.index < rhs.index : lhs.position < rhs.position;
110 return sortedHandles_;
116 int handleIndex = -1;
118 if (position <= sortedHandles_.first().position) {
119 handleIndex = sortedHandles_.first().index;
120 }
else if (position >= sortedHandles_.last().position) {
121 handleIndex = sortedHandles_.last().index;
123 for (
int i = 0; i < sortedHandles_.size() - 1; ++i) {
124 if (position >= sortedHandles_[i + 1].position) {
127 const qreal middlePoint = (sortedHandles_[i].position + sortedHandles_[i + 1].position) / 2.0;
128 handleIndex = position <= middlePoint ? sortedHandles_[i].index : sortedHandles_[i + 1].index;
139 return static_cast<qreal
>(x - gradientRect_.left()) /
static_cast<qreal
>(gradientRect_.width());
150 return static_cast<int>(position *
static_cast<qreal
>(gradientRect_.width())) + gradientRect_.left();
155 if (direction == 0) {
158 if (modifiers & Qt::ControlModifier) {
171 painter.setRenderHint(QPainter::Antialiasing,
false);
174 const QPolygon shape({
175 {
rect.left() + halfHandleWidth,
rect.top()},
176 {
rect.right() + 1,
rect.top() + halfHandleWidth},
177 {
rect.right() + 1,
rect.bottom() + 1},
179 {
rect.left(),
rect.top() + halfHandleWidth}
181 painter.setPen(Qt::NoPen);
184 isSelected && hasFocus()
188 brushColor.setAlpha(64);
190 painter.setBrush(brushColor);
191 painter.drawPolygon(shape);
194 const QPolygon shape({
195 {
rect.left() + halfHandleWidth,
rect.top()},
196 {
rect.right(),
rect.top() + halfHandleWidth},
199 {
rect.left(),
rect.top() + halfHandleWidth}
202 QColor penColor = isHovered && isEnabled() ?
palette().highlight().color() :
palette().text().color();
204 penColor.setAlpha(64);
206 painter.setPen(penColor);
207 painter.setBrush(Qt::NoBrush);
208 painter.drawPolygon(shape);
216 QPainter painter(
this);
223 QColor borderColor =
palette().text().color();
224 borderColor.setAlpha(64);
225 painter.setPen(borderColor);
226 painter.drawRect(gradientRect_.adjusted(0, 0, -1, -1));
231 for (
const Handle &handle : sortedHandles_) {
232 const int handleX =
static_cast<int>(qRound(handle.position *
static_cast<qreal
>(gradientRect_.width() - 1))) + halfHandleWidth;
244 if (e->button() != Qt::LeftButton) {
251 if (handleIndex != -1) {
272 if (handleIndex != -1) {
284 QWidget::leaveEvent(e);
304 QWidget::keyPressEvent(e);
310 if (e->angleDelta().y() != 0) {
314 QWidget::wheelEvent(e);
331 [
this](
int handleIndex, qreal position)
333 if (handleIndex ==
m_handles.first().index) {
334 Q_EMIT blackPointChanged(position);
335 }
else if (handleIndex ==
m_handles.last().index) {
336 Q_EMIT whitePointChanged(position);
367 newBlackPoint = qBound(0.0, newBlackPoint, 1.0);
368 newWhitePoint = qBound(0.0, newWhitePoint, 1.0);
373 if (newWhitePoint < 1.0) {
397 const int startPos =
static_cast<int>(qRound(sortedHandles_.first().position *
static_cast<qreal
>(gradientImage.width() - 1))) + 1;
398 const int endPos =
static_cast<int>(qRound(sortedHandles_.last().position *
static_cast<qreal
>(gradientImage.width() - 1))) + 1;
399 QRgb *pixel =
reinterpret_cast<QRgb*
>(gradientImage.bits()) + startPos;
400 for (
int x = startPos; x < endPos; ++x, ++pixel) {
401 const qreal t =
static_cast<qreal
>(x - startPos) /
static_cast<qreal
>(endPos - startPos);
403 sortedHandles_.last().color,
404 sortedHandles_.first().color,
416 painter.setRenderHint(QPainter::SmoothPixmapTransform);
418 painter.setOpacity(0.5);
420 QImage gradientImage(
rect.width(), 1, QImage::Format_ARGB32);
421 const int halfGradientHeight =
rect.height() / 2;
425 QRgb *pixel =
reinterpret_cast<QRgb*
>(gradientImage.bits());
426 for (
int x = 0; x < gradientImage.width(); ++x, ++pixel) {
427 const qreal t =
static_cast<qreal
>(x) /
static_cast<qreal
>(gradientImage.width() - 1);
431 painter.drawImage(
rect.adjusted(0, 0, 0, -(halfGradientHeight + (
rect.height() & 1 ? 1 : 0))), gradientImage);
436 const int endPos =
static_cast<int>(qRound(sortedHandles_.first().position *
static_cast<qreal
>(gradientImage.width() - 1)) + 1);
437 QRgb *pixel =
reinterpret_cast<QRgb*
>(gradientImage.bits());
438 for (
int x = 0; x < endPos; ++x, ++pixel) {
439 *pixel = sortedHandles_.first().color.rgba();
444 const int startPos =
static_cast<int>(qRound(sortedHandles_.last().position *
static_cast<qreal
>(gradientImage.width() - 1)) + 1);
445 QRgb *pixel =
reinterpret_cast<QRgb*
>(gradientImage.bits()) + startPos;
446 for (
int x = startPos; x <
rect.width(); ++x, ++pixel) {
447 *pixel = sortedHandles_.last().color.rgba();
450 painter.drawImage(
rect.adjusted(0, halfGradientHeight, 0, 0), gradientImage);
472 Q_ASSERT(handleIndex >= 0 && handleIndex <
m_handles.size());
474 if (handleIndex == 1) {
475 newPosition = qBound(
m_handles.first().position, newPosition,
m_handles.last().position);
476 if (newPosition ==
m_handles[1].position) {
485 if (handleIndex ==
m_handles.first().index) {
487 }
else if (handleIndex ==
m_handles.last().index) {
490 if (newPosition ==
m_handles[handleIndex].position) {
493 m_handles[handleIndex].position = newPosition;
504 newGamma = qBound(0.1, newGamma, 10.0);
522 reset(newBlackPoint, newWhitePoint);
536 const qreal inverseGamma = 1.0 /
m_gamma;
537 const int startPos =
static_cast<int>(qRound(sortedHandles_.first().position *
static_cast<qreal
>(gradientImage.width() - 1)));
538 const int endPos =
static_cast<int>(qRound(sortedHandles_.last().position *
static_cast<qreal
>(gradientImage.width() - 1))) + 1;
539 QRgb *pixel =
reinterpret_cast<QRgb*
>(gradientImage.bits()) + startPos;
540 for (
int x = startPos; x < endPos; ++x, ++pixel) {
541 const qreal t =
static_cast<qreal
>(x - startPos) /
static_cast<qreal
>(endPos - startPos);
543 sortedHandles_.last().color,
544 sortedHandles_.first().color,
545 std::pow(t, inverseGamma)
552 qreal relativePosition;
553 const qreal log1_2 = std::log(0.5);
559 const qreal mappedPosition = std::exp(10.0 * log1_2);
560 relativePosition = (std::exp(
m_gamma * log1_2) - mappedPosition) / (1.0 - mappedPosition * 2.0);
562 const qreal mappedPosition = std::exp(0.1 * log1_2);
563 relativePosition = (std::exp(
m_gamma * log1_2) + mappedPosition - 1.0) / (mappedPosition * 2.0 - 1.0);
571 const qreal log1_2 = std::log(0.5);
576 if (relativePosition < 0.5) {
577 const qreal mappedPosition = std::exp(10.0 * log1_2);
578 return std::log(mappedPosition + relativePosition - relativePosition * mappedPosition * 2.0) / log1_2;
580 const qreal mappedPosition = std::exp(0.1 * log1_2);
581 return std::log(1 - (mappedPosition + relativePosition) + relativePosition * mappedPosition * 2.0) / log1_2;
612 Q_ASSERT(handleIndex >= 0 && handleIndex <
m_handles.size());
613 reset(newPosition, newPosition);
618 reset(newBlackPoint, newBlackPoint);
623 reset(newWhitePoint, newWhitePoint);
628 Q_UNUSED(newWhitePoint);
630 newBlackPoint = qBound(0.0, newBlackPoint, 1.0);
645 reset(newThreshold, newThreshold);
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
A base class for levels slider like widgets: a slider with a gradient and multiple handles.
int m_constrainPositions
This variable indicates if the handles can have unordered positions. If it is set to true then the us...
virtual void setHandlePosition(int handleIndex, qreal newPosition)
Sets the normalized position of the given handle.
void handleIncrementInput(int direction, Qt::KeyboardModifiers modifiers)
static constexpr qreal normalPositionIncrement
void handlePositionChanged(int handleIndex, qreal position)
Signal emitted when the position of a handle changes.
QVector< Handle > sortedHandles() const
Regardless the index of a handle, they can be unordered in terms of the position. This returns a sort...
int xFromPosition(qreal position) const
Given a gradient rect relative position, this function returns the x position in pixels relative to t...
virtual void setHandleColor(int handleIndex, const QColor &newColor)
Sets the color associated with the given handle.
QSize minimumSizeHint() const override
virtual void paintHandle(QPainter &painter, const QRect &rect, const Handle &handle)
Override this function to paint custom handles.
QColor handleColor(int handleIndex) const
Gets the color associated with a given handle.
int closestHandleToPosition(qreal position) const
Given a normalized position, this function returns the closest handle to that position.
void handleColorChanged(int handleIndex, const QColor &color)
Signal emitted when the color associated with a handle changes.
void wheelEvent(QWheelEvent *e) override
static constexpr qreal minimumSpaceBetweenHandles
qreal positionFromX(int x) const
Given a widget-relative x position in pixels, this function returns the normalized position relative ...
void mousePressEvent(QMouseEvent *e) override
static constexpr int handleHeight
virtual void paintGradient(QPainter &painter, const QRect &rect)=0
Derived classes must override this function to draw the gradient inside the given rect....
void keyPressEvent(QKeyEvent *e) override
virtual QRect gradientRect() const
Gets the rect where the gradient will be painted.
KisLevelsSlider(QWidget *parent)
QSize sizeHint() const override
void paintEvent(QPaintEvent *e) override
static constexpr qreal slowPositionIncrement
void mouseMoveEvent(QMouseEvent *e) override
QVector< Handle > m_handles
The collection of handles.
static constexpr int handleWidth
int closestHandleToX(int x) const
Given a widget-relative x position, this function returns the closest handle to that position.
void leaveEvent(QEvent *e) override
qreal handlePosition(int handleIndex) const
Gets the normalized position of a given handle.
KisOutputLevelsSlider(QWidget *parent=nullptr)
KisThresholdSlider(QWidget *parent=nullptr)
void setThreshold(qreal newGamma)
Sets the gamma value.
qreal threshold() const
Get the gamma value.
void paintBottomGradientMiddleSection(QImage &gradientImage, const QVector< Handle > &sortedHandles_) override
This is used to fill the space between the tho handles in the bottom bar of the "gradient"....
void setWhitePoint(qreal newWhitePoint) override
Sets the white point. For this slider the black and the white points are always in the same position,...
void reset(qreal newBlackPoint, qreal newWhitePoint) override
void setHandlePosition(int handleIndex, qreal newPosition) override
Sets the black point. For this slider the black and the white points are always in the same position,...
void paintHandle(QPainter &painter, const QRect &rect, const Handle &handle) override
Override this function to paint custom handles.
void setBlackPoint(qreal newBlackPoint) override
Sets the black point. For this slider the black and the white points are always in the same position,...
void thresholdChanged(qreal newThreshold)
Signal emitted when the threshold value changes.
QColor blendColors(const QColor &c1, const QColor &c2, qreal r1)
rgba palette[MAX_PALETTE]