chalk

kis_gradient_painter.cpp
1/*
2 * Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#include <stdlib.h>
20#include <string.h>
21#include <cfloat>
22
23#include "tqbrush.h"
24#include "tqcolor.h"
25#include "tqfontinfo.h"
26#include "tqfontmetrics.h"
27#include "tqpen.h"
28#include "tqregion.h"
29#include "tqwmatrix.h"
30#include <tqimage.h>
31#include <tqmap.h>
32#include <tqpainter.h>
33#include <tqpixmap.h>
34#include <tqpointarray.h>
35#include <tqrect.h>
36#include <tqstring.h>
37
38#include <kdebug.h>
39#include <tdelocale.h>
40
41#include "kis_brush.h"
42#include "kis_debug_areas.h"
43#include "kis_gradient.h"
44#include "kis_image.h"
45#include "kis_iterators_pixel.h"
46#include "kis_layer.h"
47#include "kis_paint_device.h"
48#include "kis_pattern.h"
49#include "kis_rect.h"
50#include "kis_colorspace.h"
51#include "kis_types.h"
52#include "kis_vec.h"
53#include "kis_selection.h"
54#include "kis_gradient_painter.h"
55#include "kis_meta_registry.h"
56#include "kis_colorspace_factory_registry.h"
57
58namespace {
59
60 class GradientShapeStrategy {
61 public:
62 GradientShapeStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
63 virtual ~GradientShapeStrategy() {}
64
65 virtual double valueAt(double x, double y) const = 0;
66
67 protected:
68 KisPoint m_gradientVectorStart;
69 KisPoint m_gradientVectorEnd;
70 };
71
72 GradientShapeStrategy::GradientShapeStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
73 : m_gradientVectorStart(gradientVectorStart), m_gradientVectorEnd(gradientVectorEnd)
74 {
75 }
76
77
78 class LinearGradientStrategy : public GradientShapeStrategy {
79 typedef GradientShapeStrategy super;
80 public:
81 LinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
82
83 virtual double valueAt(double x, double y) const;
84
85 protected:
86 double m_normalisedVectorX;
87 double m_normalisedVectorY;
88 double m_vectorLength;
89 };
90
91 LinearGradientStrategy::LinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
92 : super(gradientVectorStart, gradientVectorEnd)
93 {
94 double dx = gradientVectorEnd.x() - gradientVectorStart.x();
95 double dy = gradientVectorEnd.y() - gradientVectorStart.y();
96
97 m_vectorLength = sqrt((dx * dx) + (dy * dy));
98
99 if (m_vectorLength < DBL_EPSILON) {
100 m_normalisedVectorX = 0;
101 m_normalisedVectorY = 0;
102 }
103 else {
104 m_normalisedVectorX = dx / m_vectorLength;
105 m_normalisedVectorY = dy / m_vectorLength;
106 }
107 }
108
109 double LinearGradientStrategy::valueAt(double x, double y) const
110 {
111 double vx = x - m_gradientVectorStart.x();
112 double vy = y - m_gradientVectorStart.y();
113
114 // Project the vector onto the normalised gradient vector.
115 double t = vx * m_normalisedVectorX + vy * m_normalisedVectorY;
116
117 if (m_vectorLength < DBL_EPSILON) {
118 t = 0;
119 }
120 else {
121 // Scale to 0 to 1 over the gradient vector length.
122 t /= m_vectorLength;
123 }
124
125 return t;
126 }
127
128
129 class BiLinearGradientStrategy : public LinearGradientStrategy {
130 typedef LinearGradientStrategy super;
131 public:
132 BiLinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
133
134 virtual double valueAt(double x, double y) const;
135 };
136
137 BiLinearGradientStrategy::BiLinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
138 : super(gradientVectorStart, gradientVectorEnd)
139 {
140 }
141
142 double BiLinearGradientStrategy::valueAt(double x, double y) const
143 {
144 double t = super::valueAt(x, y);
145
146 // Reflect
147 if (t < -DBL_EPSILON) {
148 t = -t;
149 }
150
151 return t;
152 }
153
154
155 class RadialGradientStrategy : public GradientShapeStrategy {
156 typedef GradientShapeStrategy super;
157 public:
158 RadialGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
159
160 virtual double valueAt(double x, double y) const;
161
162 protected:
163 double m_radius;
164 };
165
166 RadialGradientStrategy::RadialGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
167 : super(gradientVectorStart, gradientVectorEnd)
168 {
169 double dx = gradientVectorEnd.x() - gradientVectorStart.x();
170 double dy = gradientVectorEnd.y() - gradientVectorStart.y();
171
172 m_radius = sqrt((dx * dx) + (dy * dy));
173 }
174
175 double RadialGradientStrategy::valueAt(double x, double y) const
176 {
177 double dx = x - m_gradientVectorStart.x();
178 double dy = y - m_gradientVectorStart.y();
179
180 double distance = sqrt((dx * dx) + (dy * dy));
181
182 double t;
183
184 if (m_radius < DBL_EPSILON) {
185 t = 0;
186 }
187 else {
188 t = distance / m_radius;
189 }
190
191 return t;
192 }
193
194
195 class SquareGradientStrategy : public GradientShapeStrategy {
196 typedef GradientShapeStrategy super;
197 public:
198 SquareGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
199
200 virtual double valueAt(double x, double y) const;
201
202 protected:
203 double m_normalisedVectorX;
204 double m_normalisedVectorY;
205 double m_vectorLength;
206 };
207
208 SquareGradientStrategy::SquareGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
209 : super(gradientVectorStart, gradientVectorEnd)
210 {
211 double dx = gradientVectorEnd.x() - gradientVectorStart.x();
212 double dy = gradientVectorEnd.y() - gradientVectorStart.y();
213
214 m_vectorLength = sqrt((dx * dx) + (dy * dy));
215
216 if (m_vectorLength < DBL_EPSILON) {
217 m_normalisedVectorX = 0;
218 m_normalisedVectorY = 0;
219 }
220 else {
221 m_normalisedVectorX = dx / m_vectorLength;
222 m_normalisedVectorY = dy / m_vectorLength;
223 }
224 }
225
226 double SquareGradientStrategy::valueAt(double x, double y) const
227 {
228 double px = x - m_gradientVectorStart.x();
229 double py = y - m_gradientVectorStart.y();
230
231 double distance1 = 0;
232 double distance2 = 0;
233
234 if (m_vectorLength > DBL_EPSILON) {
235
236 // Point to line distance is:
237 // distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / m_vectorLength;
238 //
239 // Here l0 = (0, 0) and |l1 - l0| = 1
240
241 distance1 = -m_normalisedVectorY * px + m_normalisedVectorX * py;
242 distance1 = fabs(distance1);
243
244 // Rotate point by 90 degrees and get the distance to the perpendicular
245 distance2 = -m_normalisedVectorY * -py + m_normalisedVectorX * px;
246 distance2 = fabs(distance2);
247 }
248
249 double t = TQMAX(distance1, distance2) / m_vectorLength;
250
251 return t;
252 }
253
254
255 class ConicalGradientStrategy : public GradientShapeStrategy {
256 typedef GradientShapeStrategy super;
257 public:
258 ConicalGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
259
260 virtual double valueAt(double x, double y) const;
261
262 protected:
263 double m_vectorAngle;
264 };
265
266 ConicalGradientStrategy::ConicalGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
267 : super(gradientVectorStart, gradientVectorEnd)
268 {
269 double dx = gradientVectorEnd.x() - gradientVectorStart.x();
270 double dy = gradientVectorEnd.y() - gradientVectorStart.y();
271
272 // Get angle from 0 to 2 PI.
273 m_vectorAngle = atan2(dy, dx) + M_PI;
274 }
275
276 double ConicalGradientStrategy::valueAt(double x, double y) const
277 {
278 double px = x - m_gradientVectorStart.x();
279 double py = y - m_gradientVectorStart.y();
280
281 double angle = atan2(py, px) + M_PI;
282
283 angle -= m_vectorAngle;
284
285 if (angle < 0) {
286 angle += 2 * M_PI;
287 }
288
289 double t = angle / (2 * M_PI);
290
291 return t;
292 }
293
294
295 class ConicalSymetricGradientStrategy : public GradientShapeStrategy {
296 typedef GradientShapeStrategy super;
297 public:
298 ConicalSymetricGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd);
299
300 virtual double valueAt(double x, double y) const;
301
302 protected:
303 double m_vectorAngle;
304 };
305
306 ConicalSymetricGradientStrategy::ConicalSymetricGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd)
307 : super(gradientVectorStart, gradientVectorEnd)
308 {
309 double dx = gradientVectorEnd.x() - gradientVectorStart.x();
310 double dy = gradientVectorEnd.y() - gradientVectorStart.y();
311
312 // Get angle from 0 to 2 PI.
313 m_vectorAngle = atan2(dy, dx) + M_PI;
314 }
315
316 double ConicalSymetricGradientStrategy::valueAt(double x, double y) const
317 {
318 double px = x - m_gradientVectorStart.x();
319 double py = y - m_gradientVectorStart.y();
320
321 double angle = atan2(py, px) + M_PI;
322
323 angle -= m_vectorAngle;
324
325 if (angle < 0) {
326 angle += 2 * M_PI;
327 }
328
329 double t;
330
331 if (angle < M_PI) {
332 t = angle / M_PI;
333 }
334 else {
335 t = 1 - ((angle - M_PI) / M_PI);
336 }
337
338 return t;
339 }
340
341
342 class GradientRepeatStrategy {
343 public:
344 GradientRepeatStrategy() {}
345 virtual ~GradientRepeatStrategy() {}
346
347 virtual double valueAt(double t) const = 0;
348 };
349
350
351 class GradientRepeatNoneStrategy : public GradientRepeatStrategy {
352 public:
353 static GradientRepeatNoneStrategy *instance();
354
355 virtual double valueAt(double t) const;
356
357 private:
358 GradientRepeatNoneStrategy() {}
359
360 static GradientRepeatNoneStrategy *m_instance;
361 };
362
363 GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::m_instance = 0;
364
365 GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::instance()
366 {
367 if (m_instance == 0) {
368 m_instance = new GradientRepeatNoneStrategy();
369 TQ_CHECK_PTR(m_instance);
370 }
371
372 return m_instance;
373 }
374
375 // Output is clamped to 0 to 1.
376 double GradientRepeatNoneStrategy::valueAt(double t) const
377 {
378 double value = t;
379
380 if (t < DBL_EPSILON) {
381 value = 0;
382 }
383 else
384 if (t > 1 - DBL_EPSILON) {
385 value = 1;
386 }
387
388 return value;
389 }
390
391
392 class GradientRepeatForwardsStrategy : public GradientRepeatStrategy {
393 public:
394 static GradientRepeatForwardsStrategy *instance();
395
396 virtual double valueAt(double t) const;
397
398 private:
399 GradientRepeatForwardsStrategy() {}
400
401 static GradientRepeatForwardsStrategy *m_instance;
402 };
403
404 GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::m_instance = 0;
405
406 GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::instance()
407 {
408 if (m_instance == 0) {
409 m_instance = new GradientRepeatForwardsStrategy();
410 TQ_CHECK_PTR(m_instance);
411 }
412
413 return m_instance;
414 }
415
416 // Output is 0 to 1, 0 to 1, 0 to 1...
417 double GradientRepeatForwardsStrategy::valueAt(double t) const
418 {
419 int i = static_cast<int>(t);
420
421 if (t < DBL_EPSILON) {
422 i--;
423 }
424
425 double value = t - i;
426
427 return value;
428 }
429
430
431 class GradientRepeatAlternateStrategy : public GradientRepeatStrategy {
432 public:
433 static GradientRepeatAlternateStrategy *instance();
434
435 virtual double valueAt(double t) const;
436
437 private:
438 GradientRepeatAlternateStrategy() {}
439
440 static GradientRepeatAlternateStrategy *m_instance;
441 };
442
443 GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::m_instance = 0;
444
445 GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::instance()
446 {
447 if (m_instance == 0) {
448 m_instance = new GradientRepeatAlternateStrategy();
449 TQ_CHECK_PTR(m_instance);
450 }
451
452 return m_instance;
453 }
454
455 // Output is 0 to 1, 1 to 0, 0 to 1, 1 to 0...
456 double GradientRepeatAlternateStrategy::valueAt(double t) const
457 {
458 if (t < 0) {
459 t = -t;
460 }
461
462 int i = static_cast<int>(t);
463
464 double value = t - i;
465
466 if (i % 2 == 1) {
467 value = 1 - value;
468 }
469
470 return value;
471 }
472}
473
474KisGradientPainter::KisGradientPainter()
475 : super()
476{
477 m_gradient = 0;
478}
479
480KisGradientPainter::KisGradientPainter(KisPaintDeviceSP device) : super(device), m_gradient(0)
481{
482}
483
484bool KisGradientPainter::paintGradient(const KisPoint& gradientVectorStart,
485 const KisPoint& gradientVectorEnd,
486 enumGradientShape shape,
487 enumGradientRepeat repeat,
488 double antiAliasThreshold,
489 bool reverseGradient,
490 TQ_INT32 startx,
491 TQ_INT32 starty,
492 TQ_INT32 width,
493 TQ_INT32 height)
494{
495 m_cancelRequested = false;
496
497 if (!m_gradient) return false;
498
499 GradientShapeStrategy *shapeStrategy = 0;
500
501 switch (shape) {
502 case GradientShapeLinear:
503 shapeStrategy = new LinearGradientStrategy(gradientVectorStart, gradientVectorEnd);
504 break;
505 case GradientShapeBiLinear:
506 shapeStrategy = new BiLinearGradientStrategy(gradientVectorStart, gradientVectorEnd);
507 break;
508 case GradientShapeRadial:
509 shapeStrategy = new RadialGradientStrategy(gradientVectorStart, gradientVectorEnd);
510 break;
511 case GradientShapeSquare:
512 shapeStrategy = new SquareGradientStrategy(gradientVectorStart, gradientVectorEnd);
513 break;
514 case GradientShapeConical:
515 shapeStrategy = new ConicalGradientStrategy(gradientVectorStart, gradientVectorEnd);
516 break;
517 case GradientShapeConicalSymetric:
518 shapeStrategy = new ConicalSymetricGradientStrategy(gradientVectorStart, gradientVectorEnd);
519 break;
520 }
521 TQ_CHECK_PTR(shapeStrategy);
522
523 GradientRepeatStrategy *repeatStrategy = 0;
524
525 switch (repeat) {
526 case GradientRepeatNone:
527 repeatStrategy = GradientRepeatNoneStrategy::instance();
528 break;
529 case GradientRepeatForwards:
530 repeatStrategy = GradientRepeatForwardsStrategy::instance();
531 break;
532 case GradientRepeatAlternate:
533 repeatStrategy = GradientRepeatAlternateStrategy::instance();
534 break;
535 }
536 Q_ASSERT(repeatStrategy != 0);
537
538
539 //If the device has a selection only iterate over that selection
540 TQRect r;
541 if( m_device->hasSelection() ) {
542 r = m_device->selection()->selectedExactRect();
543 startx = r.x();
544 starty = r.y();
545 width = r.width();
546 height = r.height();
547 }
548
549 TQ_INT32 endx = startx + width - 1;
550 TQ_INT32 endy = starty + height - 1;
551
552 TQImage layer (width, height, 32);
553 layer.setAlphaBuffer(true);
554
555 int pixelsProcessed = 0;
556 int lastProgressPercent = 0;
557
558 emit notifyProgressStage(i18n("Rendering gradient..."), 0);
559
560 int totalPixels = width * height;
561
562 if (antiAliasThreshold < 1 - DBL_EPSILON) {
563 totalPixels *= 2;
564 }
565
566 for (int y = starty; y <= endy; y++) {
567 for (int x = startx; x <= endx; x++) {
568
569 double t = shapeStrategy->valueAt( x, y);
570 t = repeatStrategy->valueAt(t);
571
572 if (reverseGradient) {
573 t = 1 - t;
574 }
575
576 TQColor color;
577 TQ_UINT8 opacity;
578
579 m_gradient->colorAt(t, &color, &opacity);
580
581 layer.setPixel(x - startx, y - starty,
582 tqRgba(color.red(), color.green(), color.blue(), opacity));
583
584 pixelsProcessed++;
585
586 int progressPercent = (pixelsProcessed * 100) / totalPixels;
587
588 if (progressPercent > lastProgressPercent) {
589 emit notifyProgress(progressPercent);
590 lastProgressPercent = progressPercent;
591
592 if (m_cancelRequested) {
593 break;
594 }
595 }
596 if (m_cancelRequested) {
597 break;
598 }
599 }
600 }
601
602 if (!m_cancelRequested && antiAliasThreshold < 1 - DBL_EPSILON) {
603
604 TQColor color;
605 emit notifyProgressStage(i18n("Anti-aliasing gradient..."), lastProgressPercent);
606 TQ_UINT8 * layerPointer = layer.bits();
607 for (int y = starty; y <= endy; y++) {
608 for (int x = startx; x <= endx; x++) {
609
610 double maxDistance = 0;
611
612 TQ_UINT8 redThis = layerPointer[2];
613 TQ_UINT8 greenThis = layerPointer[1];
614 TQ_UINT8 blueThis = layerPointer[0];
615 TQ_UINT8 thisPixelOpacity = layerPointer[3];
616
617 for (int yOffset = -1; yOffset < 2; yOffset++) {
618 for (int xOffset = -1; xOffset < 2; xOffset++) {
619
620 if (xOffset != 0 || yOffset != 0) {
621 int sampleX = x + xOffset;
622 int sampleY = y + yOffset;
623
624 if (sampleX >= startx && sampleX <= endx && sampleY >= starty && sampleY <= endy) {
625 uint x = sampleX - startx;
626 uint y = sampleY - starty;
627 TQ_UINT8 * pixelPos = layer.bits() + (y * width * 4) + (x * 4);
628 TQ_UINT8 red = *(pixelPos +2);
629 TQ_UINT8 green = *(pixelPos + 1);
630 TQ_UINT8 blue = *pixelPos;
631 TQ_UINT8 opacity = *(pixelPos + 3);
632
633 double dRed = (red * opacity - redThis * thisPixelOpacity) / 65535.0;
634 double dGreen = (green * opacity - greenThis * thisPixelOpacity) / 65535.0;
635 double dBlue = (blue * opacity - blueThis * thisPixelOpacity) / 65535.0;
636
637 #define SQRT_3 1.7320508
638
639 double distance =/* sqrt(*/dRed * dRed + dGreen * dGreen + dBlue * dBlue/*) / SQRT_3*/;
640
641 if (distance > maxDistance) {
642 maxDistance = distance;
643 }
644 }
645 }
646 }
647 }
648
649 if (maxDistance > 3.*antiAliasThreshold*antiAliasThreshold) {
650 const int numSamples = 4;
651
652 int totalRed = 0;
653 int totalGreen = 0;
654 int totalBlue = 0;
655 int totalOpacity = 0;
656
657 for (int ySample = 0; ySample < numSamples; ySample++) {
658 for (int xSample = 0; xSample < numSamples; xSample++) {
659
660 double sampleWidth = 1.0 / numSamples;
661
662 double sampleX = x - 0.5 + (sampleWidth / 2) + xSample * sampleWidth;
663 double sampleY = y - 0.5 + (sampleWidth / 2) + ySample * sampleWidth;
664
665 double t = shapeStrategy->valueAt(sampleX, sampleY);
666 t = repeatStrategy->valueAt(t);
667
668 if (reverseGradient) {
669 t = 1 - t;
670 }
671
672 TQ_UINT8 opacity;
673
674 m_gradient->colorAt(t, &color, &opacity);
675
676 totalRed += color.red();
677 totalGreen += color.green();
678 totalBlue += color.blue();
679 totalOpacity += opacity;
680 }
681 }
682
683 int red = totalRed / (numSamples * numSamples);
684 int green = totalGreen / (numSamples * numSamples);
685 int blue = totalBlue / (numSamples * numSamples);
686 int opacity = totalOpacity / (numSamples * numSamples);
687
688 layer.setPixel(x - startx, y - starty, tqRgba(red, green, blue, opacity));
689 }
690
691 pixelsProcessed++;
692
693 int progressPercent = (pixelsProcessed * 100) / totalPixels;
694
695 if (progressPercent > lastProgressPercent) {
696 emit notifyProgress(progressPercent);
697 lastProgressPercent = progressPercent;
698
699 if (m_cancelRequested) {
700 break;
701 }
702 }
703 layerPointer += 4;
704 }
705
706 if (m_cancelRequested) {
707 break;
708 }
709 }
710 }
711
712 if (!m_cancelRequested) {
713 kdDebug() << "Have we got a selection? " << m_device->hasSelection() << endl;
714 KisPaintDeviceSP dev = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "temporary device for gradient");
715 dev->writeBytes(layer.bits(), startx, starty, width, height);
716 bltSelection(startx, starty, m_compositeOp, dev, m_opacity, startx, starty, width, height);
717 }
718 delete shapeStrategy;
719
720 emit notifyProgressDone();
721
722 return !m_cancelRequested;
723}
TQ_UINT8 opacity() const
Returns the opacity that is used in painting.
void bltSelection(TQ_INT32 dx, TQ_INT32 dy, const KisCompositeOp &op, KisPaintDeviceSP src, KisSelectionSP selMask, TQ_UINT8 opacity, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 sw, TQ_INT32 sh)
A version of bitBlt that renders using an external selection mask, ignoring the src device's own sele...
A double-based point class that can return it's coordinates approximated to integers.
Definition kis_point.h:28