File indexing completed on 2024-05-12 16:02:03

0001 /*
0002  * KDE. Krita Project.
0003  *
0004  * SPDX-FileCopyrightText: 2021 Deif Lou <ginoba@gmail.com>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #ifndef KIS_LEVELS_SLIDER_H
0010 #define KIS_LEVELS_SLIDER_H
0011 
0012 #include <QWidget>
0013 #include <QVector>
0014 
0015 #include "kritawidgets_export.h"
0016 
0017 /**
0018  * @brief A base class for levels slider like widgets: a slider with a gradient
0019  * and multiple handles
0020  */
0021 class KRITAWIDGETS_EXPORT KisLevelsSlider : public QWidget
0022 {
0023     Q_OBJECT
0024 
0025 public:
0026     KisLevelsSlider(QWidget *parent);
0027     ~KisLevelsSlider();
0028 
0029     QSize sizeHint() const override;
0030     QSize minimumSizeHint() const override;
0031 
0032     /**
0033      * @brief Gets the normalized position of a given handle
0034      */
0035     qreal handlePosition(int handleIndex) const;
0036     /**
0037      * @brief Gets the color associated with a given handle
0038      */
0039     QColor handleColor(int handleIndex) const;
0040     /**
0041      * @brief Gets the rect where the gradient will be painted
0042      */
0043     virtual QRect gradientRect() const;
0044 
0045 public Q_SLOTS:
0046     /**
0047      * @brief Sets the normalized position of the given handle
0048      */
0049     virtual void setHandlePosition(int handleIndex, qreal newPosition);
0050     /**
0051      * @brief Sets the color associated with the given handle
0052      */
0053     virtual void setHandleColor(int handleIndex, const QColor &newColor);
0054 
0055 Q_SIGNALS:
0056     /**
0057      * @brief Signal emited when the position of a handle changes
0058      */
0059     void handlePositionChanged(int handleIndex, qreal position);
0060     /**
0061      * @brief Signal emited when the color associated with a handle changes
0062      */
0063     void handleColorChanged(int handleIndex, const QColor &color);
0064 
0065 protected:
0066     struct Handle
0067     {
0068         int index;
0069         qreal position;
0070         QColor color;
0071     };
0072 
0073     static constexpr int handleWidth{11};
0074     static constexpr int handleHeight{11};
0075     static constexpr qreal minimumSpaceBetweenHandles{0.001};
0076     static constexpr qreal normalPositionIncrement{0.01};
0077     static constexpr qreal slowPositionIncrement{0.001};
0078 
0079     /**
0080      * @brief The collection of handles
0081      */
0082     QVector<Handle> m_handles;
0083     /**
0084      * @brief This variable indicates if the handles can have unordered
0085      * positions. If it is set to true then the user won't be able to move a
0086      * handle pass another one. If it is set to false then the ser will be able
0087      * to move the handles freely
0088      */
0089     int m_constrainPositions;
0090 
0091     int m_selectedHandle;
0092     int m_hoveredHandle;
0093     
0094     /**
0095      * @brief Regardless the index of a handle, they can be unordered in terms
0096      * of the position. This returns a sorted vector with the handles that have
0097      * a smaller position first. If two handles have the same position then the
0098      * index is used for sorting
0099      */
0100     QVector<Handle> sortedHandles() const;
0101     /**
0102      * @brief Given a normalized position, this function returns the closest
0103      * handle to that position
0104      */
0105     int closestHandleToPosition(qreal position) const;
0106     /**
0107      * @brief Given a widget-relative x position in pixels, this function
0108      * returns the normalized position relative to the gradient rect
0109      */
0110     qreal positionFromX(int x) const;
0111     /**
0112      * @brief Given a widget-relative x position, this function returns the
0113      * closest handle to that position
0114      */
0115     int closestHandleToX(int x) const;
0116     /**
0117      * @brief Given a gradient rect relative position, this function returns the
0118      * x position in pixels relative to the widget
0119      */
0120     int xFromPosition(qreal position) const;
0121     /**
0122      * @brief Derived classes must override this function to draw the gradient
0123      * inside the given rect. A border is automatically drawn after
0124      */
0125     virtual void paintGradient(QPainter &painter, const QRect &rect) = 0;
0126     /**
0127      * @brief Override this function to paint custom handles
0128      */
0129     virtual void paintHandle(QPainter &painter, const QRect &rect, const Handle &handle);
0130 
0131     void handleIncrementInput(int direction, Qt::KeyboardModifiers modifiers);
0132 
0133     void paintEvent(QPaintEvent *e) override;
0134     void mousePressEvent(QMouseEvent *e) override;
0135     void mouseMoveEvent(QMouseEvent *e) override;
0136     void leaveEvent(QEvent *e) override;
0137     void keyPressEvent(QKeyEvent *e) override;
0138     void wheelEvent(QWheelEvent *e) override;
0139 };
0140 
0141 /**
0142  * @brief This is a simple input levels slider that has no gamma handle. Use it
0143  * if you want to show a simple mapping or contrast adjustement. The handles
0144  * are constrained so that the black point handle can not pass the white point
0145  * handle and viceversa
0146  */
0147 class KRITAWIDGETS_EXPORT KisInputLevelsSlider : public KisLevelsSlider
0148 {
0149     Q_OBJECT
0150 
0151 public:
0152     KisInputLevelsSlider(QWidget *parent = nullptr);
0153     ~KisInputLevelsSlider();
0154 
0155     /**
0156      * @brief Get the normalized black point
0157      */
0158     qreal blackPoint() const;
0159     /**
0160      * @brief Get the normalized white point
0161      */
0162     qreal whitePoint() const;
0163 
0164 public Q_SLOTS:
0165     /**
0166      * @brief Sets the black point
0167      */
0168     virtual void setBlackPoint(qreal newBlackPoint);
0169     /**
0170      * @brief Sets the white point
0171      */
0172     virtual void setWhitePoint(qreal newWhitePoint);
0173     /**
0174      * @brief Sometimes you want to set the range to a totally different place,
0175      * but the new black point can be greater than the old white point so the
0176      * new black point position will be constrained to the old white position.
0177      * This function allows to set both values at once to prevent that
0178      * undesirable effect. Use it when the widget has to show new values, for a
0179      * different set of levels parameters for example
0180      */
0181     virtual void reset(qreal newBlackPoint, qreal newWhitePoint);
0182 
0183 Q_SIGNALS:
0184     /**
0185      * @brief Signal emited when the black point changes
0186      */
0187     void blackPointChanged(qreal newBlackPoint);
0188     /**
0189      * @brief Signal emited when the white point changes
0190      */
0191     void whitePointChanged(qreal newWhitePoint);
0192 
0193 protected:
0194     /**
0195      * @brief Custom gradient painter. This paints two bars in the gradient
0196      * rect, one on top of the other. The top one shows a simple black to white
0197      * (more exactly, color of the first handle to color of the last handle)
0198      * linear gradient. The bottom one shows black (first handle's color) from
0199      * the right side of the rect to the black point, and white (last handle's
0200      * color) from the white point to the left side of the rect. In the middle
0201      * of the two handles, the "paintBottomGradientMiddleSection" is used to
0202      * fill the bar
0203      */
0204     void paintGradient(QPainter &painter, const QRect &rect) override;
0205     /**
0206      * @brief This is used to fill the space between the tho handles in the
0207      * bottom bar of the "gradient". It just paints a linear gradient that goes
0208      * from black (first handle's color) to white (last handle's color). Derived
0209      * classes can override this function if they only want to change that area.
0210      * "gradientImage" is a 256x1px image.
0211      */
0212     virtual void paintBottomGradientMiddleSection(QImage &gradientImage, const QVector<Handle> &sortedHandles_);
0213 };
0214 
0215 /**
0216  * @brief This is a input levels slider that has a gamma handle. The handles
0217  * are constrained so that the black point handle can not pass the white point
0218  * handle and viceversa
0219  */
0220 class KRITAWIDGETS_EXPORT KisInputLevelsSliderWithGamma : public KisInputLevelsSlider
0221 {
0222     Q_OBJECT
0223 
0224 public:
0225     KisInputLevelsSliderWithGamma(QWidget *parent = nullptr);
0226     ~KisInputLevelsSliderWithGamma();
0227 
0228     /**
0229      * @brief Get the gamma value
0230      */
0231     qreal gamma() const;
0232 
0233 public Q_SLOTS:
0234     void setHandlePosition(int handleIndex, qreal newPosition) override;
0235     /**
0236      * @brief Sets the gamma value
0237      */
0238     void setGamma(qreal newGamma);
0239     /**
0240      * @see KisInputLevelsSlider::reset
0241      */
0242     void reset(qreal newBlackPoint, qreal newWhitePoint) override;
0243     /**
0244      * @see KisInputLevelsSlider::reset
0245      */
0246     void reset(qreal newBlackPoint, qreal newWhitePoint, qreal newGamma);
0247 
0248 Q_SIGNALS:
0249     /**
0250      * @brief Signal emited when the gamma value changes
0251      */
0252     void gammaChanged(qreal newGamma);
0253 
0254 protected:
0255     void paintBottomGradientMiddleSection(QImage &gradientImage, const QVector<Handle> &sortedHandles_) override;
0256 
0257 private:
0258     qreal m_gamma;
0259 
0260     qreal gammaToPosition() const;
0261     qreal positionToGamma() const;
0262 };
0263 
0264 /**
0265  * @brief This is a simple output levels slider. It is basically the same as
0266  * KisInputLevelsSlider but the handles are not constrained and can move freely
0267  */
0268 class KRITAWIDGETS_EXPORT KisOutputLevelsSlider : public KisInputLevelsSlider
0269 {
0270     Q_OBJECT
0271 
0272 public:
0273     KisOutputLevelsSlider(QWidget *parent = nullptr);
0274     ~KisOutputLevelsSlider();
0275 };
0276 
0277 /**
0278  * @brief This is a threshold slider that only has one handle
0279  */
0280 class KRITAWIDGETS_EXPORT KisThresholdSlider : public KisInputLevelsSlider
0281 {
0282     Q_OBJECT
0283 
0284 public:
0285     KisThresholdSlider(QWidget *parent = nullptr);
0286     ~KisThresholdSlider();
0287 
0288     /**
0289      * @brief Get the gamma value
0290      */
0291     qreal threshold() const;
0292 
0293 public Q_SLOTS:
0294     /**
0295      * @brief Sets the black point. For this slider the black and the white
0296      * points are always in the same position, so changing the black point will
0297      * also change the white point and viceversa
0298      */
0299     void setHandlePosition(int handleIndex, qreal newPosition) override;
0300     /**
0301      * @brief Sets the black point. For this slider the black and the white
0302      * points are always in the same position, so changing the black point will
0303      * also change the white point and viceversa
0304      */
0305     void setBlackPoint(qreal newBlackPoint) override;
0306     /**
0307      * @brief Sets the white point. For this slider the black and the white
0308      * points are always in the same position, so changing the black point will
0309      * also change the white point and viceversa
0310      */
0311     void setWhitePoint(qreal newWhitePoint) override;
0312     void reset(qreal newBlackPoint, qreal newWhitePoint) override;
0313     /**
0314      * @brief Sets the gamma value
0315      */
0316     void setThreshold(qreal newGamma);
0317 
0318 Q_SIGNALS:
0319     /**
0320      * @brief Signal emited when the threshold value changes
0321      */
0322     void thresholdChanged(qreal newThreshold);
0323 
0324 protected:
0325     void paintBottomGradientMiddleSection(QImage &gradientImage, const QVector<Handle> &sortedHandles_) override;
0326     void paintHandle(QPainter &painter, const QRect &rect, const Handle &handle) override;
0327 };
0328 
0329 #endif