Krita Source Code Documentation
Loading...
Searching...
No Matches
KoSegmentGradient.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2000 Matthias Elter <elter@kde.org>
3 SPDX-FileCopyrightText: 2001 John Califf
4 SPDX-FileCopyrightText: 2004 Boudewijn Rempt <boud@valdyas.org>
5 SPDX-FileCopyrightText: 2004 Adrian Page <adrian@pagenet.plus.com>
6 SPDX-FileCopyrightText: 2004, 2007 Sven Langkamp <sven.langkamp@gmail.com>
7 SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
8 SPDX-FileCopyrightText: 2021 Deif Lou <ginoba@gmail.com>
9
10 SPDX-License-Identifier: LGPL-2.1-or-later
11 */
12
14
15#include <array>
16#include <cfloat>
17#include <cmath>
18
19#include <QTextStream>
20#include <QFile>
21#include <QByteArray>
22#include <QDomDocument>
23#include <QDomElement>
24#include <QBuffer>
25
26#include <DebugPigment.h>
30#include <kis_dom_utils.h>
31#include <kis_global.h>
32#include <klocalizedstring.h>
33
34#include "KoColor.h"
35#include "KoColorSpace.h"
37#include "KoMixColorsOp.h"
38
42
48
50 : KoAbstractGradient(filename)
51{
52}
53
55{
56 for (int i = 0; i < m_segments.count(); i++) {
57 delete m_segments[i];
58 m_segments[i] = 0;
59 }
60}
61
64{
65 Q_FOREACH (KoGradientSegment *segment, rhs.m_segments) {
66 pushSegment(new KoGradientSegment(*segment));
67 }
68}
69
74
75bool KoSegmentGradient::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
76{
77 Q_UNUSED(resourcesInterface);
78
79 QByteArray data = dev->readAll();
80
81 QTextStream fileContent(data, QIODevice::ReadOnly);
83 fileContent.setAutoDetectUnicode(true);
84
85 QString header = fileContent.readLine();
86
87 if (header != "GIMP Gradient") {
88 return false;
89 }
90
91 QString nameDefinition = fileContent.readLine();
92 QString numSegmentsText;
93
94 if (nameDefinition.startsWith("Name: ")) {
95 QString nameText = nameDefinition.right(nameDefinition.length() - 6);
96 setName(nameText);
97
98 numSegmentsText = fileContent.readLine();
99 } else {
100 // Older format without name.
101
102 numSegmentsText = nameDefinition;
103 }
104
105 dbgPigment << "Loading gradient: " << name();
106
107 int numSegments;
108 bool ok;
109
110 numSegments = numSegmentsText.toInt(&ok);
111
112 if (!ok || numSegments < 1) {
113 return false;
114 }
115 m_segments.clear();
116
117 dbgPigment << "Number of segments = " << numSegments;
118
119 const KoColorSpace *rgbColorSpace = KoColorSpaceRegistry::instance()->rgb16(KoColorSpaceRegistry::instance()->p709SRGBProfile());
120
121 for (int i = 0; i < numSegments; i++) {
122
123 QString segmentText = fileContent.readLine();
124 QStringList values = segmentText.split(' ');
125
126 qreal leftOffset = values[0].toDouble();
127 qreal middleOffset = values[1].toDouble();
128 qreal rightOffset = values[2].toDouble();
129
130 qreal leftRed = values[3].toDouble();
131 qreal leftGreen = values[4].toDouble();
132 qreal leftBlue = values[5].toDouble();
133 qreal leftAlpha = values[6].toDouble();
134
135 qreal rightRed = values[7].toDouble();
136 qreal rightGreen = values[8].toDouble();
137 qreal rightBlue = values[9].toDouble();
138 qreal rightAlpha = values[10].toDouble();
139
140 int interpolationType = values[11].toInt();
141 int colorInterpolationType = values[12].toInt();
142
143 KoGradientSegmentEndpointType startType, endType;
144 if (values.count() >= 15) { //file supports FG/BG colors
145 startType = static_cast<KoGradientSegmentEndpointType>(values[13].toInt());
146 endType = static_cast<KoGradientSegmentEndpointType>(values[14].toInt());
147 }
148 else {
149 startType = endType = COLOR_ENDPOINT;
150 }
151 std::array<quint16, 4> data;
152 data[2] = static_cast<quint16>(leftRed * quint16_MAX + 0.5);
153 data[1] = static_cast<quint16>(leftGreen * quint16_MAX + 0.5);
154 data[0] = static_cast<quint16>(leftBlue * quint16_MAX + 0.5);
155 data[3] = static_cast<quint16>(leftAlpha * quint16_MAX + 0.5);
156
157 KoColor leftColor(reinterpret_cast<quint8 *>(data.data()), rgbColorSpace);
158
159 data[2] = static_cast<quint16>(rightRed * quint16_MAX + 0.5);
160 data[1] = static_cast<quint16>(rightGreen * quint16_MAX + 0.5);
161 data[0] = static_cast<quint16>(rightBlue * quint16_MAX + 0.5);
162 data[3] = static_cast<quint16>(rightAlpha * quint16_MAX + 0.5);
163
164 KoColor rightColor(reinterpret_cast<quint8 *>(data.data()), rgbColorSpace);
165 KoGradientSegmentEndpoint left(leftOffset, leftColor, startType);
166 KoGradientSegmentEndpoint right(rightOffset, rightColor, endType);
167
168 KoGradientSegment *segment = new KoGradientSegment(interpolationType, colorInterpolationType, left, right, middleOffset);
169 Q_CHECK_PTR(segment);
170
171 if (!segment -> isValid()) {
172 delete segment;
173 return false;
174 }
175
176 m_segments.push_back(segment);
177 }
178
179 if (!m_segments.isEmpty()) {
181 setValid(true);
182 return true;
183 } else {
184 return false;
185 }
186
187}
188
189bool KoSegmentGradient::saveToDevice(QIODevice *dev) const
190{
191 QTextStream fileContent(dev);
193 fileContent << "GIMP Gradient\n";
194 fileContent << "Name: " << name() << "\n";
195 fileContent << m_segments.count() << "\n";
196
197 Q_FOREACH (KoGradientSegment* segment, m_segments) {
198 fileContent << QString::number(segment->startOffset(), 'f') << " " << QString::number(segment->middleOffset(), 'f') << " "
199 << QString::number(segment->endOffset(), 'f') << " ";
200
201 QColor startColor = segment->startColor().toQColor();
202 QColor endColor = segment->endColor().toQColor();
203 fileContent << QString::number(startColor.redF(), 'f') << " " << QString::number(startColor.greenF(), 'f') << " "
204 << QString::number(startColor.blueF(), 'f') << " " << QString::number(startColor.alphaF(), 'f') << " ";
205 fileContent << QString::number(endColor.redF(), 'f') << " " << QString::number(endColor.greenF(), 'f') << " "
206 << QString::number(endColor.blueF(), 'f') << " " << QString::number(endColor.alphaF(), 'f') << " ";
207
208 fileContent << (int)segment->interpolation() << " " << (int)segment->colorInterpolation() << " ";
209
210 fileContent << (int)segment->startType() << " " << (int)segment->endType() << "\n";
211
212 }
213
214 return true;
215}
216
218{
219 if (t < 0.0) return 0;
220 if (t > 1.0) return 0;
221 if (m_segments.isEmpty()) return 0;
222
223 for (QList<KoGradientSegment *>::const_iterator it = m_segments.begin(); it != m_segments.end(); ++it) {
224 if (t > (*it)->startOffset() - DBL_EPSILON && t < (*it)->endOffset() + DBL_EPSILON) {
225 return *it;
226 }
227 }
228
229 return 0;
230}
231
232void KoSegmentGradient::colorAt(KoColor& dst, qreal t) const
233{
234 const KoGradientSegment *segment = segmentAt(t);
235 if (segment) {
236 segment->colorAt(dst, t);
237 }
238}
239
241{
242 QGradient* gradient = new QLinearGradient();
243
244 QColor color;
245 Q_FOREACH (KoGradientSegment* segment, m_segments) {
246 segment->startColor().toQColor(&color);
247 gradient->setColorAt(segment->startOffset() , color);
248 segment->endColor().toQColor(&color);
249 gradient->setColorAt(segment->endOffset() , color);
250 }
251 return gradient;
252}
253
255{
256 return QString(".ggr");
257}
258
259void KoSegmentGradient::toXML(QDomDocument &doc, QDomElement &gradientElt) const
260{
261 gradientElt.setAttribute("type", "segment");
262 Q_FOREACH(KoGradientSegment *segment, this->segments()) {
263 QDomElement segmentElt = doc.createElement("segment");
264 QDomElement start = doc.createElement("start");
265 QDomElement end = doc.createElement("end");
266 segmentElt.setAttribute("start-offset", KisDomUtils::toString(segment->startOffset()));
267 const KoColor startColor = segment->startColor();
268 segmentElt.setAttribute("start-bitdepth", startColor.colorSpace()->colorDepthId().id());
269 segmentElt.setAttribute("start-alpha", KisDomUtils::toString(startColor.opacityF()));
270 segmentElt.setAttribute("start-type", KisDomUtils::toString(segment->startType()));
271 startColor.toXML(doc, start);
272 segmentElt.setAttribute("middle-offset", KisDomUtils::toString(segment->middleOffset()));
273 segmentElt.setAttribute("end-offset", KisDomUtils::toString(segment->endOffset()));
274 const KoColor endColor = segment->endColor();
275 segmentElt.setAttribute("end-bitdepth", endColor.colorSpace()->colorDepthId().id());
276 segmentElt.setAttribute("end-alpha", KisDomUtils::toString(endColor.opacityF()));
277 segmentElt.setAttribute("end-type", KisDomUtils::toString(segment->endType()));
278 endColor.toXML(doc, end);
279 segmentElt.setAttribute("interpolation", KisDomUtils::toString(segment->interpolation()));
280 segmentElt.setAttribute("color-interpolation", KisDomUtils::toString(segment->colorInterpolation()));
281 segmentElt.appendChild(start);
282 segmentElt.appendChild(end);
283 gradientElt.appendChild(segmentElt);
284 }
285}
286
288{
289 KoSegmentGradient gradient;
290 QDomElement segmentElt = elt.firstChildElement("segment");
291 while (!segmentElt.isNull()) {
292 int interpolation = KisDomUtils::toInt(segmentElt.attribute("interpolation", "0.0"));
293 int colorInterpolation = KisDomUtils::toInt(segmentElt.attribute("color-interpolation", "0.0"));
294 double startOffset = KisDomUtils::toDouble(segmentElt.attribute("start-offset", "0.0"));
295 qreal middleOffset = KisDomUtils::toDouble(segmentElt.attribute("middle-offset", "0.0"));
296 qreal endOffset = KisDomUtils::toDouble(segmentElt.attribute("end-offset", "0.0"));
297 QDomElement start = segmentElt.firstChildElement("start");
298 QString startBitdepth = segmentElt.attribute("start-bitdepth", Integer8BitsColorDepthID.id());
299 QColor left = KoColor::fromXML(start.firstChildElement(), startBitdepth).toQColor();
300 left.setAlphaF(KisDomUtils::toDouble(segmentElt.attribute("start-alpha", "1.0")));
301 QString endBitdepth = segmentElt.attribute("end-bitdepth", Integer8BitsColorDepthID.id());
302 QDomElement end = segmentElt.firstChildElement("end");
303 QColor right = KoColor::fromXML(end.firstChildElement(), endBitdepth).toQColor();
304 right.setAlphaF(KisDomUtils::toDouble(segmentElt.attribute("end-alpha", "1.0")));
305 KoGradientSegmentEndpointType leftType = static_cast<KoGradientSegmentEndpointType>(KisDomUtils::toInt(segmentElt.attribute("start-type", "0")));
306 KoGradientSegmentEndpointType rightType = static_cast<KoGradientSegmentEndpointType>(KisDomUtils::toInt(segmentElt.attribute("end-type", "0")));
307 gradient.createSegment(interpolation, colorInterpolation, startOffset, endOffset, middleOffset, left, right, leftType, rightType);
308 segmentElt = segmentElt.nextSiblingElement("segment");
309 }
310 if (!gradient.segments().isEmpty()) {
311 gradient.setValid(true);
312 }
313 return gradient;
314}
315
316KoGradientSegment::KoGradientSegment(int interpolationType, int colorInterpolationType, KoGradientSegmentEndpoint start, KoGradientSegmentEndpoint end, qreal middleOffset)
317 : m_start(start), m_end(end)
318{
319 m_interpolator = 0;
320
321 switch (interpolationType) {
322 case INTERP_LINEAR:
324 break;
325 case INTERP_CURVED:
327 break;
328 case INTERP_SINE:
330 break;
333 break;
336 break;
337 }
338
340
341 switch (colorInterpolationType) {
342 case COLOR_INTERP_RGB:
344 break;
347 break;
350 break;
351 }
352
353
354
355 if (m_start.offset < DBL_EPSILON) {
356 m_start.offset = 0;
357 } else if (m_start.offset > 1 - DBL_EPSILON) {
358 m_start.offset = 1;
359 }
360
361 if (middleOffset < m_start.offset + DBL_EPSILON) {
363 } else if (middleOffset > 1 - DBL_EPSILON) {
364 m_middleOffset = 1;
365 } else {
367 }
368
369 if (m_end.offset < m_middleOffset + DBL_EPSILON) {
371 } else if (m_end.offset > 1 - DBL_EPSILON) {
372 m_end.offset = 1;
373 }
374
376
377 if (m_length < DBL_EPSILON) {
378 m_middleT = 0.5;
379 } else {
381 }
382
384
385}
386
388{
389 return m_start.color;
390}
391
393{
394 return m_end.color;
395}
396
398{
399 return m_start.offset;
400}
401
403{
404 return m_middleOffset;
405}
406
408{
409 return m_end.offset;
410}
411
416
421
423 m_start.type = type;
424 if (type != COLOR_ENDPOINT) {
425 m_hasVariableColors = true;
426 }
427 else if (m_end.type == COLOR_ENDPOINT) {
428 m_hasVariableColors = false;
429 }
430}
431
433 m_end.type = type;
434 if (type != COLOR_ENDPOINT) {
435 m_hasVariableColors = true;
436 }
437 else if (m_start.type == COLOR_ENDPOINT) {
438 m_hasVariableColors = false;
439 }
440}
441
443{
444 m_start.offset = t;
446
447 if (m_length < DBL_EPSILON) {
448 m_middleT = 0.5;
449 } else {
451 }
452}
454{
455 m_middleOffset = t;
456
457 if (m_length < DBL_EPSILON) {
458 m_middleT = 0.5;
459 } else {
461 }
462}
463
465{
466 m_end.offset = t;
468
469 if (m_length < DBL_EPSILON) {
470 m_middleT = 0.5;
471 } else {
473 }
474}
475
476void KoGradientSegment::setVariableColors(const KoColor& foreground, const KoColor& background) {
477 switch (m_start.type) {
478 case COLOR_ENDPOINT:
479 break;
481 m_start.color = foreground;
482 break;
483 case FOREGROUND_TRANSPARENT_ENDPOINT: //TODO: add Transparent options to gradient editor...
484 m_start.color = foreground;
485 m_start.color.setOpacity(quint8(0));
486 break;
488 m_start.color = background;
489 break;
491 m_start.color = background;
492 m_start.color.setOpacity(quint8(0));
493 break;
494 }
495
496 switch (m_end.type) {
497 case COLOR_ENDPOINT:
498 break;
500 m_end.color = foreground;
501 break;
503 m_end.color = foreground;
504 m_end.color.setOpacity(quint8(0));
505 break;
507 m_end.color = background;
508 break;
510 m_end.color = background;
511 m_end.color.setOpacity(quint8(0));
512 break;
513 }
514}
515
519
521{
522 return m_interpolator->type();
523}
524
545
550
551void KoGradientSegment::setColorInterpolation(int colorInterpolationType)
552{
553 switch (colorInterpolationType) {
554 case COLOR_INTERP_RGB:
556 break;
559 break;
562 break;
563 }
564}
565
566void KoGradientSegment::colorAt(KoColor& dst, qreal t) const
567{
568 Q_ASSERT(t > m_start.offset - DBL_EPSILON && t < m_end.offset + DBL_EPSILON);
569
570 qreal segmentT;
571
572 if (m_length < DBL_EPSILON) {
573 segmentT = 0.5;
574 } else {
575 segmentT = qBound(0.0, (t - m_start.offset) / m_length, 1.0);
576 }
577
578 qreal colorT = m_interpolator->valueAt(segmentT, m_middleT);
579
581
582}
583
608
610{
611 if (m_interpolator == 0 || m_colorInterpolator == 0)
612 return false;
613 return true;
614}
615
620
622{
623 if (m_instance == 0) {
624 m_instance = new RGBColorInterpolationStrategy();
625 Q_CHECK_PTR(m_instance);
626 }
627
628 return m_instance;
629}
630
631void KoGradientSegment::RGBColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& _start, const KoColor& _end) const
632{
633 const KoColorSpace *mixSpace = dst.colorSpace();
634
635 KoColor startDummy(_start, mixSpace);
636 KoColor endDummy(_end, mixSpace);
637
638 const std::array<quint8*, 2> colors = {{startDummy.data(), endDummy.data()}};
639
640 std::array<qint16, 2> colorWeights{};
641 colorWeights[0] = std::lround((1.0 - t) * qint16_MAX);
642 colorWeights[1] = qint16_MAX - colorWeights[0];
643
644 mixSpace->mixColorsOp()->mixColors(colors.data(), colorWeights.data(), 2, dst.data(), qint16_MAX);
645}
646
651
653{
654 if (m_instance == 0) {
655 m_instance = new HSVCWColorInterpolationStrategy();
656 Q_CHECK_PTR(m_instance);
657 }
658
659 return m_instance;
660}
661
663{
664 QColor sc;
665 QColor ec;
666
667 start.toQColor(&sc);
668 end.toQColor(&ec);
669
670 int s = static_cast<int>(sc.saturation() + t * (ec.saturation() - sc.saturation()) + 0.5);
671 int v = static_cast<int>(sc.value() + t * (ec.value() - sc.value()) + 0.5);
672 int h;
673
674 if (ec.hue() < sc.hue()) {
675 h = static_cast<int>(ec.hue() + (1 - t) * (sc.hue() - ec.hue()) + 0.5);
676 } else {
677 h = static_cast<int>(ec.hue() + (1 - t) * (360 - ec.hue() + sc.hue()) + 0.5);
678
679 if (h > 359) {
680 h -= 360;
681 }
682 }
683
684 qreal opacity{sc.alphaF() + t * (ec.alphaF() - sc.alphaF())};
685
686 QColor result;
687 result.setHsv(h, s, v);
688 result.setAlphaF(opacity);
689 dst.fromQColor(result);
690}
691
696
697
699{
700 if (m_instance == 0) {
701 m_instance = new HSVCCWColorInterpolationStrategy();
702 Q_CHECK_PTR(m_instance);
703 }
704
705 return m_instance;
706}
707
709{
710 QColor sc;
711 QColor se;
712
713 start.toQColor(&sc);
714 end.toQColor(&se);
715
716 int s = static_cast<int>(sc.saturation() + t * (se.saturation() - sc.saturation()) + 0.5);
717 int v = static_cast<int>(sc.value() + t * (se.value() - sc.value()) + 0.5);
718 int h;
719
720 if (sc.hue() < se.hue()) {
721 h = static_cast<int>(sc.hue() + t * (se.hue() - sc.hue()) + 0.5);
722 } else {
723 h = static_cast<int>(sc.hue() + t * (360 - sc.hue() + se.hue()) + 0.5);
724
725 if (h > 359) {
726 h -= 360;
727 }
728 }
729
730 qreal opacity = sc.alphaF() + t * (se.alphaF() - sc.alphaF());
731
732 QColor result;
733 result.setHsv(h, s, v);
734 result.setAlphaF(opacity);
735 dst.fromQColor(result);
736}
737
739{
740 if (m_instance == 0) {
741 m_instance = new LinearInterpolationStrategy();
742 Q_CHECK_PTR(m_instance);
743 }
744
745 return m_instance;
746}
747
749{
750 Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
751 Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON);
752
753 qreal value = 0;
754
755 if (t <= middle) {
756 if (middle < DBL_EPSILON) {
757 value = 0;
758 } else {
759 value = (t / middle) * 0.5;
760 }
761 } else {
762 if (middle > 1 - DBL_EPSILON) {
763 value = 1;
764 } else {
765 value = ((t - middle) / (1 - middle)) * 0.5 + 0.5;
766 }
767 }
768
769 return value;
770}
771
773{
774 return calcValueAt(t, middle);
775}
776
781
783{
784 if (m_instance == 0) {
785 m_instance = new CurvedInterpolationStrategy();
786 Q_CHECK_PTR(m_instance);
787 }
788
789 return m_instance;
790}
791
793{
794 Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
795 Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON);
796
797 qreal value = 0;
798
799 if (middle < DBL_EPSILON) {
800 middle = DBL_EPSILON;
801 }
802
803 value = pow(t, m_logHalf / log(middle));
804
805 return value;
806}
807
809{
810 if (m_instance == 0) {
811 m_instance = new SineInterpolationStrategy();
812 Q_CHECK_PTR(m_instance);
813 }
814
815 return m_instance;
816}
817
819{
820 qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle);
821 qreal value = (sin(-M_PI_2 + M_PI * lt) + 1.0) / 2.0;
822
823 return value;
824}
825
827{
828 if (m_instance == 0) {
829 m_instance = new SphereIncreasingInterpolationStrategy();
830 Q_CHECK_PTR(m_instance);
831 }
832
833 return m_instance;
834}
835
837{
838 qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle) - 1;
839 qreal value = sqrt(1 - lt * lt);
840
841 return value;
842}
843
845{
846 if (m_instance == 0) {
847 m_instance = new SphereDecreasingInterpolationStrategy();
848 Q_CHECK_PTR(m_instance);
849 }
850
851 return m_instance;
852}
853
855{
856 qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle);
857 qreal value = 1 - sqrt(1 - lt * lt);
858
859 return value;
860}
861
862void KoSegmentGradient::createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, const QColor & leftColor, const QColor & rightColor,
864{
865 createSegment(interpolation
868 , endOffset
870 , KoColor(leftColor, colorSpace())
871 , KoColor(rightColor, colorSpace())
872 , leftType
873 , rightType);
874
875}
876
878{
879 KoGradientSegmentEndpoint left(startOffset, KoColor(leftColor, colorSpace()), leftType);
880 KoGradientSegmentEndpoint right(endOffset, KoColor(rightColor, colorSpace()), rightType);
881 pushSegment(new KoGradientSegment(interpolation, colorInterpolation, left, right, middleOffset));
882}
883
885{
886 QList<double> handlePositions;
887
888 handlePositions.push_back(m_segments[0]->startOffset());
889 for (int i = 0; i < m_segments.count(); i++) {
890 handlePositions.push_back(m_segments[i]->endOffset());
891 }
892 return handlePositions;
893}
894
896{
897 QList<double> middleHandlePositions;
898
899 for (int i = 0; i < m_segments.count(); i++) {
900 middleHandlePositions.push_back(m_segments[i]->middleOffset());
901 }
902 return middleHandlePositions;
903}
904
906{
907 QList<KoGradientSegment*>::iterator it = std::find(m_segments.begin(), m_segments.end(), segment);
908 if (it != m_segments.end()) {
909 if (it == m_segments.begin()) {
910 segment->setStartOffset(0.0);
911 return;
912 }
913 KoGradientSegment* previousSegment = (*(it - 1));
914 if (t > segment->startOffset()) {
915 if (t > segment->middleOffset())
916 t = segment->middleOffset();
917 } else {
918 if (t < previousSegment->middleOffset())
919 t = previousSegment->middleOffset();
920 }
921 previousSegment->setEndOffset(t);
922 segment->setStartOffset(t);
923 }
924}
925
927{
928 QList<KoGradientSegment*>::iterator it = std::find(m_segments.begin(), m_segments.end(), segment);
929 if (it != m_segments.end()) {
930 if (it + 1 == m_segments.end()) {
931 segment->setEndOffset(1.0);
932 return;
933 }
934 KoGradientSegment* followingSegment = (*(it + 1));
935 if (t < segment->endOffset()) {
936 if (t < segment->middleOffset())
937 t = segment->middleOffset();
938 } else {
939 if (t > followingSegment->middleOffset())
940 t = followingSegment->middleOffset();
941 }
942 followingSegment->setStartOffset(t);
943 segment->setEndOffset(t);
944 }
945}
946
948{
949 if (segment) {
950 if (t > segment->endOffset())
951 segment->setMiddleOffset(segment->endOffset());
952 else if (t < segment->startOffset())
953 segment->setMiddleOffset(segment->startOffset());
954 else
955 segment->setMiddleOffset(t);
956 }
957}
958
960{
961 Q_ASSERT(segment != 0);
962 QList<KoGradientSegment*>::iterator it = std::find(m_segments.begin(), m_segments.end(), segment);
963 if (it != m_segments.end()) {
964 KoColor midleoffsetColor(segment->endColor().colorSpace());
965 segment->colorAt(midleoffsetColor, segment->middleOffset());
966 KoGradientSegmentEndpoint left(segment->startOffset(), segment->startColor(), segment->startType());
967 KoGradientSegmentEndpoint right(segment->middleOffset(), midleoffsetColor, COLOR_ENDPOINT);
968 KoGradientSegment* newSegment = new KoGradientSegment(
969 segment->interpolation(), segment->colorInterpolation(),
970 left, right,
971 (segment->middleOffset() - segment->startOffset()) / 2 + segment->startOffset());
972 m_segments.insert(it, newSegment);
973 segment->setStartColor(midleoffsetColor);
974 segment->setStartOffset(segment->middleOffset());
975 segment->setMiddleOffset((segment->endOffset() - segment->startOffset()) / 2 + segment->startOffset());
976 }
977}
978
980{
981 Q_ASSERT(segment != 0);
982 QList<KoGradientSegment*>::iterator it = std::find(m_segments.begin(), m_segments.end(), segment);
983 if (it != m_segments.end()) {
984 double middlePositionPercentage = (segment->middleOffset() - segment->startOffset()) / segment->length();
985 double center = segment->startOffset() + segment->length() / 2;
986 KoGradientSegmentEndpoint left(segment->startOffset(), segment->startColor(), segment->startType());
987 KoGradientSegmentEndpoint right(center, segment->endColor(), segment->endType());
988 KoGradientSegment* newSegment = new KoGradientSegment(
989 segment->interpolation(), segment->colorInterpolation(),
990 left, right,
991 segment->length() / 2 * middlePositionPercentage + segment->startOffset());
992 m_segments.insert(it, newSegment);
993 segment->setStartOffset(center);
994 segment->setMiddleOffset(segment->length() * middlePositionPercentage + segment->startOffset());
995 }
996}
997
999{
1000 Q_ASSERT(segment != 0);
1001
1002 segment->mirrorSegment();
1003}
1004
1006{
1007 Q_ASSERT(segment != 0);
1008 if (m_segments.count() < 2)
1009 return 0;
1010 QList<KoGradientSegment*>::iterator it = std::find(m_segments.begin(), m_segments.end(), segment);
1011 if (it != m_segments.end()) {
1012 double middlePositionPercentage;
1013 KoGradientSegment* nextSegment;
1014 if (it == m_segments.begin()) {
1015 nextSegment = (*(it + 1));
1016 middlePositionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length();
1017 nextSegment->setStartOffset(segment->startOffset());
1018 nextSegment->setMiddleOffset(middlePositionPercentage * nextSegment->length() + nextSegment->startOffset());
1019 } else {
1020 nextSegment = (*(it - 1));
1021 middlePositionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length();
1022 nextSegment->setEndOffset(segment->endOffset());
1023 nextSegment->setMiddleOffset(middlePositionPercentage * nextSegment->length() + nextSegment->startOffset());
1024 }
1025
1026 delete segment;
1027 m_segments.erase(it);
1028 return nextSegment;
1029 }
1030 return 0;
1031}
1032
1034{
1035 Q_ASSERT(segment != 0);
1036 if (m_segments.count() < 2)
1037 return 0;
1038 QList<KoGradientSegment*>::iterator it = std::find(m_segments.begin(), m_segments.end(), segment);
1039 if (it != m_segments.end()) {
1040 double nextMiddlePositionPercentage, prevMiddlePositionPercentage;
1041 KoGradientSegment *nextSegment, *prevSegment, *returnSegment;
1042 if (it == m_segments.begin()) {
1043 nextSegment = (*(it + 1));
1044 nextMiddlePositionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length();
1045 nextSegment->setStartOffset(segment->startOffset());
1046 nextSegment->setMiddleOffset(nextMiddlePositionPercentage * nextSegment->length() + nextSegment->startOffset());
1047 returnSegment = nextSegment;
1048 } else if (it == m_segments.end() - 1) {
1049 prevSegment = (*(it - 1));
1050 prevMiddlePositionPercentage = (prevSegment->middleOffset() - prevSegment->startOffset()) / prevSegment->length();
1051 prevSegment->setEndOffset(segment->endOffset());
1052 prevSegment->setMiddleOffset(prevMiddlePositionPercentage * prevSegment->length() + prevSegment->startOffset());
1053 returnSegment = prevSegment;
1054 } else {
1055 prevSegment = (*(it - 1));
1056 nextSegment = (*(it + 1));
1057 prevMiddlePositionPercentage = (prevSegment->middleOffset() - prevSegment->startOffset()) / prevSegment->length();
1058 nextMiddlePositionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length();
1059 qreal offset = (segment->startOffset() + segment->endOffset()) / 2.0;
1060 prevSegment->setEndOffset(offset);
1061 prevSegment->setMiddleOffset(prevMiddlePositionPercentage * prevSegment->length() + prevSegment->startOffset());
1062 nextSegment->setStartOffset(offset);
1063 nextSegment->setMiddleOffset(nextMiddlePositionPercentage * nextSegment->length() + nextSegment->startOffset());
1064 returnSegment = prevSegment;
1065 }
1066
1067 delete segment;
1068 m_segments.erase(it);
1069 return returnSegment;
1070 }
1071 return 0;
1072}
1073
1075{
1076 if (m_segments.count() < 2)
1077 return false;
1078 return true;
1079}
1080
1082{
1083 return m_segments;
1084}
1085
1087{
1088 for (int i = 0; i < m_segments.count(); i++) {
1089 delete m_segments[i];
1090 }
1091 m_segments.clear();
1092 KoColor color;
1093 for (const KoGradientSegment *segment : segments) {
1094 KoGradientSegment *newSegment =
1096 segment->interpolation(),
1097 segment->colorInterpolation(),
1099 segment->startOffset(),
1100 segment->startColor().convertedTo(colorSpace()),
1101 segment->startType()
1102 ),
1104 segment->endOffset(),
1105 segment->endColor().convertedTo(colorSpace()),
1106 segment->endType()
1107 ),
1108 segment->middleOffset()
1109 );
1110 m_segments.append(newSegment);
1111 }
1112 if (!m_segments.isEmpty()) {
1113 setValid(true);
1114 } else {
1115 setValid(false);
1116 }
1117 updatePreview();
1118}
1119
1121{
1122 bool hasVariableColors = false;
1123 for (int i = 0; i < m_segments.count(); i++) {
1124 if (m_segments[i]->hasVariableColors()) {
1125 hasVariableColors = true;
1126 break;
1127 }
1128 }
1129
1130 QList<int> result;
1131 if (hasVariableColors) {
1133 }
1134
1135 return result;
1136}
1137
1139{
1140 const KoColor fgColor = canvasResourcesInterface->resource(KoCanvasResource::ForegroundColor).value<KoColor>().convertedTo(colorSpace());
1141 const KoColor bgColor = canvasResourcesInterface->resource(KoCanvasResource::BackgroundColor).value<KoColor>().convertedTo(colorSpace());
1142
1143 for (auto it = m_segments.begin(); it != m_segments.end(); ++it) {
1144 if ((*it)->hasVariableColors()) {
1145 (*it)->setVariableColors(fgColor, bgColor);
1146 (*it)->setStartType(COLOR_ENDPOINT);
1147 (*it)->setEndType(COLOR_ENDPOINT);
1148 }
1149 }
1150}
1151
1153{
1154 const KoColor fgColor = canvasResourcesInterface->resource(KoCanvasResource::ForegroundColor).value<KoColor>().convertedTo(colorSpace());
1155 const KoColor bgColor = canvasResourcesInterface->resource(KoCanvasResource::BackgroundColor).value<KoColor>().convertedTo(colorSpace());
1156
1157 for (auto it = m_segments.begin(); it != m_segments.end(); ++it) {
1158 if ((*it)->hasVariableColors()) {
1159 (*it)->setVariableColors(fgColor, bgColor);
1160 }
1161 }
1162}
#define dbgPigment
float value(const T *src, size_t ch)
qreal v
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
KoGradientSegmentEndpointType
@ COLOR_ENDPOINT
@ BACKGROUND_TRANSPARENT_ENDPOINT
@ FOREGROUND_ENDPOINT
@ BACKGROUND_ENDPOINT
@ FOREGROUND_TRANSPARENT_ENDPOINT
@ INTERP_SPHERE_DECREASING
@ INTERP_LINEAR
@ INTERP_CURVED
@ INTERP_SINE
@ INTERP_SPHERE_INCREASING
@ COLOR_INTERP_RGB
@ COLOR_INTERP_HSV_CCW
@ COLOR_INTERP_HSV_CW
PythonPluginManager * instance
virtual KoID colorDepthId() const =0
KoMixColorsOp * mixColorsOp
qreal opacityF() const
Definition KoColor.cpp:345
static KoColor fromXML(const QDomElement &elt, const QString &channelDepthId)
Definition KoColor.cpp:350
void toXML(QDomDocument &doc, QDomElement &colorElt) const
Definition KoColor.cpp:304
void setOpacity(quint8 alpha)
Definition KoColor.cpp:333
void fromQColor(const QColor &c)
Convenient function for converting from a QColor.
Definition KoColor.cpp:213
quint8 * data()
Definition KoColor.h:144
const KoColorSpace * colorSpace() const
return the current colorSpace
Definition KoColor.h:82
void toQColor(QColor *c) const
a convenience method for the above.
Definition KoColor.cpp:198
virtual void colorAt(KoColor &dst, qreal t, const KoColor &start, const KoColor &end) const =0
qreal valueAt(qreal t, qreal middle) const override
static CurvedInterpolationStrategy * instance()
static CurvedInterpolationStrategy * m_instance
static HSVCCWColorInterpolationStrategy * instance()
static HSVCCWColorInterpolationStrategy * m_instance
void colorAt(KoColor &dst, qreal t, const KoColor &start, const KoColor &end) const override
static HSVCWColorInterpolationStrategy * m_instance
static HSVCWColorInterpolationStrategy * instance()
void colorAt(KoColor &dst, qreal t, const KoColor &start, const KoColor &end) const override
virtual qreal valueAt(qreal t, qreal middle) const =0
qreal valueAt(qreal t, qreal middle) const override
static qreal calcValueAt(qreal t, qreal middle)
static LinearInterpolationStrategy * m_instance
static LinearInterpolationStrategy * instance()
void colorAt(KoColor &dst, qreal t, const KoColor &start, const KoColor &end) const override
static RGBColorInterpolationStrategy * m_instance
static RGBColorInterpolationStrategy * instance()
static SineInterpolationStrategy * instance()
static SineInterpolationStrategy * m_instance
qreal valueAt(qreal t, qreal middle) const override
static SphereDecreasingInterpolationStrategy * m_instance
qreal valueAt(qreal t, qreal middle) const override
static SphereDecreasingInterpolationStrategy * instance()
qreal valueAt(qreal t, qreal middle) const override
static SphereIncreasingInterpolationStrategy * m_instance
static SphereIncreasingInterpolationStrategy * instance()
Write API docs here.
ColorInterpolationStrategy * m_colorInterpolator
void setEndType(KoGradientSegmentEndpointType type)
KoGradientSegment(int interpolationType, int colorInterpolationType, KoGradientSegmentEndpoint start, KoGradientSegmentEndpoint end, qreal middleOffset)
void setInterpolation(int interpolationType)
void setMiddleOffset(qreal t)
void setVariableColors(const KoColor &foreground, const KoColor &background)
void colorAt(KoColor &, qreal t) const
KoGradientSegmentEndpointType endType() const
InterpolationStrategy * m_interpolator
void setColorInterpolation(int colorInterpolationType)
KoGradientSegmentEndpointType startType() const
void setStartColor(const KoColor &color)
void setStartType(KoGradientSegmentEndpointType type)
KoGradientSegmentEndpoint m_end
KoGradientSegmentEndpoint m_start
void setStartOffset(qreal t)
void setEndColor(const KoColor &color)
const KoColor & startColor() const
const KoColor & endColor() const
QString id() const
Definition KoID.cpp:63
virtual void mixColors(const quint8 *const *colors, const qint16 *weights, int nColors, quint8 *dst, int weightSum=255) const =0
KoGradientSegment * removeSegment(KoGradientSegment *segment)
void createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, const QColor &leftColor, const QColor &rightColor, KoGradientSegmentEndpointType leftType=COLOR_ENDPOINT, KoGradientSegmentEndpointType rightType=COLOR_ENDPOINT)
const QList< KoGradientSegment * > & segments() const
KoGradientSegment * collapseSegment(KoGradientSegment *segment)
void moveSegmentMiddleOffset(KoGradientSegment *segment, double t)
KoResourceSP clone() const override
QList< int > requiredCanvasResources() const override
void toXML(QDomDocument &doc, QDomElement &gradientElt) const
toXML convert the gradient to xml.
static KoSegmentGradient fromXML(const QDomElement &elt)
fromXML get a segment gradient from xml.
void moveSegmentEndOffset(KoGradientSegment *segment, double t)
KoSegmentGradient(const QString &file=QString())
const QList< double > getMiddleHandlePositions() const
void bakeVariableColors(KoCanvasResourcesInterfaceSP canvasResourcesInterface) override
void duplicateSegment(KoGradientSegment *segment)
KoGradientSegment * segmentAt(qreal t) const
QList< KoGradientSegment * > m_segments
void setSegments(const QList< KoGradientSegment * > &segments)
void updateVariableColors(KoCanvasResourcesInterfaceSP canvasResourcesInterface) override
void moveSegmentStartOffset(KoGradientSegment *segment, double t)
void mirrorSegment(KoGradientSegment *segment)
void colorAt(KoColor &dst, qreal t) const override
reimplemented
QString defaultFileExtension() const override
reimplemented
bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override
bool saveToDevice(QIODevice *dev) const override
void pushSegment(KoGradientSegment *segment)
QGradient * toQGradient() const override
reimplemented
const QList< double > getHandlePositions() const
void splitSegment(KoGradientSegment *segment)
bool removeSegmentPossible() const
const qint16 qint16_MAX
Definition kis_global.h:28
const quint16 quint16_MAX
Definition kis_global.h:25
#define M_PI
Definition kis_global.h:111
QSharedPointer< KoResource > KoResourceSP
double toDouble(const QString &str, bool *ok=nullptr)
int toInt(const QString &str, bool *ok=nullptr)
QString toString(const QString &value)
void setUtf8OnStream(QTextStream &stream)
@ BackgroundColor
The active background color selected for this canvas.
@ ForegroundColor
The active foreground color selected for this canvas.
static KoColorSpaceRegistry * instance()
const KoColorSpace * rgb16(const QString &profileName=QString())
KoGradientSegmentEndpointType type
void setValid(bool valid)
void setName(const QString &name)
QString name