1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2005-17-07
7  * Description : A Gaussian Blur threaded image filter.
8  *
9  * Copyright (C) 2005-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  * Copyright (C) 2009      by Andi Clemens <andi dot clemens at gmail dot com>
11  * Copyright (C) 2010      by Martin Klapetek <martin dot klapetek at gmail dot com>
12  *
13  * This program is free software; you can redistribute it
14  * and/or modify it under the terms of the GNU General
15  * Public License as published by the Free Software Foundation;
16  * either version 2, or (at your option)
17  * any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * ============================================================ */
25 
26 #include "blurfilter.h"
27 
28 // Qt includes
29 
30 #include <QtConcurrent>    // krazy:exclude=includes
31 #include <QtMath>
32 #include <QMutex>
33 
34 // KDE includes
35 
36 #include <klocalizedstring.h>
37 
38 // Local includes
39 
40 #include "digikam_debug.h"
41 
42 namespace Digikam
43 {
44 
45 class Q_DECL_HIDDEN BlurFilter::Private
46 {
47 public:
48 
Private()49     explicit Private()
50       : radius        (3),
51         globalProgress(0)
52     {
53     }
54 
55     int    radius;
56     int    globalProgress;
57 
58     QMutex lock;
59 };
60 
BlurFilter(QObject * const parent)61 BlurFilter::BlurFilter(QObject* const parent)
62     : DImgThreadedFilter(parent),
63       d(new Private)
64 {
65     initFilter();
66 }
67 
BlurFilter(DImg * const orgImage,QObject * const parent,int radius)68 BlurFilter::BlurFilter(DImg* const orgImage, QObject* const parent, int radius)
69     : DImgThreadedFilter(orgImage, parent, QLatin1String("GaussianBlur")),
70       d(new Private)
71 {
72     d->radius = radius;
73     initFilter();
74 }
75 
BlurFilter(DImgThreadedFilter * const parentFilter,const DImg & orgImage,const DImg & destImage,int progressBegin,int progressEnd,int radius)76 BlurFilter::BlurFilter(DImgThreadedFilter* const parentFilter,
77                        const DImg& orgImage,
78                        const DImg& destImage,
79                        int progressBegin,
80                        int progressEnd,
81                        int radius)
82     : DImgThreadedFilter(parentFilter,
83                          orgImage,
84                          destImage,
85                          progressBegin,
86                          progressEnd,
87                          parentFilter->filterName() + QLatin1String(": GaussianBlur")),
88       d(new Private)
89 {
90     d->radius = radius;
91     filterImage();
92 }
93 
~BlurFilter()94 BlurFilter::~BlurFilter()
95 {
96     cancelFilter();
97     delete d;
98 }
99 
DisplayableName()100 QString BlurFilter::DisplayableName()
101 {
102     return QString::fromUtf8(I18N_NOOP("Blur Filter"));
103 }
104 
blurMultithreaded(uint start,uint stop)105 void BlurFilter::blurMultithreaded(uint start, uint stop)
106 {
107     bool sixteenBit  = m_orgImage.sixteenBit();
108     int  height      = m_orgImage.height();
109     int  width       = m_orgImage.width();
110     int  radius      = d->radius;
111     int  oldProgress = 0;
112     int  progress    = 0;
113     uint a, r, g, b;
114     int  mx;
115     int  my;
116     int  mw;
117     int  mh;
118     int  mt;
119     int* as = new int[width];
120     int* rs = new int[width];
121     int* gs = new int[width];
122     int* bs = new int[width];
123 
124     for (uint y = start ; runningFlag() && (y < stop) ; ++y)
125     {
126         my = y - radius;
127         mh = (radius << 1) + 1;
128 
129         if (my < 0)
130         {
131             mh += my;
132             my  = 0;
133         }
134 
135         if ((my + mh) > height)
136         {
137             mh = height - my;
138         }
139 
140         uchar* pDst8           = m_destImage.scanLine(y);
141         unsigned short* pDst16 = reinterpret_cast<unsigned short*>(m_destImage.scanLine(y));
142 
143         memset(as, 0, width * sizeof(int));
144         memset(rs, 0, width * sizeof(int));
145         memset(gs, 0, width * sizeof(int));
146         memset(bs, 0, width * sizeof(int));
147 
148         for (int yy = 0 ; yy < mh ; ++yy)
149         {
150             uchar* pSrc8           = m_orgImage.scanLine(yy + my);
151             unsigned short* pSrc16 = reinterpret_cast<unsigned short*>(m_orgImage.scanLine(yy + my));
152 
153             for (int x = 0 ; x < width ; ++x)
154             {
155                 if (sixteenBit)
156                 {
157                     bs[x]  += pSrc16[0];
158                     gs[x]  += pSrc16[1];
159                     rs[x]  += pSrc16[2];
160                     as[x]  += pSrc16[3];
161                     pSrc16 += 4;
162                 }
163                 else
164                 {
165                     bs[x] += pSrc8[0];
166                     gs[x] += pSrc8[1];
167                     rs[x] += pSrc8[2];
168                     as[x] += pSrc8[3];
169                     pSrc8 += 4;
170                 }
171             }
172         }
173 
174         if (width > ((radius << 1) + 1))
175         {
176             for (int x = 0 ; x < width ; ++x)
177             {
178                 a  = 0;
179                 r  = 0;
180                 g  = 0;
181                 b  = 0;
182                 mx = x - radius;
183                 mw = (radius << 1) + 1;
184 
185                 if (mx < 0)
186                 {
187                     mw += mx;
188                     mx  = 0;
189                 }
190 
191                 if ((mx + mw) > width)
192                 {
193                     mw = width - mx;
194                 }
195 
196                 mt = mw * mh;
197 
198                 for (int xx = mx ; xx < (mw + mx) ; ++xx)
199                 {
200                     a += as[xx];
201                     r += rs[xx];
202                     g += gs[xx];
203                     b += bs[xx];
204                 }
205 
206                 if (mt != 0)
207                 {
208                     a = a / mt;
209                     r = r / mt;
210                     g = g / mt;
211                     b = b / mt;
212                 }
213 
214                 if (sixteenBit)
215                 {
216                     pDst16[0] = b;
217                     pDst16[1] = g;
218                     pDst16[2] = r;
219                     pDst16[3] = a;
220                     pDst16   += 4;
221                 }
222                 else
223                 {
224                     pDst8[0] = b;
225                     pDst8[1] = g;
226                     pDst8[2] = r;
227                     pDst8[3] = a;
228                     pDst8   += 4;
229                 }
230             }
231         }
232         else
233         {
234             qCDebug(DIGIKAM_DIMG_LOG) << "Radius too small...";
235         }
236 
237         progress = (int)(((double)y * (100.0 / QThreadPool::globalInstance()->maxThreadCount())) / (stop-start));
238 
239         if (((progress % 5) == 0) && (progress > oldProgress))
240         {
241             d->lock.lock();
242             oldProgress        = progress;
243             d->globalProgress += 5;
244             postProgress(d->globalProgress);
245             d->lock.unlock();
246         }
247     }
248 
249     delete [] as;
250     delete [] rs;
251     delete [] gs;
252     delete [] bs;
253 }
254 
filterImage()255 void BlurFilter::filterImage()
256 {
257     if (d->radius < 1)
258     {
259         qCDebug(DIGIKAM_DIMG_LOG) << "Radius out of range...";
260         m_destImage = m_orgImage;
261         return;
262     }
263 
264     QList<int> vals = multithreadedSteps(m_orgImage.height());
265     QList <QFuture<void> > tasks;
266 
267     for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j)
268     {
269         tasks.append(QtConcurrent::run(this,
270                                        &BlurFilter::blurMultithreaded,
271                                        vals[j],
272                                        vals[j+1]
273                                       ));
274     }
275 
276     foreach (QFuture<void> t, tasks)
277     {
278         t.waitForFinished();
279     }
280 }
281 
filterAction()282 FilterAction BlurFilter::filterAction()
283 {
284     FilterAction action(FilterIdentifier(), CurrentVersion());
285     action.setDisplayableName(DisplayableName());
286 
287     action.addParameter(QLatin1String("radius"), d->radius);
288 
289     return action;
290 }
291 
readParameters(const FilterAction & action)292 void BlurFilter::readParameters(const FilterAction& action)
293 {
294     d->radius = action.parameter(QLatin1String("radius")).toInt();
295 }
296 
297 } // namespace Digikam
298