Krita Source Code Documentation
Loading...
Searching...
No Matches
KisLevelsSlider.cpp
Go to the documentation of this file.
1/*
2 * KDE. Krita Project.
3 *
4 * SPDX-FileCopyrightText: 2021 Deif Lou <ginoba@gmail.com>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9#include <QPainter>
10#include <QMouseEvent>
11
12#include <algorithm>
13#include <cmath>
14
15#include <kis_painting_tweaks.h>
16
17#include "KisLevelsSlider.h"
18
20 : QWidget(parent)
21 , m_constrainPositions(true)
22 , m_selectedHandle(-1)
23 , m_hoveredHandle(-1)
24{
25 setFocusPolicy(Qt::WheelFocus);
26 setMouseTracking(true);
27}
28
31
32qreal KisLevelsSlider::handlePosition(int handleIndex) const
33{
34 Q_ASSERT(handleIndex >= 0 && handleIndex < m_handles.size());
35
36 return m_handles[handleIndex].position;
37}
38
39QColor KisLevelsSlider::handleColor(int handleIndex) const
40{
41 Q_ASSERT(handleIndex >= 0 && handleIndex < m_handles.size());
42
43 return m_handles[handleIndex].color;
44}
45
46void KisLevelsSlider::setHandlePosition(int handleIndex, qreal newPosition)
47{
48 Q_ASSERT(handleIndex >= 0 && handleIndex < m_handles.size());
49
51 newPosition =
52 qBound(
53 handleIndex == m_handles.first().index ? 0.0 : m_handles[handleIndex - 1].position + minimumSpaceBetweenHandles,
54 newPosition,
55 handleIndex == m_handles.last().index ? 1.0 : m_handles[handleIndex + 1].position - minimumSpaceBetweenHandles
56 );
57 } else {
58 newPosition = qBound(0.0, newPosition, 1.0);
59 }
60
61 if (newPosition == m_handles[handleIndex].position) {
62 return;
63 }
64
65 m_handles[handleIndex].position = newPosition;
66
67 update();
68 Q_EMIT handlePositionChanged(handleIndex, newPosition);
69}
70
71void KisLevelsSlider::setHandleColor(int handleIndex, const QColor &newColor)
72{
73 Q_ASSERT(handleIndex >= 0 && handleIndex < m_handles.size());
74
75 if (newColor == m_handles[handleIndex].color) {
76 return;
77 }
78
79 m_handles[handleIndex].color = newColor;
80
81 update();
82 Q_EMIT handleColorChanged(handleIndex, newColor);
83}
84
86{
87 return QSize(256, 20) + QSize(handleWidth, handleHeight);
88}
89
91{
92 return QSize(128, 20) + QSize(handleWidth, handleHeight);
93}
94
96{
97 const int margin = handleWidth / 2;
98 return rect().adjusted(margin, 0, -margin, -handleHeight);
99}
100
102{
103 QVector<Handle> sortedHandles_ = m_handles;
104 std::sort(sortedHandles_.begin(), sortedHandles_.end(),
105 [](const Handle &lhs, const Handle &rhs)
106 {
107 return qFuzzyCompare(lhs.position, rhs.position) ? lhs.index < rhs.index : lhs.position < rhs.position;
108 }
109 );
110 return sortedHandles_;
111}
112
114{
115 const QVector<Handle> sortedHandles_ = sortedHandles();
116 int handleIndex = -1;
117
118 if (position <= sortedHandles_.first().position) {
119 handleIndex = sortedHandles_.first().index;
120 } else if (position >= sortedHandles_.last().position) {
121 handleIndex = sortedHandles_.last().index;
122 } else {
123 for (int i = 0; i < sortedHandles_.size() - 1; ++i) {
124 if (position >= sortedHandles_[i + 1].position) {
125 continue;
126 }
127 const qreal middlePoint = (sortedHandles_[i].position + sortedHandles_[i + 1].position) / 2.0;
128 handleIndex = position <= middlePoint ? sortedHandles_[i].index : sortedHandles_[i + 1].index;
129 break;
130 }
131 }
132
133 return handleIndex;
134}
135
137{
138 const QRect gradientRect_ = gradientRect();
139 return static_cast<qreal>(x - gradientRect_.left()) / static_cast<qreal>(gradientRect_.width());
140}
141
146
147int KisLevelsSlider::xFromPosition(qreal position) const
148{
149 const QRect gradientRect_ = gradientRect();
150 return static_cast<int>(position * static_cast<qreal>(gradientRect_.width())) + gradientRect_.left();
151}
152
153void KisLevelsSlider::handleIncrementInput(int direction, Qt::KeyboardModifiers modifiers)
154{
155 if (direction == 0) {
156 return;
157 }
158 if (modifiers & Qt::ControlModifier) {
159 m_selectedHandle += direction < 0 ? -1 : 1;
160 m_selectedHandle = qBound(0, m_selectedHandle, m_handles.size() - 1);
161 update();
162 } else if (m_selectedHandle >= 0 && m_selectedHandle < m_handles.size()) {
163 const qreal increment = modifiers & Qt::ShiftModifier ? slowPositionIncrement : normalPositionIncrement;
164 const qreal position = m_handles[m_selectedHandle].position + (direction < 0 ? -increment : increment);
166 }
167}
168
169void KisLevelsSlider::paintHandle(QPainter &painter, const QRect &rect, const Handle &handle)
170{
171 painter.setRenderHint(QPainter::Antialiasing, false);
172 const int halfHandleWidth = handleWidth / 2.0;
173 {
174 const QPolygon shape({
175 {rect.left() + halfHandleWidth, rect.top()},
176 {rect.right() + 1, rect.top() + halfHandleWidth},
177 {rect.right() + 1, rect.bottom() + 1},
178 {rect.left(), rect.bottom() + 1},
179 {rect.left(), rect.top() + halfHandleWidth}
180 });
181 painter.setPen(Qt::NoPen);
182 const bool isSelected = handle.index == m_selectedHandle;
183 QColor brushColor =
184 isSelected && hasFocus()
185 ? KisPaintingTweaks::blendColors(handle.color, palette().highlight().color(), 0.25)
186 : handle.color;
187 if (!isEnabled()) {
188 brushColor.setAlpha(64);
189 }
190 painter.setBrush(brushColor);
191 painter.drawPolygon(shape);
192 }
193 {
194 const QPolygon shape({
195 {rect.left() + halfHandleWidth, rect.top()},
196 {rect.right(), rect.top() + halfHandleWidth},
197 {rect.right(), rect.bottom()},
198 {rect.left(), rect.bottom()},
199 {rect.left(), rect.top() + halfHandleWidth}
200 });
201 const bool isHovered = handle.index == m_hoveredHandle;
202 QColor penColor = isHovered && isEnabled() ? palette().highlight().color() : palette().text().color();
203 if (!isHovered) {
204 penColor.setAlpha(64);
205 }
206 painter.setPen(penColor);
207 painter.setBrush(Qt::NoBrush);
208 painter.drawPolygon(shape);
209 }
210}
211
212void KisLevelsSlider::paintEvent(QPaintEvent *e)
213{
214 Q_UNUSED(e);
215
216 QPainter painter(this);
217 const QRect gradientRect_ = gradientRect();
218 // Gradient
219 painter.save();
220 paintGradient(painter, gradientRect_);
221 painter.restore();
222 // Border
223 QColor borderColor = palette().text().color();
224 borderColor.setAlpha(64);
225 painter.setPen(borderColor);
226 painter.drawRect(gradientRect_.adjusted(0, 0, -1, -1));
227 // Handles
228 const QVector<Handle> sortedHandles_ = sortedHandles();
229 const int halfHandleWidth = handleWidth / 2;
230 painter.save();
231 for (const Handle &handle : sortedHandles_) {
232 const int handleX = static_cast<int>(qRound(handle.position * static_cast<qreal>(gradientRect_.width() - 1))) + halfHandleWidth;
233 const QRect handleRect(handleX - halfHandleWidth, gradientRect_.bottom() + 1, handleWidth, handleHeight);
234 paintHandle(painter, handleRect, handle);
235 }
236 painter.restore();
237}
238
240{
241 if (m_handles.size() == 0) {
242 return;
243 }
244 if (e->button() != Qt::LeftButton) {
245 return;
246 }
247
248 qreal mousePosition = positionFromX(e->x());
249 int handleIndex = closestHandleToPosition(mousePosition);
250
251 if (handleIndex != -1) {
252 m_selectedHandle = handleIndex;
253 const int handleX = xFromPosition(m_handles[handleIndex].position);
254 if (qAbs(handleX - e->x()) > handleWidth) {
255 setHandlePosition(handleIndex, mousePosition);
256 } else {
257 update();
258 }
259 }
260}
261
263{
264 if (m_handles.size() == 0) {
265 return;
266 }
267
268 if (e->buttons() & Qt::LeftButton && m_selectedHandle != -1) {
270 } else {
271 int handleIndex = closestHandleToX(e->x());
272 if (handleIndex != -1) {
273 m_hoveredHandle = handleIndex;
274 update();
275 }
276 }
277
278}
279
281{
282 m_hoveredHandle = -1;
283 update();
284 QWidget::leaveEvent(e);
285}
286
288{
289 if (m_handles.size() == 0) {
290 return;
291 }
292 if (m_selectedHandle == -1) {
293 return;
294 }
295
296 switch (e->key()) {
297 case Qt::Key_Left:
298 handleIncrementInput(-1, e->modifiers());
299 return;
300 case Qt::Key_Right:
301 handleIncrementInput(1, e->modifiers());
302 return;
303 default:
304 QWidget::keyPressEvent(e);
305 }
306}
307
308void KisLevelsSlider::wheelEvent(QWheelEvent *e)
309{
310 if (e->angleDelta().y() != 0) {
311 handleIncrementInput(e->angleDelta().y(), e->modifiers());
312 e->accept();
313 } else {
314 QWidget::wheelEvent(e);
315 }
316}
317
318
320 : KisLevelsSlider(parent)
321{
322 m_handles.resize(2);
323 m_handles[0].index = 0;
324 m_handles[0].position = 0.0;
325 m_handles[0].color = Qt::black;
326 m_handles[1].index = 1;
327 m_handles[1].position = 1.0;
328 m_handles[1].color = Qt::white;
331 [this](int handleIndex, qreal position)
332 {
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);
337 }
338 }
339 );
340}
341
344
346{
347 return m_handles.first().position;
348}
349
351{
352 return m_handles.last().position;
353}
354
355void KisInputLevelsSlider::setBlackPoint(qreal newBlackPoint)
356{
357 setHandlePosition(m_handles.first().index, newBlackPoint);
358}
359
360void KisInputLevelsSlider::setWhitePoint(qreal newWhitePoint)
361{
362 setHandlePosition(m_handles.last().index, newWhitePoint);
363}
364
365void KisInputLevelsSlider::reset(qreal newBlackPoint, qreal newWhitePoint)
366{
367 newBlackPoint = qBound(0.0, newBlackPoint, 1.0);
368 newWhitePoint = qBound(0.0, newWhitePoint, 1.0);
369
371 if (newWhitePoint < newBlackPoint + minimumSpaceBetweenHandles) {
372 newWhitePoint = newBlackPoint + minimumSpaceBetweenHandles;
373 if (newWhitePoint < 1.0) {
374 newWhitePoint = 1.0;
375 newBlackPoint = 1.0 - minimumSpaceBetweenHandles;
376 }
377 }
378 if (newBlackPoint <= whitePoint() - minimumSpaceBetweenHandles) {
379 setBlackPoint(newBlackPoint);
380 setWhitePoint(newWhitePoint);
381 } else {
382 setWhitePoint(newWhitePoint);
383 setBlackPoint(newBlackPoint);
384 }
385 } else {
386 setBlackPoint(newBlackPoint);
387 setWhitePoint(newWhitePoint);
388 }
389}
390
391void KisInputLevelsSlider::paintBottomGradientMiddleSection(QImage &gradientImage, const QVector<Handle> &sortedHandles_)
392{
393 if (m_handles.size() < 2) {
394 return;
395 }
396
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,
405 t
406 ).rgba();
407 }
408}
409
410void KisInputLevelsSlider::paintGradient(QPainter &painter, const QRect &rect)
411{
412 if (m_handles.size() == 0) {
413 return;
414 }
415
416 painter.setRenderHint(QPainter::SmoothPixmapTransform);
417 if (!isEnabled()) {
418 painter.setOpacity(0.5);
419 }
420 QImage gradientImage(rect.width(), 1, QImage::Format_ARGB32);
421 const int halfGradientHeight = rect.height() / 2;
422
423 // Top gradient
424 {
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);
428 *pixel =KisPaintingTweaks::blendColors(m_handles.last().color, m_handles.first().color, t).rgba();
429 }
430 }
431 painter.drawImage(rect.adjusted(0, 0, 0, -(halfGradientHeight + (rect.height() & 1 ? 1 : 0))), gradientImage);
432
433 // Bottom gradient
434 QVector<Handle> sortedHandles_ = sortedHandles();
435 {
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();
440 }
441 }
442 paintBottomGradientMiddleSection(gradientImage, sortedHandles_);
443 {
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();
448 }
449 }
450 painter.drawImage(rect.adjusted(0, halfGradientHeight, 0, 0), gradientImage);
451}
452
453
455 : KisInputLevelsSlider(parent)
456 , m_gamma(1.0)
457{
458 m_handles.last().index = 2;
459 m_handles.insert(1, {1, 0.5, Qt::gray});
460}
461
464
466{
467 return m_gamma;
468}
469
470void KisInputLevelsSliderWithGamma::setHandlePosition(int handleIndex, qreal newPosition)
471{
472 Q_ASSERT(handleIndex >= 0 && handleIndex < m_handles.size());
473
474 if (handleIndex == 1) {
475 newPosition = qBound(m_handles.first().position, newPosition, m_handles.last().position);
476 if (newPosition == m_handles[1].position) {
477 return;
478 }
479 m_handles[1].position = newPosition;
481 update();
482 Q_EMIT handlePositionChanged(1, newPosition);
483 Q_EMIT gammaChanged(m_gamma);
484 } else {
485 if (handleIndex == m_handles.first().index) {
486 newPosition = qBound(0.0, newPosition, m_handles.last().position - minimumSpaceBetweenHandles);
487 } else if (handleIndex == m_handles.last().index) {
488 newPosition = qBound(m_handles.first().position + minimumSpaceBetweenHandles, newPosition, 1.0);
489 }
490 if (newPosition == m_handles[handleIndex].position) {
491 return;
492 }
493 m_handles[handleIndex].position = newPosition;
494 m_handles[1].position = gammaToPosition();
495 update();
496 Q_EMIT handlePositionChanged(handleIndex, newPosition);
497 Q_EMIT handlePositionChanged(1, newPosition);
498 }
499
500}
501
503{
504 newGamma = qBound(0.1, newGamma, 10.0);
505 if (newGamma == m_gamma) {
506 return;
507 }
508 m_gamma = newGamma;
509 m_handles[1].position = gammaToPosition();
510 update();
511 Q_EMIT gammaChanged(m_gamma);
512 Q_EMIT handlePositionChanged(1, m_handles[1].position);
513}
514
515void KisInputLevelsSliderWithGamma::reset(qreal newBlackPoint, qreal newWhitePoint)
516{
517 KisInputLevelsSlider::reset(newBlackPoint, newWhitePoint);
518}
519
520void KisInputLevelsSliderWithGamma::reset(qreal newBlackPoint, qreal newWhitePoint, qreal newGamma)
521{
522 reset(newBlackPoint, newWhitePoint);
523 setGamma(newGamma);
524}
525
527{
528 if (m_handles.size() < 2) {
529 return;
530 }
531 if (m_handles.size() < 3) {
532 KisInputLevelsSlider::paintBottomGradientMiddleSection(gradientImage, sortedHandles_);
533 return;
534 }
535
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)
546 ).rgba();
547 }
548}
549
551{
552 qreal relativePosition;
553 const qreal log1_2 = std::log(0.5);
554 // the function would be "relativePosition = exp(gamma * ln(1/2)" but since
555 // we want to limit the gamma from 0.1 to 10 we map the gamma
556 // in a way such that "exp(max_gamma * ln(1/2)) = 1" and
557 // "exp(min_gamma * ln(1/2)) = 0"
558 if (m_gamma > 1.0) {
559 const qreal mappedPosition = std::exp(10.0 * log1_2);
560 relativePosition = (std::exp(m_gamma * log1_2) - mappedPosition) / (1.0 - mappedPosition * 2.0);
561 } else {
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);
564 }
565 return blackPoint() + relativePosition * (whitePoint() - blackPoint());
566}
567
569{
570 const qreal relativePosition = (handlePosition(1) - blackPoint()) / (whitePoint() - blackPoint());
571 const qreal log1_2 = std::log(0.5);
572 // the function would be "gamma = ln(relativePosition) / ln(1/2)" but since
573 // we want to limit the gamma from 0.1 to 10 we map the relative position
574 // in a way such that "ln(min_relativePosition) / ln(1/2) = 10" and
575 // "ln(max_relativePosition) / ln(1/2) = 0.1"
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;
579 } else {
580 const qreal mappedPosition = std::exp(0.1 * log1_2);
581 return std::log(1 - (mappedPosition + relativePosition) + relativePosition * mappedPosition * 2.0) / log1_2;
582 }
583}
584
585
591
594
595
597 : KisInputLevelsSlider(parent)
598{
599 m_constrainPositions = false;
600}
601
604
606{
607 return blackPoint();
608}
609
610void KisThresholdSlider::setHandlePosition(int handleIndex, qreal newPosition)
611{
612 Q_ASSERT(handleIndex >= 0 && handleIndex < m_handles.size());
613 reset(newPosition, newPosition);
614}
615
616void KisThresholdSlider::setBlackPoint(qreal newBlackPoint)
617{
618 reset(newBlackPoint, newBlackPoint);
619}
620
621void KisThresholdSlider::setWhitePoint(qreal newWhitePoint)
622{
623 reset(newWhitePoint, newWhitePoint);
624}
625
626void KisThresholdSlider::reset(qreal newBlackPoint, qreal newWhitePoint)
627{
628 Q_UNUSED(newWhitePoint);
629
630 newBlackPoint = qBound(0.0, newBlackPoint, 1.0);
631 if (newBlackPoint == blackPoint()) {
632 return;
633 }
634 m_handles.first().position = m_handles.last().position = newBlackPoint;
635 update();
636 Q_EMIT handlePositionChanged(0, newBlackPoint);
637 Q_EMIT blackPointChanged(newBlackPoint);
638 Q_EMIT handlePositionChanged(1, newBlackPoint);
639 Q_EMIT whitePointChanged(newBlackPoint);
640 Q_EMIT thresholdChanged(newBlackPoint);
641}
642
643void KisThresholdSlider::setThreshold(qreal newThreshold)
644{
645 reset(newThreshold, newThreshold);
646}
647
650
651void KisThresholdSlider::paintHandle(QPainter &painter, const QRect &rect, const Handle &handle)
652{
653 if (handle.index != m_handles.first().index) {
654 return;
655 }
656 if (m_hoveredHandle >= 0) {
657 m_hoveredHandle = 0;
658 }
659 if (m_selectedHandle >= 0) {
661 }
662 KisLevelsSlider::paintHandle(painter, rect, handle);
663}
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
void setHandlePosition(int handleIndex, qreal newPosition) override
KisInputLevelsSliderWithGamma(QWidget *parent=nullptr)
void setGamma(qreal newGamma)
Sets the gamma value.
void reset(qreal newBlackPoint, qreal newWhitePoint) override
qreal gamma() 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 gammaChanged(qreal newGamma)
Signal emitted when the gamma value changes.
This is a simple input levels slider that has no gamma handle. Use it if you want to show a simple ma...
void blackPointChanged(qreal newBlackPoint)
Signal emitted when the black point changes.
KisInputLevelsSlider(QWidget *parent=nullptr)
virtual void reset(qreal newBlackPoint, qreal newWhitePoint)
Sometimes you want to set the range to a totally different place, but the new black point can be grea...
void paintGradient(QPainter &painter, const QRect &rect) override
Custom gradient painter. This paints two bars in the gradient rect, one on top of the other....
qreal whitePoint() const
Get the normalized white point.
virtual void paintBottomGradientMiddleSection(QImage &gradientImage, const QVector< Handle > &sortedHandles_)
This is used to fill the space between the tho handles in the bottom bar of the "gradient"....
void whitePointChanged(qreal newWhitePoint)
Signal emitted when the white point changes.
virtual void setBlackPoint(qreal newBlackPoint)
Sets the black point.
qreal blackPoint() const
Get the normalized black point.
virtual void setWhitePoint(qreal newWhitePoint)
Sets the white point.
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.
unsigned int QRgb
QColor blendColors(const QColor &c1, const QColor &c2, qreal r1)
rgba palette[MAX_PALETTE]
Definition palette.c:35