File indexing completed on 2024-12-22 04:11:45

0001 /*
0002     SPDX-FileCopyrightText: 2000 Matthias Elter <elter@kde.org>
0003     SPDX-FileCopyrightText: 2004 Boudewijn Rempt <boud@valdyas.org>
0004     SPDX-FileCopyrightText: 2004 Adrian Page <adrian@pagenet.plus.com>
0005     SPDX-FileCopyrightText: 2004, 2007 Sven Langkamp <sven.langkamp@gmail.com>
0006     SPDX-FileCopyrightText: 2017 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
0007 
0008     SPDX-License-Identifier: LGPL-2.1-or-later
0009  */
0010 
0011 #ifndef KOSEGMENTGRADIENT_H
0012 #define KOSEGMENTGRADIENT_H
0013 
0014 #include <QList>
0015 #include <QColor>
0016 
0017 #include <KoResource.h>
0018 #include <resources/KoAbstractGradient.h>
0019 #include "KoColor.h"
0020 
0021 #include <kritapigment_export.h>
0022 
0023 
0024 enum {
0025     INTERP_LINEAR = 0,
0026     INTERP_CURVED,
0027     INTERP_SINE,
0028     INTERP_SPHERE_INCREASING,
0029     INTERP_SPHERE_DECREASING
0030 };
0031 
0032 enum {
0033     COLOR_INTERP_RGB,
0034     COLOR_INTERP_HSV_CCW,
0035     COLOR_INTERP_HSV_CW
0036 };
0037 
0038 //For saving to .ggr to match GIMP format, we also have Foreground (transparent) and Background (transparent) modes, currently unused...
0039 enum KoGradientSegmentEndpointType {
0040     COLOR_ENDPOINT,
0041     FOREGROUND_ENDPOINT,
0042     FOREGROUND_TRANSPARENT_ENDPOINT,
0043     BACKGROUND_ENDPOINT,
0044     BACKGROUND_TRANSPARENT_ENDPOINT
0045 };
0046 
0047 struct KoGradientSegmentEndpoint {
0048     KoGradientSegmentEndpoint(qreal _off, KoColor _color, KoGradientSegmentEndpointType _type) :
0049         offset(_off), color(_color), type(_type)
0050     {
0051 
0052     }
0053 
0054     qreal offset;
0055     KoColor color;
0056     KoGradientSegmentEndpointType type;
0057 
0058 
0059 };
0060 
0061 /// Write API docs here
0062 class KRITAPIGMENT_EXPORT KoGradientSegment
0063 {
0064 public:
0065     KoGradientSegment(int interpolationType, int colorInterpolationType, KoGradientSegmentEndpoint start, KoGradientSegmentEndpoint end, qreal middleOffset);
0066 
0067     // startOffset <= t <= endOffset
0068     void colorAt(KoColor&, qreal t) const;
0069 
0070     const KoColor& startColor() const;
0071     const KoColor& endColor() const;
0072     KoGradientSegmentEndpointType startType() const;
0073     KoGradientSegmentEndpointType endType() const;
0074 
0075     void setStartColor(const KoColor& color) {
0076         m_start.color = color;
0077         if (m_start.type == FOREGROUND_TRANSPARENT_ENDPOINT || m_start.type == BACKGROUND_TRANSPARENT_ENDPOINT) {
0078             m_start.color.setOpacity(quint8(0));
0079         } else if (m_start.type == FOREGROUND_ENDPOINT || m_start.type == BACKGROUND_ENDPOINT) {
0080             m_start.color.setOpacity(quint8(255));
0081         }
0082     }
0083     void setEndColor(const KoColor& color) {
0084         m_end.color = color;
0085         if (m_end.type == FOREGROUND_TRANSPARENT_ENDPOINT || m_end.type == BACKGROUND_TRANSPARENT_ENDPOINT) {
0086             m_end.color.setOpacity(quint8(0));
0087         } else if (m_end.type == FOREGROUND_ENDPOINT || m_end.type == BACKGROUND_ENDPOINT) {
0088             m_end.color.setOpacity(quint8(255));
0089         }
0090     }
0091 
0092     void setStartType(KoGradientSegmentEndpointType type);
0093     void setEndType(KoGradientSegmentEndpointType type);
0094 
0095     qreal startOffset() const;
0096     qreal middleOffset() const;
0097     qreal endOffset() const;
0098 
0099     void setStartOffset(qreal t);
0100     void setMiddleOffset(qreal t);
0101     void setEndOffset(qreal t);
0102 
0103     void setVariableColors(const KoColor& foreground, const KoColor& background);
0104     bool hasVariableColors();
0105 
0106     qreal length() {
0107         return m_length;
0108     }
0109 
0110     int interpolation() const;
0111     int colorInterpolation() const;
0112 
0113     void setInterpolation(int interpolationType);
0114     void setColorInterpolation(int colorInterpolationType);
0115 
0116     void mirrorSegment();
0117 
0118     bool isValid() const;
0119 
0120 protected:
0121 
0122     class ColorInterpolationStrategy
0123     {
0124     public:
0125         ColorInterpolationStrategy() {}
0126         virtual ~ColorInterpolationStrategy() {}
0127 
0128         virtual void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const = 0;
0129         virtual int type() const = 0;
0130     };
0131 
0132     class RGBColorInterpolationStrategy : public ColorInterpolationStrategy
0133     {
0134     public:
0135         static RGBColorInterpolationStrategy *instance();
0136 
0137         void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const override;
0138         int type() const override {
0139             return COLOR_INTERP_RGB;
0140         }
0141 
0142     private:
0143         RGBColorInterpolationStrategy();
0144 
0145         static RGBColorInterpolationStrategy *m_instance;
0146         const KoColorSpace * const m_colorSpace;
0147     };
0148 
0149     class HSVCWColorInterpolationStrategy : public ColorInterpolationStrategy
0150     {
0151     public:
0152         static HSVCWColorInterpolationStrategy *instance();
0153 
0154         void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const override;
0155         int type() const override {
0156             return COLOR_INTERP_HSV_CW;
0157         }
0158     private:
0159         HSVCWColorInterpolationStrategy();
0160 
0161         static HSVCWColorInterpolationStrategy *m_instance;
0162         const KoColorSpace * const m_colorSpace;
0163     };
0164 
0165     class HSVCCWColorInterpolationStrategy : public ColorInterpolationStrategy
0166     {
0167     public:
0168         static HSVCCWColorInterpolationStrategy *instance();
0169 
0170         void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const override;
0171         int type() const override {
0172             return COLOR_INTERP_HSV_CCW;
0173         }
0174     private:
0175         HSVCCWColorInterpolationStrategy();
0176 
0177         static HSVCCWColorInterpolationStrategy *m_instance;
0178         const KoColorSpace * const m_colorSpace;
0179     };
0180 
0181     class InterpolationStrategy
0182     {
0183     public:
0184         InterpolationStrategy() {}
0185         virtual ~InterpolationStrategy() {}
0186 
0187         virtual qreal valueAt(qreal t, qreal middle) const = 0;
0188         virtual int type() const = 0;
0189     };
0190 
0191     class LinearInterpolationStrategy : public InterpolationStrategy
0192     {
0193     public:
0194         static LinearInterpolationStrategy *instance();
0195 
0196         qreal valueAt(qreal t, qreal middle) const override;
0197         int type() const override {
0198             return INTERP_LINEAR;
0199         }
0200 
0201         // This does the actual calculation and is made
0202         // static as an optimization for the other
0203         // strategies that need this for their own calculation.
0204         static qreal calcValueAt(qreal t, qreal middle);
0205 
0206     private:
0207         LinearInterpolationStrategy() {}
0208 
0209         static LinearInterpolationStrategy *m_instance;
0210     };
0211 
0212     class CurvedInterpolationStrategy : public InterpolationStrategy
0213     {
0214     public:
0215         static CurvedInterpolationStrategy *instance();
0216 
0217         qreal valueAt(qreal t, qreal middle) const override;
0218         int type() const override {
0219             return INTERP_CURVED;
0220         }
0221     private:
0222         CurvedInterpolationStrategy();
0223 
0224         static CurvedInterpolationStrategy *m_instance;
0225         qreal m_logHalf;
0226     };
0227 
0228     class SphereIncreasingInterpolationStrategy : public InterpolationStrategy
0229     {
0230     public:
0231         static SphereIncreasingInterpolationStrategy *instance();
0232 
0233         qreal valueAt(qreal t, qreal middle) const override;
0234         int type() const override {
0235             return INTERP_SPHERE_INCREASING;
0236         }
0237     private:
0238         SphereIncreasingInterpolationStrategy() {}
0239 
0240         static SphereIncreasingInterpolationStrategy *m_instance;
0241     };
0242 
0243     class SphereDecreasingInterpolationStrategy : public InterpolationStrategy
0244     {
0245     public:
0246         static SphereDecreasingInterpolationStrategy *instance();
0247 
0248         qreal valueAt(qreal t, qreal middle) const override;
0249         int type() const override {
0250             return INTERP_SPHERE_DECREASING;
0251         }
0252     private:
0253         SphereDecreasingInterpolationStrategy() {}
0254 
0255         static SphereDecreasingInterpolationStrategy *m_instance;
0256     };
0257 
0258     class SineInterpolationStrategy : public InterpolationStrategy
0259     {
0260     public:
0261         static SineInterpolationStrategy *instance();
0262 
0263         qreal valueAt(qreal t, qreal middle) const override;
0264         int type() const override {
0265             return INTERP_SINE;
0266         }
0267     private:
0268         SineInterpolationStrategy() {}
0269 
0270         static SineInterpolationStrategy *m_instance;
0271     };
0272 private:
0273     InterpolationStrategy *m_interpolator;
0274     ColorInterpolationStrategy *m_colorInterpolator;
0275 
0276     qreal m_middleOffset;
0277     qreal m_length;
0278     qreal m_middleT;
0279 
0280     KoGradientSegmentEndpoint m_start, m_end;
0281     bool m_hasVariableColors = false;
0282 
0283 };
0284 
0285 /**
0286   * KoSegmentGradient stores a segment based gradients like Gimp gradients
0287   */
0288 class KRITAPIGMENT_EXPORT KoSegmentGradient : public KoAbstractGradient
0289 {
0290 
0291 public:
0292     explicit KoSegmentGradient(const QString &file = QString());
0293     ~KoSegmentGradient() override;
0294     KoSegmentGradient(const KoSegmentGradient &rhs);
0295     KoSegmentGradient &operator=(const KoSegmentGradient &rhs) = delete;
0296     KoResourceSP clone() const override;
0297 
0298     bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
0299     bool saveToDevice(QIODevice* dev) const override;
0300 
0301     QPair<QString, QString> resourceType() const override {
0302         return QPair<QString, QString>(ResourceType::Gradients, ResourceSubType::SegmentedGradients);
0303     }
0304 
0305     /// reimplemented
0306     void colorAt(KoColor& dst, qreal t) const override;
0307 
0308     QList<int> requiredCanvasResources() const override;
0309     void bakeVariableColors(KoCanvasResourcesInterfaceSP canvasResourcesInterface) override;
0310     void updateVariableColors(KoCanvasResourcesInterfaceSP canvasResourcesInterface) override;
0311 
0312     /**
0313      * Returns the segment at a given position
0314      * @param t position inside the gradient, with 0 <= t <= 1
0315      * @return the segment the position, 0 if no segment is found
0316      */
0317     KoGradientSegment *segmentAt(qreal t) const;
0318 
0319     /// reimplemented
0320     QGradient* toQGradient() const override;
0321 
0322     /// reimplemented
0323     QString defaultFileExtension() const override;
0324 
0325     /**
0326      * @brief toXML
0327      * convert the gradient to xml.
0328      */
0329     void toXML(QDomDocument& doc, QDomElement& gradientElt) const;
0330     /**
0331      * @brief fromXML
0332      * get a segment gradient from xml.
0333      * @return gradient
0334      */
0335     static KoSegmentGradient fromXML(const QDomElement& elt);
0336 
0337         /**
0338      * a gradient color picker can consist of one or more segments.
0339      * A segment has two end points - each color in the gradient
0340      * color picker represents a segment end point.
0341      * @param interpolation
0342      * @param colorInterpolation
0343      * @param startOffset
0344      * @param endOffset
0345      * @param middleOffset
0346      * @param left
0347      * @param right
0348      * @param leftType
0349      * @param rightType
0350      * @return void
0351      */
0352     void createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset,
0353                        const QColor & leftColor, const QColor & rightColor,
0354                        KoGradientSegmentEndpointType leftType = COLOR_ENDPOINT, KoGradientSegmentEndpointType rightType = COLOR_ENDPOINT);
0355 
0356     void createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset,
0357                        const KoColor & leftColor, const KoColor & rightColor,
0358                        KoGradientSegmentEndpointType leftType = COLOR_ENDPOINT, KoGradientSegmentEndpointType rightType = COLOR_ENDPOINT);
0359 
0360     /**
0361      * gets a list of end points of the segments in the gradient
0362      * color picker. If two colors, one segment then two end
0363      * points, and if three colors, then two segments with four
0364      * endpoints.
0365      * @return a list of double values
0366      */
0367     const QList<double> getHandlePositions() const;
0368 
0369     /**
0370      * gets a list of middle points of the segments in the gradient
0371      * color picker.
0372      * @return a list of double values
0373      */
0374     const QList<double> getMiddleHandlePositions() const;
0375 
0376     /**
0377      * Moves the StartOffset of the specified segment to the
0378      * specified value and corrects the endoffset of the previous
0379      * segment. If the segment is the first Segment the startoffset
0380      * will be set to 0.0 . The offset will maximally be moved till
0381      * the middle of the current or the previous segment. This is
0382      * useful if someone clicks to move the handler for a segment,
0383      * to set the half the segment to the right and half the segment
0384      * to the left of the handler.
0385      * @param segment the segment for which to move the relative
0386      * offset within the gradient color picker.
0387      * @param t the new startoff position for the segment
0388      * @return void
0389      */
0390     void moveSegmentStartOffset(KoGradientSegment* segment, double t);
0391 
0392     /**
0393      * Moves the endoffset of the specified segment to the specified
0394      * value and corrects the startoffset of the following segment.
0395      * If the segment is the last segment the endoffset will be set
0396      * to 1.0 . The offset will maximally be moved till the middle
0397      * of the current or the following segment. This is useful if
0398      * someone moves the segment handler in the gradient color
0399      * picker, and needs the segment to move with it. Sets the end
0400      * position of the segment to the correct new position.
0401      * @param segment the segment for which to move the relative
0402      * end position within the gradient color picker.
0403      * @param t the new end position for the segment
0404      * @return void
0405      */
0406     void moveSegmentEndOffset(KoGradientSegment* segment, double t);
0407 
0408     /**
0409      * moves the Middle of the specified segment to the specified
0410      * value. The offset will maximally be moved till the endoffset
0411      * or startoffset of the segment. This sets the middle of the
0412      * segment to the same position as the handler of the gradient
0413      * color picker.
0414      * @param segment the segment for which to move the relative
0415      * middle position within the gradient color picker.
0416      * @param t the new middle position for the segment
0417      * @return void
0418      */
0419     void moveSegmentMiddleOffset(KoGradientSegment* segment, double t);
0420 
0421     /**
0422      * splits the specified segment into two equal parts
0423      * @param segment the segment to split
0424      * @return void
0425      */
0426     void splitSegment(KoGradientSegment* segment);
0427 
0428     /**
0429      * duplicate the specified segment
0430      * @param segment the segment to duplicate
0431      * @return void
0432      */
0433     void duplicateSegment(KoGradientSegment* segment);
0434 
0435     /**
0436      * create a segment horizontally reversed to the specified one.
0437      * @param segment the segment to reverse
0438      * @return void
0439      */
0440     void mirrorSegment(KoGradientSegment* segment);
0441 
0442     /**
0443      * removes the specific segment from the gradient color picker.
0444      * @param segment the segment to remove
0445      * @return the segment which will be at the place of the old
0446      * segment. 0 if the segment is not in the gradient or it is
0447      * not possible to remove the segment.
0448      */
0449     KoGradientSegment* removeSegment(KoGradientSegment* segment);
0450 
0451     /**
0452      * removes the specific segment from the gradient color picker and
0453      * modifies the previous and next segments so that they end and start
0454      * where the middle of the passed segment was respectively.
0455      * @param segment the segment to remove
0456      * @return the segment which will be at the place of the old
0457      * segment: the previous segment if the passed segment was not the first;
0458      * the next segment if the passed segment was the first; 0 if the passed
0459      * segment is not in the gradient or it is not possible to remove the segment.
0460      */
0461     KoGradientSegment* collapseSegment(KoGradientSegment* segment);
0462 
0463     /**
0464      * checks if it's possible to remove a segment (at least two
0465      * segments in the gradient)
0466      * @return true if it's possible to remove an segment
0467      */
0468     bool removeSegmentPossible() const;
0469 
0470     const QList<KoGradientSegment *>& segments() const;
0471     void setSegments(const QList<KoGradientSegment*> &segments);
0472 
0473 protected:
0474 
0475     inline void pushSegment(KoGradientSegment* segment) {
0476         m_segments.push_back(segment);
0477     }
0478 
0479     QList<KoGradientSegment *> m_segments;
0480 
0481 private:
0482     bool init();
0483 };
0484 
0485 typedef QSharedPointer<KoSegmentGradient> KoSegmentGradientSP;
0486 
0487 #endif // KOSEGMENTGRADIENT_H
0488