Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_gradient_painter.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2004 Adrian Page <adrian@pagenet.plus.com>
3 * SPDX-FileCopyrightText: 2019 Miguel Lopez <reptillia39@live.com>
4 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
10
11#include <algorithm>
12#include <cfloat>
13
14#include <KoColorSpace.h>
16#include <KoUpdater.h>
17
20#include "kis_global.h"
21#include "kis_paint_device.h"
22#include <resources/KoPattern.h>
23#include "kis_selection.h"
24
26#include "kis_image.h"
31#include "krita_utils.h"
32#include "KoMixColorsOp.h"
33#include <KisDitherMaths.h>
34#include <KisDitherOp.h>
35#include <KoCachedGradient.h>
36
37namespace
38{
39
40class LinearGradientStrategy : public KisGradientShapeStrategy
41{
42
43public:
44 LinearGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
45
46 double valueAt(double x, double y) const override;
47
48protected:
49 double m_normalisedVectorX;
50 double m_normalisedVectorY;
51 double m_vectorLength;
52};
53
54LinearGradientStrategy::LinearGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
55 : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
56{
57 double dx = gradientVectorEnd.x() - gradientVectorStart.x();
58 double dy = gradientVectorEnd.y() - gradientVectorStart.y();
59
60 m_vectorLength = sqrt((dx * dx) + (dy * dy));
61
62 if (m_vectorLength < DBL_EPSILON) {
63 m_normalisedVectorX = 0;
64 m_normalisedVectorY = 0;
65 } else {
66 m_normalisedVectorX = dx / m_vectorLength;
67 m_normalisedVectorY = dy / m_vectorLength;
68 }
69}
70
71double LinearGradientStrategy::valueAt(double x, double y) const
72{
73 double vx = x - m_gradientVectorStart.x();
74 double vy = y - m_gradientVectorStart.y();
75
76 // Project the vector onto the normalised gradient vector.
77 double t = vx * m_normalisedVectorX + vy * m_normalisedVectorY;
78
79 if (m_vectorLength < DBL_EPSILON) {
80 t = 0;
81 } else {
82 // Scale to 0 to 1 over the gradient vector length.
83 t /= m_vectorLength;
84 }
85
86 return t;
87}
88
89
90class BiLinearGradientStrategy : public LinearGradientStrategy
91{
92
93public:
94 BiLinearGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
95
96 double valueAt(double x, double y) const override;
97};
98
99BiLinearGradientStrategy::BiLinearGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
100 : LinearGradientStrategy(gradientVectorStart, gradientVectorEnd)
101{
102}
103
104double BiLinearGradientStrategy::valueAt(double x, double y) const
105{
106 double t = LinearGradientStrategy::valueAt(x, y);
107
108 // Reflect
109 if (t < -DBL_EPSILON) {
110 t = -t;
111 }
112
113 return t;
114}
115
116
117class RadialGradientStrategy : public KisGradientShapeStrategy
118{
119
120public:
121 RadialGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
122
123 double valueAt(double x, double y) const override;
124
125protected:
126 double m_radius;
127};
128
129RadialGradientStrategy::RadialGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
130 : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
131{
132 double dx = gradientVectorEnd.x() - gradientVectorStart.x();
133 double dy = gradientVectorEnd.y() - gradientVectorStart.y();
134
135 m_radius = sqrt((dx * dx) + (dy * dy));
136}
137
138double RadialGradientStrategy::valueAt(double x, double y) const
139{
140 double dx = x - m_gradientVectorStart.x();
141 double dy = y - m_gradientVectorStart.y();
142
143 double distance = sqrt((dx * dx) + (dy * dy));
144
145 double t;
146
147 if (m_radius < DBL_EPSILON) {
148 t = 0;
149 } else {
150 t = distance / m_radius;
151 }
152
153 return t;
154}
155
156
157class SquareGradientStrategy : public KisGradientShapeStrategy
158{
159
160public:
161 SquareGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
162
163 double valueAt(double x, double y) const override;
164
165protected:
166 double m_normalisedVectorX;
167 double m_normalisedVectorY;
168 double m_vectorLength;
169};
170
171SquareGradientStrategy::SquareGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
172 : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
173{
174 double dx = gradientVectorEnd.x() - gradientVectorStart.x();
175 double dy = gradientVectorEnd.y() - gradientVectorStart.y();
176
177 m_vectorLength = sqrt((dx * dx) + (dy * dy));
178
179 if (m_vectorLength < DBL_EPSILON) {
180 m_normalisedVectorX = 0;
181 m_normalisedVectorY = 0;
182 } else {
183 m_normalisedVectorX = dx / m_vectorLength;
184 m_normalisedVectorY = dy / m_vectorLength;
185 }
186}
187
188double SquareGradientStrategy::valueAt(double x, double y) const
189{
190 double px = x - m_gradientVectorStart.x();
191 double py = y - m_gradientVectorStart.y();
192
193 double distance1 = 0;
194 double distance2 = 0;
195
196 double t;
197
198 if (m_vectorLength > DBL_EPSILON) {
199
200 // Point to line distance is:
201 // distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / m_vectorLength;
202 //
203 // Here l0 = (0, 0) and |l1 - l0| = 1
204
205 distance1 = -m_normalisedVectorY * px + m_normalisedVectorX * py;
206 distance1 = fabs(distance1);
207
208 // Rotate point by 90 degrees and get the distance to the perpendicular
209 distance2 = -m_normalisedVectorY * -py + m_normalisedVectorX * px;
210 distance2 = fabs(distance2);
211
212 t = qMax(distance1, distance2) / m_vectorLength;
213 } else {
214 t = 0;
215 }
216
217 return t;
218}
219
220
221class ConicalGradientStrategy : public KisGradientShapeStrategy
222{
223
224public:
225 ConicalGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
226
227 double valueAt(double x, double y) const override;
228
229protected:
230 double m_vectorAngle;
231};
232
233ConicalGradientStrategy::ConicalGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
234 : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
235{
236 double dx = gradientVectorEnd.x() - gradientVectorStart.x();
237 double dy = gradientVectorEnd.y() - gradientVectorStart.y();
238
239 // Get angle from 0 to 2 PI.
240 m_vectorAngle = atan2(dy, dx) + M_PI;
241}
242
243double ConicalGradientStrategy::valueAt(double x, double y) const
244{
245 double px = x - m_gradientVectorStart.x();
246 double py = y - m_gradientVectorStart.y();
247
248 double angle = atan2(py, px) + M_PI;
249
250 angle -= m_vectorAngle;
251
252 if (angle < 0) {
253 angle += 2 * M_PI;
254 }
255
256 double t = angle / (2 * M_PI);
257
258 return t;
259}
260
261
262class ConicalSymetricGradientStrategy : public KisGradientShapeStrategy
263{
264public:
265 ConicalSymetricGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
266
267 double valueAt(double x, double y) const override;
268
269protected:
270 double m_vectorAngle;
271};
272
273ConicalSymetricGradientStrategy::ConicalSymetricGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
274 : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
275{
276 double dx = gradientVectorEnd.x() - gradientVectorStart.x();
277 double dy = gradientVectorEnd.y() - gradientVectorStart.y();
278
279 // Get angle from 0 to 2 PI.
280 m_vectorAngle = atan2(dy, dx) + M_PI;
281}
282
283double ConicalSymetricGradientStrategy::valueAt(double x, double y) const
284{
285 double px = x - m_gradientVectorStart.x();
286 double py = y - m_gradientVectorStart.y();
287
288 double angle = atan2(py, px) + M_PI;
289
290 angle -= m_vectorAngle;
291
292 if (angle < 0) {
293 angle += 2 * M_PI;
294 }
295
296 double t;
297
298 if (angle < M_PI) {
299 t = angle / M_PI;
300 } else {
301 t = 1 - ((angle - M_PI) / M_PI);
302 }
303
304 return t;
305}
306
307class SpiralGradientStrategy : public KisGradientShapeStrategy
308{
309public:
310 SpiralGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
311
312 double valueAt(double x, double y) const override;
313
314protected:
315 double m_vectorAngle;
316 double m_radius;
317};
318
319SpiralGradientStrategy::SpiralGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
320 : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
321{
322 double dx = gradientVectorEnd.x() - gradientVectorStart.x();
323 double dy = gradientVectorEnd.y() - gradientVectorStart.y();
324
325 // Get angle from 0 to 2 PI.
326 m_vectorAngle = atan2(dy, dx) + M_PI;
327 m_radius = sqrt((dx * dx) + (dy * dy));
328};
329
330double SpiralGradientStrategy::valueAt(double x, double y) const
331{
332 double dx = x - m_gradientVectorStart.x();
333 double dy = y - m_gradientVectorStart.y();
334
335 double distance = sqrt((dx * dx) + (dy * dy));
336 double angle = atan2(dy, dx) + M_PI;
337
338 double t;
339 angle -= m_vectorAngle;
340
341 if (m_radius < DBL_EPSILON) {
342 t = 0;
343 } else {
344 t = distance / m_radius;
345 }
346
347 if (angle < 0) {
348 angle += 2 * M_PI;
349 }
350
351 t += angle / (2 * M_PI);
352
353 return t;
354
355};
356
357class ReverseSpiralGradientStrategy : public KisGradientShapeStrategy
358{
359public:
360 ReverseSpiralGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
361
362 double valueAt(double x, double y) const override;
363
364protected:
365 double m_vectorAngle;
366 double m_radius;
367};
368
369ReverseSpiralGradientStrategy::ReverseSpiralGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
370 : KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
371{
372 double dx = gradientVectorEnd.x() - gradientVectorStart.x();
373 double dy = gradientVectorEnd.y() - gradientVectorStart.y();
374
375 // Get angle from 0 to 2 PI.
376 m_vectorAngle = atan2(dy, dx) + M_PI;
377 m_radius = sqrt((dx * dx) + (dy * dy));
378};
379
380double ReverseSpiralGradientStrategy::valueAt(double x, double y) const
381{
382 double dx = x - m_gradientVectorStart.x();
383 double dy = y - m_gradientVectorStart.y();
384
385 double distance = sqrt((dx * dx) + (dy * dy));
386 double angle = atan2(dy, dx) + M_PI;
387
388 double t;
389 angle -= m_vectorAngle;
390
391 if (m_radius < DBL_EPSILON) {
392 t = 0;
393 } else {
394 t = distance / m_radius;
395 }
396
397 if (angle < 0) {
398 angle += 2 * M_PI;
399 }
400
401 //Reverse direction of spiral gradient
402 t += 1 - (angle / (2 * M_PI));
403
404 return t;
405
406};
407
408class DitheredGradientStrategy : public KisGradientShapeStrategy
409{
410
411public:
412 DitheredGradientStrategy(KisGradientPainter::enumDither dither, int ditherSteps, QSharedPointer<KisGradientShapeStrategy> shapeStrategy);
413
414 double valueAt(double x, double y) const override;
415
416protected:
418 int m_ditherSteps;
420};
421
422DitheredGradientStrategy::DitheredGradientStrategy(KisGradientPainter::enumDither dither,
423 int ditherSteps,
425 : m_dither(dither)
426 , m_ditherSteps(ditherSteps)
427 , m_shapeStrategy(shapeStrategy)
428{
429}
430
431double DitheredGradientStrategy::valueAt(double x, double y) const
432{
433 double value = m_shapeStrategy->valueAt(x, y);
434 double ditherFactor = 0.0;
435
436 switch (m_dither) {
439 break;
442 break;
445 break;
446 default:
447 return value;
448 }
449
450 // Divide gradient into discrete steps and adjust value with dithering matrix.
451 value *= m_ditherSteps - 1;
452 value = std::floor(value + ditherFactor);
453
454 // Bring value to original scale.
455 value /= m_ditherSteps - 1;
456
457 return value;
458}
459
460class GradientRepeatStrategy
461{
462public:
463 GradientRepeatStrategy() {}
464 virtual ~GradientRepeatStrategy() {}
465
466 virtual double valueAt(double t) const = 0;
467};
468
469
470class GradientRepeatNoneStrategy : public GradientRepeatStrategy
471{
472public:
473 static GradientRepeatNoneStrategy *instance();
474
475 double valueAt(double t) const override;
476
477private:
478 GradientRepeatNoneStrategy() {}
479
480 static GradientRepeatNoneStrategy *m_instance;
481};
482
483GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::m_instance = 0;
484
485GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::instance()
486{
487 if (m_instance == 0) {
488 m_instance = new GradientRepeatNoneStrategy();
489 Q_CHECK_PTR(m_instance);
490 }
491
492 return m_instance;
493}
494
495// Output is clamped to 0 to 1.
496double GradientRepeatNoneStrategy::valueAt(double t) const
497{
498 double value = t;
499
500 if (t < DBL_EPSILON) {
501 value = 0;
502 } else if (t > 1 - DBL_EPSILON) {
503 value = 1;
504 }
505
506 return value;
507}
508
509
510class GradientRepeatForwardsStrategy : public GradientRepeatStrategy
511{
512public:
513 static GradientRepeatForwardsStrategy *instance();
514
515 double valueAt(double t) const override;
516
517private:
518 GradientRepeatForwardsStrategy() {}
519
520 static GradientRepeatForwardsStrategy *m_instance;
521};
522
523GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::m_instance = 0;
524
525GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::instance()
526{
527 if (m_instance == 0) {
528 m_instance = new GradientRepeatForwardsStrategy();
529 Q_CHECK_PTR(m_instance);
530 }
531
532 return m_instance;
533}
534
535// Output is 0 to 1, 0 to 1, 0 to 1...
536double GradientRepeatForwardsStrategy::valueAt(double t) const
537{
538 int i = static_cast<int>(t);
539
540 if (t < DBL_EPSILON) {
541 i--;
542 }
543
544 double value = t - i;
545
546 return value;
547}
548
549
550class GradientRepeatAlternateStrategy : public GradientRepeatStrategy
551{
552public:
553 static GradientRepeatAlternateStrategy *instance();
554
555 double valueAt(double t) const override;
556
557private:
558 GradientRepeatAlternateStrategy() {}
559
560 static GradientRepeatAlternateStrategy *m_instance;
561};
562
563GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::m_instance = 0;
564
565GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::instance()
566{
567 if (m_instance == 0) {
568 m_instance = new GradientRepeatAlternateStrategy();
569 Q_CHECK_PTR(m_instance);
570 }
571
572 return m_instance;
573}
574
575// Output is 0 to 1, 1 to 0, 0 to 1, 1 to 0...
576double GradientRepeatAlternateStrategy::valueAt(double t) const
577{
578 if (t < 0) {
579 t = -t;
580 }
581
582 int i = static_cast<int>(t);
583
584 double value = t - i;
585
586 if (i % 2 == 1) {
587 value = 1 - value;
588 }
589
590 return value;
591}
592//Had to create this class to solve alternating mode for cases where values should be repeated for every HalfValues like for example, spirals...
593class GradientRepeatModuloDivisiveContinuousHalfStrategy : public GradientRepeatStrategy
594{
595public:
596 static GradientRepeatModuloDivisiveContinuousHalfStrategy *instance();
597
598 double valueAt(double t) const override;
599
600private:
601 GradientRepeatModuloDivisiveContinuousHalfStrategy() {}
602
603 static GradientRepeatModuloDivisiveContinuousHalfStrategy *m_instance;
604};
605
606GradientRepeatModuloDivisiveContinuousHalfStrategy *GradientRepeatModuloDivisiveContinuousHalfStrategy::m_instance = 0;
607
608GradientRepeatModuloDivisiveContinuousHalfStrategy *GradientRepeatModuloDivisiveContinuousHalfStrategy::instance()
609{
610 if (m_instance == 0) {
611 m_instance = new GradientRepeatModuloDivisiveContinuousHalfStrategy();
612 Q_CHECK_PTR(m_instance);
613 }
614
615 return m_instance;
616}
617
618// Output is 0 to 1, 1 to 0, 0 to 1, 1 to 0 per HalfValues
619double GradientRepeatModuloDivisiveContinuousHalfStrategy::valueAt(double t) const
620{
621 if (t < 0) {
622 t = -t;
623 }
624
625 int i = static_cast<int>(t*2);
626 int ti = static_cast<int>(t);
627
628 double value = t - ti;
629
630 if (i % 2 == 1) {
631 value = 1 - value;
632 }
633
634 return value*2;
635}
636
637class RepeatForwardsPaintPolicy
638{
639public:
640 RepeatForwardsPaintPolicy(KisGradientPainter::enumGradientShape shape);
641
642 void setup(const QPointF& gradientVectorStart,
643 const QPointF& gradientVectorEnd,
644 const QSharedPointer<KisGradientShapeStrategy> &shapeStrategy,
645 const GradientRepeatStrategy *repeatStrategy,
646 qreal antiAliasThreshold,
647 bool reverseGradient,
648 const KoCachedGradient * cachedGradient);
649
650 const quint8 *colorAt(qreal x, qreal y) const;
651
652private:
654 qreal m_antiAliasThresholdNormalized {0};
655 qreal m_antiAliasThresholdNormalizedRev {0};
656 qreal m_antiAliasThresholdNormalizedDbl {0};
658 const GradientRepeatStrategy *m_repeatStrategy {0};
659 bool m_reverseGradient {false};
660 const KoCachedGradient *m_cachedGradient {0};
661 const quint8 *m_extremeColors[2];
662 const KoColorSpace *m_colorSpace {0};
663 mutable QVector<quint8> m_resultColor;
664};
665
666RepeatForwardsPaintPolicy::RepeatForwardsPaintPolicy(KisGradientPainter::enumGradientShape shape)
667 : m_shape(shape)
668{}
669
670void RepeatForwardsPaintPolicy::setup(const QPointF& gradientVectorStart,
671 const QPointF& gradientVectorEnd,
672 const QSharedPointer<KisGradientShapeStrategy> &shapeStrategy,
673 const GradientRepeatStrategy *repeatStrategy,
674 qreal antiAliasThreshold,
675 bool reverseGradient,
676 const KoCachedGradient * cachedGradient)
677{
678 qreal dx = gradientVectorEnd.x() - gradientVectorStart.x();
679 qreal dy = gradientVectorEnd.y() - gradientVectorStart.y();
680 qreal distanceInPixels = sqrt(dx * dx + dy * dy);
681 // Compute the area to be be smoothed
682 // based on the length of the gradient
683 m_antiAliasThresholdNormalized = antiAliasThreshold / distanceInPixels;
684 m_antiAliasThresholdNormalizedRev = 1. - m_antiAliasThresholdNormalized;
685 m_antiAliasThresholdNormalizedDbl = 2. * m_antiAliasThresholdNormalized;
686
687 m_shapeStrategy = shapeStrategy;
688 m_repeatStrategy = repeatStrategy;
689
690 m_reverseGradient = reverseGradient;
691
692 m_cachedGradient = cachedGradient;
693 m_extremeColors[0] = m_cachedGradient->cachedAt(1.);
694 m_extremeColors[1] = m_cachedGradient->cachedAt(0.);
695
696 m_colorSpace = m_cachedGradient->colorSpace();
697
698 m_resultColor = QVector<quint8>(m_colorSpace->pixelSize());
699}
700
701const quint8 *RepeatForwardsPaintPolicy::colorAt(qreal x, qreal y) const
702{
703 qreal t = m_shapeStrategy->valueAt(x, y);
704 // Early return if the pixel is near the center of the gradient if
705 // the shape is radial or square.
706 // This prevents applying smoothing since there are
707 // no aliasing artifacts in these gradient shapes at the center
708 if (t <= m_antiAliasThresholdNormalized &&
712 if (m_reverseGradient) {
713 t = 1 - t;
714 }
715 return m_cachedGradient->cachedAt(t);
716 }
717
718 t = m_repeatStrategy->valueAt(t);
719
720 if (m_reverseGradient) {
721 t = 1 - t;
722 }
723
724 // If this pixel is in the area of the smoothing,
725 // then perform bilinear interpolation between the extreme colors.
726 if (t <= m_antiAliasThresholdNormalized || t >= m_antiAliasThresholdNormalizedRev) {
727 qreal s;
728 if (t <= m_antiAliasThresholdNormalized) {
729 s = .5 + t / m_antiAliasThresholdNormalizedDbl;
730 } else {
731 s = (t - m_antiAliasThresholdNormalizedRev) / m_antiAliasThresholdNormalizedDbl;
732 }
733
734 qint16 colorWeights[2];
735 colorWeights[0] = std::lround((1.0 - s) * qint16_MAX);
736 colorWeights[1] = qint16_MAX - colorWeights[0];
737
738 m_colorSpace->mixColorsOp()->mixColors(m_extremeColors, colorWeights, 2, m_resultColor.data(), qint16_MAX);
739
740 return m_resultColor.data();
741 }
742
743 return m_cachedGradient->cachedAt(t);
744}
745
746class ConicalGradientPaintPolicy
747{
748public:
749 void setup(const QPointF& gradientVectorStart,
750 const QPointF& gradientVectorEnd,
751 const QSharedPointer<KisGradientShapeStrategy> &shapeStrategy,
752 const GradientRepeatStrategy *repeatStrategy,
753 qreal antiAliasThreshold,
754 bool reverseGradient,
755 const KoCachedGradient * cachedGradient);
756
757 const quint8 *colorAt(qreal x, qreal y) const;
758
759private:
760 QPointF m_gradientVectorStart;
762 const GradientRepeatStrategy *m_repeatStrategy;
763 qreal m_singularityThreshold;
764 qreal m_antiAliasThreshold;
765 bool m_reverseGradient;
766 const KoCachedGradient *m_cachedGradient;
767 const quint8 *m_extremeColors[2];
768 const KoColorSpace *m_colorSpace;
769 mutable QVector<quint8> m_resultColor;
770};
771
772void ConicalGradientPaintPolicy::setup(const QPointF& gradientVectorStart,
773 const QPointF& gradientVectorEnd,
774 const QSharedPointer<KisGradientShapeStrategy> &shapeStrategy,
775 const GradientRepeatStrategy *repeatStrategy,
776 qreal antiAliasThreshold,
777 bool reverseGradient,
778 const KoCachedGradient * cachedGradient)
779{
780 Q_UNUSED(gradientVectorEnd);
781
782 m_gradientVectorStart = gradientVectorStart;
783
784 m_shapeStrategy = shapeStrategy;
785 m_repeatStrategy = repeatStrategy;
786
787 m_singularityThreshold = 8.;
788 m_antiAliasThreshold = antiAliasThreshold;
789
790 m_reverseGradient = reverseGradient;
791
792 m_cachedGradient = cachedGradient;
793 m_extremeColors[0] = m_cachedGradient->cachedAt(1.);
794 m_extremeColors[1] = m_cachedGradient->cachedAt(0.);
795
796 m_colorSpace = m_cachedGradient->colorSpace();
797
798 m_resultColor = QVector<quint8>(m_colorSpace->pixelSize());
799}
800
801const quint8 *ConicalGradientPaintPolicy::colorAt(qreal x, qreal y) const
802{
803 // Compute the distance from the center of the gradient to the current pixel
804 qreal dx = x - m_gradientVectorStart.x();
805 qreal dy = y - m_gradientVectorStart.y();
806 qreal distanceInPixels = sqrt(dx * dx + dy * dy);
807 // Compute the perimeter for this distance
808 qreal perimeter = 2. * M_PI * distanceInPixels;
809 // The smoothing is applied in the vicinity of the aliased border.
810 // The width of the vicinity is an area antiAliasThreshold pixels wide
811 // to each side of the border, but in this case the area is scaled down
812 // if it is too close to the center
813 qreal antiAliasThresholdNormalized;
814 if (distanceInPixels < m_singularityThreshold){
815 antiAliasThresholdNormalized = distanceInPixels * m_antiAliasThreshold / m_singularityThreshold;
816 } else {
817 antiAliasThresholdNormalized = m_antiAliasThreshold;
818 }
819 antiAliasThresholdNormalized = antiAliasThresholdNormalized / perimeter;
820 qreal antiAliasThresholdNormalizedRev = 1. - antiAliasThresholdNormalized;
821 qreal antiAliasThresholdNormalizedDbl = 2. * antiAliasThresholdNormalized;
822
823 qreal t = m_shapeStrategy->valueAt(x, y);
824 t = m_repeatStrategy->valueAt(t);
825
826 if (m_reverseGradient) {
827 t = 1 - t;
828 }
829
830 // If this pixel is in the area of the smoothing,
831 // then perform bilinear interpolation between the extreme colors.
832 if (t <= antiAliasThresholdNormalized || t >= antiAliasThresholdNormalizedRev) {
833 qreal s;
834 if (t <= antiAliasThresholdNormalized) {
835 s = .5 + t / antiAliasThresholdNormalizedDbl;
836 } else {
837 s = (t - antiAliasThresholdNormalizedRev) / antiAliasThresholdNormalizedDbl;
838 }
839
840 qint16 colorWeights[2];
841 colorWeights[0] = std::lround((1.0 - s) * qint16_MAX);
842 colorWeights[1] = qint16_MAX - colorWeights[0];
843
844 m_colorSpace->mixColorsOp()->mixColors(m_extremeColors, colorWeights, 2, m_resultColor.data(), qint16_MAX);
845
846 return m_resultColor.data();
847 }
848
849 return m_cachedGradient->cachedAt(t);
850}
851
852class SpyralGradientRepeatNonePaintPolicy
853{
854public:
855 SpyralGradientRepeatNonePaintPolicy(bool isReverseSpiral = false);
856
857 void setup(const QPointF& gradientVectorStart,
858 const QPointF& gradientVectorEnd,
859 const QSharedPointer<KisGradientShapeStrategy> &shapeStrategy,
860 const GradientRepeatStrategy *repeatStrategy,
861 qreal antiAliasThreshold,
862 bool reverseGradient,
863 const KoCachedGradient * cachedGradient);
864
865 const quint8 *colorAt(qreal x, qreal y) const;
866
867private:
868 QPointF m_gradientVectorStart;
869 qreal m_distanceInPixels {0};
870 qreal m_singularityThreshold {0};
871 qreal m_angle {0};
873 const GradientRepeatStrategy *m_repeatStrategy {0};
874 qreal m_antiAliasThreshold {0};
875 bool m_reverseGradient {false};
876 const KoCachedGradient *m_cachedGradient {0};
877 mutable const quint8 *m_extremeColors[2];
878 const KoColorSpace *m_colorSpace {0};
879 mutable QVector<quint8> m_resultColor;
880 bool m_isReverseSpiral {false};
881};
882
883SpyralGradientRepeatNonePaintPolicy::SpyralGradientRepeatNonePaintPolicy(bool isReverseSpiral)
884 : m_isReverseSpiral(isReverseSpiral)
885{
886}
887
888void SpyralGradientRepeatNonePaintPolicy::setup(const QPointF& gradientVectorStart,
889 const QPointF& gradientVectorEnd,
890 const QSharedPointer<KisGradientShapeStrategy> &shapeStrategy,
891 const GradientRepeatStrategy *repeatStrategy,
892 qreal antiAliasThreshold,
893 bool reverseGradient,
894 const KoCachedGradient * cachedGradient)
895{
896 m_gradientVectorStart = gradientVectorStart;
897
898 qreal dx = gradientVectorEnd.x() - gradientVectorStart.x();
899 qreal dy = gradientVectorEnd.y() - gradientVectorStart.y();
900 m_distanceInPixels = sqrt(dx * dx + dy * dy);
901 m_singularityThreshold = m_distanceInPixels / 32.;
902 m_angle = atan2(dy, dx) + M_PI;
903
904 m_shapeStrategy = shapeStrategy;
905 m_repeatStrategy = repeatStrategy;
906
907 m_antiAliasThreshold = antiAliasThreshold;
908
909 m_reverseGradient = reverseGradient;
910
911 m_cachedGradient = cachedGradient;
912
913 m_colorSpace = m_cachedGradient->colorSpace();
914
915 m_resultColor = QVector<quint8>(m_colorSpace->pixelSize());
916}
917
918const quint8 *SpyralGradientRepeatNonePaintPolicy::colorAt(qreal x, qreal y) const
919{
920 // Compute the distance from the center of the gradient to thecurrent pixel
921 qreal dx = x - m_gradientVectorStart.x();
922 qreal dy = y - m_gradientVectorStart.y();
923 qreal distanceInPixels = sqrt(dx * dx + dy * dy);
924 // Compute the perimeter for this distance
925 qreal perimeter = 2. * M_PI * distanceInPixels;
926 // The smoothing is applied in the vicinity of the aliased border.
927 // The width of the vicinity is an area antiAliasThreshold pixels wide
928 // to each side of the border, but in this case the area is scaled down
929 // if it is too close to the center
930 qreal antiAliasThresholdNormalized;
931 if (distanceInPixels < m_singularityThreshold) {
932 antiAliasThresholdNormalized = distanceInPixels * m_antiAliasThreshold / m_singularityThreshold;
933 } else {
934 antiAliasThresholdNormalized = m_antiAliasThreshold;
935 }
936 antiAliasThresholdNormalized = antiAliasThresholdNormalized / perimeter;
937 qreal antiAliasThresholdNormalizedRev = 1. - antiAliasThresholdNormalized;
938 qreal antiAliasThresholdNormalizedDbl = 2. * antiAliasThresholdNormalized;
939
940 qreal t = m_shapeStrategy->valueAt(x, y);
941 t = m_repeatStrategy->valueAt(t);
942
943 if (m_reverseGradient) {
944 t = 1 - t;
945 }
946
947 // Compute the area to be be smoothed based on the angle of the gradient
948 // and the angle of the current pixel to the center of the gradient
949 qreal angle = atan2(dy, dx) + M_PI;
950 angle -= m_angle;
951 if (angle < 0.) {
952 angle += 2. * M_PI;
953 }
954 angle /= (2. * M_PI);
955
956 angle = m_repeatStrategy->valueAt(angle);
957
958 // If this pixel is in the area of the smoothing,
959 // then perform bilinear interpolation between the extreme colors.
960 if (distanceInPixels < m_distanceInPixels && (angle <= antiAliasThresholdNormalized || angle >= antiAliasThresholdNormalizedRev)) {
961 qreal s;
962 if (angle <= antiAliasThresholdNormalized) {
963 s = .5 + angle / antiAliasThresholdNormalizedDbl;
964 } else {
965 s = (angle - antiAliasThresholdNormalizedRev) / antiAliasThresholdNormalizedDbl;
966 }
967
968 if (m_reverseGradient) {
969 distanceInPixels = m_distanceInPixels - distanceInPixels;
970 m_extremeColors[0] = m_cachedGradient->cachedAt(0.);
971 } else {
972 m_extremeColors[0] = m_cachedGradient->cachedAt(1.);
973 }
974
975 if (m_isReverseSpiral) {
976 m_extremeColors[1] = m_extremeColors[0];
977 m_extremeColors[0] = (m_cachedGradient->cachedAt(distanceInPixels / m_distanceInPixels));
978 } else {
979 m_extremeColors[1] = (m_cachedGradient->cachedAt(distanceInPixels / m_distanceInPixels));
980 }
981
982 qint16 colorWeights[2];
983 colorWeights[0] = std::lround((1.0 - s) * qint16_MAX);
984 colorWeights[1] = qint16_MAX - colorWeights[0];
985
986 m_colorSpace->mixColorsOp()->mixColors(m_extremeColors, colorWeights, 2, m_resultColor.data(), qint16_MAX);
987
988 return m_resultColor.data();
989 }
990
991 return m_cachedGradient->cachedAt(t);
992}
993
994class NoAntialiasPaintPolicy
995{
996public:
997 void setup(const QPointF& gradientVectorStart,
998 const QPointF& gradientVectorEnd,
999 const QSharedPointer<KisGradientShapeStrategy> &shapeStrategy,
1000 const GradientRepeatStrategy *repeatStrategy,
1001 qreal antiAliasThreshold,
1002 bool reverseGradient,
1003 const KoCachedGradient * cachedGradient);
1004
1005 const quint8 *colorAt(qreal x, qreal y) const;
1006
1007private:
1009 const GradientRepeatStrategy *m_repeatStrategy {0};
1010 bool m_reverseGradient {false};
1011 const KoCachedGradient *m_cachedGradient {0};
1012};
1013
1014void NoAntialiasPaintPolicy::setup(const QPointF& gradientVectorStart,
1015 const QPointF& gradientVectorEnd,
1016 const QSharedPointer<KisGradientShapeStrategy> &shapeStrategy,
1017 const GradientRepeatStrategy *repeatStrategy,
1018 qreal antiAliasThreshold,
1019 bool reverseGradient,
1020 const KoCachedGradient * cachedGradient)
1021{
1022 Q_UNUSED(gradientVectorStart);
1023 Q_UNUSED(gradientVectorEnd);
1024 Q_UNUSED(antiAliasThreshold);
1025 m_shapeStrategy = shapeStrategy;
1026 m_repeatStrategy = repeatStrategy;
1027 m_reverseGradient = reverseGradient;
1028 m_cachedGradient = cachedGradient;
1029}
1030
1031const quint8 *NoAntialiasPaintPolicy::colorAt(qreal x, qreal y) const
1032{
1033 qreal t = m_shapeStrategy->valueAt(x, y);
1034 t = m_repeatStrategy->valueAt(t);
1035
1036 if (m_reverseGradient) {
1037 t = 1 - t;
1038 }
1039
1040 return m_cachedGradient->cachedAt(t);
1041}
1042
1043}
1044
1045struct Q_DECL_HIDDEN KisGradientPainter::Private
1046{
1048
1052 const QRect &_processRect)
1053 : precalculatedShapeStrategy(_precalculatedShapeStrategy),
1054 processRect(_processRect) {}
1055
1058 };
1059
1061};
1062
1064 : m_d(new Private())
1065{
1066}
1067
1069 : KisPainter(device),
1070 m_d(new Private())
1071{
1072}
1073
1075 : KisPainter(device, selection),
1076 m_d(new Private())
1077{
1078}
1079
1083
1088
1089KisGradientShapeStrategy* createPolygonShapeStrategy(const QPainterPath &path, const QRect &boundingRect)
1090{
1091 // TODO: implement UI for exponent option
1092 const qreal exponent = 2.0;
1093 KisGradientShapeStrategy *strategy =
1094 new KisPolygonalGradientShapeStrategy(path, exponent);
1095
1096 KIS_ASSERT_RECOVER_NOOP(boundingRect.width() >= 3 &&
1097 boundingRect.height() >= 3);
1098
1099 const qreal step =
1100 qMin(qreal(8.0), KritaUtils::maxDimensionPortion(boundingRect, 0.01, 2));
1101
1102 return new KisCachedGradientShapeStrategy(boundingRect, step, step, strategy);
1103}
1104
1109{
1110 if (!m_d->processRegions.isEmpty()) return;
1111
1112 QPainterPath path;
1113
1114 if (selection()) {
1115 if (!selection()->outlineCacheValid()) {
1117 }
1118
1119 KIS_ASSERT_RECOVER_RETURN(selection()->outlineCacheValid());
1120 KIS_ASSERT_RECOVER_RETURN(!selection()->outlineCache().isEmpty());
1121
1122 path = selection()->outlineCache();
1123 } else {
1124 path.addRect(device()->defaultBounds()->bounds());
1125 }
1126
1128
1129 Q_FOREACH (const QPainterPath &subpath, splitPaths) {
1130 QRect boundingRect = subpath.boundingRect().toAlignedRect();
1131
1132 if (boundingRect.width() < 3 || boundingRect.height() < 3) {
1133 boundingRect = kisGrowRect(boundingRect, 2);
1134 }
1135
1136 Private::ProcessRegion r(toQShared(createPolygonShapeStrategy(subpath, boundingRect)),
1137 boundingRect);
1138 m_d->processRegions << r;
1139 }
1140}
1141
1142bool KisGradientPainter::paintGradient(const QPointF& gradientVectorStart,
1143 const QPointF& gradientVectorEnd,
1144 enumGradientRepeat repeat,
1145 double antiAliasThreshold,
1146 bool reverseGradient,
1147 qint32 startx,
1148 qint32 starty,
1149 qint32 width,
1150 qint32 height,
1151 enumDither dither,
1152 int ditherSteps)
1153{
1154 return paintGradient(gradientVectorStart,
1155 gradientVectorEnd,
1156 repeat,
1157 antiAliasThreshold,
1158 reverseGradient,
1159 QRect(startx, starty, width, height),
1160 dither,
1161 ditherSteps);
1162}
1163
1164bool KisGradientPainter::paintGradient(const QPointF& gradientVectorStart,
1165 const QPointF& gradientVectorEnd,
1166 enumGradientRepeat repeat,
1167 double antiAliasThreshold,
1168 bool reverseGradient,
1169 const QRect &applyRect,
1170 enumDither dither,
1171 int ditherSteps)
1172{
1173 // The following combinations of options have aliasing artifacts
1174 // where the first color meets the last color of the gradient.
1175 // so antialias threshold is used to compute if the pixel is in
1176 // the smoothing area. Then linear interpolation is used to blend
1177 // between the first and last colors
1178 if (antiAliasThreshold > DBL_EPSILON) {
1179 if ((m_d->shape == GradientShapeLinear || m_d->shape == GradientShapeBiLinear ||
1180 m_d->shape == GradientShapeRadial || m_d->shape == GradientShapeSquare ||
1182 && repeat == GradientRepeatForwards) {
1183 RepeatForwardsPaintPolicy paintPolicy(m_d->shape);
1184 return paintGradient(gradientVectorStart,
1185 gradientVectorEnd,
1186 repeat,
1187 antiAliasThreshold,
1188 reverseGradient,
1189 dither,
1190 ditherSteps,
1191 applyRect,
1192 paintPolicy);
1193
1194 } else if (m_d->shape == GradientShapeConical) {
1195 ConicalGradientPaintPolicy paintPolicy;
1196 return paintGradient(gradientVectorStart,
1197 gradientVectorEnd,
1198 repeat,
1199 antiAliasThreshold,
1200 reverseGradient,
1201 dither,
1202 ditherSteps,
1203 applyRect,
1204 paintPolicy);
1205
1206 } else if ((m_d->shape == GradientShapeSpiral || m_d->shape == GradientShapeReverseSpiral) &&
1207 repeat == GradientRepeatNone) {
1208 SpyralGradientRepeatNonePaintPolicy paintPolicy(m_d->shape == GradientShapeReverseSpiral);
1209 return paintGradient(gradientVectorStart,
1210 gradientVectorEnd,
1211 repeat,
1212 antiAliasThreshold,
1213 reverseGradient,
1214 dither,
1215 ditherSteps,
1216 applyRect,
1217 paintPolicy);
1218 }
1219 }
1220
1221 // Default behavior: no antialiasing required
1222 NoAntialiasPaintPolicy paintPolicy;
1223 return paintGradient(gradientVectorStart,
1224 gradientVectorEnd,
1225 repeat,
1226 antiAliasThreshold,
1227 reverseGradient,
1228 dither,
1229 ditherSteps,
1230 applyRect,
1231 paintPolicy);
1232}
1233
1234template <class T>
1235bool KisGradientPainter::paintGradient(const QPointF& gradientVectorStart,
1236 const QPointF& gradientVectorEnd,
1237 enumGradientRepeat repeat,
1238 double antiAliasThreshold,
1239 bool reverseGradient,
1240 enumDither dither,
1241 int ditherSteps,
1242 const QRect &applyRect,
1243 T & paintPolicy)
1244{
1245 if (!gradient()) return false;
1246
1247 QRect requestedRect = applyRect;
1248
1249 //If the device has a selection only iterate over that selection united with our area of interest
1250 if (selection()) {
1251 requestedRect &= selection()->selectedExactRect();
1252 }
1253
1255
1256 switch (m_d->shape) {
1257 case GradientShapeLinear: {
1258 Private::ProcessRegion r(toQShared(new LinearGradientStrategy(gradientVectorStart, gradientVectorEnd)),
1259 requestedRect);
1260 m_d->processRegions.clear();
1261 m_d->processRegions << r;
1262 break;
1263 }
1264 case GradientShapeBiLinear: {
1265 Private::ProcessRegion r(toQShared(new BiLinearGradientStrategy(gradientVectorStart, gradientVectorEnd)),
1266 requestedRect);
1267 m_d->processRegions.clear();
1268 m_d->processRegions << r;
1269 break;
1270 }
1271 case GradientShapeRadial: {
1272 Private::ProcessRegion r(toQShared(new RadialGradientStrategy(gradientVectorStart, gradientVectorEnd)),
1273 requestedRect);
1274 m_d->processRegions.clear();
1275 m_d->processRegions << r;
1276 break;
1277 }
1278 case GradientShapeSquare: {
1279 Private::ProcessRegion r(toQShared(new SquareGradientStrategy(gradientVectorStart, gradientVectorEnd)),
1280 requestedRect);
1281 m_d->processRegions.clear();
1282 m_d->processRegions << r;
1283 break;
1284 }
1285 case GradientShapeConical: {
1286 Private::ProcessRegion r(toQShared(new ConicalGradientStrategy(gradientVectorStart, gradientVectorEnd)),
1287 requestedRect);
1288 m_d->processRegions.clear();
1289 m_d->processRegions << r;
1290 break;
1291 }
1293 Private::ProcessRegion r(toQShared(new ConicalSymetricGradientStrategy(gradientVectorStart, gradientVectorEnd)),
1294 requestedRect);
1295 m_d->processRegions.clear();
1296 m_d->processRegions << r;
1297 break;
1298 }
1299 case GradientShapeSpiral: {
1300 Private::ProcessRegion r(toQShared(new SpiralGradientStrategy(gradientVectorStart, gradientVectorEnd)),
1301 requestedRect);
1302 m_d->processRegions.clear();
1303 m_d->processRegions << r;
1304 break;
1305 }
1307 Private::ProcessRegion r(toQShared(new ReverseSpiralGradientStrategy(gradientVectorStart, gradientVectorEnd)),
1308 requestedRect);
1309 m_d->processRegions.clear();
1310 m_d->processRegions << r;
1311 break;
1312 }
1315 repeat = GradientRepeatNone;
1316 break;
1317 }
1318
1319 if (isBayerDither(dither)) {
1320 // Wrap each shape strategy in a dithering filter.
1321 for (Private::ProcessRegion& region : m_d->processRegions) {
1322 region.precalculatedShapeStrategy = toQShared(new DitheredGradientStrategy(dither, ditherSteps, region.precalculatedShapeStrategy));
1323 }
1324 }
1325
1326 GradientRepeatStrategy *repeatStrategy = 0;
1327
1328 switch (repeat) {
1329 case GradientRepeatNone:
1330 repeatStrategy = GradientRepeatNoneStrategy::instance();
1331 break;
1333 repeatStrategy = GradientRepeatForwardsStrategy::instance();
1334 break;
1336 if (m_d->shape == GradientShapeSpiral || m_d->shape == GradientShapeReverseSpiral) {repeatStrategy = GradientRepeatModuloDivisiveContinuousHalfStrategy::instance();}
1337 else {repeatStrategy = GradientRepeatAlternateStrategy::instance();}
1338 break;
1339 }
1340 Q_ASSERT(repeatStrategy != 0);
1341
1342
1344
1345 KoID depthId;
1346 const KoColorSpace *destCs = dev->colorSpace();
1347
1348 if (destCs->colorDepthId() == Integer8BitsColorDepthID) {
1349 depthId = Integer16BitsColorDepthID;
1350 } else {
1351 depthId = destCs->colorDepthId();
1352 }
1353
1354 const KoColorSpace *mixCs = KoColorSpaceRegistry::instance()->colorSpace(destCs->colorModelId().id(), depthId.id(), destCs->profile());
1355 const quint32 mixPixelSize = mixCs->pixelSize();
1356
1358 tmp->setDefaultBounds(dev->defaultBounds());
1359 tmp->clear();
1360
1361 const KisDitherOp* op = mixCs->ditherOp(destCs->colorDepthId().id(), dither == DitherBlueNoise ? DITHER_BEST : DITHER_NONE);
1362
1363 Q_FOREACH (const Private::ProcessRegion &r, m_d->processRegions) {
1364 QRect processRect = r.processRect;
1365 QSharedPointer<KisGradientShapeStrategy> shapeStrategy = r.precalculatedShapeStrategy;
1366
1367 KoCachedGradient cachedGradient(gradient(), qMax(processRect.width(), processRect.height()), mixCs);
1368
1370
1371 paintPolicy.setup(gradientVectorStart,
1372 gradientVectorEnd,
1373 shapeStrategy,
1374 repeatStrategy,
1375 antiAliasThreshold,
1376 reverseGradient,
1377 &cachedGradient);
1378
1379 while (it.nextPixel()) {
1380 const quint8 *const pixel {paintPolicy.colorAt(it.x(), it.y())};
1381 memcpy(it.rawData(), pixel, mixPixelSize);
1382 }
1383
1385 KisRandomConstAccessorSP srcIt = tmp->createRandomConstAccessorNG();
1386
1387 int rows = 1;
1388 int columns = 1;
1389
1390 for (int y = processRect.y(); y <= processRect.bottom(); y += rows) {
1391 rows = qMin(srcIt->numContiguousRows(y), qMin(dstIt->numContiguousRows(y), processRect.bottom() - y + 1));
1392
1393 for (int x = processRect.x(); x <= processRect.right(); x += columns) {
1394 columns = qMin(srcIt->numContiguousColumns(x), qMin(dstIt->numContiguousColumns(x), processRect.right() - x + 1));
1395
1396 srcIt->moveTo(x, y);
1397 dstIt->moveTo(x, y);
1398
1399 const qint32 srcRowStride = srcIt->rowStride(x, y);
1400 const qint32 dstRowStride = dstIt->rowStride(x, y);
1401 const quint8 *srcPtr = srcIt->rawDataConst();
1402 quint8 *dstPtr = dstIt->rawData();
1403
1404 op->dither(srcPtr, srcRowStride, dstPtr, dstRowStride, x, y, columns, rows);
1405 }
1406 }
1407 }
1408
1409 bitBlt(requestedRect.topLeft(), dev, requestedRect);
1410
1411 return true;
1412}
1413
float value(const T *src, size_t ch)
@ DITHER_NONE
Definition KisDitherOp.h:22
@ DITHER_BEST
Definition KisDitherOp.h:24
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID Integer16BitsColorDepthID("U16", ki18n("16-bit integer/channel"))
qreal distance(const QPointF &p1, const QPointF &p2)
PythonPluginManager * instance
virtual quint8 * rawData()=0
virtual const quint8 * rawDataConst() const =0
virtual void dither(const quint8 *src, quint8 *dst, int x, int y) const =0
KisPaintDeviceSP createCompositionSourceDevice() const
const KoColorSpace * colorSpace() const
KisDefaultBoundsBaseSP defaultBounds() const
KisRandomAccessorSP createRandomAccessorNG()
KoUpdater * progressUpdater
KisSelectionSP selection
void bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
KisPaintDeviceSP device
KoAbstractGradientSP gradient
virtual qint32 rowStride(qint32 x, qint32 y) const =0
virtual qint32 numContiguousRows(qint32 y) const =0
virtual void moveTo(qint32 x, qint32 y)=0
virtual qint32 numContiguousColumns(qint32 x) const =0
ALWAYS_INLINE quint8 * rawData()
ALWAYS_INLINE int x() const
ALWAYS_INLINE int y() const
const quint8 * cachedAt(qreal t) const
gets the color data at position 0 <= t <= 1
const KoColorSpace * colorSpace() const
virtual quint32 pixelSize() const =0
virtual const KisDitherOp * ditherOp(const QString &depth, DitherType type) const
virtual KoID colorModelId() const =0
virtual KoID colorDepthId() const =0
KoMixColorsOp * mixColorsOp
virtual const KoColorProfile * profile() const =0
Definition KoID.h:30
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
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define bounds(x, a, b)
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
const qint16 qint16_MAX
Definition kis_global.h:28
#define M_PI
Definition kis_global.h:111
KisGradientShapeStrategy * createPolygonShapeStrategy(const QPainterPath &path, const QRect &boundingRect)
QSharedPointer< T > toQShared(T *ptr)
float dither_factor_bayer_4_normalized(int x, int y)
float dither_factor_bayer_2_normalized(int x, int y)
float dither_factor_bayer_8_normalized(int x, int y)
KRITAIMAGE_EXPORT qreal atan2(qreal y, qreal x)
atan2 replacement
KRITAFLAKE_EXPORT QColor colorAt(qreal position, const QGradientStops &stops)
Calculates color at given position from given gradient stops.
qreal KRITAIMAGE_EXPORT maxDimensionPortion(const QRectF &bounds, qreal portion, qreal minValue)
QList< QPainterPath > splitDisjointPaths(const QPainterPath &path)
QSharedPointer< KisGradientShapeStrategy > precalculatedShapeStrategy
ProcessRegion(QSharedPointer< KisGradientShapeStrategy > _precalculatedShapeStrategy, const QRect &_processRect)
void setGradientShape(enumGradientShape shape)
static bool isBayerDither(enumDither dither)
const QScopedPointer< Private > m_d
bool paintGradient(const QPointF &gradientVectorStart, const QPointF &gradientVectorEnd, enumGradientRepeat repeat, double antiAliasThreshold, bool reverseGradient, qint32 startx, qint32 starty, qint32 width, qint32 height, enumDither dither=DitherNone, int ditherSteps=defaultDitherSteps)
QVector< ProcessRegion > processRegions
void recalculateOutlineCache()
QRect selectedExactRect() const
Slow, but exact way of determining the rectangle that encloses the selection.
QPainterPath outlineCache() const
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
static KoColorSpaceRegistry * instance()