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