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 Sharpen threaded image filter.
8 *
9 * Copyright (C) 2005-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10 * Copyright (C) 2009 by Matthias Welwarsky <matze at welwarsky dot de>
11 * Copyright (C) 2010 by Martin Klapetek <martin dot klapetek at gmail dot com>
12 * Copyright (C) 2002 by Daniel M. Duley <mosfet at kde dot org>
13 *
14 * This program is free software; you can redistribute it
15 * and/or modify it under the terms of the GNU General
16 * Public License as published by the Free Software Foundation;
17 * either version 2, or (at your option)
18 * any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * ============================================================ */
26
27 #include "unsharpmaskfilter.h"
28
29 // C++ includes
30
31 #include <cmath>
32 #include <cstdlib>
33
34 // Qt includes
35
36 #include <QtConcurrent> // krazy:exclude=includes
37
38 // KDE includes
39
40 #include <klocalizedstring.h>
41
42 // Local includes
43
44 #include "dimg.h"
45 #include "digikam_debug.h"
46 #include "dcolor.h"
47 #include "blurfilter.h"
48
49 namespace Digikam
50 {
51
UnsharpMaskFilter(QObject * const parent)52 UnsharpMaskFilter::UnsharpMaskFilter(QObject* const parent)
53 : DImgThreadedFilter(parent),
54 m_radius (1),
55 m_amount (1.0),
56 m_threshold (0.05),
57 m_luma (false)
58 {
59 initFilter();
60 }
61
UnsharpMaskFilter(DImg * const orgImage,QObject * const parent,double radius,double amount,double threshold,bool luma)62 UnsharpMaskFilter::UnsharpMaskFilter(DImg* const orgImage,
63 QObject* const parent,
64 double radius,
65 double amount,
66 double threshold,
67 bool luma)
68 : DImgThreadedFilter(orgImage, parent, QLatin1String("UnsharpMask")),
69 m_radius (radius),
70 m_amount (amount),
71 m_threshold(threshold),
72 m_luma (luma)
73 {
74 initFilter();
75 }
76
~UnsharpMaskFilter()77 UnsharpMaskFilter::~UnsharpMaskFilter()
78 {
79 cancelFilter();
80 }
81
DisplayableName()82 QString UnsharpMaskFilter::DisplayableName()
83 {
84 return QString::fromUtf8(I18N_NOOP("Unsharp Mask Tool"));
85 }
86
unsharpMaskMultithreaded(uint start,uint stop,uint y)87 void UnsharpMaskFilter::unsharpMaskMultithreaded(uint start, uint stop, uint y)
88 {
89 long int zero = 0;
90 double value = 0.0;
91 DColor p;
92 DColor q;
93
94 long int quantum = m_destImage.sixteenBit() ? 65535 : 255;
95 double quantumThreshold = quantum * m_threshold;
96 int hp = 0, sp = 0, lp = 0, hq = 0, sq = 0, lq = 0;
97
98 for (uint x = start ; runningFlag() && (x < stop) ; ++x)
99 {
100 p = m_orgImage.getPixelColor(x, y);
101 q = m_destImage.getPixelColor(x, y);
102
103 if (m_luma)
104 {
105 p.getHSL(&hp, &sp, &lp);
106 q.getHSL(&hq, &sq, &lq);
107
108 // luma channel
109
110 value = (double)(lp) - (double)(lq);
111
112 if (fabs(2.0 * value) < quantumThreshold)
113 {
114 value = (double)(lp);
115 }
116 else
117 {
118 value = (double)(lp) + value * m_amount;
119 }
120
121 q.setHSL(hp, sp, CLAMP(lround(value), zero, quantum), m_destImage.sixteenBit());
122 q.setAlpha(p.alpha());
123 }
124 else
125 {
126 // Red channel.
127
128 value = (double)(p.red()) - (double)(q.red());
129
130 if (fabs(2.0 * value) < quantumThreshold)
131 {
132 value = (double)(p.red());
133 }
134 else
135 {
136 value = (double)(p.red()) + value * m_amount;
137 }
138
139 q.setRed(CLAMP(lround(value), zero, quantum));
140
141 // Green Channel.
142
143 value = (double)(p.green()) - (double)(q.green());
144
145 if (fabs(2.0 * value) < quantumThreshold)
146 {
147 value = (double)(p.green());
148 }
149 else
150 {
151 value = (double)(p.green()) + value * m_amount;
152 }
153
154 q.setGreen(CLAMP(lround(value), zero, quantum));
155
156 // Blue Channel.
157
158 value = (double)(p.blue()) - (double)(q.blue());
159
160 if (fabs(2.0 * value) < quantumThreshold)
161 {
162 value = (double)(p.blue());
163 }
164 else
165 {
166 value = (double)(p.blue()) + value * m_amount;
167 }
168
169 q.setBlue(CLAMP(lround(value), zero, quantum));
170
171 // Alpha Channel.
172
173 value = (double)(p.alpha()) - (double)(q.alpha());
174
175 if (fabs(2.0 * value) < quantumThreshold)
176 {
177 value = (double)(p.alpha());
178 }
179 else
180 {
181 value = (double)(p.alpha()) + value * m_amount;
182 }
183
184 q.setAlpha(CLAMP(lround(value), zero, quantum));
185 }
186
187 m_destImage.setPixelColor(x, y, q);
188 }
189 }
190
filterImage()191 void UnsharpMaskFilter::filterImage()
192 {
193 int progress;
194
195 if (m_orgImage.isNull())
196 {
197 qCWarning(DIGIKAM_DIMG_LOG) << "No image data available!";
198 return;
199 }
200
201 // cppcheck-suppress unusedScopedObject
202 BlurFilter(this, m_orgImage, m_destImage, 0, 10, (int)(m_radius*10.0));
203
204 QList<int> vals = multithreadedSteps(m_destImage.width());
205
206 for (uint y = 0 ; runningFlag() && (y < m_destImage.height()) ; ++y)
207 {
208 QList <QFuture<void> > tasks;
209
210 for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j)
211 {
212 tasks.append(QtConcurrent::run(this,
213 &UnsharpMaskFilter::unsharpMaskMultithreaded,
214 vals[j],
215 vals[j+1],
216 y));
217 }
218
219 foreach (QFuture<void> t, tasks)
220 {
221 t.waitForFinished();
222 }
223
224 progress = (int)(10.0 + ((double)y * 90.0) / m_destImage.height());
225
226 if ((progress % 5) == 0)
227 {
228 postProgress(progress);
229 }
230 }
231 }
232
filterAction()233 FilterAction UnsharpMaskFilter::filterAction()
234 {
235 FilterAction action(FilterIdentifier(), CurrentVersion());
236 action.setDisplayableName(DisplayableName());
237
238 action.addParameter(QLatin1String("amount"), m_amount);
239 action.addParameter(QLatin1String("radius"), m_radius);
240 action.addParameter(QLatin1String("threshold"), m_threshold);
241 action.addParameter(QLatin1String("luma"), m_luma);
242
243 return action;
244 }
245
readParameters(const FilterAction & action)246 void UnsharpMaskFilter::readParameters(const FilterAction& action)
247 {
248 m_amount = action.parameter(QLatin1String("amount")).toDouble();
249 m_radius = action.parameter(QLatin1String("radius")).toDouble();
250 m_threshold = action.parameter(QLatin1String("threshold")).toDouble();
251 m_luma = action.parameter(QLatin1String("luma")).toBool();
252 }
253
254 } // namespace Digikam
255