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