1
/* ============================================================
3
* This file is a part of digiKam project
4
* http://www.digikam.org
7
* Description : a tool to resize an image
9
* Copyright (C) 2005-2010 by Gilles Caulier <caulier dot gilles at gmail dot com>
11
* This program is free software; you can redistribute it
12
* and/or modify it under the terms of the GNU General
13
* Public License as published by the Free Software Foundation;
14
* either version 2, or (at your option)
17
* This program is distributed in the hope that it will be useful,
18
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
* GNU General Public License for more details.
22
* ============================================================ */
24
#include "resizetool.moc"
34
#include <QCloseEvent>
36
#include <QCustomEvent>
40
#include <QGridLayout>
45
#include <QPushButton>
47
#include <QVBoxLayout>
51
#include <kapplication.h>
53
#include <kfiledialog.h>
55
#include <kglobalsettings.h>
56
#include <kiconloader.h>
58
#include <kmessagebox.h>
59
#include <kseparator.h>
60
#include <kstandarddirs.h>
61
#include <ktabwidget.h>
62
#include <ktoolinvocation.h>
63
#include <kurllabel.h>
67
#include <libkdcraw/rnuminput.h>
72
#include "imageiface.h"
73
#include "imageguidewidget.h"
74
#include "editortoolsettings.h"
75
#include "editortooliface.h"
76
#include "dimgthreadedfilter.h"
77
#include "greycstorationfilter.h"
78
#include "greycstorationsettings.h"
80
using namespace KDcrawIface;
82
namespace DigikamTransformImagePlugin
85
class ResizeImage : public DImgThreadedFilter
90
ResizeImage(DImg* orgImage, int newWidth, int newHeight, QObject* parent=0)
91
: DImgThreadedFilter(orgImage, parent, "resizeimage")
94
m_newWidth = newWidth;
95
m_newHeight = newHeight;
106
m_destImage = m_orgImage.copy();
108
m_destImage.resize(m_newWidth, m_newHeight);
118
// -------------------------------------------------------------
125
configGroupName("resize Tool"),
126
configFastApproxEntry("FastApprox"),
127
configInterpolationEntry("Interpolation"),
128
configAmplitudeEntry("Amplitude"),
129
configSharpnessEntry("Sharpness"),
130
configAnisotropyEntry("Anisotropy"),
131
configAlphaEntry("Alpha"),
132
configSigmaEntry("Sigma"),
133
configGaussPrecEntry("GaussPrec"),
136
configIterationEntry("Iteration"),
137
configTileEntry("Tile"),
138
configBTileEntry("BTile"),
148
useGreycstorationBox(0),
160
const QString configGroupName;
161
const QString configFastApproxEntry;
162
const QString configInterpolationEntry;
163
const QString configAmplitudeEntry;
164
const QString configSharpnessEntry;
165
const QString configAnisotropyEntry;
166
const QString configAlphaEntry;
167
const QString configSigmaEntry;
168
const QString configGaussPrecEntry;
169
const QString configDlEntry;
170
const QString configDaEntry;
171
const QString configIterationEntry;
172
const QString configTileEntry;
173
const QString configBTileEntry;
183
QLabel* restorationTips;
185
QCheckBox* preserveRatioBox;
186
QCheckBox* useGreycstorationBox;
190
KUrlLabel* cimgLogoLabel;
192
ImageGuideWidget* previewWidget;
194
RIntNumInput* wInput;
195
RIntNumInput* hInput;
197
RDoubleNumInput* wpInput;
198
RDoubleNumInput* hpInput;
200
EditorToolSettings* gboxSettings;
201
GreycstorationSettings* settingsWidget;
204
// -------------------------------------------------------------
206
ResizeTool::ResizeTool(QObject* parent)
207
: EditorToolThreaded(parent), d(new ResizeToolPriv)
209
setObjectName("resizeimage");
210
setToolName(i18n("Resize Image"));
211
setToolIcon(SmallIcon("transform-scale"));
213
d->previewWidget = new ImageGuideWidget(0, false, ImageGuideWidget::HVGuideMode, Qt::red, 1, false, false);
214
setToolView(d->previewWidget);
215
setPreviewModeMask(PreviewToolBar::UnSplitPreviewModes);
217
// -------------------------------------------------------------
219
d->gboxSettings = new EditorToolSettings;
220
d->gboxSettings->setButtons(EditorToolSettings::Default|
221
EditorToolSettings::Try|
222
EditorToolSettings::Ok|
223
EditorToolSettings::Load|
224
EditorToolSettings::SaveAs|
225
EditorToolSettings::Cancel);
227
ImageIface iface(0, 0);
228
d->orgWidth = iface.originalWidth();
229
d->orgHeight = iface.originalHeight();
230
d->prevW = d->orgWidth;
231
d->prevH = d->orgHeight;
235
// -------------------------------------------------------------
237
QGridLayout* gridSettings = new QGridLayout(d->gboxSettings->plainPage());
238
d->mainTab = new KTabWidget(d->gboxSettings->plainPage());
239
QWidget* firstPage = new QWidget(d->mainTab);
240
QGridLayout* grid = new QGridLayout(firstPage);
242
d->mainTab->addTab(firstPage, i18n("New Size"));
244
QLabel *label1 = new QLabel(i18n("Width:"), firstPage);
245
d->wInput = new RIntNumInput(firstPage);
246
d->wInput->setRange(1, qMax(d->orgWidth * 10, 9999), 1);
247
d->wInput->setDefaultValue(d->orgWidth);
248
d->wInput->setSliderEnabled(true);
249
d->wInput->setObjectName("wInput");
250
d->wInput->setWhatsThis( i18n("Set here the new image width in pixels."));
252
QLabel *label2 = new QLabel(i18n("Height:"), firstPage);
253
d->hInput = new RIntNumInput(firstPage);
254
d->hInput->setRange(1, qMax(d->orgHeight * 10, 9999), 1);
255
d->hInput->setDefaultValue(d->orgHeight);
256
d->hInput->setSliderEnabled(true);
257
d->hInput->setObjectName("hInput");
258
d->hInput->setWhatsThis( i18n("New image height in pixels (px)."));
260
QLabel *label3 = new QLabel(i18n("Width (%):"), firstPage);
261
d->wpInput = new RDoubleNumInput(firstPage);
262
d->wpInput->input()->setRange(1.0, 999.0, 1.0, true);
263
d->wpInput->setDefaultValue(100.0);
264
d->wpInput->setObjectName("wpInput");
265
d->wpInput->setWhatsThis( i18n("New image width in percent (%)."));
267
QLabel *label4 = new QLabel(i18n("Height (%):"), firstPage);
268
d->hpInput = new RDoubleNumInput(firstPage);
269
d->hpInput->input()->setRange(1.0, 999.0, 1.0, true);
270
d->hpInput->setDefaultValue(100.0);
271
d->hpInput->setObjectName("hpInput");
272
d->hpInput->setWhatsThis( i18n("New image height in percent (%)."));
274
d->preserveRatioBox = new QCheckBox(i18n("Maintain aspect ratio"), firstPage);
275
d->preserveRatioBox->setWhatsThis( i18n("Enable this option to maintain aspect "
276
"ratio with new image sizes."));
278
d->cimgLogoLabel = new KUrlLabel(firstPage);
279
d->cimgLogoLabel->setText(QString());
280
d->cimgLogoLabel->setUrl("http://cimg.sourceforge.net");
281
d->cimgLogoLabel->setPixmap(QPixmap(KStandardDirs::locate("data", "digikam/data/logo-cimg.png")));
282
d->cimgLogoLabel->setToolTip(i18n("Visit CImg library website"));
284
d->useGreycstorationBox = new QCheckBox(i18n("Restore photograph (slow)"), firstPage);
285
d->useGreycstorationBox->setWhatsThis( i18n("Enable this option to scale-up an image to a huge size. "
286
"<b>Warning</b>: This process can take some time."));
288
d->restorationTips = new QLabel(i18n("<b>Note:</b> use Restoration Mode to scale-up an image to a huge size. "
289
"This process can take some time."), firstPage);
290
d->restorationTips->setWordWrap(true);
292
grid->addWidget(d->preserveRatioBox, 0, 0, 1, 3);
293
grid->addWidget(label1, 1, 0, 1, 1);
294
grid->addWidget(d->wInput, 1, 1, 1, 2);
295
grid->addWidget(label2, 2, 0, 1, 1);
296
grid->addWidget(d->hInput, 2, 1, 1, 2);
297
grid->addWidget(label3, 3, 0, 1, 1);
298
grid->addWidget(d->wpInput, 3, 1, 1, 2);
299
grid->addWidget(label4, 4, 0, 1, 1);
300
grid->addWidget(d->hpInput, 4, 1, 1, 2);
301
grid->addWidget(new KSeparator(firstPage), 5, 0, 1, 3);
302
grid->addWidget(d->cimgLogoLabel, 6, 0, 3, 1);
303
grid->addWidget(d->useGreycstorationBox, 6, 1, 1, 2);
304
grid->addWidget(d->restorationTips, 7, 1, 1, 2);
305
grid->setRowStretch(8, 10);
306
grid->setMargin(d->gboxSettings->spacingHint());
307
grid->setSpacing(d->gboxSettings->spacingHint());
309
// -------------------------------------------------------------
311
d->settingsWidget = new GreycstorationSettings(d->mainTab);
313
gridSettings->addWidget(d->mainTab, 0, 1, 1, 1);
314
gridSettings->addWidget(new QLabel(d->gboxSettings->plainPage()), 1, 1, 1, 1);
315
gridSettings->setMargin(d->gboxSettings->spacingHint());
316
gridSettings->setSpacing(d->gboxSettings->spacingHint());
317
gridSettings->setRowStretch(2, 10);
319
setToolSettings(d->gboxSettings);
322
// -------------------------------------------------------------
324
connect(d->cimgLogoLabel, SIGNAL(leftClickedUrl(const QString&)),
325
this, SLOT(processCImgUrl(const QString&)));
327
connect(d->wInput, SIGNAL(valueChanged(int)),
328
this, SLOT(slotValuesChanged()));
330
connect(d->hInput, SIGNAL(valueChanged(int)),
331
this, SLOT(slotValuesChanged()));
333
connect(d->wpInput, SIGNAL(valueChanged(double)),
334
this, SLOT(slotValuesChanged()));
336
connect(d->hpInput, SIGNAL(valueChanged(double)),
337
this, SLOT(slotValuesChanged()));
339
connect(d->useGreycstorationBox, SIGNAL(toggled(bool)),
340
this, SLOT(slotRestorationToggled(bool)) );
342
// -------------------------------------------------------------
344
GreycstorationContainer defaults;
345
defaults.setResizeDefaultSettings();
346
d->settingsWidget->setDefaultSettings(defaults);
348
QTimer::singleShot(0, this, SLOT(slotResetSettings()));
351
ResizeTool::~ResizeTool()
356
void ResizeTool::readSettings()
358
KSharedConfig::Ptr config = KGlobal::config();
359
KConfigGroup group = config->group(d->configGroupName);
361
GreycstorationContainer prm;
362
GreycstorationContainer defaults;
363
defaults.setResizeDefaultSettings();
365
prm.fastApprox = group.readEntry(d->configFastApproxEntry, defaults.fastApprox);
366
prm.interp = group.readEntry(d->configInterpolationEntry, defaults.interp);
367
prm.amplitude = group.readEntry(d->configAmplitudeEntry, (double)defaults.amplitude);
368
prm.sharpness = group.readEntry(d->configSharpnessEntry, (double)defaults.sharpness);
369
prm.anisotropy = group.readEntry(d->configAnisotropyEntry, (double)defaults.anisotropy);
370
prm.alpha = group.readEntry(d->configAlphaEntry, (double)defaults.alpha);
371
prm.sigma = group.readEntry(d->configSigmaEntry, (double)defaults.sigma);
372
prm.gaussPrec = group.readEntry(d->configGaussPrecEntry, (double)defaults.gaussPrec);
373
prm.dl = group.readEntry(d->configDlEntry, (double)defaults.dl);
374
prm.da = group.readEntry(d->configDaEntry, (double)defaults.da);
375
prm.nbIter = group.readEntry(d->configIterationEntry, defaults.nbIter);
376
prm.tile = group.readEntry(d->configTileEntry, defaults.tile);
377
prm.btile = group.readEntry(d->configBTileEntry, defaults.btile);
378
d->settingsWidget->setSettings(prm);
381
void ResizeTool::writeSettings()
383
GreycstorationContainer prm = d->settingsWidget->settings();
384
KConfigGroup group = KGlobal::config()->group(d->configGroupName);
386
group.writeEntry(d->configFastApproxEntry, prm.fastApprox);
387
group.writeEntry(d->configInterpolationEntry, prm.interp);
388
group.writeEntry(d->configAmplitudeEntry, (double)prm.amplitude);
389
group.writeEntry(d->configSharpnessEntry, (double)prm.sharpness);
390
group.writeEntry(d->configAnisotropyEntry, (double)prm.anisotropy);
391
group.writeEntry(d->configAlphaEntry, (double)prm.alpha);
392
group.writeEntry(d->configSigmaEntry, (double)prm.sigma);
393
group.writeEntry(d->configGaussPrecEntry, (double)prm.gaussPrec);
394
group.writeEntry(d->configDlEntry, (double)prm.dl);
395
group.writeEntry(d->configDaEntry, (double)prm.da);
396
group.writeEntry(d->configIterationEntry, prm.nbIter);
397
group.writeEntry(d->configTileEntry, prm.tile);
398
group.writeEntry(d->configBTileEntry, prm.btile);
399
group.writeEntry("RestorePhotograph", d->useGreycstorationBox->isChecked());
403
void ResizeTool::slotResetSettings()
405
GreycstorationContainer prm;
406
prm.setResizeDefaultSettings();
408
d->settingsWidget->setSettings(prm);
409
d->useGreycstorationBox->setChecked(false);
410
slotRestorationToggled(d->useGreycstorationBox->isChecked());
412
blockWidgetSignals(true);
414
d->preserveRatioBox->setChecked(true);
415
d->wInput->slotReset();
416
d->hInput->slotReset();
417
d->wpInput->slotReset();
418
d->hpInput->slotReset();
420
blockWidgetSignals(false);
423
void ResizeTool::slotValuesChanged()
425
blockWidgetSignals(true);
427
QString s(sender()->objectName());
431
double val = d->wInput->value();
432
double pval = val / (double)(d->orgWidth) * 100.0;
434
d->wpInput->setValue(pval);
436
if (d->preserveRatioBox->isChecked())
438
int h = (int)(pval * d->orgHeight / 100);
440
d->hpInput->setValue(pval);
441
d->hInput->setValue(h);
444
else if (s == "hInput")
446
double val = d->hInput->value();
447
double pval = val / (double)(d->orgHeight) * 100.0;
449
d->hpInput->setValue(pval);
451
if (d->preserveRatioBox->isChecked())
453
int w = (int)(pval * d->orgWidth / 100);
455
d->wpInput->setValue(pval);
456
d->wInput->setValue(w);
459
else if (s == "wpInput")
461
double val = d->wpInput->value();
462
int w = (int)(val * d->orgWidth / 100);
464
d->wInput->setValue(w);
466
if (d->preserveRatioBox->isChecked())
468
int h = (int)(val * d->orgHeight / 100);
470
d->hpInput->setValue(val);
471
d->hInput->setValue(h);
474
else if (s == "hpInput")
476
double val = d->hpInput->value();
477
int h = (int)(val * d->orgHeight / 100);
479
d->hInput->setValue(h);
481
if (d->preserveRatioBox->isChecked())
483
int w = (int)(val * d->orgWidth / 100);
485
d->wpInput->setValue(val);
486
d->wInput->setValue(w);
490
d->prevW = d->wInput->value();
491
d->prevH = d->hInput->value();
492
d->prevWP = d->wpInput->value();
493
d->prevHP = d->hpInput->value();
495
blockWidgetSignals(false);
498
void ResizeTool::prepareEffect()
500
if (d->prevW != d->wInput->value() || d->prevH != d->hInput->value() ||
501
d->prevWP != d->wpInput->value() || d->prevHP != d->hpInput->value())
504
d->settingsWidget->setEnabled(false);
505
d->preserveRatioBox->setEnabled(false);
506
d->useGreycstorationBox->setEnabled(false);
507
d->wInput->setEnabled(false);
508
d->hInput->setEnabled(false);
509
d->wpInput->setEnabled(false);
510
d->hpInput->setEnabled(false);
512
ImageIface* iface = d->previewWidget->imageIface();
513
int w = iface->previewWidth();
514
int h = iface->previewHeight();
515
DImg imTemp = iface->getOriginalImg()->smoothScale(w, h, Qt::KeepAspectRatio);
516
int new_w = (int)(w*d->wpInput->value()/100.0);
517
int new_h = (int)(h*d->hpInput->value()/100.0);
519
if (d->useGreycstorationBox->isChecked())
521
setFilter(new GreycstorationFilter(&imTemp,
522
d->settingsWidget->settings(),
523
GreycstorationFilter::Resize,
530
// See B.K.O #152192: CImg resize() sound like defective or unadapted
531
// to resize image without good quality.
532
setFilter(new ResizeImage(&imTemp, new_w, new_h, this));
536
void ResizeTool::prepareFinal()
538
if (d->prevW != d->wInput->value() || d->prevH != d->hInput->value() ||
539
d->prevWP != d->wpInput->value() || d->prevHP != d->hpInput->value())
542
d->mainTab->setCurrentIndex(0);
543
d->settingsWidget->setEnabled(false);
544
d->preserveRatioBox->setEnabled(false);
545
d->useGreycstorationBox->setEnabled(false);
546
d->wInput->setEnabled(false);
547
d->hInput->setEnabled(false);
548
d->wpInput->setEnabled(false);
549
d->hpInput->setEnabled(false);
551
ImageIface iface(0, 0);
553
if (d->useGreycstorationBox->isChecked())
555
setFilter(new GreycstorationFilter(iface.getOriginalImg(),
556
d->settingsWidget->settings(),
557
GreycstorationFilter::Resize,
565
// See B.K.O #152192: CImg resize() sound like defective or unadapted
566
// to resize image without good quality.
567
setFilter(new ResizeImage(iface.getOriginalImg(),
574
void ResizeTool::putPreviewData()
576
ImageIface* iface = d->previewWidget->imageIface();
577
int w = iface->previewWidth();
578
int h = iface->previewHeight();
579
DImg imTemp = filter()->getTargetImage().smoothScale(w, h, Qt::KeepAspectRatio);
580
DImg imDest(w, h, filter()->getTargetImage().sixteenBit(), filter()->getTargetImage().hasAlpha());
582
QColor background = toolView()->backgroundRole();
583
imDest.fill(DColor(background, filter()->getTargetImage().sixteenBit()));
584
imDest.bitBltImage(&imTemp, (w-imTemp.width())/2, (h-imTemp.height())/2);
586
iface->putPreviewImage((imDest.smoothScale(iface->previewWidth(), iface->previewHeight())).bits());
587
d->previewWidget->updatePreview();
590
void ResizeTool::renderingFinished()
592
d->useGreycstorationBox->setEnabled(true);
593
d->settingsWidget->setEnabled(d->useGreycstorationBox->isChecked());
594
d->preserveRatioBox->setEnabled(true);
595
d->wInput->setEnabled(true);
596
d->hInput->setEnabled(true);
597
d->wpInput->setEnabled(true);
598
d->hpInput->setEnabled(true);
601
void ResizeTool::putFinalData()
603
ImageIface iface(0, 0);
604
DImg targetImage = filter()->getTargetImage();
605
iface.putOriginalImage(i18n("Resize"),
607
targetImage.width(), targetImage.height());
610
void ResizeTool::blockWidgetSignals(bool b)
612
d->preserveRatioBox->blockSignals(b);
613
d->wInput->blockSignals(b);
614
d->hInput->blockSignals(b);
615
d->wpInput->blockSignals(b);
616
d->hpInput->blockSignals(b);
619
void ResizeTool::slotRestorationToggled(bool b)
621
d->settingsWidget->setEnabled(b);
622
d->cimgLogoLabel->setEnabled(b);
623
toolSettings()->enableButton(EditorToolSettings::Load, b);
624
toolSettings()->enableButton(EditorToolSettings::SaveAs, b);
627
void ResizeTool::processCImgUrl(const QString& url)
629
KToolInvocation::invokeBrowser(url);
632
void ResizeTool::slotLoadSettings()
634
KUrl loadBlowupFile = KFileDialog::getOpenUrl(KGlobalSettings::documentPath(),
635
QString( "*" ), kapp->activeWindow(),
636
QString( i18n("Photograph Resizing Settings File to Load")) );
637
if ( loadBlowupFile.isEmpty() )
640
QFile file(loadBlowupFile.toLocalFile());
642
if ( file.open(QIODevice::ReadOnly) )
644
if (!d->settingsWidget->loadSettings(file, QString("# Photograph Resizing Configuration File")))
646
KMessageBox::error(kapp->activeWindow(),
647
i18n("\"%1\" is not a Photograph Resizing settings text file.",
648
loadBlowupFile.fileName()));
655
KMessageBox::error(kapp->activeWindow(),
656
i18n("Cannot load settings from the Photograph Resizing text file."));
662
void ResizeTool::slotSaveAsSettings()
664
KUrl saveBlowupFile = KFileDialog::getSaveUrl(KGlobalSettings::documentPath(),
665
QString( "*" ), kapp->activeWindow(),
666
QString( i18n("Photograph Resizing Settings File to Save")) );
667
if ( saveBlowupFile.isEmpty() )
670
QFile file(saveBlowupFile.toLocalFile());
672
if ( file.open(QIODevice::WriteOnly) )
673
d->settingsWidget->saveSettings(file, QString("# Photograph Resizing Configuration File"));
675
KMessageBox::error(kapp->activeWindow(), i18n("Cannot save settings to the Photograph Resizing text file."));
680
} // namespace DigikamTransformImagePlugin