File indexing completed on 2024-05-19 04:26:12

0001 /*
0002  *  SPDX-FileCopyrightText: 2004 Adrian Page <adrian@pagenet.plus.com>
0003  *  SPDX-FileCopyrightText: 2004 Bart Coppens <kde@bartcoppens.be>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 #ifndef KIS_FILL_PAINTER_H_
0008 #define KIS_FILL_PAINTER_H_
0009 
0010 #include <QRect>
0011 
0012 #include <KoColor.h>
0013 #include <KoColorSpaceRegistry.h>
0014 #include <KoPattern.h>
0015 
0016 #include "kis_painter.h"
0017 #include "kis_types.h"
0018 #include "kis_selection.h"
0019 
0020 #include <KisRunnableStrokeJobUtils.h>
0021 #include <kis_processing_visitor.h>
0022 
0023 #include <kritaimage_export.h>
0024 
0025 
0026 class KisFilterConfiguration;
0027 
0028 // XXX: Filling should set dirty rect.
0029 /**
0030  * This painter can be used to fill paint devices in different ways. This can also be used
0031  * for flood filling related operations.
0032  */
0033 class KRITAIMAGE_EXPORT KisFillPainter : public KisPainter
0034 {
0035 
0036 public:
0037     enum RegionFillingMode
0038     {
0039         RegionFillingMode_FloodFill,
0040         RegionFillingMode_BoundaryFill
0041     };
0042 
0043     /**
0044      * Construct an empty painter. Use the begin(KisPaintDeviceSP) method to attach
0045      * to a paint device
0046      */
0047     KisFillPainter();
0048     /**
0049      * Start painting on the specified paint device
0050      */
0051     KisFillPainter(KisPaintDeviceSP device);
0052 
0053     KisFillPainter(KisPaintDeviceSP device, KisSelectionSP selection);
0054 
0055 private:
0056 
0057     void initFillPainter();
0058 
0059 public:
0060     /**
0061      * Fill current selection of KisPainter with a specified \p color.
0062      *
0063      * The filling rect is limited by \p rc to allow multithreaded
0064      * filling/processing.
0065      */
0066     void fillSelection(const QRect &rc, const KoColor &color);
0067 
0068     /**
0069      * Fill a rectangle with a certain color and opacity.
0070      */
0071     void fillRect(qint32 x,
0072                   qint32 y,
0073                   qint32 w,
0074                   qint32 h,
0075                   const KoColor &c,
0076                   quint8 opacity);
0077 
0078     /**
0079      * Overloaded version of the above function.
0080      */
0081     inline void fillRect(const QRect &rc, const KoColor &c, quint8 opacity)
0082     {
0083         fillRect(rc.x(), rc.y(), rc.width(), rc.height(), c, opacity);
0084     }
0085 
0086     /**
0087      * Fill a rectangle with a certain color.
0088      */
0089     inline void
0090     fillRect(qint32 x, qint32 y, qint32 w, qint32 h, const KoColor &c)
0091     {
0092         fillRect(x, y, w, h, c, OPACITY_OPAQUE_U8);
0093     }
0094 
0095     /**
0096      * Overloaded version of the above function.
0097      */
0098     inline void fillRect(const QRect &rc, const KoColor &c)
0099     {
0100         fillRect(rc.x(), rc.y(), rc.width(), rc.height(), c, OPACITY_OPAQUE_U8);
0101     }
0102 
0103     /**
0104      * Fill a rectangle with a certain pattern. The pattern is repeated if it does not fit the
0105      * entire rectangle.
0106      */
0107     void fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoPatternSP pattern, const QPoint &offset = QPoint());
0108 
0109     /**
0110      * Fill a rectangle with a certain pattern. The pattern is repeated if it does not fit the
0111      * entire rectangle.
0112      *
0113      * This one uses blitting and thus makes use of proper composition.
0114      */
0115     void fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisPaintDeviceSP device, const QRect& deviceRect);
0116 
0117     /**
0118      * Overloaded version of the above function.
0119      */
0120     void fillRect(const QRect &rc, const KisPaintDeviceSP device, const QRect &deviceRect);
0121 
0122     /**
0123      * Overloaded version of the above function.
0124      */
0125     void fillRect(const QRect& rc, const KoPatternSP pattern, const QPoint &offset = QPoint());
0126 
0127     /**
0128      * Fill a rectangle with black transparent pixels (0, 0, 0, 0 for RGBA).
0129      */
0130     inline void eraseRect(qint32 x1, qint32 y1, qint32 w, qint32 h)
0131     {
0132         const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
0133         KoColor c(Qt::black, cs);
0134         fillRect(x1, y1, w, h, c, OPACITY_TRANSPARENT_U8);
0135     }
0136 
0137     /**
0138      * Overloaded version of the above function.
0139      */
0140     inline void eraseRect(const QRect &rc)
0141     {
0142         const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
0143         KoColor c(Qt::black, cs);
0144         fillRect(rc.x(),
0145                  rc.y(),
0146                  rc.width(),
0147                  rc.height(),
0148                  c,
0149                  OPACITY_TRANSPARENT_U8);
0150     }
0151 
0152     /**
0153      * @brief fillRect
0154      * Fill a rectangle with a certain pattern. The pattern is repeated if it does not fit the
0155      * entire rectangle. Differs from other functions that it uses a transform, does not support
0156      * composite ops in turn.
0157      * @param rc rectangle to fill.
0158      * @param pattern pattern to use.
0159      * @param transform transformation to apply to the pattern.
0160      */
0161     void fillRectNoCompose(const QRect& rc, const KoPatternSP pattern, const QTransform transform);
0162 
0163     /**
0164      * Fill a rectangle with a certain pattern. The pattern is repeated if it does not fit the
0165      * entire rectangle.
0166      *
0167      * This one supports transforms, but does not use blitting.
0168      */
0169     void fillRectNoCompose(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisPaintDeviceSP device, const QRect& deviceRect, const QTransform transform);
0170 
0171     /**
0172      * Fill the specified area with the output of the generator plugin that is configured
0173      * in the generator parameter
0174      */
0175     void fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisFilterConfigurationSP  generator);
0176 
0177     /**
0178      * Fills the enclosed area around the point with the set color. If
0179      * there is a selection, the whole selection is filled. Note that
0180      * you must have set the width and height on the painter if you
0181      * don't have a selection.
0182      *
0183      * @param startX the X position where the floodfill starts
0184      * @param startY the Y position where the floodfill starts
0185      * @param sourceDevice the sourceDevice that determines the area that
0186      * is floodfilled if sampleMerged is on
0187      */
0188     void fillColor(int startX, int startY, KisPaintDeviceSP sourceDevice);
0189 
0190     /**
0191      * Fills the enclosed area around the point with the set pattern.
0192      * If there is a selection, the whole selection is filled. Note
0193      * that you must have set the width and height on the painter if
0194      * you don't have a selection.
0195      *
0196      * @param startX the X position where the floodfill starts
0197      * @param startY the Y position where the floodfill starts
0198      * @param sourceDevice the sourceDevice that determines the area that
0199      * is floodfilled if sampleMerged is on
0200      * @param patternTransform transform applied to the pattern;
0201      */
0202     void fillPattern(int startX, int startY, KisPaintDeviceSP sourceDevice, QTransform patternTransform = QTransform());
0203 
0204     /**
0205      * Returns a selection mask for the floodfill starting at the specified position.
0206      * This variant basically creates a new selection object and passes it down
0207      *   to the other variant of the function.
0208      *
0209      * @param startX the X position where the floodfill starts
0210      * @param startY the Y position where the floodfill starts
0211      * @param sourceDevice the sourceDevice that determines the area that
0212      * is floodfilled if sampleMerged is on
0213      */
0214     KisPixelSelectionSP createFloodSelection(int startX, int startY,
0215                                              KisPaintDeviceSP sourceDevice, KisPaintDeviceSP existingSelection);
0216 
0217     /**
0218      * Returns a selection mask for the floodfill starting at the specified position.
0219      * This variant requires an empty selection object. It is used in cases where the pointer
0220      *    to the selection must be known beforehand, for example when the selection is filled
0221      *    in a stroke and then the pointer to the pixel selection is needed later.
0222      *
0223      * @param selection empty new selection object
0224      * @param startX the X position where the floodfill starts
0225      * @param startY the Y position where the floodfill starts
0226      * @param sourceDevice the sourceDevice that determines the area that
0227      * is floodfilled if sampleMerged is on
0228      */
0229     KisPixelSelectionSP createFloodSelection(KisPixelSelectionSP newSelection, int startX, int startY,
0230                                              KisPaintDeviceSP sourceDevice, KisPaintDeviceSP existingSelection);
0231 
0232     /**
0233      * Fills all the pixels of the @ref outSelection device inside @ref rect
0234      * if the corresponding pixels on @ref referenceDevice are similar
0235      * to @ref referenceColor
0236      *
0237      * @param outSelection the selection where the values are written to
0238      * @param referenceColor the color that we have to compare pixels to
0239      * @param referenceDevice the device that we have to use to compare colors
0240      * @param rect the rectangle that defines the area to be processed
0241      * @param mask a selection to mask the results. Set to nullptr if not needed
0242      */
0243     void createSimilarColorsSelection(KisPixelSelectionSP outSelection,
0244                                       const KoColor &referenceColor,
0245                                       KisPaintDeviceSP referenceDevice,
0246                                       const QRect &rect,
0247                                       KisPixelSelectionSP mask);
0248 
0249     /**
0250      * Create a list of jobs that will fill synchronously all the pixels of the
0251      * @ref outSelection device inside @ref rect if the corresponding pixels
0252      * on @ref referenceDevice are similar to @ref referenceColor. @ref rect
0253      * is splitted into smaller rects if needed, and the painting of each one
0254      * is distributed on several jobs
0255      *
0256      * @param outSelection the selection where the values are written to
0257      * @param referenceColor the color that we have to compare pixels to
0258      * @param referenceDevice the device that we have to use to compare colors
0259      * @param rect the rectangle that defines the area to be processed
0260      * @param mask a selection to mask the results. Set to nullptr if not needed
0261      */
0262     QVector<KisStrokeJobData*> createSimilarColorsSelectionJobs(
0263         KisPixelSelectionSP outSelection,
0264         const QSharedPointer<KoColor> referenceColor,
0265         KisPaintDeviceSP referenceDevice,
0266         const QRect &rect,
0267         KisPixelSelectionSP mask,
0268         QSharedPointer<KisProcessingVisitor::ProgressHelper> progressHelper = nullptr
0269     );
0270 
0271     /**
0272      * Set the threshold for floodfill. The range is 0-255: 0 means the fill will only
0273      * fill parts that are the exact same color, 255 means anything will be filled
0274      */
0275     inline void setFillThreshold(int threshold)
0276     {
0277         m_threshold = threshold;
0278     }
0279 
0280     /** Returns the fill threshold, see setFillThreshold for details */
0281     int fillThreshold() const {
0282         return m_threshold;
0283     }
0284 
0285     /**
0286      * Set the opacity spread for floodfill. The range is 0-100: 0% means that
0287      * the fully opaque area only encompasses the pixels exactly equal to the
0288      * seed point with the other pixels of the selected region being
0289      * semi-transparent (depending on how similar they are to the seed pixel)
0290      * up to the region boundary (given by the threshold value). 100 means that
0291      * the fully opaque area will encompass all the pixels of the selected
0292      * region up to the contour. Any value in between will make the fully opaque
0293      * portion of the region vary in size, with semi-transparent pixels
0294      * in between it and  the region boundary
0295      */
0296     void setOpacitySpread(int opacitySpread)
0297     {
0298         m_opacitySpread = opacitySpread;
0299     }
0300 
0301     /** Returns the fill opacity spread, see setOpacitySpread for details */
0302     int opacitySpread() const {
0303         return m_opacitySpread;
0304     }
0305 
0306     bool useCompositing() const {
0307         return m_useCompositing;
0308     }
0309 
0310     void setUseCompositing(bool useCompositing) {
0311         m_useCompositing = useCompositing;
0312     }
0313 
0314     /** Sets the width of the paint device */
0315     void setWidth(int w) {
0316         m_width = w;
0317     }
0318 
0319     /** Sets the height of the paint device */
0320     void setHeight(int h) {
0321         m_height = h;
0322     }
0323 
0324     /** If true, floodfill doesn't fill outside the selected area of a layer */
0325     bool careForSelection() const {
0326         return m_careForSelection;
0327     }
0328 
0329     /** Set caring for selection. See careForSelection for details */
0330     void setCareForSelection(bool set) {
0331         m_careForSelection = set;
0332     }
0333 
0334     /** Sets if antiAlias should be applied to the selection */
0335     void setAntiAlias(bool antiAlias) {
0336         m_antiAlias = antiAlias;
0337     }
0338     
0339     /** Get if antiAlias should be applied to the selection */
0340     bool antiAlias() const {
0341         return m_antiAlias;
0342     }
0343 
0344     /** Sets the auto growth/shrinking radius */
0345     void setSizemod(int sizemod) {
0346         m_sizemod = sizemod;
0347     }
0348     
0349     /** Sets how much to auto-grow or shrink (if @p sizemod is negative) the selection
0350     flood before painting, this affects every fill operation except fillRect */
0351     int sizemod() const {
0352         return m_sizemod;
0353     }
0354     
0355     /** Sets feathering radius */
0356     void setFeather(int feather) {
0357         m_feather = feather;
0358     }
0359     
0360     /** defines the feathering radius for selection flood operations, this affects every
0361     fill operation except fillRect */
0362     uint feather() const {
0363         return m_feather;
0364     }
0365 
0366     /** Sets selection borders being treated as boundary */
0367     void setUseSelectionAsBoundary(bool useSelectionAsBoundary) {
0368         m_useSelectionAsBoundary = useSelectionAsBoundary;
0369     }
0370 
0371     /** defines if the selection borders are treated as boundary in flood fill or not */
0372     uint useSelectionAsBoundary() const {
0373         return m_useSelectionAsBoundary;
0374     }
0375 
0376     /** Sets the region filling mode */
0377     void setRegionFillingMode(RegionFillingMode regionFillingMode) {
0378         m_regionFillingMode = regionFillingMode;
0379     }
0380 
0381     /** Gets the region filling mode */
0382     RegionFillingMode regionFillingMode() const {
0383         return m_regionFillingMode;
0384     }
0385 
0386     /** Sets the color of the boundary used when the region filling mode is
0387      *  RegionFillingMode_BoundaryFill
0388      */
0389     void setRegionFillingBoundaryColor(const KoColor &regionFillingBoundaryColor) {
0390         m_regionFillingBoundaryColor = regionFillingBoundaryColor;
0391     }
0392 
0393     /** Gets the color of the boundary used when the region filling mode is
0394      *  RegionFillingMode_BoundaryFill
0395      */
0396     KoColor regionFillingBoundaryColor() const {
0397         return m_regionFillingBoundaryColor;
0398     }
0399 
0400     /**
0401      *  Sets if the selection should stop growing at the darkest and/or more
0402      *  opaque pixel when using a positive grow value (sizemod)
0403      */
0404     void setStopGrowingAtDarkestPixel(bool stopGrowingAtDarkestPixel) {
0405         m_stopGrowingAtDarkestPixel = stopGrowingAtDarkestPixel;
0406     }
0407     
0408     /**
0409      *  Gets if the selection should stop growing at the darkest and/or more
0410      *  opaque pixel when using a positive grow value (sizemod)
0411      */
0412     bool stopGrowingAtDarkestPixel() const {
0413         return m_stopGrowingAtDarkestPixel;
0414     }
0415 
0416 protected:
0417     void setCurrentFillSelection(KisSelectionSP fillSelection)
0418     {
0419         m_fillSelection = fillSelection;
0420     }
0421 
0422     KisSelectionSP currentFillSelection() const
0423     {
0424         return m_fillSelection;
0425     }
0426 
0427     // for floodfill
0428     void genericFillStart(int startX, int startY, KisPaintDeviceSP sourceDevice);
0429     void genericFillEnd(KisPaintDeviceSP filled);
0430 
0431 private:
0432     KisSelectionSP m_fillSelection;
0433 
0434     int m_feather;
0435     int m_sizemod;
0436     bool m_antiAlias;
0437     int m_threshold;
0438     int m_opacitySpread;
0439     int m_width, m_height;
0440     QRect m_rect;
0441     bool m_careForSelection;
0442     bool m_useCompositing;
0443     bool m_useSelectionAsBoundary;
0444     RegionFillingMode m_regionFillingMode;
0445     KoColor m_regionFillingBoundaryColor;
0446     bool m_stopGrowingAtDarkestPixel;
0447 };
0448 
0449 #endif //KIS_FILL_PAINTER_H_