1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2005-05-25
7 * Description : Blur FX threaded image filter.
8 *
9 * Copyright 2005-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10 * Copyright 2006-2010 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
11 *
12 * Original Blur algorithms copyrighted 2004 by
13 * Pieter Z. Voloshyn <pieter dot voloshyn at gmail dot com>.
14 *
15 * This program is free software; you can redistribute it
16 * and/or modify it under the terms of the GNU General
17 * Public License as published by the Free Software Foundation;
18 * either version 2, or (at your option)
19 * any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * ============================================================ */
27
28 #define ANGLE_RATIO 0.017453292519943295769236907685
29
30 #include "blurfxfilter.h"
31
32 // C++ includes
33
34 #include <cstdlib>
35 #include <cstring>
36
37 // Qt includes
38
39 #include <QDateTime>
40 #include <QtConcurrent> // krazy:exclude=includes
41 #include <QtMath>
42
43 // KDE includes
44
45 #include <klocalizedstring.h>
46
47 // Local includes
48
49 #include "dimg.h"
50 #include "blurfilter.h"
51 #include "randomnumbergenerator.h"
52
53 namespace Digikam
54 {
55
56 class Q_DECL_HIDDEN BlurFXFilter::Private
57 {
58 public:
59
Private()60 explicit Private()
61 : blurFXType (ZoomBlur),
62 distance (100),
63 level (45),
64 randomSeed (RandomNumberGenerator::timeSeed())
65 {
66 }
67
68 int blurFXType;
69 int distance;
70 int level;
71 quint32 randomSeed;
72 };
73
BlurFXFilter(QObject * const parent)74 BlurFXFilter::BlurFXFilter(QObject* const parent)
75 : DImgThreadedFilter(parent),
76 d (new Private)
77 {
78 initFilter();
79 }
80
BlurFXFilter(DImg * const orgImage,QObject * const parent,int blurFXType,int distance,int level)81 BlurFXFilter::BlurFXFilter(DImg* const orgImage, QObject* const parent, int blurFXType, int distance, int level)
82 : DImgThreadedFilter(orgImage, parent, QLatin1String("BlurFX")),
83 d (new Private)
84 {
85 d->blurFXType = blurFXType;
86 d->distance = distance;
87 d->level = level;
88
89 initFilter();
90 }
91
~BlurFXFilter()92 BlurFXFilter::~BlurFXFilter()
93 {
94 cancelFilter();
95 delete d;
96 }
97
DisplayableName()98 QString BlurFXFilter::DisplayableName()
99 {
100 return QString::fromUtf8(I18N_NOOP("Blur FX Filter"));
101 }
102
filterImage()103 void BlurFXFilter::filterImage()
104 {
105 int w = m_orgImage.width();
106 int h = m_orgImage.height();
107
108 switch (d->blurFXType)
109 {
110 case ZoomBlur:
111 zoomBlur(&m_orgImage, &m_destImage, w / 2, h / 2, d->distance);
112 break;
113
114 case RadialBlur:
115 radialBlur(&m_orgImage, &m_destImage, w / 2, h / 2, d->distance);
116 break;
117
118 case FarBlur:
119 farBlur(&m_orgImage, &m_destImage, d->distance);
120 break;
121
122 case MotionBlur:
123 motionBlur(&m_orgImage, &m_destImage, d->distance, (double)d->level);
124 break;
125
126 case SoftenerBlur:
127 softenerBlur(&m_orgImage, &m_destImage);
128 break;
129
130 case ShakeBlur:
131 shakeBlur(&m_orgImage, &m_destImage, d->distance);
132 break;
133
134 case FocusBlur:
135 focusBlur(&m_orgImage, &m_destImage, w / 2, h / 2, d->distance, d->level * 10);
136 break;
137
138 case SmartBlur:
139 smartBlur(&m_orgImage, &m_destImage, d->distance, d->level);
140 break;
141
142 case FrostGlass:
143 frostGlass(&m_orgImage, &m_destImage, d->distance);
144 break;
145
146 case Mosaic:
147 mosaic(&m_orgImage, &m_destImage, d->distance, d->distance);
148 break;
149 }
150 }
151
zoomBlurMultithreaded(const Args & prm)152 void BlurFXFilter::zoomBlurMultithreaded(const Args& prm)
153 {
154 int nh, nw;
155 int sumR, sumG, sumB, nCount=0;
156 double lfRadius, lfNewRadius, lfAngle;
157
158 DColor color;
159 int offset;
160
161 int Width = prm.orgImage->width();
162 int Height = prm.orgImage->height();
163 uchar* data = prm.orgImage->bits();
164 bool sixteenBit = prm.orgImage->sixteenBit();
165 int bytesDepth = prm.orgImage->bytesDepth();
166 uchar* pResBits = prm.destImage->bits();
167
168 double lfRadMax = qSqrt(Height * Height + Width * Width);
169
170 for (uint w = prm.start; runningFlag() && (w < prm.stop); ++w)
171 {
172 // ...we enter this loop to sum the bits
173
174 // we initialize the variables
175 sumR = sumG = sumB = nCount = 0;
176
177 nw = prm.X - w;
178 nh = prm.Y - prm.h;
179
180 lfRadius = qSqrt(nw * nw + nh * nh);
181 lfAngle = qAtan2((double)nh, (double)nw);
182 lfNewRadius = (lfRadius * prm.Distance) / lfRadMax;
183
184 for (int r = 0; runningFlag() && (r <= lfNewRadius); ++r)
185 {
186 // we need to calc the positions
187 nw = (int)(prm.X - (lfRadius - r) * cos(lfAngle));
188 nh = (int)(prm.Y - (lfRadius - r) * sin(lfAngle));
189
190 if (IsInside(Width, Height, nw, nh))
191 {
192 // read color
193 offset = GetOffset(Width, nw, nh, bytesDepth);
194 color.setColor(data + offset, sixteenBit);
195
196 // we sum the bits
197 sumR += color.red();
198 sumG += color.green();
199 sumB += color.blue();
200 ++nCount;
201 }
202 }
203
204 if (nCount == 0)
205 {
206 nCount = 1;
207 }
208
209 // calculate pointer
210 offset = GetOffset(Width, w, prm.h, bytesDepth);
211 // read color to preserve alpha
212 color.setColor(data + offset, sixteenBit);
213
214 // now, we have to calc the arithmetic average
215 color.setRed(sumR / nCount);
216 color.setGreen(sumG / nCount);
217 color.setBlue(sumB / nCount);
218
219 // write color to destination
220 color.setPixel(pResBits + offset);
221 }
222 }
223
224 /* Function to apply the ZoomBlur effect backported from ImageProcessing version 2
225 *
226 * data => The image data in RGBA mode.
227 * Width => Width of image.
228 * Height => Height of image.
229 * X, Y => Center of zoom in the image
230 * Distance => Distance value
231 * pArea => Preview area.
232 *
233 * Theory => Here we have a effect similar to RadialBlur mode Zoom from
234 * Photoshop. The theory is very similar to RadialBlur, but has one
235 * difference. Instead we use pixels with the same radius and
236 * near angles, we take pixels with the same angle but near radius
237 * This radius is always from the center to out of the image, we
238 * calc a proportional radius from the center.
239 */
zoomBlur(DImg * const orgImage,DImg * const destImage,int X,int Y,int Distance,const QRect & pArea)240 void BlurFXFilter::zoomBlur(DImg* const orgImage, DImg* const destImage, int X, int Y, int Distance, const QRect& pArea)
241 {
242 if (Distance <= 1)
243 {
244 return;
245 }
246
247 int progress;
248
249 // We working on full image.
250 int xMin = 0;
251 int xMax = orgImage->width();
252 int yMin = 0;
253 int yMax = orgImage->height();
254
255 // If we working in preview mode, else we using the preview area.
256 if (pArea.isValid())
257 {
258 xMin = pArea.x();
259 xMax = pArea.x() + pArea.width();
260 yMin = pArea.y();
261 yMax = pArea.y() + pArea.height();
262 }
263
264 QList<int> vals = multithreadedSteps(xMax, xMin);
265 QList <QFuture<void> > tasks;
266
267 Args prm;
268 prm.orgImage = orgImage;
269 prm.destImage = destImage;
270 prm.X = X;
271 prm.Y = Y;
272 prm.Distance = Distance;
273
274 // we have reached the main loop
275 for (int h = yMin; runningFlag() && (h < yMax); ++h)
276 {
277 for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j)
278 {
279 prm.start = vals[j];
280 prm.stop = vals[j+1];
281 prm.h = h;
282 tasks.append(QtConcurrent::run(this,
283 &BlurFXFilter::zoomBlurMultithreaded,
284 prm
285 ));
286 }
287
288 foreach (QFuture<void> t, tasks)
289 t.waitForFinished();
290
291 // Update the progress bar in dialog.
292 progress = (int)(((double)(h - yMin) * 100.0) / (yMax - yMin));
293
294 if (progress % 5 == 0)
295 {
296 postProgress(progress);
297 }
298 }
299 }
300
radialBlurMultithreaded(const Args & prm)301 void BlurFXFilter::radialBlurMultithreaded(const Args& prm)
302 {
303 int Width = prm.orgImage->width();
304 int Height = prm.orgImage->height();
305 uchar* data = prm.orgImage->bits();
306 bool sixteenBit = prm.orgImage->sixteenBit();
307 int bytesDepth = prm.orgImage->bytesDepth();
308 uchar* pResBits = prm.destImage->bits();
309
310 int sumR, sumG, sumB, nw, nh;
311 double Radius, Angle, AngleRad;
312
313 DColor color;
314 int offset;
315
316 QScopedArrayPointer<double> nMultArray(new double[prm.Distance * 2 + 1]);
317
318 for (int i = -prm.Distance; i <= prm.Distance; ++i)
319 {
320 nMultArray[i + prm.Distance] = i * ANGLE_RATIO;
321 }
322
323 // number of added pixels
324 int nCount = 0;
325
326 for (uint w = prm.start; runningFlag() && (w < prm.stop); ++w)
327 {
328 // ...we enter this loop to sum the bits
329
330 // we initialize the variables
331 sumR = sumG = sumB = nCount = 0;
332
333 nw = prm.X - w;
334 nh = prm.Y - prm.h;
335
336 Radius = qSqrt(nw * nw + nh * nh);
337 AngleRad = qAtan2((double)nh, (double)nw);
338
339 for (int a = -prm.Distance; runningFlag() && (a <= prm.Distance); ++a)
340 {
341 Angle = AngleRad + nMultArray[a + prm.Distance];
342 // we need to calc the positions
343 nw = (int)(prm.X - Radius * qCos(Angle));
344 nh = (int)(prm.Y - Radius * qSin(Angle));
345
346 if (IsInside(Width, Height, nw, nh))
347 {
348 // read color
349 offset = GetOffset(Width, nw, nh, bytesDepth);
350 color.setColor(data + offset, sixteenBit);
351
352 // we sum the bits
353 sumR += color.red();
354 sumG += color.green();
355 sumB += color.blue();
356 ++nCount;
357 }
358 }
359
360 if (nCount == 0)
361 {
362 nCount = 1;
363 }
364
365 // calculate pointer
366 offset = GetOffset(Width, w, prm.h, bytesDepth);
367 // read color to preserve alpha
368 color.setColor(data + offset, sixteenBit);
369
370 // now, we have to calc the arithmetic average
371 color.setRed(sumR / nCount);
372 color.setGreen(sumG / nCount);
373 color.setBlue(sumB / nCount);
374
375 // write color to destination
376 color.setPixel(pResBits + offset);
377 }
378 }
379
380 /* Function to apply the radialBlur effect backported from ImageProcessing version 2
381 *
382 * data => The image data in RGBA mode.
383 * Width => Width of image.
384 * Height => Height of image.
385 * X, Y => Center of radial in the image
386 * Distance => Distance value
387 * pArea => Preview area.
388 *
389 * Theory => Similar to RadialBlur from Photoshop, its an amazing effect
390 * Very easy to understand but a little hard to implement.
391 * We have all the image and find the center pixel. Now, we analyze
392 * all the pixels and calc the radius from the center and find the
393 * angle. After this, we sum this pixel with others with the same
394 * radius, but different angles. Here I'm using degrees angles.
395 */
radialBlur(DImg * const orgImage,DImg * const destImage,int X,int Y,int Distance,const QRect & pArea)396 void BlurFXFilter::radialBlur(DImg* const orgImage, DImg* const destImage, int X, int Y, int Distance, const QRect& pArea)
397 {
398 if (Distance <= 1)
399 {
400 return;
401 }
402
403 int progress;
404
405 // We working on full image.
406 int xMin = 0;
407 int xMax = orgImage->width();
408 int yMin = 0;
409 int yMax = orgImage->height();
410
411 // If we working in preview mode, else we using the preview area.
412 if (pArea.isValid())
413 {
414 xMin = pArea.x();
415 xMax = pArea.x() + pArea.width();
416 yMin = pArea.y();
417 yMax = pArea.y() + pArea.height();
418 }
419
420 QList<int> vals = multithreadedSteps(xMax, xMin);
421 QList <QFuture<void> > tasks;
422
423 Args prm;
424 prm.orgImage = orgImage;
425 prm.destImage = destImage;
426 prm.X = X;
427 prm.Y = Y;
428 prm.Distance = Distance;
429
430 // we have reached the main loop
431
432 for (int h = yMin; runningFlag() && (h < yMax); ++h)
433 {
434 for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j)
435 {
436 prm.start = vals[j];
437 prm.stop = vals[j+1];
438 prm.h = h;
439 tasks.append(QtConcurrent::run(this,
440 &BlurFXFilter::radialBlurMultithreaded,
441 prm
442 ));
443 }
444
445 foreach (QFuture<void> t, tasks)
446 t.waitForFinished();
447
448 // Update the progress bar in dialog.
449 progress = (int)(((double)(h - yMin) * 100.0) / (yMax - yMin));
450
451 if (progress % 5 == 0)
452 {
453 postProgress(progress);
454 }
455 }
456 }
457
458 /* Function to apply the farBlur effect backported from ImageProcessing version 2
459 *
460 * data => The image data in RGBA mode.
461 * Width => Width of image.
462 * Height => Height of image.
463 * Distance => Distance value
464 *
465 * Theory => This is an interesting effect, the blur is applied in that
466 * way: (the value "1" means pixel to be used in a blur calc, ok?)
467 * e.g. With distance = 2
468 * |1|1|1|1|1|
469 * |1|0|0|0|1|
470 * |1|0|C|0|1|
471 * |1|0|0|0|1|
472 * |1|1|1|1|1|
473 * We sum all the pixels with value = 1 and apply at the pixel with*
474 * the position "C".
475 */
farBlur(DImg * const orgImage,DImg * const destImage,int Distance)476 void BlurFXFilter::farBlur(DImg* const orgImage, DImg* const destImage, int Distance)
477 {
478 if (Distance < 1)
479 {
480 return;
481 }
482
483 // we need to create our kernel
484 // e.g. distance = 3, so kernel={3 1 1 2 1 1 3}
485
486 QScopedArrayPointer<int> nKern(new int[Distance * 2 + 1]);
487
488 for (int i = 0; i < Distance * 2 + 1; ++i)
489 {
490 // the first element is 3
491 if (i == 0)
492 {
493 nKern[i] = 2;
494 }
495 // the center element is 2
496 else if (i == Distance)
497 {
498 nKern[i] = 3;
499 }
500 // the last element is 3
501 else if (i == Distance * 2)
502 {
503 nKern[i] = 3;
504 }
505 // all other elements will be 1
506 else
507 {
508 nKern[i] = 1;
509 }
510 }
511
512 // now, we apply a convolution with kernel
513 MakeConvolution(orgImage, destImage, Distance, nKern.data());
514 }
515
motionBlurMultithreaded(const Args & prm)516 void BlurFXFilter::motionBlurMultithreaded(const Args& prm)
517 {
518 int Width = prm.orgImage->width();
519 int Height = prm.orgImage->height();
520 uchar* data = prm.orgImage->bits();
521 bool sixteenBit = prm.orgImage->sixteenBit();
522 int bytesDepth = prm.orgImage->bytesDepth();
523 uchar* pResBits = prm.destImage->bits();
524 int nCount = prm.nCount;
525
526 DColor color;
527 int offset, sumR, sumG, sumB, nw, nh;
528
529 for (uint w = prm.start; runningFlag() && (w < prm.stop); ++w)
530 {
531 // we initialize the variables
532 sumR = sumG = sumB = 0;
533
534 // ...we enter this loop to sum the bits
535 for (int a = -prm.Distance; runningFlag() && (a <= prm.Distance); ++a)
536 {
537 // we need to calc the positions
538 nw = w + prm.lpXArray[a + prm.Distance];
539 nh = prm.h + prm.lpYArray[a + prm.Distance];
540
541 offset = GetOffsetAdjusted(Width, Height, nw, nh, bytesDepth);
542 color.setColor(data + offset, sixteenBit);
543
544 // we sum the bits
545 sumR += color.red();
546 sumG += color.green();
547 sumB += color.blue();
548 }
549
550 if (nCount == 0)
551 {
552 nCount = 1;
553 }
554
555 // calculate pointer
556 offset = GetOffset(Width, w, prm.h, bytesDepth);
557 // read color to preserve alpha
558 color.setColor(data + offset, sixteenBit);
559
560 // now, we have to calc the arithmetic average
561 color.setRed(sumR / nCount);
562 color.setGreen(sumG / nCount);
563 color.setBlue(sumB / nCount);
564
565 // write color to destination
566 color.setPixel(pResBits + offset);
567 }
568 }
569
570 /* Function to apply the motionBlur effect backported from ImageProcessing version 2
571 *
572 * data => The image data in RGBA mode.
573 * Width => Width of image.
574 * Height => Height of image.
575 * Distance => Distance value
576 * Angle => Angle direction (degrees)
577 *
578 * Theory => Similar to MotionBlur from Photoshop, the engine is very
579 * simple to understand, we take a pixel (duh!), with the angle we
580 * will taking near pixels. After this we blur (add and do a
581 * division).
582 */
motionBlur(DImg * const orgImage,DImg * const destImage,int Distance,double Angle)583 void BlurFXFilter::motionBlur(DImg* const orgImage, DImg* const destImage, int Distance, double Angle)
584 {
585 if (Distance == 0)
586 {
587 return;
588 }
589
590 int progress;
591
592 // we try to avoid division by 0 (zero)
593 if (Angle == 0.0)
594 {
595 Angle = 360.0;
596 }
597
598 // we initialize cos and sin for a best performance
599 double nAngX = cos((2.0 * M_PI) / (360.0 / Angle));
600 double nAngY = sin((2.0 * M_PI) / (360.0 / Angle));
601
602 // total of bits to be taken is given by this formula
603 int nCount = Distance * 2 + 1;
604
605 // we will alloc size and calc the possible results
606 QScopedArrayPointer<int> lpXArray(new int[nCount]);
607 QScopedArrayPointer<int> lpYArray(new int[nCount]);
608
609 for (int i = 0; i < nCount; ++i)
610 {
611 lpXArray[i] = lround((double)(i - Distance) * nAngX);
612 lpYArray[i] = lround((double)(i - Distance) * nAngY);
613 }
614
615 QList<int> vals = multithreadedSteps(orgImage->width());
616 QList <QFuture<void> > tasks;
617
618 Args prm;
619 prm.orgImage = orgImage;
620 prm.destImage = destImage;
621 prm.Distance = Distance;
622 prm.nCount = nCount;
623 prm.lpXArray = lpXArray.data();
624 prm.lpYArray = lpYArray.data();
625
626 // we have reached the main loop
627
628 for (uint h = 0; runningFlag() && (h < orgImage->height()); ++h)
629 {
630 for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j)
631 {
632 prm.start = vals[j];
633 prm.stop = vals[j+1];
634 prm.h = h;
635 tasks.append(QtConcurrent::run(this,
636 &BlurFXFilter::motionBlurMultithreaded,
637 prm
638 ));
639 }
640
641 foreach (QFuture<void> t, tasks)
642 t.waitForFinished();
643
644 // Update the progress bar in dialog.
645 progress = (int)(((double)h * 100.0) / orgImage->height());
646
647 if (progress % 5 == 0)
648 {
649 postProgress(progress);
650 }
651 }
652 }
653
softenerBlurMultithreaded(const Args & prm)654 void BlurFXFilter::softenerBlurMultithreaded(const Args& prm)
655 {
656 int Width = prm.orgImage->width();
657 int Height = prm.orgImage->height();
658 uchar* data = prm.orgImage->bits();
659 bool sixteenBit = prm.orgImage->sixteenBit();
660 int bytesDepth = prm.orgImage->bytesDepth();
661 uchar* pResBits = prm.destImage->bits();
662
663 int SomaR = 0, SomaG = 0, SomaB = 0;
664 int Gray;
665
666 DColor color, colorSoma;
667 int offset, offsetSoma;
668
669 int grayLimit = sixteenBit ? 32767 : 127;
670
671 for (uint w = prm.start; runningFlag() && (w < prm.stop); ++w)
672 {
673 SomaR = SomaG = SomaB = 0;
674
675 offset = GetOffset(Width, w, prm.h, bytesDepth);
676 color.setColor(data + offset, sixteenBit);
677
678 Gray = (color.red() + color.green() + color.blue()) / 3;
679
680 if (Gray > grayLimit)
681 {
682 // 7x7
683 for (int a = -3; runningFlag() && (a <= 3); ++a)
684 {
685 for (int b = -3; runningFlag() && (b <= 3); ++b)
686 {
687 if ((((int)prm.h + a) < 0) || (((int)w + b) < 0))
688 {
689 offsetSoma = offset;
690 }
691 else
692 {
693 offsetSoma = GetOffset(Width, (w + Lim_Max(w, b, Width)),
694 (prm.h + Lim_Max(prm.h, a, Height)), bytesDepth);
695 }
696
697 colorSoma.setColor(data + offsetSoma, sixteenBit);
698
699 SomaR += colorSoma.red();
700 SomaG += colorSoma.green();
701 SomaB += colorSoma.blue();
702 }
703 }
704
705 // 7*7 = 49
706 color.setRed(SomaR / 49);
707 color.setGreen(SomaG / 49);
708 color.setBlue(SomaB / 49);
709 color.setPixel(pResBits + offset);
710 }
711 else
712 {
713 // 3x3
714 for (int a = -1; runningFlag() && (a <= 1); ++a)
715 {
716 for (int b = -1; runningFlag() && (b <= 1); ++b)
717 {
718 if ((((int)prm.h + a) < 0) || (((int)w + b) < 0))
719 {
720 offsetSoma = offset;
721 }
722 else
723 {
724 offsetSoma = GetOffset(Width, (w + Lim_Max(w, b, Width)),
725 (prm.h + Lim_Max(prm.h, a, Height)), bytesDepth);
726 }
727
728 colorSoma.setColor(data + offsetSoma, sixteenBit);
729
730 SomaR += colorSoma.red();
731 SomaG += colorSoma.green();
732 SomaB += colorSoma.blue();
733 }
734 }
735
736 // 3*3 = 9
737 color.setRed(SomaR / 9);
738 color.setGreen(SomaG / 9);
739 color.setBlue(SomaB / 9);
740 color.setPixel(pResBits + offset);
741 }
742 }
743 }
744
745 /* Function to apply the softenerBlur effect
746 *
747 * data => The image data in RGBA mode.
748 * Width => Width of image.
749 * Height => Height of image.
750 *
751 * Theory => An interesting blur-like function. In dark tones we apply a
752 * blur with 3x3 dimensions, in light tones, we apply a blur with
753 * 5x5 dimensions. Easy, hun?
754 */
softenerBlur(DImg * const orgImage,DImg * const destImage)755 void BlurFXFilter::softenerBlur(DImg* const orgImage, DImg* const destImage)
756 {
757 int progress;
758
759 QList<int> vals = multithreadedSteps(orgImage->width());
760 QList <QFuture<void> > tasks;
761
762 Args prm;
763 prm.orgImage = orgImage;
764 prm.destImage = destImage;
765
766 // we have reached the main loop
767
768 for (uint h = 0; runningFlag() && (h < orgImage->height()); ++h)
769 {
770 for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j)
771 {
772 prm.start = vals[j];
773 prm.stop = vals[j+1];
774 prm.h = h;
775 tasks.append(QtConcurrent::run(this,
776 &BlurFXFilter::softenerBlurMultithreaded,
777 prm
778 ));
779 }
780
781 foreach (QFuture<void> t, tasks)
782 t.waitForFinished();
783
784 // Update the progress bar in dialog.
785 progress = (int)(((double)h * 100.0) / orgImage->height());
786
787 if (progress % 5 == 0)
788 {
789 postProgress(progress);
790 }
791 }
792 }
793
shakeBlurStage1Multithreaded(const Args & prm)794 void BlurFXFilter::shakeBlurStage1Multithreaded(const Args& prm)
795 {
796 uint Width = prm.orgImage->width();
797 uint Height = prm.orgImage->height();
798 uchar* data = prm.orgImage->bits();
799 bool sixteenBit = prm.orgImage->sixteenBit();
800 int bytesDepth = prm.orgImage->bytesDepth();
801
802 DColor color;
803 int offset, offsetLayer;
804 int nw, nh;
805
806 for (uint w = prm.start; runningFlag() && (w < prm.stop); ++w)
807 {
808 offsetLayer = GetOffset(Width, w, prm.h, bytesDepth);
809
810 nh = ((prm.h + prm.Distance) >= Height) ? Height - 1 : prm.h + prm.Distance;
811 offset = GetOffset(Width, w, nh, bytesDepth);
812 color.setColor(data + offset, sixteenBit);
813 color.setPixel(prm.layer1 + offsetLayer);
814
815 nh = (((int)prm.h - prm.Distance) < 0) ? 0 : prm.h - prm.Distance;
816 offset = GetOffset(Width, w, nh, bytesDepth);
817 color.setColor(data + offset, sixteenBit);
818 color.setPixel(prm.layer2 + offsetLayer);
819
820 nw = ((w + prm.Distance) >= Width) ? Width - 1 : w + prm.Distance;
821 offset = GetOffset(Width, nw, prm.h, bytesDepth);
822 color.setColor(data + offset, sixteenBit);
823 color.setPixel(prm.layer3 + offsetLayer);
824
825 nw = (((int)w - prm.Distance) < 0) ? 0 : w - prm.Distance;
826 offset = GetOffset(Width, nw, prm.h, bytesDepth);
827 color.setColor(data + offset, sixteenBit);
828 color.setPixel(prm.layer4 + offsetLayer);
829 }
830 }
831
shakeBlurStage2Multithreaded(const Args & prm)832 void BlurFXFilter::shakeBlurStage2Multithreaded(const Args& prm)
833 {
834 int Width = prm.orgImage->width();
835 uchar* data = prm.orgImage->bits();
836 bool sixteenBit = prm.orgImage->sixteenBit();
837 int bytesDepth = prm.orgImage->bytesDepth();
838 uchar* pResBits = prm.destImage->bits();
839
840 DColor color, color1, color2, color3, color4;
841 int offset;
842
843 for (uint w = prm.start; runningFlag() && (w < prm.stop); ++w)
844 {
845 offset = GetOffset(Width, w, prm.h, bytesDepth);
846 // read original data to preserve alpha
847 color.setColor(data + offset, sixteenBit);
848 // read colors from all four layers
849 color1.setColor(prm.layer1 + offset, sixteenBit);
850 color2.setColor(prm.layer2 + offset, sixteenBit);
851 color3.setColor(prm.layer3 + offset, sixteenBit);
852 color4.setColor(prm.layer4 + offset, sixteenBit);
853
854 // set color components of resulting color
855 color.setRed((color1.red() + color2.red() + color3.red() + color4.red()) / 4);
856 color.setGreen((color1.green() + color2.green() + color3.green() + color4.green()) / 4);
857 color.setBlue((color1.blue() + color2.blue() + color3.blue() + color4.blue()) / 4);
858
859 color.setPixel(pResBits + offset);
860 }
861 }
862
863 /* Function to apply the shake blur effect
864 *
865 * data => The image data in RGBA mode.
866 * Width => Width of image.
867 * Height => Height of image.
868 * Distance => Distance between layers (from origin)
869 *
870 * Theory => Similar to Fragment effect from Photoshop. We create 4 layers
871 * each one has the same distance from the origin, but have
872 * different positions (top, button, left and right), with these 4
873 * layers, we join all the pixels.
874 */
shakeBlur(DImg * const orgImage,DImg * const destImage,int Distance)875 void BlurFXFilter::shakeBlur(DImg* const orgImage, DImg* const destImage, int Distance)
876 {
877 int progress;
878
879 quint64 numBytes = orgImage->numBytes();
880 QScopedArrayPointer<uchar> layer1(new uchar[numBytes]);
881 QScopedArrayPointer<uchar> layer2(new uchar[numBytes]);
882 QScopedArrayPointer<uchar> layer3(new uchar[numBytes]);
883 QScopedArrayPointer<uchar> layer4(new uchar[numBytes]);
884
885 QList<int> vals = multithreadedSteps(orgImage->width());
886 QList <QFuture<void> > tasks;
887
888 Args prm;
889 prm.orgImage = orgImage;
890 prm.destImage = destImage;
891 prm.Distance = Distance;
892 prm.layer1 = layer1.data();
893 prm.layer2 = layer2.data();
894 prm.layer3 = layer3.data();
895 prm.layer4 = layer4.data();
896
897 // we have reached the main loop
898
899 for (uint h = 0; runningFlag() && (h < orgImage->height()); ++h)
900 {
901 for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j)
902 {
903 prm.start = vals[j];
904 prm.stop = vals[j+1];
905 prm.h = h;
906 tasks.append(QtConcurrent::run(this,
907 &BlurFXFilter::shakeBlurStage1Multithreaded,
908 prm
909 ));
910 }
911
912 foreach (QFuture<void> t, tasks)
913 t.waitForFinished();
914
915 // Update the progress bar in dialog.
916 progress = (int)(((double)h * 50.0) / orgImage->height());
917
918 if (progress % 5 == 0)
919 {
920 postProgress(progress);
921 }
922 }
923
924 tasks.clear();
925
926 for (uint h = 0; runningFlag() && (h < orgImage->height()); ++h)
927 {
928 for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j)
929 {
930 prm.start = vals[j];
931 prm.stop = vals[j+1];
932 prm.h = h;
933 tasks.append(QtConcurrent::run(this,
934 &BlurFXFilter::shakeBlurStage2Multithreaded,
935 prm
936 ));
937 }
938
939 foreach (QFuture<void> t, tasks)
940 t.waitForFinished();
941
942 // Update the progress bar in dialog.
943 progress = (int)(50.0 + ((double)h * 50.0) / orgImage->height());
944
945 if (progress % 5 == 0)
946 {
947 postProgress(progress);
948 }
949 }
950 }
951
focusBlurMultithreaded(const Args & prm)952 void BlurFXFilter::focusBlurMultithreaded(const Args& prm)
953 {
954 int nBlendFactor;
955 double lfRadius;
956 int offset;
957
958 DColor colorOrgImage, colorBlurredImage;
959 int alpha;
960 uchar* ptr = nullptr;
961
962 // get composer for default blending
963 DColorComposer* const composer = DColorComposer::getComposer(DColorComposer::PorterDuffNone);
964
965 int Width = prm.orgImage->width();
966 uchar* data = prm.orgImage->bits();
967 bool sixteenBit = prm.orgImage->sixteenBit();
968 int bytesDepth = prm.orgImage->bytesDepth();
969 uchar* pResBits = prm.destImage->bits();
970
971 int nw = 0;
972 int nh = prm.Y - prm.h;
973
974 for (uint w = prm.start; runningFlag() && (w < prm.stop); ++w)
975 {
976 nw = prm.X - w;
977
978 lfRadius = qSqrt(nh * nh + nw * nw);
979
980 if (sixteenBit)
981 {
982 nBlendFactor = CLAMP065535((int)(65535.0 * lfRadius / (double)prm.BlendRadius));
983 }
984 else
985 {
986 nBlendFactor = (uchar)CLAMP0255((int)(255.0 * lfRadius / (double)prm.BlendRadius));
987 }
988
989 // Read color values
990 offset = GetOffset(Width, w, prm.h, bytesDepth);
991 ptr = pResBits + offset;
992 colorOrgImage.setColor(data + offset, sixteenBit);
993 colorBlurredImage.setColor(ptr, sixteenBit);
994
995 // Preserve alpha
996 alpha = colorOrgImage.alpha();
997
998 // In normal mode, the image is focused in the middle
999 // and less focused towards the border.
1000 // In inverse mode, the image is more focused towards the edge
1001 // and less focused in the middle.
1002 // This is achieved by swapping src and dest while blending.
1003 if (prm.bInversed)
1004 {
1005 // set blending alpha value as src alpha. Original value is stored above.
1006 colorOrgImage.setAlpha(nBlendFactor);
1007 // compose colors, writing to dest - colorBlurredImage
1008 composer->compose(colorBlurredImage, colorOrgImage);
1009 // restore alpha
1010 colorBlurredImage.setAlpha(alpha);
1011 // write color to destination
1012 colorBlurredImage.setPixel(ptr);
1013 }
1014 else
1015 {
1016 // set blending alpha value as src alpha. Original value is stored above.
1017 colorBlurredImage.setAlpha(nBlendFactor);
1018 // compose colors, writing to dest - colorOrgImage
1019 composer->compose(colorOrgImage, colorBlurredImage);
1020 // restore alpha
1021 colorOrgImage.setAlpha(alpha);
1022 // write color to destination
1023 colorOrgImage.setPixel(ptr);
1024 }
1025 }
1026
1027 delete composer;
1028 }
1029
1030 /* Function to apply the focusBlur effect backported from ImageProcessing version 2
1031 *
1032 * data => The image data in RGBA mode.
1033 * Width => Width of image.
1034 * Height => Height of image.
1035 * BlurRadius => Radius of blurred image.
1036 * BlendRadius => Radius of blending effect.
1037 * bInversed => If true, invert focus effect.
1038 * pArea => Preview area.
1039 *
1040 */
focusBlur(DImg * const orgImage,DImg * const destImage,int X,int Y,int BlurRadius,int BlendRadius,bool bInversed,const QRect & pArea)1041 void BlurFXFilter::focusBlur(DImg* const orgImage, DImg* const destImage,
1042 int X, int Y, int BlurRadius, int BlendRadius,
1043 bool bInversed, const QRect& pArea)
1044 {
1045 int progress;
1046
1047 // We working on full image.
1048
1049 int xMin = 0;
1050 int xMax = orgImage->width();
1051 int yMin = 0;
1052 int yMax = orgImage->height();
1053
1054 // If we working in preview mode, else we using the preview area.
1055
1056 if (pArea.isValid())
1057 {
1058 xMin = pArea.x();
1059 xMax = pArea.x() + pArea.width();
1060 yMin = pArea.y();
1061 yMax = pArea.y() + pArea.height();
1062
1063 // We do not have access to the loop of the Gaussian blur,
1064 // so we have to cut the image that we run the effect on.
1065
1066 int xMinBlur = xMin - BlurRadius;
1067 int xMaxBlur = xMax + BlurRadius;
1068 int yMinBlur = yMin - BlurRadius;
1069 int yMaxBlur = yMax + BlurRadius;
1070 DImg areaImage = orgImage->copy(xMinBlur, yMaxBlur, xMaxBlur - xMinBlur, yMaxBlur - yMinBlur);
1071
1072 // cppcheck-suppress unusedScopedObject
1073 BlurFilter(this, *orgImage, *destImage, 10, 75, BlurRadius);
1074
1075 // I am unsure about differences of 1 pixel
1076
1077 destImage->bitBltImage(&areaImage, xMinBlur, yMinBlur);
1078 destImage->bitBltImage(orgImage, 0, 0, orgImage->width(), yMinBlur, 0, 0);
1079 destImage->bitBltImage(orgImage, 0, yMinBlur, xMinBlur, yMaxBlur - yMinBlur, 0, yMinBlur);
1080 destImage->bitBltImage(orgImage, xMaxBlur + 1, yMinBlur, orgImage->width() - xMaxBlur - 1, yMaxBlur - yMinBlur, yMaxBlur, yMinBlur);
1081 destImage->bitBltImage(orgImage, 0, yMaxBlur + 1, orgImage->width(), orgImage->height() - yMaxBlur - 1, 0, yMaxBlur);
1082
1083 postProgress(80);
1084 }
1085 else
1086 {
1087 // copy bits for blurring
1088
1089 memcpy(destImage->bits(), orgImage->bits(), orgImage->numBytes());
1090
1091 // Gaussian blur using the BlurRadius parameter.
1092
1093 BlurFilter(this, *orgImage, *destImage, 10, 80, BlurRadius);
1094 }
1095
1096 // Blending results.
1097
1098 QList<int> vals = multithreadedSteps(xMax, xMin);
1099 QList <QFuture<void> > tasks;
1100
1101 Args prm;
1102 prm.orgImage = orgImage;
1103 prm.destImage = destImage;
1104 prm.X = X;
1105 prm.Y = Y;
1106 prm.BlendRadius = BlendRadius;
1107 prm.bInversed = bInversed;
1108
1109 // we have reached the main loop
1110
1111 for (int h = yMin ; runningFlag() && (h < yMax) ; ++h)
1112 {
1113 for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j)
1114 {
1115 prm.start = vals[j];
1116 prm.stop = vals[j+1];
1117 prm.h = h;
1118 tasks.append(QtConcurrent::run(this,
1119 &BlurFXFilter::focusBlurMultithreaded,
1120 prm
1121 ));
1122 }
1123
1124 foreach (QFuture<void> t, tasks)
1125 {
1126 t.waitForFinished();
1127 }
1128
1129 // Update the progress bar in dialog.
1130
1131 progress = (int)(80.0 + ((double)(h - yMin) * 20.0) / (yMax - yMin));
1132
1133 if (progress % 5 == 0)
1134 {
1135 postProgress(progress);
1136 }
1137 }
1138 }
1139
smartBlurStage1Multithreaded(const Args & prm)1140 void BlurFXFilter::smartBlurStage1Multithreaded(const Args& prm)
1141 {
1142 int Width = prm.orgImage->width();
1143 int Height = prm.orgImage->height();
1144 uchar* data = prm.orgImage->bits();
1145 bool sixteenBit = prm.orgImage->sixteenBit();
1146 int bytesDepth = prm.orgImage->bytesDepth();
1147
1148 int sumR, sumG, sumB, nCount;
1149 DColor color, radiusColor, radiusColorBlur;
1150 int offset, loopOffset;
1151
1152 for (uint w = prm.start ; runningFlag() && (w < prm.stop) ; ++w)
1153 {
1154 // we initialize the variables
1155 sumR = sumG = sumB = nCount = 0;
1156
1157 // read color
1158 offset = GetOffset(Width, w, prm.h, bytesDepth);
1159 color.setColor(data + offset, sixteenBit);
1160
1161 // ...we enter this loop to sum the bits
1162 for (int a = -prm.Radius ; runningFlag() && (a <= prm.Radius) ; ++a)
1163 {
1164 // verify if is inside the rect
1165 if (IsInside(Width, Height, w + a, prm.h))
1166 {
1167 // read color
1168 loopOffset = GetOffset(Width, w + a, prm.h, bytesDepth);
1169 radiusColor.setColor(data + loopOffset, sixteenBit);
1170
1171 // now, we have to check if is inside the sensibility filter
1172 if (IsColorInsideTheRange(color.red(), color.green(), color.blue(),
1173 radiusColor.red(), radiusColor.green(), radiusColor.blue(),
1174 prm.StrengthRange))
1175 {
1176 // finally we sum the bits
1177 sumR += radiusColor.red();
1178 sumG += radiusColor.green();
1179 sumB += radiusColor.blue();
1180 }
1181 else
1182 {
1183 // finally we sum the bits
1184 sumR += color.red();
1185 sumG += color.green();
1186 sumB += color.blue();
1187 }
1188
1189 // increment counter
1190 ++nCount;
1191 }
1192 }
1193
1194 if (nCount == 0)
1195 {
1196 nCount = 1;
1197 }
1198
1199 // now, we have to calc the arithmetic average
1200 color.setRed(sumR / nCount);
1201 color.setGreen(sumG / nCount);
1202 color.setBlue(sumB / nCount);
1203
1204 // write color to destination
1205 color.setPixel(prm.pBlur + offset);
1206 }
1207 }
1208
smartBlurStage2Multithreaded(const Args & prm)1209 void BlurFXFilter::smartBlurStage2Multithreaded(const Args& prm)
1210 {
1211 int Width = prm.orgImage->width();
1212 int Height = prm.orgImage->height();
1213 uchar* data = prm.orgImage->bits();
1214 bool sixteenBit = prm.orgImage->sixteenBit();
1215 int bytesDepth = prm.orgImage->bytesDepth();
1216 uchar* pResBits = prm.destImage->bits();
1217
1218 int sumR, sumG, sumB, nCount;
1219 DColor color, radiusColor, radiusColorBlur;
1220 int offset, loopOffset;
1221
1222 for (uint h = prm.start ; runningFlag() && (h < prm.stop) ; ++h)
1223 {
1224 // we initialize the variables
1225 sumR = sumG = sumB = nCount = 0;
1226
1227 // read color
1228 offset = GetOffset(Width, prm.w, h, bytesDepth);
1229 color.setColor(data + offset, sixteenBit);
1230
1231 // ...we enter this loop to sum the bits
1232 for (int a = -prm.Radius ; runningFlag() && (a <= prm.Radius) ; ++a)
1233 {
1234 // verify if is inside the rect
1235 if (IsInside(Width, Height, prm.w, h + a))
1236 {
1237 // read color
1238 loopOffset = GetOffset(Width, prm.w, h + a, bytesDepth);
1239 radiusColor.setColor(data + loopOffset, sixteenBit);
1240
1241 // now, we have to check if is inside the sensibility filter
1242 if (IsColorInsideTheRange(color.red(), color.green(), color.blue(),
1243 radiusColor.red(), radiusColor.green(), radiusColor.blue(),
1244 prm.StrengthRange))
1245 {
1246 radiusColorBlur.setColor(prm.pBlur + loopOffset, sixteenBit);
1247 // finally we sum the bits
1248 sumR += radiusColorBlur.red();
1249 sumG += radiusColorBlur.green();
1250 sumB += radiusColorBlur.blue();
1251 }
1252 else
1253 {
1254 // finally we sum the bits
1255 sumR += color.red();
1256 sumG += color.green();
1257 sumB += color.blue();
1258 }
1259
1260 // increment counter
1261 ++nCount;
1262 }
1263 }
1264
1265 if (nCount == 0)
1266 {
1267 nCount = 1;
1268 }
1269
1270 // now, we have to calc the arithmetic average
1271 color.setRed(sumR / nCount);
1272 color.setGreen(sumG / nCount);
1273 color.setBlue(sumB / nCount);
1274
1275 // write color to destination
1276 color.setPixel(pResBits + offset);
1277 }
1278 }
1279
1280 /* Function to apply the SmartBlur effect
1281 *
1282 * data => The image data in RGBA mode.
1283 * Width => Width of image.
1284 * Height => Height of image.
1285 * Radius => blur matrix radius.
1286 * Strength => Color strength.
1287 *
1288 * Theory => Similar to SmartBlur from Photoshop, this function has the
1289 * same engine as Blur function, but, in a matrix with n
1290 * dimensions, we take only colors that pass by sensibility filter
1291 * The result is a clean image, not totally blurred, but a image
1292 * with correction between pixels.
1293 */
1294
smartBlur(DImg * const orgImage,DImg * const destImage,int Radius,int Strength)1295 void BlurFXFilter::smartBlur(DImg* const orgImage, DImg* const destImage, int Radius, int Strength)
1296 {
1297 if (Radius <= 0)
1298 {
1299 return;
1300 }
1301
1302 int progress;
1303 int StrengthRange = Strength;
1304
1305 if (orgImage->sixteenBit())
1306 {
1307 StrengthRange = (StrengthRange + 1) * 256 - 1;
1308 }
1309
1310 QScopedArrayPointer<uchar> pBlur(new uchar[orgImage->numBytes()]);
1311
1312 // We need to copy our bits to blur bits
1313
1314 memcpy(pBlur.data(), orgImage->bits(), orgImage->numBytes());
1315
1316 QList<int> valsw = multithreadedSteps(orgImage->width());
1317 QList<int> valsh = multithreadedSteps(orgImage->height());
1318 QList <QFuture<void> > tasks;
1319
1320 Args prm;
1321 prm.orgImage = orgImage;
1322 prm.destImage = destImage;
1323 prm.StrengthRange = StrengthRange;
1324 prm.pBlur = pBlur.data();
1325 prm.Radius = Radius;
1326
1327 // we have reached the main loop
1328
1329 for (uint h = 0 ; runningFlag() && (h < orgImage->height()) ; ++h)
1330 {
1331 for (int j = 0 ; runningFlag() && (j < valsw.count()-1) ; ++j)
1332 {
1333 prm.start = valsw[j];
1334 prm.stop = valsw[j+1];
1335 prm.h = h;
1336 tasks.append(QtConcurrent::run(this,
1337 &BlurFXFilter::smartBlurStage1Multithreaded,
1338 prm
1339 ));
1340 }
1341
1342 foreach (QFuture<void> t, tasks)
1343 t.waitForFinished();
1344
1345 // Update the progress bar in dialog.
1346 progress = (int)(((double)h * 50.0) / orgImage->height());
1347
1348 if (progress % 5 == 0)
1349 {
1350 postProgress(progress);
1351 }
1352 }
1353
1354 // we have reached the second part of main loop
1355
1356 tasks.clear();
1357
1358 for (uint w = 0 ; runningFlag() && (w < orgImage->width()) ; ++w)
1359 {
1360 for (int j = 0 ; runningFlag() && (j < valsh.count()-1) ; ++j)
1361 {
1362 prm.start = valsh[j];
1363 prm.stop = valsh[j+1];
1364 prm.w = w;
1365 tasks.append(QtConcurrent::run(this,
1366 &BlurFXFilter::smartBlurStage2Multithreaded,
1367 prm
1368 ));
1369 }
1370
1371 foreach (QFuture<void> t, tasks)
1372 t.waitForFinished();
1373
1374 // Update the progress bar in dialog.
1375 progress = (int)(50.0 + ((double)w * 50.0) / orgImage->width());
1376
1377 if (progress % 5 == 0)
1378 {
1379 postProgress(progress);
1380 }
1381 }
1382 }
1383
1384 // NOTE: there is no gain to parallelize this method due to non re-entrancy of RandomColor()
1385 // (dixit RandomNumberGenerator which non re-entrant - Boost lib problem).
1386
1387 /* Function to apply the frostGlass effect
1388 *
1389 * data => The image data in RGBA mode.
1390 * Width => Width of image.
1391 * Height => Height of image.
1392 * Frost => Frost value
1393 *
1394 * Theory => Similar to Diffuse effect, but the random byte is defined
1395 * in a matrix. Diffuse uses a random diagonal byte.
1396 */
frostGlass(DImg * const orgImage,DImg * const destImage,int Frost)1397 void BlurFXFilter::frostGlass(DImg* const orgImage, DImg* const destImage, int Frost)
1398 {
1399 int progress;
1400
1401 int Width = orgImage->width();
1402 int Height = orgImage->height();
1403 uchar* data = orgImage->bits();
1404 bool sixteenBit = orgImage->sixteenBit();
1405 int bytesDepth = orgImage->bytesDepth();
1406 uchar* pResBits = destImage->bits();
1407
1408 Frost = (Frost < 1) ? 1 : (Frost > 10) ? 10 : Frost;
1409
1410 int h, w;
1411
1412 DColor color;
1413 int offset;
1414
1415 // Randomize.
1416 RandomNumberGenerator generator;
1417 generator.seed(d->randomSeed);
1418
1419 int range = sixteenBit ? 65535 : 255;
1420
1421 // it is a huge optimization to allocate these here once
1422 QScopedArrayPointer<uchar> IntensityCount(new uchar[range + 1]);
1423 QScopedArrayPointer<uint> AverageColorR(new uint[range + 1]);
1424 QScopedArrayPointer<uint> AverageColorG(new uint[range + 1]);
1425 QScopedArrayPointer<uint> AverageColorB(new uint[range + 1]);
1426
1427 for (h = 0; runningFlag() && (h < Height); ++h)
1428 {
1429 for (w = 0; runningFlag() && (w < Width); ++w)
1430 {
1431 offset = GetOffset(Width, w, h, bytesDepth);
1432 // read color to preserve alpha
1433 color.setColor(data + offset, sixteenBit);
1434
1435 // get random color from surrounding of w|h
1436 color = RandomColor(data, Width, Height, sixteenBit, bytesDepth,
1437 w, h, Frost, color.alpha(), generator, range, IntensityCount.data(),
1438 AverageColorR.data(), AverageColorG.data(), AverageColorB.data());
1439
1440 // write color to destination
1441 color.setPixel(pResBits + offset);
1442 }
1443
1444 // Update the progress bar in dialog.
1445 progress = (int)(((double)h * 100.0) / Height);
1446
1447 if (progress % 5 == 0)
1448 {
1449 postProgress(progress);
1450 }
1451 }
1452 }
1453
mosaicMultithreaded(const Args & prm)1454 void BlurFXFilter::mosaicMultithreaded(const Args& prm)
1455 {
1456 int Width = prm.orgImage->width();
1457 int Height = prm.orgImage->height();
1458 uchar* data = prm.orgImage->bits();
1459 bool sixteenBit = prm.orgImage->sixteenBit();
1460 int bytesDepth = prm.orgImage->bytesDepth();
1461 uchar* pResBits = prm.destImage->bits();
1462
1463 DColor color;
1464 int offsetCenter, offset;
1465
1466 for (uint w = prm.start; runningFlag() && (w < prm.stop); w += prm.SizeW)
1467 {
1468 // we have to find the center pixel for mosaic's rectangle
1469
1470 offsetCenter = GetOffsetAdjusted(Width, Height, w + (prm.SizeW / 2), prm.h + (prm.SizeH / 2), bytesDepth);
1471 color.setColor(data + offsetCenter, sixteenBit);
1472
1473 // now, we fill the mosaic's rectangle with the center pixel color
1474
1475 for (uint subw = w; runningFlag() && (subw <= w + prm.SizeW); ++subw)
1476 {
1477 for (uint subh = prm.h; runningFlag() && (subh <= prm.h + prm.SizeH); ++subh)
1478 {
1479 // if is inside...
1480 if (IsInside(Width, Height, subw, subh))
1481 {
1482 // set color
1483 offset = GetOffset(Width, subw, subh, bytesDepth);
1484 color.setPixel(pResBits + offset);
1485 }
1486 }
1487 }
1488 }
1489 }
1490
1491 /* Function to apply the mosaic effect backported from ImageProcessing version 2
1492 *
1493 * data => The image data in RGBA mode.
1494 * Width => Width of image.
1495 * Height => Height of image.
1496 * Size => Size of mosaic.
1497 *
1498 * Theory => Ok, you can find some mosaic effects on PSC, but this one
1499 * has a great feature, if you see a mosaic in other code you will
1500 * see that the corner pixel doesn't change. The explanation is
1501 * simple, the color of the mosaic is the same as the first pixel
1502 * get. Here, the color of the mosaic is the same as the mosaic
1503 * center pixel.
1504 * Now the function scan the rows from the top (like photoshop).
1505 */
mosaic(DImg * const orgImage,DImg * const destImage,int SizeW,int SizeH)1506 void BlurFXFilter::mosaic(DImg* const orgImage, DImg* const destImage, int SizeW, int SizeH)
1507 {
1508 int progress;
1509
1510 // we need to check for valid values
1511 if (SizeW < 1)
1512 {
1513 SizeW = 1;
1514 }
1515
1516 if (SizeH < 1)
1517 {
1518 SizeH = 1;
1519 }
1520
1521 if ((SizeW == 1) && (SizeH == 1))
1522 {
1523 return;
1524 }
1525
1526 QList<int> vals = multithreadedSteps(orgImage->width());
1527 QList <QFuture<void> > tasks;
1528
1529 Args prm;
1530 prm.orgImage = orgImage;
1531 prm.destImage = destImage;
1532 prm.SizeW = SizeW;
1533 prm.SizeH = SizeH;
1534
1535 // this loop will never look for transparent colors
1536
1537 for (uint h = 0; runningFlag() && (h < orgImage->height()); h += SizeH)
1538 {
1539 for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j)
1540 {
1541 prm.start = vals[j];
1542 prm.stop = vals[j+1];
1543 prm.h = h;
1544 tasks.append(QtConcurrent::run(this,
1545 &BlurFXFilter::mosaicMultithreaded,
1546 prm
1547 ));
1548 }
1549
1550 foreach (QFuture<void> t, tasks)
1551 t.waitForFinished();
1552
1553 // Update the progress bar in dialog.
1554 progress = (int)(((double)h * 100.0) / orgImage->height());
1555
1556 if (progress % 5 == 0)
1557 {
1558 postProgress(progress);
1559 }
1560 }
1561 }
1562
1563 /* Function to get a color in a matrix with a determined size
1564 *
1565 * Bits => Bits array
1566 * Width => Image width
1567 * Height => Image height
1568 * X => Position horizontal
1569 * Y => Position vertical
1570 * Radius => The radius of the matrix to be created
1571 *
1572 * Theory => This function takes from a distinct matrix a random color
1573 */
RandomColor(uchar * const Bits,int Width,int Height,bool sixteenBit,int bytesDepth,int X,int Y,int Radius,int alpha,RandomNumberGenerator & generator,int range,uchar * const IntensityCount,uint * const AverageColorR,uint * const AverageColorG,uint * const AverageColorB)1574 DColor BlurFXFilter::RandomColor(uchar* const Bits, int Width, int Height, bool sixteenBit, int bytesDepth,
1575 int X, int Y, int Radius,
1576 int alpha, RandomNumberGenerator& generator, int range, uchar* const IntensityCount,
1577 uint* const AverageColorR, uint* const AverageColorG, uint* const AverageColorB)
1578 {
1579 DColor color;
1580 int offset;
1581
1582 int w, h, counter = 0;
1583
1584 int I;
1585
1586 // For 16 bit we have a problem here because this takes 255 times longer,
1587 // and the algorithm is really slow for 16 bit, but I think this cannot be avoided.
1588 memset(IntensityCount, 0, (range + 1) * sizeof(uchar));
1589 memset(AverageColorR, 0, (range + 1) * sizeof(uint));
1590 memset(AverageColorG, 0, (range + 1) * sizeof(uint));
1591 memset(AverageColorB, 0, (range + 1) * sizeof(uint));
1592
1593 for (w = X - Radius; runningFlag() && (w <= X + Radius); ++w)
1594 {
1595 for (h = Y - Radius; runningFlag() && (h <= Y + Radius); ++h)
1596 {
1597 if ((w >= 0) && (w < Width) && (h >= 0) && (h < Height))
1598 {
1599 offset = GetOffset(Width, w, h, bytesDepth);
1600 color.setColor(Bits + offset, sixteenBit);
1601 I = GetIntensity(color.red(), color.green(), color.blue());
1602 IntensityCount[I]++;
1603 ++counter;
1604
1605 if (IntensityCount[I] == 1)
1606 {
1607 AverageColorR[I] = color.red();
1608 AverageColorG[I] = color.green();
1609 AverageColorB[I] = color.blue();
1610 }
1611 else
1612 {
1613 AverageColorR[I] += color.red();
1614 AverageColorG[I] += color.green();
1615 AverageColorB[I] += color.blue();
1616 }
1617 }
1618 }
1619 }
1620
1621 // check for runningFlag here before entering the do loop (will crash with SIGFPE otherwise)
1622 if (!runningFlag())
1623 {
1624 return DColor(0, 0, 0, 0, sixteenBit);
1625 }
1626
1627 int RandNumber, count, Index, ErrorCount = 0;
1628 int J;
1629
1630 do
1631 {
1632 RandNumber = generator.number(0, counter);
1633
1634 count = 0;
1635 Index = 0;
1636
1637 do
1638 {
1639 count += IntensityCount[Index];
1640 ++Index;
1641 }
1642 while (runningFlag() && (count < RandNumber));
1643
1644 J = Index - 1;
1645 ++ErrorCount;
1646 }
1647 while (runningFlag() && (IntensityCount[J] == 0) && (ErrorCount <= counter));
1648
1649 if (!runningFlag())
1650 {
1651 return DColor(0, 0, 0, 0, sixteenBit);
1652 }
1653
1654 color.setSixteenBit(sixteenBit);
1655 color.setAlpha(alpha);
1656 int clampMax = sixteenBit ? 655535 : 255;
1657
1658 if (ErrorCount >= counter)
1659 {
1660 if (counter == 0)
1661 {
1662 counter = 1;
1663 }
1664
1665 color.setRed(CLAMP((int)(AverageColorR[J] / counter), 0, clampMax));
1666 color.setGreen(CLAMP((int)(AverageColorG[J] / counter), 0, clampMax));
1667 color.setBlue(CLAMP((int)(AverageColorB[J] / counter), 0, clampMax));
1668 }
1669 else
1670 {
1671 if (IntensityCount[J] == 0)
1672 {
1673 IntensityCount[J] = 1;
1674 }
1675
1676 color.setRed(CLAMP((int)(AverageColorR[J] / IntensityCount[J]), 0, clampMax));
1677 color.setGreen(CLAMP((int)(AverageColorG[J] / IntensityCount[J]), 0, clampMax));
1678 color.setBlue(CLAMP((int)(AverageColorB[J] / IntensityCount[J]), 0, clampMax));
1679 }
1680
1681 return color;
1682 }
1683
MakeConvolutionStage1Multithreaded(const Args & prm)1684 void BlurFXFilter::MakeConvolutionStage1Multithreaded(const Args& prm)
1685 {
1686 int Width = prm.orgImage->width();
1687 int Height = prm.orgImage->height();
1688 uchar* data = prm.orgImage->bits();
1689 bool sixteenBit = prm.orgImage->sixteenBit();
1690 int bytesDepth = prm.orgImage->bytesDepth();
1691
1692 int n;
1693
1694 int nSumR, nSumG, nSumB, nCount;
1695 DColor color;
1696 int offset;
1697
1698 for (uint w = prm.start; runningFlag() && (w < prm.stop); ++w)
1699 {
1700 // initialize the variables
1701 nSumR = nSumG = nSumB = nCount = 0;
1702
1703 // first of all, we need to blur the horizontal lines
1704
1705 for (n = -prm.Radius; runningFlag() && (n <= prm.Radius); ++n)
1706 {
1707 // if is inside...
1708 if (IsInside(Width, Height, w + n, prm.h))
1709 {
1710 // read color from orgImage
1711 offset = GetOffset(Width, w + n, prm.h, bytesDepth);
1712 color.setColor(data + offset, sixteenBit);
1713
1714 // finally, we sum the pixels using a method similar to assigntables
1715 nSumR += prm.arrMult[n + prm.Radius][color.red()];
1716 nSumG += prm.arrMult[n + prm.Radius][color.green()];
1717 nSumB += prm.arrMult[n + prm.Radius][color.blue()];
1718
1719 // we need to add the kernel value to the counter
1720 nCount += prm.Kernel[n + prm.Radius];
1721 }
1722 }
1723
1724 if (nCount == 0)
1725 {
1726 nCount = 1;
1727 }
1728
1729 // calculate pointer
1730 offset = GetOffset(Width, w, prm.h, bytesDepth);
1731 // read color from orgImage to preserve alpha
1732 color.setColor(data + offset, sixteenBit);
1733
1734 // now, we have to calc the arithmetic average
1735 if (sixteenBit)
1736 {
1737 color.setRed(CLAMP065535(nSumR / nCount));
1738 color.setGreen(CLAMP065535(nSumG / nCount));
1739 color.setBlue(CLAMP065535(nSumB / nCount));
1740 }
1741 else
1742 {
1743 color.setRed((uchar)CLAMP0255(nSumR / nCount));
1744 color.setGreen((uchar)CLAMP0255(nSumG / nCount));
1745 color.setBlue((uchar)CLAMP0255(nSumB / nCount));
1746 }
1747
1748 // write color to blur bits
1749 color.setPixel(prm.pBlur + offset);
1750 }
1751 }
1752
MakeConvolutionStage2Multithreaded(const Args & prm)1753 void BlurFXFilter::MakeConvolutionStage2Multithreaded(const Args& prm)
1754 {
1755 int Width = prm.orgImage->width();
1756 int Height = prm.orgImage->height();
1757 uchar* data = prm.orgImage->bits();
1758 bool sixteenBit = prm.orgImage->sixteenBit();
1759 int bytesDepth = prm.orgImage->bytesDepth();
1760 uchar* pOutBits = prm.destImage->bits();
1761
1762 int n;
1763
1764 int nSumR, nSumG, nSumB, nCount;
1765 DColor color;
1766 int offset;
1767
1768 for (uint h = prm.start; runningFlag() && (h < prm.stop); ++h)
1769 {
1770 // initialize the variables
1771 nSumR = nSumG = nSumB = nCount = 0;
1772
1773 // first of all, we need to blur the vertical lines
1774 for (n = -prm.Radius; runningFlag() && (n <= prm.Radius); ++n)
1775 {
1776 // if is inside...
1777 if (IsInside(Width, Height, prm.w, h + n))
1778 {
1779 // read color from blur bits
1780 offset = GetOffset(Width, prm.w, h + n, bytesDepth);
1781 color.setColor(prm.pBlur + offset, sixteenBit);
1782
1783 // finally, we sum the pixels using a method similar to assigntables
1784 nSumR += prm.arrMult[n + prm.Radius][color.red()];
1785 nSumG += prm.arrMult[n + prm.Radius][color.green()];
1786 nSumB += prm.arrMult[n + prm.Radius][color.blue()];
1787
1788 // we need to add the kernel value to the counter
1789 nCount += prm.Kernel[n + prm.Radius];
1790 }
1791 }
1792
1793 if (nCount == 0)
1794 {
1795 nCount = 1;
1796 }
1797
1798 // calculate pointer
1799 offset = GetOffset(Width, prm.w, h, bytesDepth);
1800 // read color from orgImage to preserve alpha
1801 color.setColor(data + offset, sixteenBit);
1802
1803 // now, we have to calc the arithmetic average
1804 if (sixteenBit)
1805 {
1806 color.setRed(CLAMP065535(nSumR / nCount));
1807 color.setGreen(CLAMP065535(nSumG / nCount));
1808 color.setBlue(CLAMP065535(nSumB / nCount));
1809 }
1810 else
1811 {
1812 color.setRed((uchar)CLAMP0255(nSumR / nCount));
1813 color.setGreen((uchar)CLAMP0255(nSumG / nCount));
1814 color.setBlue((uchar)CLAMP0255(nSumB / nCount));
1815 }
1816
1817 // write color to destination
1818 color.setPixel(pOutBits + offset);
1819 }
1820 }
1821
1822 /* Function to simple convolve a unique pixel with a determined radius
1823 *
1824 * data => The image data in RGBA mode.
1825 * Width => Width of image.
1826 * Height => Height of image.
1827 * Radius => kernel radius, e.g. rad=1, so array will be 3X3
1828 * Kernel => kernel array to apply.
1829 *
1830 * Theory => I've worked hard here, but I think this is a very smart
1831 * way to convolve an array, its very hard to explain how I reach
1832 * this, but the trick here its to store the sum used by the
1833 * previous pixel, so we sum with the other pixels that wasn't get
1834 */
MakeConvolution(DImg * const orgImage,DImg * const destImage,int Radius,int Kernel[])1835 void BlurFXFilter::MakeConvolution(DImg* const orgImage, DImg* const destImage, int Radius, int Kernel[])
1836 {
1837 if (Radius <= 0)
1838 {
1839 return;
1840 }
1841
1842 int progress;
1843 int nKernelWidth = Radius * 2 + 1;
1844 int range = orgImage->sixteenBit() ? 65536 : 256;
1845
1846 QScopedArrayPointer<uchar> pBlur(new uchar[orgImage->numBytes()]);
1847
1848 // We need to copy our bits to blur bits
1849
1850 memcpy(pBlur.data(), orgImage->bits(), orgImage->numBytes());
1851
1852 // We need to alloc a 2d array to help us to store the values
1853
1854 int** const arrMult = Alloc2DArray(nKernelWidth, range);
1855
1856 for (int i = 0; i < nKernelWidth; ++i)
1857 {
1858 for (int j = 0; j < range; ++j)
1859 {
1860 arrMult[i][j] = j * Kernel[i];
1861 }
1862 }
1863
1864 QList<int> valsw = multithreadedSteps(orgImage->width());
1865 QList<int> valsh = multithreadedSteps(orgImage->height());
1866 QList <QFuture<void> > tasks;
1867
1868 Args prm;
1869 prm.orgImage = orgImage;
1870 prm.destImage = destImage;
1871 prm.Radius = Radius;
1872 prm.Kernel = Kernel;
1873 prm.arrMult = arrMult;
1874 prm.pBlur = pBlur.data();
1875
1876 // Now, we enter in the first loop
1877
1878 for (uint h = 0; runningFlag() && (h < orgImage->height()); ++h)
1879 {
1880 for (int j = 0 ; runningFlag() && (j < valsw.count()-1) ; ++j)
1881 {
1882 prm.start = valsw[j];
1883 prm.stop = valsw[j+1];
1884 prm.h = h;
1885 tasks.append(QtConcurrent::run(this,
1886 &BlurFXFilter::MakeConvolutionStage1Multithreaded,
1887 prm
1888 ));
1889 }
1890
1891 foreach (QFuture<void> t, tasks)
1892 t.waitForFinished();
1893
1894 // Update the progress bar in dialog.
1895 progress = (int)(((double)h * 50.0) / orgImage->height());
1896
1897 if (progress % 5 == 0)
1898 {
1899 postProgress(progress);
1900 }
1901 }
1902
1903 // We enter in the second main loop
1904
1905 tasks.clear();
1906
1907 for (uint w = 0; runningFlag() && (w < orgImage->width()); ++w)
1908 {
1909 for (int j = 0 ; runningFlag() && (j < valsh.count()-1) ; ++j)
1910 {
1911 prm.start = valsh[j];
1912 prm.stop = valsh[j+1];
1913 prm.w = w;
1914 tasks.append(QtConcurrent::run(this,
1915 &BlurFXFilter::MakeConvolutionStage2Multithreaded,
1916 prm
1917 ));
1918 }
1919
1920 foreach (QFuture<void> t, tasks)
1921 t.waitForFinished();
1922
1923 // Update the progress bar in dialog.
1924 progress = (int)(50.0 + ((double)w * 50.0) / orgImage->width());
1925
1926 if (progress % 5 == 0)
1927 {
1928 postProgress(progress);
1929 }
1930 }
1931
1932 // now, we must free memory
1933 Free2DArray(arrMult, nKernelWidth);
1934 }
1935
filterAction()1936 FilterAction BlurFXFilter::filterAction()
1937 {
1938 FilterAction action(FilterIdentifier(), CurrentVersion());
1939 action.setDisplayableName(DisplayableName());
1940
1941 action.addParameter(QLatin1String("type"), d->blurFXType);
1942 action.addParameter(QLatin1String("distance"), d->distance);
1943 action.addParameter(QLatin1String("level"), d->level);
1944
1945 if (d->blurFXType == FrostGlass)
1946 {
1947 action.addParameter(QLatin1String("randomSeed"), d->randomSeed);
1948 }
1949
1950 return action;
1951 }
1952
readParameters(const FilterAction & action)1953 void BlurFXFilter::readParameters(const FilterAction& action)
1954 {
1955 d->blurFXType = action.parameter(QLatin1String("type")).toInt();
1956 d->distance = action.parameter(QLatin1String("distance")).toInt();
1957 d->level = action.parameter(QLatin1String("level")).toInt();
1958
1959 if (d->blurFXType == FrostGlass)
1960 {
1961 d->randomSeed = action.parameter(QLatin1String("randomSeed")).toUInt();
1962 }
1963 }
1964
1965 } // namespace Digikam
1966