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

0001 /*
0002  * SPDX-FileCopyrightText: 2016 Wolthera van Hovell tot Westerflier <griffinvalley@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 #ifndef KIS_VISUAL_COLOR_SELECTOR_SHAPE_H
0007 #define KIS_VISUAL_COLOR_SELECTOR_SHAPE_H
0008 
0009 #include <QWidget>
0010 #include <QScopedPointer>
0011 #include <QPixmap>
0012 #include <QRegion>
0013 #include <QMouseEvent>
0014 
0015 #include <KoColor.h>
0016 #include <KoColorSpace.h>
0017 #include "KoColorDisplayRendererInterface.h"
0018 
0019 #include "KisVisualColorSelector.h"
0020 #include "KisColorSelectorConfiguration.h"
0021 
0022 /**
0023  * @brief The KisVisualColorSelectorShape class
0024  * A 2d widget can represent at maximum 2 coordinates.
0025  * So first decide howmany coordinates you need. (onedimensional, or twodimensional)
0026  * Then the model, (Channel, HSV, HSL, HSI, YUV). Channel is the raw color channels.
0027  * When it finds a non-implemented feature it'll return to Channel.
0028  * Then, select the channels you wish to be affected. This uses the model, so for cmyk
0029  * the channel is c=0, m=1, y=2, k=3, but for hsv, hue=0, sat=1, and val=2
0030  * These can also be set with 'slotsetactive channels'.
0031  * Then finally, connect the displayrenderer, you can also do this with 'setdisplayrenderer'
0032  *
0033  * Either way, this class is made to be subclassed, with a few virtuals so that the geometry
0034  * can be calculated properly.
0035  */
0036 
0037 class KisVisualColorSelectorShape : public QWidget
0038 {
0039     Q_OBJECT
0040 public:
0041     /**
0042      * @brief The Dimensions enum
0043      * Whether or not the shape is single or two dimensional.
0044      **/
0045     enum Dimensions{onedimensional, twodimensional};
0046     enum ColorModel{Channel, HSV, HSL, HSI, HSY, YUV};
0047     explicit KisVisualColorSelectorShape(QWidget *parent,
0048                                          KisVisualColorSelectorShape::Dimensions dimension,
0049                                          const KoColorSpace *cs,
0050                                          int channel1, int channel2,
0051                                          const KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance());
0052     ~KisVisualColorSelectorShape() override;
0053 
0054     /**
0055      * @brief getCursorPosition
0056      * @return current cursor position in shape-coordinates.
0057      */
0058     QPointF getCursorPosition();
0059     /**
0060      * @brief getDimensions
0061      * @return whether this is a single or twodimensional widget.
0062      */
0063     Dimensions getDimensions() const;
0064     /**
0065      * @brief getPixmap
0066      * @return the pixmap of the gradient, for drawing on with a subclass.
0067      * the pixmap will not change unless 'm_d->setPixmap=true' which is toggled by
0068      * refresh and update functions.
0069      */
0070     bool imagesNeedUpdate() const;
0071     QImage getImageMap();
0072     const QImage getAlphaMask() const;
0073     /**
0074      * @brief setFullImage
0075      * Set the full widget image to be painted.
0076      * @param full this should be the full image.
0077      */
0078     void setFullImage(QImage full);
0079     /**
0080      * @brief getCurrentColor
0081      * @return the current kocolor
0082      */
0083     KoColor getCurrentColor();
0084     /**
0085      * @brief setDisplayRenderer
0086      * disconnect the old display renderer if needed and connect the new one.
0087      * @param displayRenderer
0088      */
0089     void setDisplayRenderer (const KoColorDisplayRendererInterface *displayRenderer);
0090     /**
0091      * @brief getColorFromConverter
0092      * @param c a koColor.
0093      * @return get the qcolor from the given kocolorusing this widget's display renderer.
0094      */
0095     QColor getColorFromConverter(KoColor c);
0096 
0097     /**
0098      * @brief getSpaceForSquare
0099      * @param geom the full widget rectangle
0100      * @return rectangle with enough space for second widget
0101      */
0102     virtual QRect getSpaceForSquare(QRect geom) = 0;
0103     virtual QRect getSpaceForCircle(QRect geom) = 0;
0104     virtual QRect getSpaceForTriangle(QRect geom) = 0;
0105 
0106     /**
0107      * @brief forceImageUpdate
0108      * force the image to recache.
0109      */
0110     void forceImageUpdate();
0111 
0112     /**
0113      * @brief setBorderWidth
0114      * set the border of the single dimensional selector.
0115      * @param width
0116      */
0117     virtual void setBorderWidth(int width) = 0;
0118 
0119     /**
0120      * @brief getChannels
0121      * get used channels
0122      * @return
0123      */
0124     QVector <int> getChannels() const;
0125 
0126     /**
0127       * @brief setCursorPosition
0128       * Set the cursor to normalized shape coordinates. This will only repaint the cursor.
0129       * @param position normalized shape coordinates ([0,1] range, not yet transformed to actual channel values!)
0130       * @param signal if true, emit a sigCursorMoved signal
0131       */
0132     void setCursorPosition(QPointF position, bool signal = false);
0133 
0134     /**
0135       * @brief setChannelValues
0136       * Set the current channel values;
0137       * Note that channel values controlled by the shape itself have no effect unless setCursor is true.
0138       * This will trigger a full widget repaint.
0139       * @param position normalized shape coordinates ([0,1] range)
0140       * these are not yet transformed to color space specific ranges!
0141       * @param setCursor if true, sets the cursor too, otherwise the shape-controlled channels are not set
0142       */
0143     void setChannelValues(QVector4D channelValues, bool setCursor);
0144 
0145     void setAcceptTabletEvents(bool on);
0146 
0147 Q_SIGNALS:
0148     void sigCursorMoved(QPointF pos);
0149 
0150 public Q_SLOTS:
0151     /**
0152      * @brief slotSetActiveChannels
0153      * Change the active channels if necessary.
0154      * @param channel1 used by single and twodimensional widgets.
0155      * @param channel2 only used by twodimensional widgets.
0156      */
0157     void slotSetActiveChannels(int channel1, int channel2);
0158 
0159 protected:
0160     /**
0161      * @brief convertImageMap
0162      * convert image data containing raw KoColor data into a QImage
0163      * @param data must point to memory of size width()*height()*pixelSize
0164      * @param size the number of bytes to read from data, must match aforementioned cirteria
0165      * @return the converted QImage guaranteed to match the widget size (black content on failure)
0166      */
0167     QImage convertImageMap(const quint8 *rawColor, quint32 bufferSize, QSize imgSize) const;
0168     /**
0169      * @brief renderBackground
0170      * Render the widget background visible inside the widget's mask in current color space
0171      * Rendering shall be done with the conversion functions of KisVisualColorSelector
0172      * @param data points to zero-initialized memory of size width()*height()*pixelSize
0173      * @param pixelSize the data size to transfer from KoColor::data() to data per pixel
0174      * in the current color space
0175      * @param channelValues the normalized channel values of the currently selected color
0176      */
0177     virtual QImage renderBackground(const QVector4D &channelValues, quint32 pixelSize) const;
0178     /**
0179      * @brief render the alpha mask for the widget background
0180      * the returned image is expected to be QImage::Format_Alpha8
0181      */
0182     virtual QImage renderAlphaMask() const;
0183     /**
0184      * @brief default implementation just calls convertWidgetCoordinateToShapeCoordinate(pos)
0185     */
0186     virtual QPointF mousePositionToShapeCoordinate(const QPointF &pos, const QPointF &dragStart) const;
0187 
0188     void mousePressEvent(QMouseEvent *e) override;
0189     void mouseMoveEvent(QMouseEvent *e) override;
0190     void mouseReleaseEvent(QMouseEvent *e) override;
0191     void tabletEvent(QTabletEvent* event) override;
0192     void paintEvent(QPaintEvent*) override;
0193     void resizeEvent(QResizeEvent *) override;
0194 
0195 private:
0196     struct Private;
0197     const QScopedPointer<Private> m_d;
0198 
0199     /**
0200      * @brief convertShapeCoordinateToWidgetCoordinate
0201      * @return take the position in the shape and convert it to screen coordinates.
0202      */
0203     virtual QPointF convertShapeCoordinateToWidgetCoordinate(QPointF) const = 0;
0204 
0205     /**
0206      * @brief convertWidgetCoordinateToShapeCoordinate
0207      * Convert a coordinate in the widget's height/width to a shape coordinate.
0208      * @param coordinate the position your wish to have the shape coordinates of.
0209      */
0210     virtual QPointF convertWidgetCoordinateToShapeCoordinate(QPointF coordinate) const = 0;
0211 
0212     /**
0213      * @brief getPixmap
0214      * @return the pixmap of this shape.
0215      */
0216     virtual QRegion getMaskMap() = 0;
0217     virtual void drawCursor() = 0;
0218 };
0219 
0220 #endif