1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2005-02-09
7 * Description : a tool to apply Blur FX to images
8 *
9 * Copyright 2005-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10 * Copyright 2006-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
11 *
12 * This program is free software; you can redistribute it
13 * and/or modify it under the terms of the GNU General
14 * Public License as published by the Free Software Foundation;
15 * either version 2, or (at your option)
16 * any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * ============================================================ */
24
25 #include "blurfxtool.h"
26
27 // Qt includes
28
29 #include <QDateTime>
30 #include <QGridLayout>
31 #include <QImage>
32 #include <QLabel>
33 #include <QSlider>
34 #include <QIcon>
35
36 // KDE includes
37
38 #include <ksharedconfig.h>
39 #include <kconfiggroup.h>
40 #include <klocalizedstring.h>
41
42 // Local includes
43
44 #include "dnuminput.h"
45 #include "blurfxfilter.h"
46 #include "dcombobox.h"
47 #include "editortoolsettings.h"
48 #include "imageiface.h"
49 #include "imageregionwidget.h"
50
51 namespace DigikamEditorBlurFxToolPlugin
52 {
53
54 class Q_DECL_HIDDEN BlurFXTool::Private
55 {
56 public:
57
Private()58 explicit Private()
59 : effectTypeLabel (nullptr),
60 distanceLabel (nullptr),
61 levelLabel (nullptr),
62 effectType (nullptr),
63 distanceInput (nullptr),
64 levelInput (nullptr),
65 previewWidget (nullptr),
66 gboxSettings (nullptr)
67 {
68 }
69
70 static const QString configGroupName;
71 static const QString configEffectTypeEntry;
72 static const QString configDistanceAdjustmentEntry;
73 static const QString configLevelAdjustmentEntry;
74
75 QLabel* effectTypeLabel;
76 QLabel* distanceLabel;
77 QLabel* levelLabel;
78
79 DComboBox* effectType;
80
81 DIntNumInput* distanceInput;
82 DIntNumInput* levelInput;
83
84 ImageRegionWidget* previewWidget;
85
86 EditorToolSettings* gboxSettings;
87 };
88
89 const QString BlurFXTool::Private::configGroupName(QLatin1String("blurfx Tool"));
90 const QString BlurFXTool::Private::configEffectTypeEntry(QLatin1String("EffectType"));
91 const QString BlurFXTool::Private::configDistanceAdjustmentEntry(QLatin1String("DistanceAdjustment"));
92 const QString BlurFXTool::Private::configLevelAdjustmentEntry(QLatin1String("LevelAdjustment"));
93
94 // --------------------------------------------------------
95
BlurFXTool(QObject * const parent)96 BlurFXTool::BlurFXTool(QObject* const parent)
97 : EditorToolThreaded(parent),
98 d (new Private)
99 {
100 setObjectName(QLatin1String("blurfx"));
101
102 // -------------------------------------------------------------
103
104 d->gboxSettings = new EditorToolSettings(nullptr);
105 d->gboxSettings->setButtons(EditorToolSettings::Default|
106 EditorToolSettings::Ok|
107 EditorToolSettings::Cancel|
108 EditorToolSettings::Try);
109
110 d->previewWidget = new ImageRegionWidget;
111 d->previewWidget->setWhatsThis(i18n("This is the preview of the blur effect "
112 "applied to the photograph."));
113
114 // -------------------------------------------------------------
115
116 d->effectTypeLabel = new QLabel(i18n("Type:"));
117 d->effectType = new DComboBox;
118 d->effectType->addItem(i18n("Zoom Blur"));
119 d->effectType->addItem(i18n("Radial Blur"));
120 d->effectType->addItem(i18n("Far Blur"));
121 d->effectType->addItem(i18n("Motion Blur"));
122 d->effectType->addItem(i18n("Softener Blur"));
123 d->effectType->addItem(i18n("Shake Blur"));
124 d->effectType->addItem(i18n("Focus Blur"));
125 d->effectType->addItem(i18n("Smart Blur"));
126 d->effectType->addItem(i18n("Frost Glass"));
127 d->effectType->addItem(i18n("Mosaic"));
128 d->effectType->setDefaultIndex(BlurFXFilter::ZoomBlur);
129 d->effectType->setWhatsThis(i18n("<p>Select the blurring effect to apply to image.</p>"
130 "<p><b>Zoom Blur</b>: blurs the image along radial lines starting from "
131 "a specified center point. This simulates the blur of a zooming camera.</p>"
132 "<p><b>Radial Blur</b>: blurs the image by rotating the pixels around "
133 "the specified center point. This simulates the blur of a rotating camera.</p>"
134 "<p><b>Far Blur</b>: blurs the image by using far pixels. This simulates the blur "
135 "of an unfocalized camera lens.</p>"
136 "<p><b>Motion Blur</b>: blurs the image by moving the pixels horizontally. "
137 "This simulates the blur of a linear moving camera.</p>"
138 "<p><b>Softener Blur</b>: blurs the image softly in dark tones and hardly in light "
139 "tones. This gives images a dreamy and glossy soft focus effect. It is ideal "
140 "for creating romantic portraits, glamour photographs, or giving images a warm "
141 "and subtle glow.</p>"
142 "<p><b>Shake Blur</b>: blurs the image by shaking randomly the pixels. "
143 "This simulates the blur of a random moving camera.</p>"
144 "<p><b>Focus Blur</b>: blurs the image corners to reproduce the astigmatism distortion "
145 "of a lens.</p>"
146 "<p><b>Smart Blur</b>: finds the edges of color in your image and blurs them without "
147 "muddying the rest of the image.</p>"
148 "<p><b>Frost Glass</b>: blurs the image by randomly disperse light coming through "
149 "a frosted glass.</p>"
150 "<p><b>Mosaic</b>: divides the photograph into rectangular cells and then "
151 "recreates it by filling those cells with average pixel value.</p>"));
152
153 d->distanceLabel = new QLabel(i18n("Distance:"));
154 d->distanceInput = new DIntNumInput;
155 d->distanceInput->setRange(0, 100, 1);
156 d->distanceInput->setDefaultValue(3);
157 d->distanceInput->setWhatsThis( i18n("Set here the blur distance in pixels."));
158
159 d->levelLabel = new QLabel(i18nc("level to use for the effect", "Level:"));
160 d->levelInput = new DIntNumInput;
161 d->levelInput->setRange(0, 360, 1);
162 d->levelInput->setDefaultValue(128);
163 d->levelInput->setWhatsThis( i18n("This value controls the level to use with the current effect."));
164
165 connect(d->effectType, SIGNAL(currentIndexChanged(int)),
166 this, SLOT(slotEffectTypeChanged(int)));
167
168 // -------------------------------------------------------------
169
170 const int spacing = d->gboxSettings->spacingHint();
171
172 QGridLayout* const mainLayout = new QGridLayout;
173 mainLayout->addWidget(d->effectTypeLabel, 0, 0, 1, 2);
174 mainLayout->addWidget(d->effectType, 1, 0, 1, 2);
175 mainLayout->addWidget(d->distanceLabel, 2, 0, 1, 2);
176 mainLayout->addWidget(d->distanceInput, 3, 0, 1, 2);
177 mainLayout->addWidget(d->levelLabel, 4, 0, 1, 2);
178 mainLayout->addWidget(d->levelInput, 5, 0, 1, 2);
179 mainLayout->setRowStretch(6, 10);
180 mainLayout->setContentsMargins(spacing, spacing, spacing, spacing);
181 mainLayout->setSpacing(spacing);
182 d->gboxSettings->plainPage()->setLayout(mainLayout);
183
184 // -------------------------------------------------------------
185
186 setPreviewModeMask(PreviewToolBar::AllPreviewModes);
187 setToolSettings(d->gboxSettings);
188 setToolView(d->previewWidget);
189
190 slotEffectTypeChanged(d->effectType->defaultIndex());
191 }
192
~BlurFXTool()193 BlurFXTool::~BlurFXTool()
194 {
195 delete d;
196 }
197
readSettings()198 void BlurFXTool::readSettings()
199 {
200 KSharedConfig::Ptr config = KSharedConfig::openConfig();
201 KConfigGroup group = config->group(d->configGroupName);
202
203 blockWidgetSignals(true);
204
205 d->effectType->setCurrentIndex(group.readEntry(d->configEffectTypeEntry, d->effectType->defaultIndex()));
206 d->distanceInput->setValue(group.readEntry(d->configDistanceAdjustmentEntry, d->distanceInput->defaultValue()));
207 d->levelInput->setValue(group.readEntry(d->configLevelAdjustmentEntry, d->levelInput->defaultValue()));
208 slotEffectTypeChanged(d->effectType->defaultIndex());
209
210 blockWidgetSignals(false);
211 }
212
writeSettings()213 void BlurFXTool::writeSettings()
214 {
215 KSharedConfig::Ptr config = KSharedConfig::openConfig();
216 KConfigGroup group = config->group(d->configGroupName);
217
218 group.writeEntry(d->configEffectTypeEntry, d->effectType->currentIndex());
219 group.writeEntry(d->configDistanceAdjustmentEntry, d->distanceInput->value());
220 group.writeEntry(d->configLevelAdjustmentEntry, d->levelInput->value());
221 group.sync();
222 }
223
slotResetSettings()224 void BlurFXTool::slotResetSettings()
225 {
226 blockWidgetSignals(true);
227
228 d->effectType->slotReset();
229 d->distanceInput->slotReset();
230 d->levelInput->slotReset();
231
232 blockWidgetSignals(false);
233
234 slotEffectTypeChanged(d->effectType->defaultIndex());
235 }
236
slotEffectTypeChanged(int type)237 void BlurFXTool::slotEffectTypeChanged(int type)
238 {
239 d->distanceInput->setEnabled(true);
240 d->distanceLabel->setEnabled(true);
241
242 blockWidgetSignals(true);
243
244 d->distanceInput->setRange(0, 200, 1);
245 d->distanceInput->setValue(100);
246 d->levelInput->setRange(0, 360, 1);
247 d->levelInput->setValue(45);
248
249 d->levelInput->setEnabled(false);
250 d->levelLabel->setEnabled(false);
251
252 switch (type)
253 {
254 case BlurFXFilter::ZoomBlur:
255 break;
256
257 case BlurFXFilter::RadialBlur:
258 case BlurFXFilter::FrostGlass:
259 d->distanceInput->setRange(0, 10, 1);
260 d->distanceInput->setValue(3);
261 break;
262
263 case BlurFXFilter::FarBlur:
264 d->distanceInput->setRange(0, 20, 1);
265 d->distanceInput->setValue(10);
266 break;
267
268 case BlurFXFilter::MotionBlur:
269 case BlurFXFilter::FocusBlur:
270 d->distanceInput->setRange(0, 100, 1);
271 d->distanceInput->setValue(20);
272 d->levelInput->setEnabled(true);
273 d->levelLabel->setEnabled(true);
274 break;
275
276 case BlurFXFilter::SoftenerBlur:
277 d->distanceInput->setEnabled(false);
278 d->distanceLabel->setEnabled(false);
279 break;
280
281 case BlurFXFilter::ShakeBlur:
282 d->distanceInput->setRange(0, 100, 1);
283 d->distanceInput->setValue(20);
284 break;
285
286 case BlurFXFilter::SmartBlur:
287 d->distanceInput->setRange(0, 20, 1);
288 d->distanceInput->setValue(3);
289 d->levelInput->setEnabled(true);
290 d->levelLabel->setEnabled(true);
291 d->levelInput->setRange(0, 255, 1);
292 d->levelInput->setValue(128);
293 break;
294
295 case BlurFXFilter::Mosaic:
296 d->distanceInput->setRange(0, 50, 1);
297 d->distanceInput->setValue(3);
298 break;
299 }
300
301 blockWidgetSignals(false);
302 }
303
preparePreview()304 void BlurFXTool::preparePreview()
305 {
306 d->gboxSettings->setEnabled(false);
307
308 DImg image;
309
310 switch (d->effectType->currentIndex())
311 {
312 case BlurFXFilter::ZoomBlur:
313 case BlurFXFilter::RadialBlur:
314 case BlurFXFilter::FocusBlur:
315 {
316 ImageIface iface;
317 image = *iface.original();
318 break;
319 }
320
321 case BlurFXFilter::FarBlur:
322 case BlurFXFilter::MotionBlur:
323 case BlurFXFilter::SoftenerBlur:
324 case BlurFXFilter::ShakeBlur:
325 case BlurFXFilter::SmartBlur:
326 case BlurFXFilter::FrostGlass:
327 case BlurFXFilter::Mosaic:
328 image = d->previewWidget->getOriginalRegionImage();
329 break;
330 }
331
332 int type = d->effectType->currentIndex();
333 int dist = d->distanceInput->value();
334 int level = d->levelInput->value();
335
336 setFilter(new BlurFXFilter(&image, this, type, dist, level));
337 }
338
prepareFinal()339 void BlurFXTool::prepareFinal()
340 {
341 d->gboxSettings->setEnabled(false);
342
343 int type = d->effectType->currentIndex();
344 int dist = d->distanceInput->value();
345 int level = d->levelInput->value();
346
347 ImageIface iface;
348 setFilter(new BlurFXFilter(iface.original(), this, type, dist, level));
349 }
350
setPreviewImage()351 void BlurFXTool::setPreviewImage()
352 {
353 switch (d->effectType->currentIndex())
354 {
355 case BlurFXFilter::ZoomBlur:
356 case BlurFXFilter::RadialBlur:
357 case BlurFXFilter::FocusBlur:
358 {
359 QRect pRect = d->previewWidget->getOriginalImageRegionToRender();
360 DImg destImg = filter()->getTargetImage().copy(pRect);
361 d->previewWidget->setPreviewImage(destImg);
362 break;
363 }
364 case BlurFXFilter::FarBlur:
365 case BlurFXFilter::MotionBlur:
366 case BlurFXFilter::SoftenerBlur:
367 case BlurFXFilter::ShakeBlur:
368 case BlurFXFilter::SmartBlur:
369 case BlurFXFilter::FrostGlass:
370 case BlurFXFilter::Mosaic:
371 d->previewWidget->setPreviewImage(filter()->getTargetImage());
372 break;
373 }
374 }
375
setFinalImage()376 void BlurFXTool::setFinalImage()
377 {
378 ImageIface iface;
379 iface.setOriginal(i18n("Blur Effects"), filter()->filterAction(), filter()->getTargetImage());
380 }
381
renderingFinished()382 void BlurFXTool::renderingFinished()
383 {
384 d->gboxSettings->setEnabled(true);
385 }
386
blockWidgetSignals(bool b)387 void BlurFXTool::blockWidgetSignals(bool b)
388 {
389 d->effectType->blockSignals(b);
390 d->distanceInput->blockSignals(b);
391 d->levelInput->blockSignals(b);
392 }
393
394 } // namespace DigikamEditorBlurFxToolPlugin
395