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