1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2004-12-27
7 * Description : a tool to reduce lens distortions to an image.
8 *
9 * Copyright (C) 2004-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10 * Copyright (C) 2006-2010 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 "lensdistortiontool.h"
26
27 // C++ includes
28
29 #include <cmath>
30
31 // Qt includes
32
33 #include <QBrush>
34 #include <QGridLayout>
35 #include <QLabel>
36 #include <QPainter>
37 #include <QPen>
38 #include <QPixmap>
39 #include <QIcon>
40
41 // KDE includes
42
43 #include <ksharedconfig.h>
44 #include <kconfiggroup.h>
45 #include <klocalizedstring.h>
46
47 // Local includes
48
49 #include "dnuminput.h"
50 #include "editortoolsettings.h"
51 #include "imageiface.h"
52 #include "imageguidewidget.h"
53 #include "lensdistortionfilter.h"
54
55 namespace DigikamEditorLensDistortionToolPlugin
56 {
57
58 class Q_DECL_HIDDEN LensDistortionTool::Private
59 {
60 public:
61
Private()62 explicit Private()
63 : maskPreviewLabel(nullptr),
64 mainInput (nullptr),
65 edgeInput (nullptr),
66 rescaleInput (nullptr),
67 brightenInput (nullptr),
68 previewWidget (nullptr),
69 gboxSettings (nullptr)
70 {
71 }
72
73 static const QString configGroupName;
74 static const QString config2ndOrderDistortionEntry;
75 static const QString config4thOrderDistortionEntry;
76 static const QString configZoomFactorEntry;
77 static const QString configBrightenEntry;
78
79 QLabel* maskPreviewLabel;
80
81 DDoubleNumInput* mainInput;
82 DDoubleNumInput* edgeInput;
83 DDoubleNumInput* rescaleInput;
84 DDoubleNumInput* brightenInput;
85
86 DImg previewRasterImage;
87
88 ImageGuideWidget* previewWidget;
89 EditorToolSettings* gboxSettings;
90 };
91
92 const QString LensDistortionTool::Private::configGroupName(QLatin1String("lensdistortion Tool"));
93 const QString LensDistortionTool::Private::config2ndOrderDistortionEntry(QLatin1String("2nd Order Distortion"));
94 const QString LensDistortionTool::Private::config4thOrderDistortionEntry(QLatin1String("4th Order Distortion"));
95 const QString LensDistortionTool::Private::configZoomFactorEntry(QLatin1String("Zoom Factor"));
96 const QString LensDistortionTool::Private::configBrightenEntry(QLatin1String("Brighten"));
97
98 // --------------------------------------------------------
99
LensDistortionTool(QObject * const parent)100 LensDistortionTool::LensDistortionTool(QObject* const parent)
101 : EditorToolThreaded(parent),
102 d (new Private)
103 {
104 setObjectName(QLatin1String("lensdistortion"));
105
106 d->previewWidget = new ImageGuideWidget(nullptr, true, ImageGuideWidget::HVGuideMode);
107 setToolView(d->previewWidget);
108
109 // -------------------------------------------------------------
110
111 d->gboxSettings = new EditorToolSettings(nullptr);
112 d->gboxSettings->setTools(EditorToolSettings::ColorGuide);
113
114 QGridLayout* const gridSettings = new QGridLayout(d->gboxSettings->plainPage());
115
116 d->maskPreviewLabel = new QLabel(d->gboxSettings->plainPage());
117 d->maskPreviewLabel->setAlignment ( Qt::AlignHCenter | Qt::AlignVCenter );
118 d->maskPreviewLabel->setWhatsThis( i18n("You can see here a thumbnail preview of the "
119 "distortion correction applied to a cross pattern.") );
120
121 // -------------------------------------------------------------
122
123 QLabel* const label1 = new QLabel(i18nc("value for amount of distortion", "Main:"), d->gboxSettings->plainPage());
124
125 d->mainInput = new DDoubleNumInput(d->gboxSettings->plainPage());
126 d->mainInput->setDecimals(1);
127 d->mainInput->setRange(-100.0, 100.0, 0.1);
128 d->mainInput->setDefaultValue(0.0);
129 d->mainInput->setWhatsThis( i18n("This value controls the amount of distortion. Negative values "
130 "correct lens barrel distortion, while positive values correct lens "
131 "pincushion distortion."));
132
133 // -------------------------------------------------------------
134
135 QLabel* const label2 = new QLabel(i18n("Edge:"), d->gboxSettings->plainPage());
136
137 d->edgeInput = new DDoubleNumInput(d->gboxSettings->plainPage());
138 d->edgeInput->setDecimals(1);
139 d->edgeInput->setRange(-100.0, 100.0, 0.1);
140 d->edgeInput->setDefaultValue(0.0);
141 d->edgeInput->setWhatsThis( i18n("This value controls in the same manner as the Main control, "
142 "but has more effect at the edges of the image than at the center."));
143
144 // -------------------------------------------------------------
145
146 QLabel* const label3 = new QLabel(i18n("Zoom:"), d->gboxSettings->plainPage());
147
148 d->rescaleInput = new DDoubleNumInput(d->gboxSettings->plainPage());
149 d->rescaleInput->setDecimals(1);
150 d->rescaleInput->setRange(-100.0, 100.0, 0.1);
151 d->rescaleInput->setDefaultValue(0.0);
152 d->rescaleInput->setWhatsThis( i18n("This value rescales the overall image size."));
153
154 // -------------------------------------------------------------
155
156 QLabel* const label4 = new QLabel(i18n("Brighten:"), d->gboxSettings->plainPage());
157
158 d->brightenInput = new DDoubleNumInput(d->gboxSettings->plainPage());
159 d->brightenInput->setDecimals(1);
160 d->brightenInput->setRange(-100.0, 100.0, 0.1);
161 d->brightenInput->setDefaultValue(0.0);
162 d->brightenInput->setWhatsThis( i18n("This value adjusts the brightness in image corners."));
163
164 // -------------------------------------------------------------
165
166 const int spacing = d->gboxSettings->spacingHint();
167
168 gridSettings->addWidget(d->maskPreviewLabel, 0, 0, 1, 2);
169 gridSettings->addWidget(label1, 1, 0, 1, 2);
170 gridSettings->addWidget(d->mainInput, 2, 0, 1, 2);
171 gridSettings->addWidget(label2, 3, 0, 1, 2);
172 gridSettings->addWidget(d->edgeInput, 4, 0, 1, 2);
173 gridSettings->addWidget(label3, 5, 0, 1, 2);
174 gridSettings->addWidget(d->rescaleInput, 6, 0, 1, 2);
175 gridSettings->addWidget(label4, 7, 0, 1, 2);
176 gridSettings->addWidget(d->brightenInput, 8, 0, 1, 2);
177 gridSettings->setRowStretch(9, 10);
178 gridSettings->setContentsMargins(spacing, spacing, spacing, spacing);
179 gridSettings->setSpacing(spacing);
180
181 setToolSettings(d->gboxSettings);
182
183 // -------------------------------------------------------------
184
185 connect(d->mainInput, SIGNAL(valueChanged(double)),
186 this, SLOT(slotTimer()));
187
188 connect(d->edgeInput, SIGNAL(valueChanged(double)),
189 this, SLOT(slotTimer()));
190
191 connect(d->rescaleInput, SIGNAL(valueChanged(double)),
192 this, SLOT(slotTimer()));
193
194 connect(d->brightenInput, SIGNAL(valueChanged(double)),
195 this, SLOT(slotTimer()));
196
197 connect(d->gboxSettings, SIGNAL(signalColorGuideChanged()),
198 this, SLOT(slotColorGuideChanged()));
199
200 // -------------------------------------------------------------
201
202 /* Calc transform preview.
203 We would like a checkered area to demonstrate the effect.
204 We do not have any drawing support in DImg, so we let Qt draw.
205 First we create a white QImage. We convert this to a QPixmap,
206 on which we can draw. Then we convert back to QImage,
207 convert the QImage to a DImg which we only need to create once, here.
208 Later, we apply the effect on a copy and convert the DImg to QPixmap.
209 Longing for Qt4 where we can paint directly on the QImage...
210 */
211
212 QPixmap pix(120, 120);
213 pix.fill(Qt::white);
214 QPainter pt(&pix);
215 pt.setPen(QPen(Qt::black, 1));
216 pt.fillRect(0, 0, pix.width(), pix.height(), QBrush(Qt::black, Qt::CrossPattern));
217 pt.drawRect(0, 0, pix.width(), pix.height());
218 pt.end();
219 QImage preview = pix.toImage();
220 d->previewRasterImage = DImg(preview.width(), preview.height(), false, false, preview.bits());
221 }
222
~LensDistortionTool()223 LensDistortionTool::~LensDistortionTool()
224 {
225 delete d;
226 }
227
slotColorGuideChanged()228 void LensDistortionTool::slotColorGuideChanged()
229 {
230 d->previewWidget->slotChangeGuideColor(d->gboxSettings->guideColor());
231 d->previewWidget->slotChangeGuideSize(d->gboxSettings->guideSize());
232 }
233
readSettings()234 void LensDistortionTool::readSettings()
235 {
236 KSharedConfig::Ptr config = KSharedConfig::openConfig();
237 KConfigGroup group = config->group(d->configGroupName);
238
239 blockWidgetSignals(true);
240
241 d->mainInput->setValue(group.readEntry(d->config2ndOrderDistortionEntry, d->mainInput->defaultValue()));
242 d->edgeInput->setValue(group.readEntry(d->config4thOrderDistortionEntry, d->edgeInput->defaultValue()));
243 d->rescaleInput->setValue(group.readEntry(d->configZoomFactorEntry, d->rescaleInput->defaultValue()));
244 d->brightenInput->setValue(group.readEntry(d->configBrightenEntry, d->brightenInput->defaultValue()));
245
246 blockWidgetSignals(false);
247
248 slotColorGuideChanged();
249 slotPreview();
250 }
251
writeSettings()252 void LensDistortionTool::writeSettings()
253 {
254 KSharedConfig::Ptr config = KSharedConfig::openConfig();
255 KConfigGroup group = config->group(d->configGroupName);
256
257 group.writeEntry(d->config2ndOrderDistortionEntry, d->mainInput->value());
258 group.writeEntry(d->config4thOrderDistortionEntry, d->edgeInput->value());
259 group.writeEntry(d->configZoomFactorEntry, d->rescaleInput->value());
260 group.writeEntry(d->configBrightenEntry, d->brightenInput->value());
261
262 config->sync();
263 }
264
slotResetSettings()265 void LensDistortionTool::slotResetSettings()
266 {
267 blockWidgetSignals(true);
268
269 d->mainInput->slotReset();
270 d->edgeInput->slotReset();
271 d->rescaleInput->slotReset();
272 d->brightenInput->slotReset();
273
274 blockWidgetSignals(false);
275
276 slotPreview();
277 }
278
preparePreview()279 void LensDistortionTool::preparePreview()
280 {
281 double m = d->mainInput->value();
282 double e = d->edgeInput->value();
283 double r = d->rescaleInput->value();
284 double b = d->brightenInput->value();
285
286 LensDistortionFilter transformPreview(&d->previewRasterImage, nullptr, m, e, r, b, 0, 0);
287 transformPreview.startFilterDirectly();
288 d->maskPreviewLabel->setPixmap(transformPreview.getTargetImage().convertToPixmap());
289
290 ImageIface* const iface = d->previewWidget->imageIface();
291
292 setFilter(new LensDistortionFilter(iface->original(), this, m, e, r, b, 0, 0));
293 }
294
prepareFinal()295 void LensDistortionTool::prepareFinal()
296 {
297 double m = d->mainInput->value();
298 double e = d->edgeInput->value();
299 double r = d->rescaleInput->value();
300 double b = d->brightenInput->value();
301
302 ImageIface iface;
303 setFilter(new LensDistortionFilter(iface.original(), this, m, e, r, b, 0, 0));
304 }
305
setPreviewImage()306 void LensDistortionTool::setPreviewImage()
307 {
308 ImageIface* const iface = d->previewWidget->imageIface();
309 DImg imDest = filter()->getTargetImage().smoothScale(iface->previewSize());
310 iface->setPreview(imDest);
311
312 d->previewWidget->updatePreview();
313 }
314
setFinalImage()315 void LensDistortionTool::setFinalImage()
316 {
317 ImageIface iface;
318 iface.setOriginal(i18n("Lens Distortion"), filter()->filterAction(), filter()->getTargetImage());
319 }
320
blockWidgetSignals(bool b)321 void LensDistortionTool::blockWidgetSignals(bool b)
322 {
323 d->mainInput->blockSignals(b);
324 d->edgeInput->blockSignals(b);
325 d->rescaleInput->blockSignals(b);
326 d->brightenInput->blockSignals(b);
327 }
328
329 } // namespace DigikamEditorLensDistortionToolPlugin
330