diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-19 18:22:05 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-19 18:22:05 +0000 |
commit | 57e10fedbcb8c3e8c6590ff0935dbf017ce5587f (patch) | |
tree | 3000a7649ca4e40e43f9e7feed963236a0b0f56b /kipi-plugins/rawconverter | |
download | kipi-plugins-57e10fedbcb8c3e8c6590ff0935dbf017ce5587f.tar.gz kipi-plugins-57e10fedbcb8c3e8c6590ff0935dbf017ce5587f.zip |
Import abandoned KDE3 version of kipi plugins
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/libraries/kipi-plugins@1077221 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kipi-plugins/rawconverter')
29 files changed, 4160 insertions, 0 deletions
diff --git a/kipi-plugins/rawconverter/Makefile.am b/kipi-plugins/rawconverter/Makefile.am new file mode 100644 index 0000000..159dcb1 --- /dev/null +++ b/kipi-plugins/rawconverter/Makefile.am @@ -0,0 +1,28 @@ +METASOURCES = AUTO +SUBDIRS = profiles pics + +INCLUDES = $(KIPI_PLUGINS_COMMON_INCLUDE) $(LIBKIPI_CFLAGS) $(LIBKEXIV2_CFLAGS) \ + $(LIBKDCRAW_CFLAGS) $(all_includes) + +# Install this plugin in the KDE modules directory +kde_module_LTLIBRARIES = kipiplugin_rawconverter.la + +kipiplugin_rawconverter_la_DEPENDENCIES = $(LIBKIPI_LIBS_DEP) $(LIBKEXIV2_LIBS_DEP) $(LIBKDCRAW_LIBS_DEP) + +# Srcs for the plugin +kipiplugin_rawconverter_la_SOURCES = plugin_rawconverter.cpp rawdecodingiface.cpp savesettingswidget.cpp \ + actionthread.cpp previewwidget.cpp iccjpeg.c \ + batchdialog.cpp singledialog.cpp + +# Libs needed by the plugin +kipiplugin_rawconverter_la_LIBADD = -ljpeg -lpng $(LIBKEXIV2_LIBS) $(LIBKDCRAW_LIBS) $(LIBKIPI_LIBS) \ + $(LIB_TIFF) $(LIB_KIO) $(LIB_KDEUI) $(LIB_KDECORE) $(LIB_QT) + +# LD flags for the plugin +kipiplugin_rawconverter_la_LDFLAGS = $(KIPI_PLUGINS_COMMON_LDFLAGS) -module $(KDE_PLUGIN) $(all_libraries) -lkipiplugins + +# Install the desktop file needed to detect the plugin +kde_services_DATA = kipiplugin_rawconverter.desktop + +messages: rc.cpp + $(XGETTEXT) *.cpp *.h -o $(podir)/kipiplugin_rawconverter.pot diff --git a/kipi-plugins/rawconverter/actions.h b/kipi-plugins/rawconverter/actions.h new file mode 100644 index 0000000..5dd3e3e --- /dev/null +++ b/kipi-plugins/rawconverter/actions.h @@ -0,0 +1,63 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-12-09 + * Description : raw converter plugin action descriptions + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef ACTIONS_H +#define ACTIONS_H + +namespace KIPIRawConverterPlugin +{ + +enum Action +{ + NONE = 0, + IDENTIFY, + IDENTIFY_FULL, + PREVIEW, + PROCESS +}; + +class EventData +{ + +public: + + EventData() + { + starting = false; + success = false; + } + + bool starting; + bool success; + + QString filePath; + QString destPath; + QString message; + + QImage image; + + Action action; +}; + +} // NameSpace KIPIRawConverterPlugin + +#endif /* ACTIONS_H */ diff --git a/kipi-plugins/rawconverter/actionthread.cpp b/kipi-plugins/rawconverter/actionthread.cpp new file mode 100644 index 0000000..52a41b5 --- /dev/null +++ b/kipi-plugins/rawconverter/actionthread.cpp @@ -0,0 +1,267 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2003-12-03 + * Description : a class to manage plugin actions using threads + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * NOTE: Do not use kdDebug() in this implementation because + * it will be multithreaded. Use qDebug() instead. + * See B.K.O #133026 for details. + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include <unistd.h> +} + +// Qt includes. + +#include <qapplication.h> +#include <qdir.h> +#include <qdeepcopy.h> + +// KDE includes. + +#include <klocale.h> +#include <kstandarddirs.h> + +// LibKDcraw includes. + +#include <libkdcraw/dcrawinfocontainer.h> + +// Local includes. + +#include "actionthread.h" + +namespace KIPIRawConverterPlugin +{ + +ActionThread::ActionThread(QObject *parent) + : QThread(), m_parent(parent) +{ +} + +ActionThread::~ActionThread() +{ + // cancel the thread + cancel(); + // wait for the thread to finish + wait(); +} + +void ActionThread::identifyRawFile(const KURL& url, bool full) +{ + KURL::List oneFile; + oneFile.append(url); + identifyRawFiles(oneFile, full); +} + +void ActionThread::identifyRawFiles(const KURL::List& urlList, bool full) +{ + for (KURL::List::const_iterator it = urlList.begin(); + it != urlList.end(); ++it ) + { + Task *t = new Task; + t->filePath = QDeepCopy<QString>((*it).path()); //deep copy + t->action = full ? IDENTIFY_FULL : IDENTIFY; + m_taskQueue.enqueue(t); + } +} + +void ActionThread::processRawFile(const KURL& url) +{ + KURL::List oneFile; + oneFile.append(url); + processRawFiles(oneFile); +} + +void ActionThread::processHalfRawFile(const KURL& url) +{ + KURL::List oneFile; + oneFile.append(url); + processHalfRawFiles(oneFile); +} + +void ActionThread::setRawDecodingSettings(KDcrawIface::RawDecodingSettings rawDecodingSettings, + SaveSettingsWidget::OutputFormat outputFormat) +{ + m_rawDecodingSettings = rawDecodingSettings; + m_outputFormat = outputFormat; +} + +void ActionThread::processRawFiles(const KURL::List& urlList) +{ + for (KURL::List::const_iterator it = urlList.begin(); + it != urlList.end(); ++it ) + { + Task *t = new Task; + t->filePath = QDeepCopy<QString>((*it).path()); //deep copy + t->outputFormat = m_outputFormat; + t->decodingSettings = m_rawDecodingSettings; + t->action = PROCESS; + m_taskQueue.enqueue(t); + } +} + +void ActionThread::processHalfRawFiles(const KURL::List& urlList) +{ + for (KURL::List::const_iterator it = urlList.begin(); + it != urlList.end(); ++it ) + { + Task *t = new Task; + t->filePath = QDeepCopy<QString>((*it).path()); //deep copy + t->outputFormat = m_outputFormat; + t->decodingSettings = m_rawDecodingSettings; + t->action = PREVIEW; + m_taskQueue.enqueue(t); + } +} + +void ActionThread::cancel() +{ + m_taskQueue.flush(); + m_dcrawIface.cancel(); +} + +void ActionThread::run() +{ + while (!m_taskQueue.isEmpty()) + { + Task *t = m_taskQueue.dequeue(); + if (!t) continue; + + QString errString; + + EventData *d = new EventData; + + switch (t->action) + { + case IDENTIFY: + case IDENTIFY_FULL: + { + // Get embedded RAW file thumbnail. + QImage image; + m_dcrawIface.loadDcrawPreview(image, t->filePath); + + // Identify Camera model. + KDcrawIface::DcrawInfoContainer info; + m_dcrawIface.rawFileIdentify(info, t->filePath); + + QString identify = i18n("Cannot identify Raw image"); + if (info.isDecodable) + { + if (t->action == IDENTIFY) + identify = info.make + QString("-") + info.model; + else + { + identify = i18n("Make: %1\n").arg(info.make); + identify.append(i18n("Model: %1\n").arg(info.model)); + + if (info.dateTime.isValid()) + { + identify.append(i18n("Created: %1\n") + .arg(KGlobal::locale()->formatDateTime(info.dateTime, true, true))); + } + + if (info.aperture != -1.0) + { + identify.append(i18n("Aperture: f/%1\n").arg(QString::number(info.aperture))); + } + + if (info.focalLength != -1.0) + { + identify.append(i18n("Focal: %1 mm\n").arg(info.focalLength)); + } + + if (info.exposureTime != -1.0) + { + identify.append(i18n("Exposure: 1/%1 s\n").arg(info.exposureTime)); + } + + if (info.sensitivity != -1) + { + identify.append(i18n("Sensitivity: %1 ISO").arg(info.sensitivity)); + } + } + } + + EventData *r = new EventData; + r->action = t->action; + r->filePath = t->filePath; + r->image = image; + r->message = identify; + r->success = true; + QApplication::postEvent(m_parent, new QCustomEvent(QEvent::User, r)); + break; + } + + case PREVIEW: + { + d->action = PREVIEW; + d->filePath = t->filePath; + d->starting = true; + QApplication::postEvent(m_parent, new QCustomEvent(QEvent::User, d)); + + QString destPath; + bool result = m_dcrawIface.decodeHalfRAWImage(t->filePath, destPath, + t->outputFormat, t->decodingSettings); + + EventData *r = new EventData; + r->action = PREVIEW; + r->filePath = t->filePath; + r->destPath = destPath; + r->success = result; + QApplication::postEvent(m_parent, new QCustomEvent(QEvent::User, r)); + break; + } + + case PROCESS: + { + d->action = PROCESS; + d->filePath = t->filePath; + d->starting = true; + QApplication::postEvent(m_parent, new QCustomEvent(QEvent::User, d)); + + QString destPath; + bool result = m_dcrawIface.decodeRAWImage(t->filePath, destPath, + t->outputFormat, t->decodingSettings); + + EventData *r = new EventData; + r->action = PROCESS; + r->filePath = t->filePath; + r->destPath = destPath; + r->success = result; + QApplication::postEvent(m_parent, new QCustomEvent(QEvent::User, r)); + break; + } + + default: + { + qWarning("KIPIRawConverterPlugin:ActionThread: Unknown action specified"); + delete d; + } + } + + delete t; + } +} + +} // NameSpace KIPIRawConverterPlugin diff --git a/kipi-plugins/rawconverter/actionthread.h b/kipi-plugins/rawconverter/actionthread.h new file mode 100644 index 0000000..bebc7b9 --- /dev/null +++ b/kipi-plugins/rawconverter/actionthread.h @@ -0,0 +1,103 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2003-12-03 + * Description : a class to manage plugin actions using threads + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef ACTIONTHREAD_H +#define ACTIONTHREAD_H + +// Qt includes. + +#include <qthread.h> +#include <qstring.h> + +// KDE includes. + +#include <kurl.h> + +// LibKDcraw includes. + +#include <libkdcraw/rawdecodingsettings.h> + +// Local includes. + +#include "rawdecodingiface.h" +#include "savesettingswidget.h" +#include "actions.h" +#include "mtqueue.h" + +class QObject; + +namespace KIPIRawConverterPlugin +{ + +class ActionThread : public QThread +{ + +public: + + ActionThread(QObject *parent); + ~ActionThread(); + + void setRawDecodingSettings(KDcrawIface::RawDecodingSettings rawDecodingSettings, + SaveSettingsWidget::OutputFormat outputFormat); + + void identifyRawFile(const KURL& url, bool full=false); + void identifyRawFiles(const KURL::List& urlList, bool full=false); + + void processHalfRawFile(const KURL& url); + void processHalfRawFiles(const KURL::List& urlList); + + void processRawFile(const KURL& url); + void processRawFiles(const KURL::List& urlList); + + void cancel(); + +protected: + + void run(); + +private: + + struct Task_ + { + QString filePath; + Action action; + SaveSettingsWidget::OutputFormat outputFormat; + KDcrawIface::RawDecodingSettings decodingSettings; + }; + + typedef struct Task_ Task; + + QObject *m_parent; + + SaveSettingsWidget::OutputFormat m_outputFormat; + + KDcrawIface::RawDecodingSettings m_rawDecodingSettings; + + RawDecodingIface m_dcrawIface; + + MTQueue<Task> m_taskQueue; +}; + +} // NameSpace KIPIRawConverterPlugin + +#endif /* ACTIONTHREAD_H */ diff --git a/kipi-plugins/rawconverter/batchdialog.cpp b/kipi-plugins/rawconverter/batchdialog.cpp new file mode 100644 index 0000000..63447e6 --- /dev/null +++ b/kipi-plugins/rawconverter/batchdialog.cpp @@ -0,0 +1,661 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2003-10-24 + * Description : Raw converter batch dialog + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +} + +// C++ includes. + +#include <cstdio> + +// Qt includes. + +#include <qframe.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qtimer.h> +#include <qfileinfo.h> +#include <qevent.h> +#include <qpixmap.h> +#include <qpushbutton.h> +#include <qfile.h> + +// KDE includes. + +#include <kcursor.h> +#include <kdebug.h> +#include <klistview.h> +#include <klocale.h> +#include <kurl.h> +#include <kiconloader.h> +#include <kprogress.h> +#include <kio/renamedlg.h> +#include <kmessagebox.h> +#include <kconfig.h> +#include <kapplication.h> +#include <khelpmenu.h> +#include <kpopupmenu.h> +#include <kstandarddirs.h> + +// LibKDcraw includes. + +#include <libkdcraw/version.h> +#include <libkdcraw/dcrawsettingswidget.h> + +// Local includes. + +#include "kpaboutdata.h" +#include "pluginsversion.h" +#include "rawdecodingiface.h" +#include "savesettingswidget.h" +#include "actionthread.h" +#include "clistviewitem.h" +#include "batchdialog.h" +#include "batchdialog.moc" + +namespace KIPIRawConverterPlugin +{ + +BatchDialog::BatchDialog(QWidget* /*parent*/) + : KDialogBase(0, 0, false, i18n("Raw Images Batch Converter"), + Help|Default|User1|User2|Close, Close, true, + i18n("Con&vert"), i18n("&Abort")) +{ + m_currentConvertItem = 0; + m_thread = 0; + m_page = new QWidget( this ); + setMainWidget( m_page ); + QGridLayout *mainLayout = new QGridLayout(m_page, 2, 1, 0, spacingHint()); + + //--------------------------------------------- + + m_listView = new KListView(m_page); + m_listView->addColumn( i18n("Thumbnail") ); + m_listView->addColumn( i18n("Raw File") ); + m_listView->addColumn( i18n("Target File") ); + m_listView->addColumn( i18n("Camera") ); + m_listView->setResizeMode(QListView::AllColumns); + m_listView->setAllColumnsShowFocus(true); + m_listView->setSorting(-1); + m_listView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_listView->setSelectionMode(QListView::Single); + m_listView->setMinimumWidth(450); + + // --------------------------------------------------------------- + + m_decodingSettingsBox = new KDcrawIface::DcrawSettingsWidget(m_page, false, true, true); + m_saveSettingsBox = new SaveSettingsWidget(m_page); + +#if KDCRAW_VERSION >= 0x000105 + m_decodingSettingsBox->addItem(m_saveSettingsBox, i18n("Save settings")); + m_decodingSettingsBox->updateMinimumWidth(); +#else + m_decodingSettingsBox->insertTab(m_saveSettingsBox, i18n("Save settings")); +#endif + + m_progressBar = new KProgress(m_page); + m_progressBar->setMaximumHeight( fontMetrics().height()+2 ); + m_progressBar->hide(); + + mainLayout->addMultiCellWidget(m_listView, 0, 2, 0, 0); + mainLayout->addMultiCellWidget(m_decodingSettingsBox, 0, 0, 1, 1); + mainLayout->addMultiCellWidget(m_progressBar, 1, 1, 1, 1); + mainLayout->setColStretch(0, 10); + mainLayout->setRowStretch(2, 10); + + // --------------------------------------------------------------- + // About data and help button. + + m_about = new KIPIPlugins::KPAboutData(I18N_NOOP("RAW Image Converter"), + 0, + KAboutData::License_GPL, + I18N_NOOP("A Kipi plugin to batch convert Raw images"), + "(c) 2003-2005, Renchi Raju\n" + "(c) 2006-2008, Gilles Caulier"); + + m_about->addAuthor("Renchi Raju", I18N_NOOP("Original author"), + "renchi at pooh dot tam dot uiuc dot edu"); + + m_about->addAuthor("Gilles Caulier", I18N_NOOP("Maintainer"), + "caulier dot gilles at gmail dot com"); + + KHelpMenu* helpMenu = new KHelpMenu(this, m_about, false); + helpMenu->menu()->removeItemAt(0); + helpMenu->menu()->insertItem(i18n("Plugin Handbook"), + this, SLOT(slotHelp()), 0, -1, 0); + actionButton(Help)->setPopup( helpMenu->menu() ); + + // --------------------------------------------------------------- + + setButtonTip( User1, i18n("<p>Start converting the Raw images from current settings")); + setButtonTip( User2, i18n("<p>Abort the current Raw files conversion")); + setButtonTip( Close, i18n("<p>Exit Raw Converter")); + + m_blinkConvertTimer = new QTimer(this); + m_thread = new ActionThread(this); + + // --------------------------------------------------------------- + + connect(m_blinkConvertTimer, SIGNAL(timeout()), + this, SLOT(slotConvertBlinkTimerDone())); + + connect(m_saveSettingsBox, SIGNAL(signalSaveFormatChanged()), + this, SLOT(slotSaveFormatChanged())); + + // --------------------------------------------------------------- + + m_itemDict.setAutoDelete(true); + busy(false); + readSettings(); +} + +BatchDialog::~BatchDialog() +{ + delete m_about; + delete m_thread; +} + +void BatchDialog::closeEvent(QCloseEvent *e) +{ + if (!e) return; + m_blinkConvertTimer->stop(); + m_thread->cancel(); + saveSettings(); + e->accept(); +} + +void BatchDialog::slotClose() +{ + m_blinkConvertTimer->stop(); + m_thread->cancel(); + saveSettings(); + KDialogBase::slotClose(); +} + +void BatchDialog::slotDefault() +{ + m_decodingSettingsBox->setDefaultSettings(); + m_saveSettingsBox->setDefaultSettings(); +} + +void BatchDialog::readSettings() +{ + KConfig config("kipirc"); + config.setGroup("RawConverter Settings"); + + m_decodingSettingsBox->setWhiteBalance((KDcrawIface::RawDecodingSettings::WhiteBalance) + config.readNumEntry("White Balance", + KDcrawIface::RawDecodingSettings::CAMERA)); + m_decodingSettingsBox->setCustomWhiteBalance(config.readNumEntry("Custom White Balance", 6500)); + m_decodingSettingsBox->setCustomWhiteBalanceGreen(config.readDoubleNumEntry("Custom White Balance Green", 1.0)); + m_decodingSettingsBox->setFourColor(config.readBoolEntry("Four Color RGB", false)); + m_decodingSettingsBox->setUnclipColor(config.readNumEntry("Unclip Color", 0)); + m_decodingSettingsBox->setDontStretchPixels(config.readBoolEntry("Dont Stretch Pixels", false)); + m_decodingSettingsBox->setNoiseReduction(config.readBoolEntry("Use Noise Reduction", false)); + m_decodingSettingsBox->setBrightness(config.readDoubleNumEntry("Brightness Multiplier", 1.0)); + m_decodingSettingsBox->setUseBlackPoint(config.readBoolEntry("Use Black Point", false)); + m_decodingSettingsBox->setBlackPoint(config.readNumEntry("Black Point", 0)); +#if KDCRAW_VERSION >= 0x000105 + m_decodingSettingsBox->setUseWhitePoint(config.readBoolEntry("Use White Point", false)); + m_decodingSettingsBox->setWhitePoint(config.readNumEntry("White Point", 0)); + m_decodingSettingsBox->setMedianFilterPasses(config.readNumEntry("Median Filter Passes", 0)); +#endif + m_decodingSettingsBox->setNRThreshold(config.readNumEntry("NR Threshold", 100)); + m_decodingSettingsBox->setUseCACorrection(config.readBoolEntry("EnableCACorrection", false)); + m_decodingSettingsBox->setcaRedMultiplier(config.readDoubleNumEntry("caRedMultiplier", 1.0)); + m_decodingSettingsBox->setcaBlueMultiplier(config.readDoubleNumEntry("caBlueMultiplier", 1.0)); + + m_decodingSettingsBox->setQuality( + (KDcrawIface::RawDecodingSettings::DecodingQuality)config.readNumEntry("Decoding Quality", + (int)(KDcrawIface::RawDecodingSettings::BILINEAR))); + + m_decodingSettingsBox->setOutputColorSpace( + (KDcrawIface::RawDecodingSettings::OutputColorSpace)config.readNumEntry("Output Color Space", + (int)(KDcrawIface::RawDecodingSettings::SRGB))); + + m_saveSettingsBox->setFileFormat( + (SaveSettingsWidget::OutputFormat)config.readNumEntry("Output Format", + (int)(SaveSettingsWidget::OUTPUT_PNG))); + + m_saveSettingsBox->setConflictRule( + (SaveSettingsWidget::ConflictRule)config.readNumEntry("Conflict", + (int)(SaveSettingsWidget::OVERWRITE))); + + resize(configDialogSize(config, QString("Batch Raw Converter Dialog"))); +} + +void BatchDialog::saveSettings() +{ + KConfig config("kipirc"); + config.setGroup("RawConverter Settings"); + + config.writeEntry("White Balance", m_decodingSettingsBox->whiteBalance()); + config.writeEntry("Custom White Balance", m_decodingSettingsBox->customWhiteBalance()); + config.writeEntry("Custom White Balance Green", m_decodingSettingsBox->customWhiteBalanceGreen()); + config.writeEntry("Four Color RGB", m_decodingSettingsBox->useFourColor()); + config.writeEntry("Unclip Color", m_decodingSettingsBox->unclipColor()); + config.writeEntry("Dont Stretch Pixels", m_decodingSettingsBox->useDontStretchPixels()); + config.writeEntry("Use Noise Reduction", m_decodingSettingsBox->useNoiseReduction()); + config.writeEntry("Brightness Multiplier", m_decodingSettingsBox->brightness()); + config.writeEntry("Use Black Point", m_decodingSettingsBox->useBlackPoint()); + config.writeEntry("Black Point", m_decodingSettingsBox->blackPoint()); +#if KDCRAW_VERSION >= 0x000105 + config.writeEntry("Use White Point", m_decodingSettingsBox->useWhitePoint()); + config.writeEntry("White Point", m_decodingSettingsBox->whitePoint()); + config.writeEntry("Median Filter Passes", m_decodingSettingsBox->medianFilterPasses()); +#endif + config.writeEntry("NR Threshold", m_decodingSettingsBox->NRThreshold()); + config.writeEntry("EnableCACorrection", m_decodingSettingsBox->useCACorrection()); + config.writeEntry("caRedMultiplier", m_decodingSettingsBox->caRedMultiplier()); + config.writeEntry("caBlueMultiplier", m_decodingSettingsBox->caBlueMultiplier()); + config.writeEntry("Decoding Quality", (int)m_decodingSettingsBox->quality()); + config.writeEntry("Output Color Space", (int)m_decodingSettingsBox->outputColorSpace()); + + config.writeEntry("Output Format", (int)m_saveSettingsBox->fileFormat()); + config.writeEntry("Conflict", (int)m_saveSettingsBox->conflictRule()); + + saveDialogSize(config, QString("Batch Raw Converter Dialog")); + config.sync(); +} + +void BatchDialog::slotHelp() +{ + KApplication::kApplication()->invokeHelp("rawconverter", "kipi-plugins"); +} + +void BatchDialog::slotUser1() +{ + m_fileList.clear(); + + QListViewItemIterator it( m_listView ); + while ( it.current() ) + { + CListViewItem *item = (CListViewItem*) it.current(); + if (item->isEnabled()) + { + item->setPixmap(1, 0); + m_fileList.append(item->rawItem->directory + QString("/") + item->rawItem->src); + } + ++it; + } + + if (m_fileList.empty()) + { + KMessageBox::error(this, i18n("There is no Raw file to process in the list!")); + busy(false); + slotAborted(); + return; + } + + m_progressBar->setTotalSteps(m_fileList.count()); + m_progressBar->setProgress(0); + m_progressBar->show(); + + KDcrawIface::RawDecodingSettings rawDecodingSettings; + rawDecodingSettings.whiteBalance = m_decodingSettingsBox->whiteBalance(); + rawDecodingSettings.customWhiteBalance = m_decodingSettingsBox->customWhiteBalance(); + rawDecodingSettings.customWhiteBalanceGreen = m_decodingSettingsBox->customWhiteBalanceGreen(); + rawDecodingSettings.RGBInterpolate4Colors = m_decodingSettingsBox->useFourColor(); + rawDecodingSettings.unclipColors = m_decodingSettingsBox->unclipColor(); + rawDecodingSettings.DontStretchPixels = m_decodingSettingsBox->useDontStretchPixels(); + rawDecodingSettings.enableNoiseReduction = m_decodingSettingsBox->useNoiseReduction(); + rawDecodingSettings.brightness = m_decodingSettingsBox->brightness(); + rawDecodingSettings.enableBlackPoint = m_decodingSettingsBox->useBlackPoint(); + rawDecodingSettings.blackPoint = m_decodingSettingsBox->blackPoint(); +#if KDCRAW_VERSION >= 0x000105 + rawDecodingSettings.enableWhitePoint = m_decodingSettingsBox->useWhitePoint(); + rawDecodingSettings.whitePoint = m_decodingSettingsBox->whitePoint(); + rawDecodingSettings.medianFilterPasses = m_decodingSettingsBox->medianFilterPasses(); +#endif + rawDecodingSettings.NRThreshold = m_decodingSettingsBox->NRThreshold(); + rawDecodingSettings.enableCACorrection = m_decodingSettingsBox->useCACorrection(); + rawDecodingSettings.caMultiplier[0] = m_decodingSettingsBox->caRedMultiplier(); + rawDecodingSettings.caMultiplier[1] = m_decodingSettingsBox->caBlueMultiplier(); + rawDecodingSettings.RAWQuality = m_decodingSettingsBox->quality(); + rawDecodingSettings.outputColorSpace = m_decodingSettingsBox->outputColorSpace(); + + m_thread->setRawDecodingSettings(rawDecodingSettings, m_saveSettingsBox->fileFormat()); + processOne(); +} + +void BatchDialog::slotUser2() +{ + m_blinkConvertTimer->stop(); + m_fileList.clear(); + m_thread->cancel(); + busy(false); + + if (m_currentConvertItem) + m_currentConvertItem->viewItem->setPixmap(1, SmallIcon("cancel")); + + QTimer::singleShot(500, this, SLOT(slotAborted())); +} + +void BatchDialog::slotAborted() +{ + m_progressBar->setProgress(0); + m_progressBar->hide(); +} + +void BatchDialog::addItems(const QStringList& itemList) +{ + QString ext; + + switch(m_saveSettingsBox->fileFormat()) + { + case SaveSettingsWidget::OUTPUT_JPEG: + ext = "jpg"; + break; + case SaveSettingsWidget::OUTPUT_TIFF: + ext = "tif"; + break; + case SaveSettingsWidget::OUTPUT_PPM: + ext = "ppm"; + break; + case SaveSettingsWidget::OUTPUT_PNG: + ext = "png"; + break; + } + + KURL::List urlList; + + QPixmap pix(SmallIcon( "file_broken", KIcon::SizeLarge, KIcon::DisabledState )); + + for (QStringList::const_iterator it = itemList.begin(); + it != itemList.end(); ++it) + { + QFileInfo fi(*it); + if (fi.exists() && !m_itemDict.find(fi.fileName())) + { + RawItem *item = new RawItem; + item->directory = fi.dirPath(); + item->src = fi.fileName(); + item->dest = fi.baseName() + QString(".") + ext; + new CListViewItem(m_listView, pix, item, m_listView->lastItem()); + m_itemDict.insert(item->src, item); + urlList.append(fi.absFilePath()); + } + } + + if (!urlList.empty()) + { + m_thread->identifyRawFiles(urlList); + if (!m_thread->running()) + m_thread->start(); + } +} + +void BatchDialog::slotSaveFormatChanged() +{ + QString ext; + + switch(m_saveSettingsBox->fileFormat()) + { + case SaveSettingsWidget::OUTPUT_JPEG: + ext = "jpg"; + break; + case SaveSettingsWidget::OUTPUT_TIFF: + ext = "tif"; + break; + case SaveSettingsWidget::OUTPUT_PPM: + ext = "ppm"; + break; + case SaveSettingsWidget::OUTPUT_PNG: + ext = "png"; + break; + } + + QListViewItemIterator it( m_listView ); + while ( it.current() ) + { + CListViewItem *item = (CListViewItem*) it.current(); + if (item->isEnabled()) + { + RawItem *rawItem = item->rawItem; + QFileInfo fi(rawItem->directory + QString("/") + rawItem->src); + rawItem->dest = fi.baseName() + QString(".") + ext; + item->setText(2, rawItem->dest); + } + ++it; + } +} + +void BatchDialog::processOne() +{ + if (m_fileList.empty()) + { + busy(false); + slotAborted(); + return; + } + + QString file(m_fileList.first()); + m_fileList.pop_front(); + + m_thread->processRawFile(KURL(file)); + if (!m_thread->running()) + m_thread->start(); +} + +void BatchDialog::busy(bool busy) +{ + enableButton(User1, !busy); + enableButton(User2, busy); + enableButton(Close, !busy); + + m_decodingSettingsBox->setEnabled(!busy); + m_saveSettingsBox->setEnabled(!busy); + m_listView->setEnabled(!busy); + + busy ? m_page->setCursor(KCursor::waitCursor()) : m_page->unsetCursor(); +} + +void BatchDialog::slotConvertBlinkTimerDone() +{ + if(m_convertBlink) + { + if (m_currentConvertItem) + m_currentConvertItem->viewItem->setPixmap(1, SmallIcon("1rightarrow")); + } + else + { + if (m_currentConvertItem) + m_currentConvertItem->viewItem->setPixmap(1, SmallIcon("2rightarrow")); + } + + m_convertBlink = !m_convertBlink; + m_blinkConvertTimer->start(500); +} + +void BatchDialog::processing(const QString& file) +{ + QString filename = QFileInfo(file).fileName(); + m_currentConvertItem = m_itemDict.find(filename); + if (m_currentConvertItem) + { + m_listView->setSelected(m_currentConvertItem->viewItem, true); + m_listView->ensureItemVisible(m_currentConvertItem->viewItem); + } + + m_convertBlink = false; + m_blinkConvertTimer->start(500); +} + +void BatchDialog::processed(const QString& file, const QString& tmpFile) +{ + m_blinkConvertTimer->stop(); + QString filename = QFileInfo(file).fileName(); + QString destFile(m_currentConvertItem->directory + QString("/") + m_currentConvertItem->dest); + + if (m_saveSettingsBox->conflictRule() != SaveSettingsWidget::OVERWRITE) + { + struct stat statBuf; + if (::stat(QFile::encodeName(destFile), &statBuf) == 0) + { + KIO::RenameDlg dlg(this, i18n("Save Raw Image converted from '%1' as") + .arg(m_currentConvertItem->src), + tmpFile, destFile, + KIO::RenameDlg_Mode(KIO::M_SINGLE | KIO::M_OVERWRITE | KIO::M_SKIP)); + + switch (dlg.exec()) + { + case KIO::R_CANCEL: + case KIO::R_SKIP: + { + destFile = QString(); + m_currentConvertItem->viewItem->setPixmap(1, SmallIcon("cancel")); + break; + } + case KIO::R_RENAME: + { + destFile = dlg.newDestURL().path(); + break; + } + default: // Overwrite. + break; + } + } + } + + if (!destFile.isEmpty()) + { + if (::rename(QFile::encodeName(tmpFile), QFile::encodeName(destFile)) != 0) + { + KMessageBox::error(this, i18n("Failed to save image %1").arg( destFile )); + m_currentConvertItem->viewItem->setPixmap(1, SmallIcon("cancel")); + } + else + { + m_currentConvertItem->dest = QFileInfo(destFile).fileName(); + m_currentConvertItem->viewItem->setText(2, m_currentConvertItem->dest); + m_currentConvertItem->viewItem->setPixmap(1, SmallIcon("ok")); + } + } + + m_progressBar->advance(1); + m_currentConvertItem = 0; +} + +void BatchDialog::processingFailed(const QString& file) +{ + QString filename = QFileInfo(file).fileName(); + m_currentConvertItem->viewItem->setPixmap(1, SmallIcon("no")); + m_progressBar->advance(1); + m_currentConvertItem = 0; +} + +void BatchDialog::customEvent(QCustomEvent *event) +{ + if (!event) return; + + EventData *d = (EventData*) event->data(); + if (!d) return; + + QString text; + + if (d->starting) // Something have been started... + { + switch (d->action) + { + case(IDENTIFY): + break; + case(PROCESS): + { + busy(true); + processing(d->filePath); + break; + } + default: + { + kdWarning( 51000 ) << "KIPIRawConverterPlugin: Unknown event" << endl; + break; + } + } + } + else + { + if (!d->success) // Something is failed... + { + switch (d->action) + { + case(IDENTIFY): + break; + case(PROCESS): + { + processingFailed(d->filePath); + processOne(); + break; + } + default: + { + kdWarning( 51000 ) << "KIPIRawConverterPlugin: Unknown event" << endl; + break; + } + } + } + else // Something is done... + { + switch (d->action) + { + case(IDENTIFY): + { + QFileInfo fi(d->filePath); + RawItem *rawItem = m_itemDict.find(fi.fileName()); + if (rawItem) + { + if (!d->image.isNull()) + { + QPixmap pix = QPixmap(d->image.scale(64, 64, QImage::ScaleMin)); + rawItem->viewItem->setThumbnail(pix); + } + rawItem->viewItem->setText(3, d->message); + rawItem->identity = d->message; + } + break; + } + case(PROCESS): + { + processed(d->filePath, d->destPath); + processOne(); + break; + } + default: + { + kdWarning( 51000 ) << "KIPIRawConverterPlugin: Unknown event" << endl; + break; + } + } + } + } + + delete d; +} + +} // NameSpace KIPIRawConverterPlugin diff --git a/kipi-plugins/rawconverter/batchdialog.h b/kipi-plugins/rawconverter/batchdialog.h new file mode 100644 index 0000000..792fca6 --- /dev/null +++ b/kipi-plugins/rawconverter/batchdialog.h @@ -0,0 +1,130 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2003-10-24 + * Description : Raw converter batch dialog + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef BATCHDIALOG_H +#define BATCHDIALOG_H + +// Qt includes. + +#include <qstringlist.h> +#include <qdict.h> + +// KDE includes. + +#include <kdialogbase.h> + +// Local includes + +#include "kpaboutdata.h" + +class QTimer; +class QWidget; +class QCustomEvent; +class QCloseEvent; + +class KListView; +class KProgress; + +namespace KDcrawIface +{ +class DcrawSettingsWidget; +} + +namespace KIPIRawConverterPlugin +{ + +class ActionThread; +class SaveSettingsWidget; +struct RawItem; + +class BatchDialog : public KDialogBase +{ + +Q_OBJECT + +public: + + BatchDialog(QWidget *parent); + ~BatchDialog(); + + void addItems(const QStringList& itemList); + +protected: + + void customEvent(QCustomEvent *event); + void closeEvent(QCloseEvent *e); + +private: + + void readSettings(); + void saveSettings(); + + void busy(bool busy); + + void processOne(); + void processing(const QString& file); + void processed(const QString& file, const QString& tmpFile); + void processingFailed(const QString& file); + +private slots: + + void slotDefault(); + void slotClose(); + void slotHelp(); + void slotUser1(); + void slotUser2(); + void slotAborted(); + + void slotSaveFormatChanged(); + void slotConvertBlinkTimerDone(); + +private: + + bool m_convertBlink; + + QTimer *m_blinkConvertTimer; + + QWidget *m_page; + + QDict<RawItem> m_itemDict; + + QStringList m_fileList; + + KProgress *m_progressBar; + + KListView *m_listView; + + RawItem *m_currentConvertItem; + + ActionThread *m_thread; + + SaveSettingsWidget *m_saveSettingsBox; + + KDcrawIface::DcrawSettingsWidget *m_decodingSettingsBox; + + KIPIPlugins::KPAboutData *m_about; +}; + +} // NameSpace KIPIRawConverterPlugin + +#endif /* BATCHDIALOG_H */ diff --git a/kipi-plugins/rawconverter/clistviewitem.h b/kipi-plugins/rawconverter/clistviewitem.h new file mode 100644 index 0000000..f7e3065 --- /dev/null +++ b/kipi-plugins/rawconverter/clistviewitem.h @@ -0,0 +1,116 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2003-10-25 + * Description : Raw file list view used into batch converter. + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef CLISTVIEWITEM_H +#define CLISTVIEWITEM_H + +// Qt includes. + +#include <qstring.h> +#include <qpainter.h> + +// KDE includes. + +#include <klistview.h> + +class QPixmap; + +namespace KIPIRawConverterPlugin +{ + +class CListViewItem; + +struct RawItem +{ + QString src; + QString dest; + QString directory; + QString identity; + + CListViewItem *viewItem; +}; + +class CListViewItem : public KListViewItem +{ + +public: + + struct RawItem *rawItem; + +public: + + CListViewItem(KListView *view, const QPixmap& pixmap, + RawItem *item, QListViewItem *after) + : KListViewItem(view, after), rawItem(item) + { + rawItem->viewItem = this; + setThumbnail(pixmap); + setText(1, rawItem->src); + setText(2, rawItem->dest); + setEnabled(true); + } + + ~CListViewItem(){} + + void setThumbnail(const QPixmap& pixmap) + { + setPixmap(0, pixmap); + } + + void setEnabled(bool d) + { + m_enabled = d; + repaint(); + } + + bool isEnabled(void) + { + return m_enabled; + } + +protected: + + void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment) + { + if (m_enabled) + { + KListViewItem::paintCell(p, cg, column, width, alignment); + } + else + { + QColorGroup _cg( cg ); + QColor c = _cg.text(); + _cg.setColor( QColorGroup::Text, Qt::gray ); + KListViewItem::paintCell( p, _cg, column, width, alignment ); + _cg.setColor( QColorGroup::Text, c ); + } + } + +private: + + bool m_enabled; +}; + +} // NameSpace KIPIRawConverterPlugin + +#endif /* CLISTVIEWITEM_H */ diff --git a/kipi-plugins/rawconverter/iccjpeg.c b/kipi-plugins/rawconverter/iccjpeg.c new file mode 100644 index 0000000..fefa950 --- /dev/null +++ b/kipi-plugins/rawconverter/iccjpeg.c @@ -0,0 +1,270 @@ +/* + * Little cms + * Copyright (C) 1998-2004 Marti Maria + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * iccprofile.c + * + * This file provides code to read and write International Color Consortium + * (ICC) device profiles embedded in JFIF JPEG image files. The ICC has + * defined a standard format for including such data in JPEG "APP2" markers. + * The code given here does not know anything about the internal structure + * of the ICC profile data; it just knows how to put the profile data into + * a JPEG file being written, or get it back out when reading. + * + * This code depends on new features added to the IJG JPEG library as of + * IJG release 6b; it will not compile or work with older IJG versions. + * + * NOTE: this code would need surgery to work on 16-bit-int machines + * with ICC profiles exceeding 64K bytes in size. If you need to do that, + * change all the "unsigned int" variables to "INT32". You'll also need + * to find a malloc() replacement that can allocate more than 64K. + */ + +#include "iccjpeg.h" +#include <stdlib.h> /* define malloc() */ + + +/* + * Since an ICC profile can be larger than the maximum size of a JPEG marker + * (64K), we need provisions to split it into multiple markers. The format + * defined by the ICC specifies one or more APP2 markers containing the + * following data: + * Identifying string ASCII "ICC_PROFILE\0" (12 bytes) + * Marker sequence number 1 for first APP2, 2 for next, etc (1 byte) + * Number of markers Total number of APP2's used (1 byte) + * Profile data (remainder of APP2 data) + * Decoders should use the marker sequence numbers to reassemble the profile, + * rather than assuming that the APP2 markers appear in the correct sequence. + */ + +#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */ +#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */ +#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */ +#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN) + + +/* + * This routine writes the given ICC profile data into a JPEG file. + * It *must* be called AFTER calling jpeg_start_compress() and BEFORE + * the first call to jpeg_write_scanlines(). + * (This ordering ensures that the APP2 marker(s) will appear after the + * SOI and JFIF or Adobe markers, but before all else.) + */ + +void +write_icc_profile (j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len) +{ + unsigned int num_markers; /* total number of markers we'll write */ + int cur_marker = 1; /* per spec, counting starts at 1 */ + unsigned int length; /* number of bytes to write in this marker */ + + /* Calculate the number of markers we'll need, rounding up of course */ + num_markers = icc_data_len / MAX_DATA_BYTES_IN_MARKER; + if (num_markers * MAX_DATA_BYTES_IN_MARKER != icc_data_len) + num_markers++; + + while (icc_data_len > 0) { + /* length of profile to put in this marker */ + length = icc_data_len; + if (length > MAX_DATA_BYTES_IN_MARKER) + length = MAX_DATA_BYTES_IN_MARKER; + icc_data_len -= length; + + /* Write the JPEG marker header (APP2 code and marker length) */ + jpeg_write_m_header(cinfo, ICC_MARKER, + (unsigned int) (length + ICC_OVERHEAD_LEN)); + + /* Write the marker identifying string "ICC_PROFILE" (null-terminated). + * We code it in this less-than-transparent way so that the code works + * even if the local character set is not ASCII. + */ + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x5F); + jpeg_write_m_byte(cinfo, 0x50); + jpeg_write_m_byte(cinfo, 0x52); + jpeg_write_m_byte(cinfo, 0x4F); + jpeg_write_m_byte(cinfo, 0x46); + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x4C); + jpeg_write_m_byte(cinfo, 0x45); + jpeg_write_m_byte(cinfo, 0x0); + + /* Add the sequencing info */ + jpeg_write_m_byte(cinfo, cur_marker); + jpeg_write_m_byte(cinfo, (int) num_markers); + + /* Add the profile data */ + while (length--) { + jpeg_write_m_byte(cinfo, *icc_data_ptr); + icc_data_ptr++; + } + cur_marker++; + } +} + + +/* + * Prepare for reading an ICC profile + */ + +void +setup_read_icc_profile (j_decompress_ptr cinfo) +{ + /* Tell the library to keep any APP2 data it may find */ + jpeg_save_markers(cinfo, ICC_MARKER, 0xFFFF); +} + + +/* + * Handy subroutine to test whether a saved marker is an ICC profile marker. + */ + +static boolean +marker_is_icc (jpeg_saved_marker_ptr marker) +{ + return + marker->marker == ICC_MARKER && + marker->data_length >= ICC_OVERHEAD_LEN && + /* verify the identifying string */ + GETJOCTET(marker->data[0]) == 0x49 && + GETJOCTET(marker->data[1]) == 0x43 && + GETJOCTET(marker->data[2]) == 0x43 && + GETJOCTET(marker->data[3]) == 0x5F && + GETJOCTET(marker->data[4]) == 0x50 && + GETJOCTET(marker->data[5]) == 0x52 && + GETJOCTET(marker->data[6]) == 0x4F && + GETJOCTET(marker->data[7]) == 0x46 && + GETJOCTET(marker->data[8]) == 0x49 && + GETJOCTET(marker->data[9]) == 0x4C && + GETJOCTET(marker->data[10]) == 0x45 && + GETJOCTET(marker->data[11]) == 0x0; +} + + +/* + * See if there was an ICC profile in the JPEG file being read; + * if so, reassemble and return the profile data. + * + * TRUE is returned if an ICC profile was found, FALSE if not. + * If TRUE is returned, *icc_data_ptr is set to point to the + * returned data, and *icc_data_len is set to its length. + * + * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc() + * and must be freed by the caller with free() when the caller no longer + * needs it. (Alternatively, we could write this routine to use the + * IJG library's memory allocator, so that the data would be freed implicitly + * at jpeg_finish_decompress() time. But it seems likely that many apps + * will prefer to have the data stick around after decompression finishes.) + * + * NOTE: if the file contains invalid ICC APP2 markers, we just silently + * return FALSE. You might want to issue an error message instead. + */ + +boolean +read_icc_profile (j_decompress_ptr cinfo, + JOCTET **icc_data_ptr, + unsigned int *icc_data_len) +{ + jpeg_saved_marker_ptr marker; + int num_markers = 0; + int seq_no; + JOCTET *icc_data; + unsigned int total_length; +#define MAX_SEQ_NO 255 /* sufficient since marker numbers are bytes */ + char marker_present[MAX_SEQ_NO+1]; /* 1 if marker found */ + unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */ + unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */ + + *icc_data_ptr = NULL; /* avoid confusion if FALSE return */ + *icc_data_len = 0; + + /* This first pass over the saved markers discovers whether there are + * any ICC markers and verifies the consistency of the marker numbering. + */ + + for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++) + marker_present[seq_no] = 0; + + for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { + if (marker_is_icc(marker)) { + if (num_markers == 0) + num_markers = GETJOCTET(marker->data[13]); + else if (num_markers != GETJOCTET(marker->data[13])) + return FALSE; /* inconsistent num_markers fields */ + seq_no = GETJOCTET(marker->data[12]); + if (seq_no <= 0 || seq_no > num_markers) + return FALSE; /* bogus sequence number */ + if (marker_present[seq_no]) + return FALSE; /* duplicate sequence numbers */ + marker_present[seq_no] = 1; + data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN; + } + } + + if (num_markers == 0) + return FALSE; + + /* Check for missing markers, count total space needed, + * compute offset of each marker's part of the data. + */ + + total_length = 0; + for (seq_no = 1; seq_no <= num_markers; seq_no++) { + if (marker_present[seq_no] == 0) + return FALSE; /* missing sequence number */ + data_offset[seq_no] = total_length; + total_length += data_length[seq_no]; + } + + if (total_length <= 0) + return FALSE; /* found only empty markers? */ + + /* Allocate space for assembled data */ + icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET)); + if (icc_data == NULL) + return FALSE; /* oops, out of memory */ + + /* and fill it in */ + for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { + if (marker_is_icc(marker)) { + JOCTET FAR *src_ptr; + JOCTET *dst_ptr; + unsigned int length; + seq_no = GETJOCTET(marker->data[12]); + dst_ptr = icc_data + data_offset[seq_no]; + src_ptr = marker->data + ICC_OVERHEAD_LEN; + length = data_length[seq_no]; + while (length--) { + *dst_ptr++ = *src_ptr++; + } + } + } + + *icc_data_ptr = icc_data; + *icc_data_len = total_length; + + return TRUE; +} diff --git a/kipi-plugins/rawconverter/iccjpeg.h b/kipi-plugins/rawconverter/iccjpeg.h new file mode 100644 index 0000000..78e7fdc --- /dev/null +++ b/kipi-plugins/rawconverter/iccjpeg.h @@ -0,0 +1,96 @@ +/* + * Little cms + * Copyright (C) 1998-2004 Marti Maria + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * iccprofile.h + * + * This file provides code to read and write International Color Consortium + * (ICC) device profiles embedded in JFIF JPEG image files. The ICC has + * defined a standard format for including such data in JPEG "APP2" markers. + * The code given here does not know anything about the internal structure + * of the ICC profile data; it just knows how to put the profile data into + * a JPEG file being written, or get it back out when reading. + * + * This code depends on new features added to the IJG JPEG library as of + * IJG release 6b; it will not compile or work with older IJG versions. + * + * NOTE: this code would need surgery to work on 16-bit-int machines + * with ICC profiles exceeding 64K bytes in size. See iccprofile.c + * for details. + */ + +#include <stdio.h> /* needed to define "FILE", "NULL" */ +#include <jpeglib.h> + + +/** + * This routine writes the given ICC profile data into a JPEG file. + * It *must* be called AFTER calling jpeg_start_compress() and BEFORE + * the first call to jpeg_write_scanlines(). + * (This ordering ensures that the APP2 marker(s) will appear after the + * SOI and JFIF or Adobe markers, but before all else.) + */ + +extern void write_icc_profile JPP((j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len)); + + +/** + * Reading a JPEG file that may contain an ICC profile requires two steps: + * + * 1. After jpeg_create_decompress() but before jpeg_read_header(), + * call setup_read_icc_profile(). This routine tells the IJG library + * to save in memory any APP2 markers it may find in the file. + * + * 2. After jpeg_read_header(), call read_icc_profile() to find out + * whether there was a profile and obtain it if so. + */ + + +/** + * Prepare for reading an ICC profile + */ + +extern void setup_read_icc_profile JPP((j_decompress_ptr cinfo)); + + +/** + * See if there was an ICC profile in the JPEG file being read; + * if so, reassemble and return the profile data. + * + * TRUE is returned if an ICC profile was found, FALSE if not. + * If TRUE is returned, *icc_data_ptr is set to point to the + * returned data, and *icc_data_len is set to its length. + * + * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc() + * and must be freed by the caller with free() when the caller no longer + * needs it. (Alternatively, we could write this routine to use the + * IJG library's memory allocator, so that the data would be freed implicitly + * at jpeg_finish_decompress() time. But it seems likely that many apps + * will prefer to have the data stick around after decompression finishes.) + */ + +extern boolean read_icc_profile JPP((j_decompress_ptr cinfo, + JOCTET **icc_data_ptr, + unsigned int *icc_data_len)); diff --git a/kipi-plugins/rawconverter/kipiplugin_rawconverter.desktop b/kipi-plugins/rawconverter/kipiplugin_rawconverter.desktop new file mode 100644 index 0000000..88a3e0c --- /dev/null +++ b/kipi-plugins/rawconverter/kipiplugin_rawconverter.desktop @@ -0,0 +1,55 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=RawConverter +Name[ca]=Convertidor RAW +Name[cs]=RAW převod +Name[da]=Konvertering af ubehandlede billeder +Name[de]=Raw-Konvertierung +Name[el]=ΜετατροπέαςΑκατέργαστηςΜορφής +Name[es]=Conversor Raw +Name[et]=Toorpiltide teisendaja +Name[fi]=Raw-muunnin +Name[gl]=Conversor de RAW +Name[it]=ConvertitoreGrezzo +Name[nds]=Roh-Ümwanneln +Name[nl]=Raw-conversie +Name[pl]=Konwerter RAW +Name[pt]=Conversor de RAW +Name[sr]=Raw претварање +Name[sr@Latn]=Raw pretvaranje +Name[sv]=Konvertering av obehandlade bilder +Name[tg]=Ковертери Raw +Name[tr]=HamDönüştürücü +Name[xx]=xxRawConverterxx +Name[zh_CN]=原图转换器 +Comment=KIPI Raw Image Converter +Comment[ca]=Convertidor KIPI d'imatges RAW +Comment[cs]=KIPI převod RAW obrázků +Comment[da]=KIPI-plugin: Konvertering af ubehandlede billeder +Comment[de]=Ein KIPI-Modul zum Umwandeln von Bildern im Raw-Format +Comment[el]=Μετατροπέας του KIPI για εικόνες ανεπεξέργαστης μορφής +Comment[es]=Conversor de imágenes Raw para KIPI +Comment[et]=KIPI toorpiltide teisendaja +Comment[fi]=Kipi-liitännäinen raw-muotoisten kuvien muunnosta varten +Comment[fr]=Module externe KIPI pour convertir des images brutes +Comment[gl]=Conversor de Imaxes en Bruto de KIPI +Comment[is]=KIPI RAW myndbreytir +Comment[it]=Convertitore di immagini grezze di KIPI +Comment[ja]=Kipi RAW 画像コンバータ +Comment[nds]=KIPI-Moduul för't Ümwanneln vun Rohbiller +Comment[nl]=KIPI-plugin voor het converteren van raw-afbeeldingen +Comment[pa]=KIPI Raw ਚਿੱਤਰ ਤਬਦੀਲੀਕਾਰ +Comment[pl]=Wtyczka KIPI - Konwerter zdjęć RAW +Comment[pt]=Conversor de Imagens em Bruto do KIPI +Comment[pt_BR]=Conversor de Imagens Raw do KIPI +Comment[sr]=KIPI-јев претварач Raw слика +Comment[sr@Latn]=KIPI-jev pretvarač Raw slika +Comment[sv]=KIPI-insticksprogram: Konvertering av obehandlade bilder +Comment[tg]=Конвертери Тасвирҳои KIPI Raw +Comment[tr]=KIPI Ham Resim Dönüştürücü +Comment[xx]=xxKIPI Raw Image Converterxx +Comment[zh_CN]=KIPI 原始图像转换器 +Type=Service +ServiceTypes=KIPI/Plugin +X-KDE-Library=kipiplugin_rawconverter +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/kipi-plugins/rawconverter/mtqueue.h b/kipi-plugins/rawconverter/mtqueue.h new file mode 100644 index 0000000..14ff5f2 --- /dev/null +++ b/kipi-plugins/rawconverter/mtqueue.h @@ -0,0 +1,87 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-12-09 + * Description : Multithread queue description class + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef MTQUEUE_H +#define MTQUEUE_H + +// Qt includes. + +#include <qptrqueue.h> +#include <qmutex.h> + +namespace KIPIRawConverterPlugin +{ + +template<class Type> class MTQueue +{ + +public: + + MTQueue() + { + m_queue.setAutoDelete(true); + } + + ~MTQueue() + { + flush(); + } + + bool isEmpty() + { + m_mutex.lock(); + bool empty = m_queue.isEmpty(); + m_mutex.unlock(); + return empty; + } + + void flush() + { + m_mutex.lock(); + m_queue.clear(); + m_mutex.unlock(); + } + + void enqueue(Type * t) + { + m_mutex.lock(); + m_queue.enqueue(t); + m_mutex.unlock(); + } + + Type * dequeue() + { + m_mutex.lock(); + Type * i = m_queue.dequeue(); + m_mutex.unlock(); + return i; + } + +private: + + QPtrQueue<Type> m_queue; + QMutex m_mutex; +}; + +} // NameSpace KIPIRawConverterPlugin + +#endif // MTQUEUE_H diff --git a/kipi-plugins/rawconverter/pics/Makefile.am b/kipi-plugins/rawconverter/pics/Makefile.am new file mode 100644 index 0000000..773dcd4 --- /dev/null +++ b/kipi-plugins/rawconverter/pics/Makefile.am @@ -0,0 +1,2 @@ +kipirawconvertericondir = $(kde_datadir)/kipiplugin_rawconverter/icons +kipirawconvertericon_ICON = AUTO diff --git a/kipi-plugins/rawconverter/pics/hi128-action-rawconverterbatch.png b/kipi-plugins/rawconverter/pics/hi128-action-rawconverterbatch.png Binary files differnew file mode 100644 index 0000000..7c224e9 --- /dev/null +++ b/kipi-plugins/rawconverter/pics/hi128-action-rawconverterbatch.png diff --git a/kipi-plugins/rawconverter/pics/hi128-action-rawconvertersingle.png b/kipi-plugins/rawconverter/pics/hi128-action-rawconvertersingle.png Binary files differnew file mode 100644 index 0000000..e51c07c --- /dev/null +++ b/kipi-plugins/rawconverter/pics/hi128-action-rawconvertersingle.png diff --git a/kipi-plugins/rawconverter/plugin_rawconverter.cpp b/kipi-plugins/rawconverter/plugin_rawconverter.cpp new file mode 100644 index 0000000..e77a146 --- /dev/null +++ b/kipi-plugins/rawconverter/plugin_rawconverter.cpp @@ -0,0 +1,225 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2003-01-31 + * Description : a kipi plugin to convert Raw file in single + * or batch mode. + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// C ANSI Includes. + +extern "C" +{ +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +} + +// C++ includes. + +#include <cstdlib> + +// Qt Includes. + +#include <qprocess.h> +#include <qfileinfo.h> + +// KDE includes. + +#include <klocale.h> +#include <kaction.h> +#include <kapplication.h> +#include <kgenericfactory.h> +#include <klibloader.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kmessagebox.h> + +// LibKDcraw includes. + +#include <libkdcraw/version.h> +#include <libkdcraw/kdcraw.h> + +#if KDCRAW_VERSION < 0x000106 +#include <libkdcraw/dcrawbinary.h> +#endif + +// Local includes. + +#include "singledialog.h" +#include "batchdialog.h" +#include "plugin_rawconverter.h" +#include "plugin_rawconverter.moc" + +typedef KGenericFactory<Plugin_RawConverter> Factory; + +K_EXPORT_COMPONENT_FACTORY( kipiplugin_rawconverter, + Factory("kipiplugin_rawconverter")) + +Plugin_RawConverter::Plugin_RawConverter(QObject *parent, const char*, const QStringList&) + : KIPI::Plugin( Factory::instance(), parent, "RawConverter") +{ + kdDebug( 51001 ) << "Loaded RawConverter" << endl; +} + +void Plugin_RawConverter::setup( QWidget* widget ) +{ + KIPI::Plugin::setup( widget ); + singleAction_ = new KAction (i18n("Raw Image Converter..."), + "rawconvertersingle", + 0, + this, + SLOT(slotActivateSingle()), + actionCollection(), + "raw_converter_single"); + + batchAction_ = new KAction (i18n("Batch Raw Converter..."), + "rawconverterbatch", + 0, + this, + SLOT(slotActivateBatch()), + actionCollection(), + "raw_converter_batch"); + + addAction( singleAction_ ); + addAction( batchAction_ ); + + KIPI::Interface* interface = dynamic_cast<KIPI::Interface*>( parent() ); + + if ( !interface ) + { + kdError( 51000 ) << "Kipi interface is null!" << endl; + return; + } + + connect( interface, SIGNAL( selectionChanged( bool ) ), + singleAction_, SLOT( setEnabled( bool ) ) ); + + connect( interface, SIGNAL( currentAlbumChanged( bool ) ), + batchAction_, SLOT( setEnabled( bool ) ) ); +} + +Plugin_RawConverter::~Plugin_RawConverter() +{ +} + +bool Plugin_RawConverter::isRAWFile(const QString& filePath) +{ +#if KDCRAW_VERSION < 0x000106 + QString rawFilesExt(KDcrawIface::DcrawBinary::instance()->rawFiles()); +#else + QString rawFilesExt(KDcrawIface::KDcraw::rawFiles()); +#endif + + QFileInfo fileInfo(filePath); + if (rawFilesExt.upper().contains( fileInfo.extension(false).upper() )) + return true; + + return false; +} + +bool Plugin_RawConverter::checkBinaries() +{ +#if KDCRAW_VERSION < 0x000106 + KDcrawIface::DcrawBinary::instance()->checkSystem(); + KDcrawIface::DcrawBinary::instance()->checkReport(); + return KDcrawIface::DcrawBinary::instance()->isAvailable(); +#else + return true; +#endif +} + +void Plugin_RawConverter::slotActivateSingle() +{ + KIPI::Interface* interface = dynamic_cast<KIPI::Interface*>( parent() ); + + if (!interface) + { + kdError( 51000 ) << "Kipi interface is null!" << endl; + return; + } + + KIPI::ImageCollection images; + images = interface->currentSelection(); + + if (!images.isValid()) + return; + + if (!checkBinaries()) + return; + + if (!isRAWFile(images.images()[0].path())) + { + KMessageBox::error(kapp->activeWindow(), + i18n("\"%1\" is not a Raw file.").arg(images.images()[0].fileName())); + return; + } + + KIPIRawConverterPlugin::SingleDialog *converter = + new KIPIRawConverterPlugin::SingleDialog(images.images()[0].path(), + kapp->activeWindow()); + + converter->show(); +} + +void Plugin_RawConverter::slotActivateBatch() +{ + KIPI::Interface* interface = dynamic_cast<KIPI::Interface*>( parent() ); + + if (!interface) + { + kdError( 51000 ) << "Kipi interface is null!" << endl; + return; + } + + KIPI::ImageCollection images; + images = interface->currentSelection(); + + if (!images.isValid()) + return; + + if (!checkBinaries()) + return; + + KIPIRawConverterPlugin::BatchDialog *converter = + new KIPIRawConverterPlugin::BatchDialog(kapp->activeWindow()); + + KURL::List urls = images.images(); + QStringList files; + + for( KURL::List::Iterator it = urls.begin(); it != urls.end(); ++it ) + { + if (isRAWFile((*it).path())) + files.append( (*it).path() ); + } + + converter->addItems(files); + converter->show(); +} + +KIPI::Category Plugin_RawConverter::category( KAction* action ) const +{ + if ( action == singleAction_ ) + return KIPI::TOOLSPLUGIN; + else if ( action == batchAction_ ) + return KIPI::BATCHPLUGIN; + + kdWarning( 51000 ) << "Unrecognized action for plugin category identification" << endl; + return KIPI::TOOLSPLUGIN; // no warning from compiler, please +} diff --git a/kipi-plugins/rawconverter/plugin_rawconverter.h b/kipi-plugins/rawconverter/plugin_rawconverter.h new file mode 100644 index 0000000..3b159ca --- /dev/null +++ b/kipi-plugins/rawconverter/plugin_rawconverter.h @@ -0,0 +1,64 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2003-01-31 + * Description : a kipi plugin to convert Raw file in single + * or batch mode. + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef PLUGIN_RAWCONVERTER_H +#define PLUGIN_RAWCONVERTER_H + +// LibKIPi includes. + +#include <libkipi/plugin.h> + +class KAction; + +class Plugin_RawConverter : public KIPI::Plugin +{ + Q_OBJECT + +public: + + Plugin_RawConverter(QObject *parent, + const char* name, + const QStringList &args); + ~Plugin_RawConverter(); + + KIPI::Category category( KAction* action ) const; + virtual void setup( QWidget* widget ); + +private: + + bool checkBinaries(); + bool isRAWFile(const QString& filePath); + +private slots: + + void slotActivateSingle(); + void slotActivateBatch(); + +private: + + KAction *singleAction_; + KAction *batchAction_; +}; + +#endif /* PLUGIN_RAWCONVERTER_H */ diff --git a/kipi-plugins/rawconverter/previewwidget.cpp b/kipi-plugins/rawconverter/previewwidget.cpp new file mode 100644 index 0000000..e1958f5 --- /dev/null +++ b/kipi-plugins/rawconverter/previewwidget.cpp @@ -0,0 +1,198 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2003-10-22 + * Description : preview raw file widget used in single convert + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// Qt includes. + +#include <qpainter.h> +#include <qimage.h> +#include <qstring.h> +#include <qevent.h> +#include <qtimer.h> +#include <qfile.h> + +// KDE includes. + +#include <klocale.h> + +// Local includes. + +#include "previewwidget.h" +#include "previewwidget.moc" + +namespace KIPIRawConverterPlugin +{ + +class PreviewWidgetPriv +{ +public: + + PreviewWidgetPriv() + { + pix = 0; + timer = 0; + } + + QPixmap *pix; + QPixmap preview; + + QTimer *timer; + + QString text; + + QImage image; +}; + +PreviewWidget::PreviewWidget(QWidget *parent) + : QFrame(parent, 0, Qt::WRepaintNoErase) +{ + d = new PreviewWidgetPriv; + setFrameStyle(QFrame::GroupBoxPanel|QFrame::Plain); + setMargin(0); + setLineWidth(1); + + setMinimumSize(QSize(400, 300)); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + d->pix = new QPixmap(400, 300); + d->pix->fill(Qt::black); + + d->timer = new QTimer(this); + + connect(d->timer, SIGNAL(timeout()), + this, SLOT(slotResize())); +} + +PreviewWidget::~PreviewWidget() +{ + delete d; +} + +void PreviewWidget::load(const QString& file) +{ + d->text = ""; + d->pix->fill(Qt::black); + d->image.load(file); + + if (!d->image.isNull()) + { + QImage img = d->image.scale(width(),height(),QImage::ScaleMin); + int x = d->pix->width()/2 - img.width()/2; + int y = d->pix->height()/2 - img.height()/2; + + QPainter p(d->pix); + p.drawImage(x, y, img); + p.setPen(QPen(Qt::white)); + p.drawRect(x,y,img.width(),img.height()); + p.end(); + } + else + { + setInfo(i18n( "Failed to load image after processing" )); + return; + } + + update(); +} + +void PreviewWidget::setInfo(const QString& text, const QColor& color, const QPixmap& preview) +{ + d->text = text; + d->preview = preview; + d->pix->fill(Qt::black); + QPainter p(d->pix); + p.setPen(QPen(color)); + + if (!d->preview.isNull()) + { + p.drawPixmap(d->pix->width()/2-d->preview.width()/2, d->pix->height()/4-d->preview.height()/2, + d->preview, 0, 0, d->preview.width(), d->preview.height()); + p.drawText(0, d->pix->height()/2, d->pix->width(), d->pix->height()/2, + Qt::AlignCenter|Qt::WordBreak, d->text); + } + else + { + p.drawText(0, 0, d->pix->width(), d->pix->height(), + Qt::AlignCenter|Qt::WordBreak, d->text); + } + p.end(); + update(); +} + +void PreviewWidget::paintEvent(QPaintEvent *e) +{ + QRect r(e->rect()); + bitBlt(this, r.topLeft(), d->pix, r, Qt::CopyROP); +} + +void PreviewWidget::resizeEvent(QResizeEvent*) +{ + d->timer->start(10,true); +} + +void PreviewWidget::slotResize() +{ + if (d->timer->isActive()) return; + + d->pix->resize(width(),height()); + d->pix->fill(Qt::black); + if (!d->text.isEmpty()) + { + QPainter p(d->pix); + p.setPen(QPen(Qt::white)); + + if (!d->preview.isNull()) + { + p.drawPixmap(d->pix->width()/2-d->preview.width()/2, d->pix->height()/4-d->preview.height()/2, + d->preview, 0, 0, d->preview.width(), d->preview.height()); + p.drawText(0, d->pix->height()/2, d->pix->width(), d->pix->height()/2, + Qt::AlignCenter|Qt::WordBreak, d->text); + } + else + { + p.drawText(0, 0, d->pix->width(), d->pix->height(), + Qt::AlignCenter|Qt::WordBreak, d->text); + } + + p.end(); + } + else + { + if (!d->image.isNull()) + { + QImage img = d->image.scale(width(),height(), QImage::ScaleMin); + int x = d->pix->width()/2 - img.width()/2; + int y = d->pix->height()/2 - img.height()/2; + + QPainter p(d->pix); + p.drawImage(x, y, img); + p.setPen(QPen(Qt::white)); + p.drawRect(x,y,img.width(),img.height()); + p.end(); + } + } + + update(); +} + +} // NameSpace KIPIRawConverterPlugin + diff --git a/kipi-plugins/rawconverter/previewwidget.h b/kipi-plugins/rawconverter/previewwidget.h new file mode 100644 index 0000000..f1e6ca7 --- /dev/null +++ b/kipi-plugins/rawconverter/previewwidget.h @@ -0,0 +1,71 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2003-10-22 + * Description : preview raw file widget used in single convert + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + + +#ifndef PREVIEWWIDGET_H +#define PREVIEWWIDGET_H + +// Qt includes. + +#include <qcolor.h> +#include <qframe.h> +#include <qpixmap.h> +#include <qstring.h> + +class QPaintEvent; +class QResizeEvent; + +namespace KIPIRawConverterPlugin +{ +class PreviewWidgetPriv; + +class PreviewWidget : public QFrame +{ + Q_OBJECT + +public: + + PreviewWidget(QWidget *parent); + ~PreviewWidget(); + + void load(const QString& file); + void setInfo(const QString& text, const QColor& color=Qt::white, + const QPixmap& preview=QPixmap()); + +protected: + + void paintEvent(QPaintEvent *e); + void resizeEvent(QResizeEvent *e); + +private slots: + + void slotResize(); + +private: + + PreviewWidgetPriv* d; +}; + +} // NameSpace KIPIRawConverterPlugin + +#endif /* PREVIEWWIDGET_H */ diff --git a/kipi-plugins/rawconverter/profiles/Makefile.am b/kipi-plugins/rawconverter/profiles/Makefile.am new file mode 100644 index 0000000..30d7e5b --- /dev/null +++ b/kipi-plugins/rawconverter/profiles/Makefile.am @@ -0,0 +1,2 @@ +kipirawconverterprofilesdir = $(kde_datadir)/kipiplugin_rawconverter/profiles +kipirawconverterprofiles_DATA = adobergb.icm srgb.icm widegamut.icm prophoto.icm diff --git a/kipi-plugins/rawconverter/profiles/adobergb.icm b/kipi-plugins/rawconverter/profiles/adobergb.icm Binary files differnew file mode 100644 index 0000000..ae368c6 --- /dev/null +++ b/kipi-plugins/rawconverter/profiles/adobergb.icm diff --git a/kipi-plugins/rawconverter/profiles/prophoto.icm b/kipi-plugins/rawconverter/profiles/prophoto.icm Binary files differnew file mode 100644 index 0000000..a20d29b --- /dev/null +++ b/kipi-plugins/rawconverter/profiles/prophoto.icm diff --git a/kipi-plugins/rawconverter/profiles/srgb.icm b/kipi-plugins/rawconverter/profiles/srgb.icm Binary files differnew file mode 100644 index 0000000..878fdf1 --- /dev/null +++ b/kipi-plugins/rawconverter/profiles/srgb.icm diff --git a/kipi-plugins/rawconverter/profiles/widegamut.icm b/kipi-plugins/rawconverter/profiles/widegamut.icm Binary files differnew file mode 100644 index 0000000..120a99a --- /dev/null +++ b/kipi-plugins/rawconverter/profiles/widegamut.icm diff --git a/kipi-plugins/rawconverter/rawdecodingiface.cpp b/kipi-plugins/rawconverter/rawdecodingiface.cpp new file mode 100644 index 0000000..1a86bae --- /dev/null +++ b/kipi-plugins/rawconverter/rawdecodingiface.cpp @@ -0,0 +1,668 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-12-09 + * Description : RAW decoding interface + * + * Copyright (C) 2006-2008 by Marcel Wiesweg <marcel.wiesweg@gmx.de> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * NOTE: Do not use kdDebug() in this implementation because + * it will be multithreaded. Use qDebug() instead. + * See B.K.O #133026 for details. + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// C++ includes. + +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <cstdio> + +// C Ansi includes. + +extern "C" +{ +#include <unistd.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/types.h> +#include <jpeglib.h> +#include <tiffio.h> +#include <tiffvers.h> +#include "iccjpeg.h" +} + +// Qt Includes. + +#include <qcstring.h> +#include <qfileinfo.h> + +// KDE includes. + +#include <kstandarddirs.h> + +// LibKExiv2 includes. + +#include <libkexiv2/kexiv2.h> + +// Local includes. + +#include "pluginsversion.h" +#include "rawdecodingiface.h" +#include "rawdecodingiface.moc" + +namespace KIPIRawConverterPlugin +{ + +RawDecodingIface::RawDecodingIface() + : KDcrawIface::KDcraw() +{ +} + +RawDecodingIface::~RawDecodingIface() +{ +} + +QByteArray RawDecodingIface::getICCProfilFromFile(KDcrawIface::RawDecodingSettings::OutputColorSpace colorSpace) +{ + QString filePath; + KGlobal::dirs()->addResourceType("profiles", KGlobal::dirs()->kde_default("data") + + "kipiplugin_rawconverter/profiles"); + + switch(colorSpace) + { + case KDcrawIface::RawDecodingSettings::SRGB: + { + filePath = KGlobal::dirs()->findResourceDir("profiles", "srgb.icm"); + filePath.append("srgb.icm"); + break; + } + case KDcrawIface::RawDecodingSettings::ADOBERGB: + { + filePath = KGlobal::dirs()->findResourceDir("profiles", "adobergb.icm"); + filePath.append("adobergb.icm"); + break; + } + case KDcrawIface::RawDecodingSettings::WIDEGAMMUT: + { + filePath = KGlobal::dirs()->findResourceDir("profiles", "widegamut.icm"); + filePath.append("widegamut.icm"); + break; + } + case KDcrawIface::RawDecodingSettings::PROPHOTO: + { + filePath = KGlobal::dirs()->findResourceDir("profiles", "prophoto.icm"); + filePath.append("prophoto.icm"); + break; + } + default: + break; + } + + if ( filePath.isEmpty() ) + return QByteArray(); + + QFile file(filePath); + if ( !file.open(IO_ReadOnly) ) + return QByteArray(); + + QByteArray data(file.size()); + QDataStream stream( &file ); + stream.readRawBytes(data.data(), data.size()); + file.close(); + return data; +} + +bool RawDecodingIface::decodeHalfRAWImage(const QString& filePath, + QString& destPath, SaveSettingsWidget::OutputFormat outputFileFormat, + KDcrawIface::RawDecodingSettings rawDecodingSettings) +{ + int width, height, rgbmax; + QByteArray imageData; + if (!KDcrawIface::KDcraw::decodeHalfRAWImage(filePath, rawDecodingSettings, + imageData, width, height, rgbmax)) + return false; + + return (loadedFromDcraw(filePath, destPath, outputFileFormat, + imageData, width, height)); +} + +bool RawDecodingIface::decodeRAWImage(const QString& filePath, + QString& destPath, SaveSettingsWidget::OutputFormat outputFileFormat, + KDcrawIface::RawDecodingSettings rawDecodingSettings) +{ + int width, height, rgbmax; + QByteArray imageData; + if (!KDcrawIface::KDcraw::decodeRAWImage(filePath, rawDecodingSettings, + imageData, width, height, rgbmax)) + return false; + + return (loadedFromDcraw(filePath, destPath, outputFileFormat, + imageData, width, height)); +} + +// ---------------------------------------------------------------------------------- + +bool RawDecodingIface::loadedFromDcraw(const QString& filePath, + QString& destPath, SaveSettingsWidget::OutputFormat outputFileFormat, + const QByteArray& imageData, int width, int height) +{ + // -- Use a QImage instance to write IPTC preview and Exif thumbnail ------- + + QImage img(width, height, 32); + uchar* dptr = img.bits(); + uchar* sptr = (uchar*)imageData.data(); + + // Set RGB color components. + for (int i = 0 ; i < width * height ; i++) + { + dptr[0] = sptr[2]; + dptr[1] = sptr[1]; + dptr[2] = sptr[0]; + dptr[3] = 0xFF; + dptr += 4; + sptr += 3; + } + + QImage iptcPreview = img.scale(1280, 1024, QImage::ScaleMin); + QImage exifThumbnail = iptcPreview.scale(160, 120, QImage::ScaleMin); + + // -- Write image data into destination file ------------------------------- + + QByteArray ICCColorProfile = getICCProfilFromFile(m_rawDecodingSettings.outputColorSpace); + QString soft = QString("Kipi-plugins v.%1").arg(kipiplugins_version); + QFileInfo fi(filePath); + destPath = fi.dirPath(true) + QString("/") + ".kipi-rawconverter-tmp-" + + QString::number(::time(0)); + + // Metadata restoration and update. + KExiv2Iface::KExiv2 exiv2Iface; + exiv2Iface.load(filePath); + exiv2Iface.setImageProgramId(QString("Kipi-plugins"), QString(kipiplugins_version)); + exiv2Iface.setImageDimensions(QSize(width, height)); + exiv2Iface.setExifThumbnail(exifThumbnail); + + // Update Iptc preview. + // NOTE: see B.K.O #130525. a JPEG segment is limited to 64K. If the IPTC byte array is + // bigger than 64K duing of image preview tag size, the target JPEG image will be + // broken. Note that IPTC image preview tag is limited to 256K!!! + // There is no limitation with TIFF and PNG about IPTC byte array size. + if (outputFileFormat != SaveSettingsWidget::OUTPUT_JPEG) + exiv2Iface.setImagePreview(iptcPreview); + + exiv2Iface.setExifTagString("Exif.Image.DocumentName", fi.fileName()); + + switch(outputFileFormat) + { + case SaveSettingsWidget::OUTPUT_JPEG: + { + FILE* f = 0; + f = fopen(QFile::encodeName(destPath), "wb"); + + if (!f) + { + qDebug("Failed to open JPEG file for writing"); + return false; + } + + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + + int row_stride; + JSAMPROW row_pointer[1]; + + // Init JPEG compressor. + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + jpeg_stdio_dest(&cinfo, f); + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults(&cinfo); + + // B.K.O #149578: set encoder horizontal and vertical chroma subsampling + // factor to 2x1, 1x1, 1x1 (4:2:2) : Medium subsampling. + // See this page for details: http://en.wikipedia.org/wiki/Chroma_subsampling + cinfo.comp_info[0].h_samp_factor = 2; + cinfo.comp_info[0].v_samp_factor = 1; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + + // B.K.O #154273: use 99 compresion level instead 100 to reduce output JPEG file size. + jpeg_set_quality(&cinfo, 99, true); + jpeg_start_compress(&cinfo, true); + + // Write ICC color profil. + if (!ICCColorProfile.isEmpty()) + write_icc_profile (&cinfo, (JOCTET *)ICCColorProfile.data(), ICCColorProfile.size()); + + // Write image data + row_stride = cinfo.image_width * 3; + while (!m_cancel && (cinfo.next_scanline < cinfo.image_height)) + { + row_pointer[0] = (uchar*)imageData.data() + (cinfo.next_scanline * row_stride); + jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + fclose(f); + + exiv2Iface.save(destPath); + break; + } + + case SaveSettingsWidget::OUTPUT_PNG: + { + FILE* f = 0; + f = fopen(QFile::encodeName(destPath), "wb"); + + if (!f) + { + qDebug("Failed to open PNG file for writing"); + return false; + } + + png_color_8 sig_bit; + png_bytep row_ptr; + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_infop info_ptr = png_create_info_struct(png_ptr); + png_init_io(png_ptr, f); + png_set_IHDR(png_ptr, info_ptr, width, height, 8, + PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + sig_bit.red = 8; + sig_bit.green = 8; + sig_bit.blue = 8; + sig_bit.alpha = 8; + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + png_set_compression_level(png_ptr, 9); + + // Write ICC profil. + if (!ICCColorProfile.isEmpty()) + { + png_set_iCCP(png_ptr, info_ptr, "icc", PNG_COMPRESSION_TYPE_BASE, + ICCColorProfile.data(), ICCColorProfile.size()); + } + + QString libpngver(PNG_HEADER_VERSION_STRING); + libpngver.replace('\n', ' '); + soft.append(QString(" (%1)").arg(libpngver)); + png_text text; + text.key = "Software"; + text.text = (char *)soft.ascii(); + text.compression = PNG_TEXT_COMPRESSION_zTXt; + png_set_text(png_ptr, info_ptr, &(text), 1); + + // Store Exif data. + QByteArray ba = exiv2Iface.getExif(); + const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; + QByteArray profile = QByteArray(ba.size() + sizeof(ExifHeader)); + memcpy(profile.data(), ExifHeader, sizeof(ExifHeader)); + memcpy(profile.data()+sizeof(ExifHeader), ba.data(), ba.size()); + writeRawProfile(png_ptr, info_ptr, "exif", profile.data(), (png_uint_32) profile.size()); + + // Store Iptc data. + QByteArray ba2 = exiv2Iface.getIptc(); + writeRawProfile(png_ptr, info_ptr, "iptc", ba2.data(), (png_uint_32) ba2.size()); + + png_write_info(png_ptr, info_ptr); + png_set_shift(png_ptr, &sig_bit); + png_set_packing(png_ptr); + unsigned char* ptr = (unsigned char*)imageData.data(); + + for (int y = 0; !m_cancel && (y < height); y++) + { + row_ptr = (png_bytep) ptr; + png_write_rows(png_ptr, &row_ptr, 1); + ptr += (width * 3); + } + + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); + png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); + fclose(f); + break; + } + + case SaveSettingsWidget::OUTPUT_TIFF: + { + TIFF *tif=0; + unsigned char *data=0; + int y; + int w; + + tif = TIFFOpen(QFile::encodeName(destPath), "wb"); + + if (!tif) + { + qDebug("Failed to open TIFF file for writing"); + return false; + } + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE); + TIFFSetField(tif, TIFFTAG_ZIPQUALITY, 9); + // NOTE : this tag values aren't defined in libtiff 3.6.1. '2' is PREDICTOR_HORIZONTAL. + // Use horizontal differencing for images which are + // likely to be continuous tone. The TIFF spec says that this + // usually leads to better compression. + // See this url for more details: + // http://www.awaresystems.be/imaging/tiff/tifftags/predictor.html + TIFFSetField(tif, TIFFTAG_PREDICTOR, 2); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + w = TIFFScanlineSize(tif); + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0)); + + // Store Iptc data. + QByteArray ba2 = exiv2Iface.getIptc(true); +#if defined(TIFFTAG_PHOTOSHOP) + TIFFSetField (tif, TIFFTAG_PHOTOSHOP, (uint32)ba2.size(), (uchar *)ba2.data()); +#endif + + QString libtiffver(TIFFLIB_VERSION_STR); + libtiffver.replace('\n', ' '); + soft.append(QString(" ( %1 )").arg(libtiffver)); + TIFFSetField(tif, TIFFTAG_SOFTWARE, (const char*)soft.ascii()); + + // Write ICC profil. + if (!ICCColorProfile.isEmpty()) + { +#if defined(TIFFTAG_ICCPROFILE) + TIFFSetField(tif, TIFFTAG_ICCPROFILE, (uint32)ICCColorProfile.size(), + (uchar *)ICCColorProfile.data()); +#endif + } + + // Write full image data in tiff directory IFD0 + + for (y = 0; !m_cancel && (y < height); y++) + { + data = (unsigned char*)imageData.data() + (y * width * 3); + TIFFWriteScanline(tif, data, y, 0); + } + + TIFFWriteDirectory(tif); + + // Write thumbnail in tiff directory IFD1 + + QImage thumb = exiv2Iface.getExifThumbnail(false); + if (!thumb.isNull()) + { + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32)thumb.width()); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32)thumb.height()); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE); + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0)); + + int i=0; + uint32 x, y; + uchar *pixelThumb; + uchar *dataThumb = thumb.bits(); + uint8 *bufThumb = (uint8 *) _TIFFmalloc(TIFFScanlineSize(tif)); + + if (!bufThumb) + { + qDebug("Cannot allocate memory buffer for TIFF thumbnail."); + TIFFClose(tif); + return false; + } + + for (y = 0 ; y < uint32(thumb.height()) ; y++) + { + i = 0; + + for (x = 0 ; x < uint32(thumb.width()) ; x++) + { + pixelThumb = &dataThumb[((y * thumb.width()) + x) * 4]; + + // This might be endian dependent + bufThumb[i++] = (uint8)pixelThumb[2]; + bufThumb[i++] = (uint8)pixelThumb[1]; + bufThumb[i++] = (uint8)pixelThumb[0]; + } + + if (!TIFFWriteScanline(tif, bufThumb, y, 0)) + { + qDebug("Cannot write TIFF thumbnail to target file."); + _TIFFfree(bufThumb); + TIFFClose(tif); + return false; + } + } + + _TIFFfree(bufThumb); + } + TIFFClose(tif); + + // Store metadata (Exiv2 0.18 support tiff writting mode) + exiv2Iface.save(destPath); + + break; + } + + case SaveSettingsWidget::OUTPUT_PPM: + { + FILE* f = fopen(QFile::encodeName(destPath), "wb"); + if (!f) + { + qDebug("Failed to open ppm file for writing"); + return false; + } + + fprintf(f, "P6\n%d %d\n255\n", width, height); + fwrite(imageData.data(), 1, width*height*3, f); + fclose(f); + break; + } + default: + { + qDebug("Invalid output file format"); + return false; + } + } + + if (m_cancel) + { + ::remove(QFile::encodeName(destPath)); + return false; + } + + return true; +} + +//---------------------------------------------------------------------------------- + +void RawDecodingIface::writeRawProfile(png_struct *ping, png_info *ping_info, char *profile_type, + char *profile_data, png_uint_32 length) +{ + png_textp text; + + register long i; + + uchar *sp; + + png_charp dp; + + png_uint_32 allocated_length, description_length; + + const uchar hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + + qDebug("Writing Raw profile: type=%s, length=%i", profile_type, (int)length); + + text = (png_textp) png_malloc(ping, (png_uint_32) sizeof(png_text)); + description_length = strlen((const char *) profile_type); + allocated_length = (png_uint_32) (length*2 + (length >> 5) + 20 + description_length); + + text[0].text = (png_charp) png_malloc(ping, allocated_length); + text[0].key = (png_charp) png_malloc(ping, (png_uint_32) 80); + text[0].key[0] = '\0'; + + concatenateString(text[0].key, "Raw profile type ", 4096); + concatenateString(text[0].key, (const char *) profile_type, 62); + + sp = (uchar*)profile_data; + dp = text[0].text; + *dp++='\n'; + + copyString(dp, (const char *) profile_type, allocated_length); + + dp += description_length; + *dp++='\n'; + + formatString(dp, allocated_length-strlen(text[0].text), "%8lu ", length); + + dp += 8; + + for (i=0; i < (long) length; i++) + { + if (i%36 == 0) + *dp++='\n'; + + *(dp++)=(char) hex[((*sp >> 4) & 0x0f)]; + *(dp++)=(char) hex[((*sp++ ) & 0x0f)]; + } + + *dp++='\n'; + *dp='\0'; + text[0].text_length = (png_size_t) (dp-text[0].text); + text[0].compression = -1; + + if (text[0].text_length <= allocated_length) + png_set_text(ping, ping_info,text, 1); + + png_free(ping, text[0].text); + png_free(ping, text[0].key); + png_free(ping, text); +} + +size_t RawDecodingIface::concatenateString(char *destination, const char *source, const size_t length) +{ + register char *q; + + register const char *p; + + register size_t i; + + size_t count; + + if ( !destination || !source || length == 0 ) + return 0; + + p = source; + q = destination; + i = length; + + while ((i-- != 0) && (*q != '\0')) + q++; + + count = (size_t) (q-destination); + i = length-count; + + if (i == 0) + return(count+strlen(p)); + + while (*p != '\0') + { + if (i != 1) + { + *q++=(*p); + i--; + } + p++; + } + + *q='\0'; + + return(count+(p-source)); +} + +size_t RawDecodingIface::copyString(char *destination, const char *source, const size_t length) +{ + register char *q; + + register const char *p; + + register size_t i; + + if ( !destination || !source || length == 0 ) + return 0; + + p = source; + q = destination; + i = length; + + if ((i != 0) && (--i != 0)) + { + do + { + if ((*q++=(*p++)) == '\0') + break; + } + while (--i != 0); + } + + if (i == 0) + { + if (length != 0) + *q='\0'; + + do + { + } + while (*p++ != '\0'); + } + + return((size_t) (p-source-1)); +} + +long RawDecodingIface::formatString(char *string, const size_t length, const char *format,...) +{ + long n; + + va_list operands; + + va_start(operands,format); + n = (long) formatStringList(string, length, format, operands); + va_end(operands); + return(n); +} + +long RawDecodingIface::formatStringList(char *string, const size_t length, const char *format, va_list operands) +{ + int n = vsnprintf(string, length, format, operands); + + if (n < 0) + string[length-1] = '\0'; + + return((long) n); +} + +} // namespace KIPIRawConverterPlugin diff --git a/kipi-plugins/rawconverter/rawdecodingiface.h b/kipi-plugins/rawconverter/rawdecodingiface.h new file mode 100644 index 0000000..6233447 --- /dev/null +++ b/kipi-plugins/rawconverter/rawdecodingiface.h @@ -0,0 +1,101 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-12-09 + * Description : RAW decoding interface + * + * Copyright (C) 2006-2008 by Marcel Wiesweg <marcel.wiesweg@gmx.de> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef RAW_DECODING_IFACE_H +#define RAW_DECODING_IFACE_H + +// C++ includes. + +#include <cstdarg> + +// LibPNG includes. + +extern "C" +{ +#include <png.h> +} + + +// Qt Includes. + +#include <qstring.h> +#include <qobject.h> +#include <qimage.h> + +// LibKDcraw includes. + +#include <libkdcraw/kdcraw.h> +#include <libkdcraw/rawdecodingsettings.h> + +// Local includes. + +#include "savesettingswidget.h" + +namespace KIPIRawConverterPlugin +{ + +class RawDecodingIface : public KDcrawIface::KDcraw +{ + Q_OBJECT + +public: + + RawDecodingIface(); + ~RawDecodingIface(); + +public: + + /** Extract a small size of decode RAW data in 8 bits/color/pixels + using sRGB color space. + */ + bool decodeHalfRAWImage(const QString& filePath, + QString& destPath, SaveSettingsWidget::OutputFormat outputFileFormat, + KDcrawIface::RawDecodingSettings rawDecodingSettings); + + /** Extract a full size of RAW data in 8 bits/color/pixels using + sRGB color space. + */ + bool decodeRAWImage(const QString& filePath, + QString& destPath, SaveSettingsWidget::OutputFormat outputFileFormat, + KDcrawIface::RawDecodingSettings rawDecodingSettings); + +private: + + QByteArray getICCProfilFromFile(KDcrawIface::RawDecodingSettings::OutputColorSpace colorSpace); + + bool loadedFromDcraw(const QString& filePath, + QString& destPath, SaveSettingsWidget::OutputFormat outputFileFormat, + const QByteArray& imageData, int width, int height); + + void writeRawProfile(png_struct *ping, png_info *ping_info, char *profile_type, + char *profile_data, png_uint_32 length); + + size_t concatenateString(char *destination, const char *source, const size_t length); + size_t copyString(char *destination, const char *source, const size_t length); + long formatString(char *string, const size_t length, const char *format,...); + long formatStringList(char *string, const size_t length, const char *format, va_list operands); +}; + +} // namespace KIPIRawConverterPlugin + +#endif /* RAW_DECODING_IFACE_H */ diff --git a/kipi-plugins/rawconverter/savesettingswidget.cpp b/kipi-plugins/rawconverter/savesettingswidget.cpp new file mode 100644 index 0000000..8531863 --- /dev/null +++ b/kipi-plugins/rawconverter/savesettingswidget.cpp @@ -0,0 +1,153 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-13 + * Description : save settings widgets + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// Qt includes. + +#include <qcombobox.h> +#include <qvbuttongroup.h> +#include <qradiobutton.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qwhatsthis.h> +#include <qstring.h> + +// KDE includes. + +#include <kdialog.h> +#include <klocale.h> + +// Local includes. + +#include "savesettingswidget.h" +#include "savesettingswidget.moc" + +namespace KIPIRawConverterPlugin +{ + +class SaveSettingsWidgetPriv +{ +public: + + SaveSettingsWidgetPriv() + { + formatLabel = 0; + conflictLabel = 0; + conflictButtonGroup = 0; + formatComboBox = 0; + overwriteButton = 0; + promptButton = 0; + } + + QLabel *formatLabel; + QLabel *conflictLabel; + + QVButtonGroup *conflictButtonGroup; + + QComboBox *formatComboBox; + + QRadioButton *overwriteButton; + QRadioButton *promptButton; +}; + +SaveSettingsWidget::SaveSettingsWidget(QWidget *parent) + : QWidget(parent, 0, Qt::WDestructiveClose) +{ + d = new SaveSettingsWidgetPriv; + QGridLayout* settingsBoxLayout = new QGridLayout(this, 3, 1, KDialog::spacingHint()); + + d->formatLabel = new QLabel(i18n("Output file format:"), this); + d->formatComboBox = new QComboBox( false, this ); + d->formatComboBox->insertItem( "JPEG", OUTPUT_JPEG ); + d->formatComboBox->insertItem( "TIFF", OUTPUT_TIFF ); + d->formatComboBox->insertItem( "PPM", OUTPUT_PPM ); + d->formatComboBox->insertItem( "PNG", OUTPUT_PNG ); + QWhatsThis::add(d->formatComboBox, i18n("<p>Set here the output file format to use:<p>" + "<b>JPEG</b>: output the processed image in JPEG Format. " + "this format will give smaller-sized files. Minimum JPEG " + "compression level will be used during Raw conversion.<p>" + "<b>Warning!!! duing of destructive compression algorithm, " + "JPEG is a lossy quality format.</b><p>" + "<b>TIFF</b>: output the processed image in TIFF Format. " + "This generates larges, without " + "losing quality. Adobe Deflate compression " + "will be used during conversion.<p>" + "<b>PPM</b>: output the processed image in PPM Format. " + "This generates the largest files, without " + "losing quality.<p>" + "<b>PNG</b>: output the processed image in PNG Format. " + "This generates larges, without " + "losing quality. Maximum PNG compression " + "will be used during conversion.")); + + d->conflictLabel = new QLabel(i18n("If Target File Exists:"), this); + d->conflictButtonGroup = new QVButtonGroup(this); + d->overwriteButton = new QRadioButton(i18n("Overwrite automatically"), d->conflictButtonGroup); + d->promptButton = new QRadioButton(i18n("Open rename-file dialog"), d->conflictButtonGroup); + d->conflictButtonGroup->insert(d->overwriteButton, OVERWRITE); + d->conflictButtonGroup->insert(d->promptButton, ASKTOUSER); + d->conflictButtonGroup->setRadioButtonExclusive(true); + d->overwriteButton->setChecked(true); + d->conflictButtonGroup->setFrameStyle(QFrame::NoFrame|QFrame::Plain); + d->conflictButtonGroup->setInsideMargin(0); + + settingsBoxLayout->addMultiCellWidget(d->formatLabel, 0, 0, 0, 0); + settingsBoxLayout->addMultiCellWidget(d->formatComboBox, 0, 0, 1, 1); + settingsBoxLayout->addMultiCellWidget(d->conflictLabel, 1, 1, 0, 1); + settingsBoxLayout->addMultiCellWidget(d->conflictButtonGroup, 2, 2, 0, 1); + settingsBoxLayout->setRowStretch(3, 10); + + connect(d->formatComboBox, SIGNAL(activated(int)), + this, SIGNAL(signalSaveFormatChanged())); +} + +SaveSettingsWidget::~SaveSettingsWidget() +{ + delete d; +} + +void SaveSettingsWidget::setDefaultSettings() +{ + setFileFormat(OUTPUT_PNG); + setConflictRule(OVERWRITE); +} + +SaveSettingsWidget::OutputFormat SaveSettingsWidget::fileFormat() +{ + return(OutputFormat)(d->formatComboBox->currentItem()); +} + +void SaveSettingsWidget::setFileFormat(SaveSettingsWidget::OutputFormat f) +{ + d->formatComboBox->setCurrentItem((int)f); +} + +SaveSettingsWidget::ConflictRule SaveSettingsWidget::conflictRule() +{ + return((ConflictRule)(d->conflictButtonGroup->selectedId())); +} + +void SaveSettingsWidget::setConflictRule(SaveSettingsWidget::ConflictRule r) +{ + d->conflictButtonGroup->setButton((int)r); +} + +} // NameSpace KIPIRawConverterPlugin diff --git a/kipi-plugins/rawconverter/savesettingswidget.h b/kipi-plugins/rawconverter/savesettingswidget.h new file mode 100644 index 0000000..a7b1f14 --- /dev/null +++ b/kipi-plugins/rawconverter/savesettingswidget.h @@ -0,0 +1,79 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-13 + * Description : save settings widgets + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef SAVESETTINGSWIDGET_H +#define SAVESETTINGSWIDGET_H + +// Qt includes. + +#include <qwidget.h> + +namespace KIPIRawConverterPlugin +{ + +class SaveSettingsWidgetPriv; + +class SaveSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + + enum OutputFormat + { + OUTPUT_JPEG = 0, + OUTPUT_TIFF, + OUTPUT_PPM, + OUTPUT_PNG + }; + + enum ConflictRule + { + OVERWRITE = 0, + ASKTOUSER + }; + +public: + + SaveSettingsWidget(QWidget *parent); + ~SaveSettingsWidget(); + + SaveSettingsWidget::OutputFormat fileFormat(); + ConflictRule conflictRule(); + + void setFileFormat(SaveSettingsWidget::OutputFormat f); + void setConflictRule(ConflictRule r); + + void setDefaultSettings(); + +signals: + + void signalSaveFormatChanged(); + +private: + + SaveSettingsWidgetPriv* d; +}; + +} // NameSpace KIPIRawConverterPlugin + +#endif /* SAVESETTINGSWIDGET_H */ diff --git a/kipi-plugins/rawconverter/singledialog.cpp b/kipi-plugins/rawconverter/singledialog.cpp new file mode 100644 index 0000000..e7c30b5 --- /dev/null +++ b/kipi-plugins/rawconverter/singledialog.cpp @@ -0,0 +1,597 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2003-10-22 + * Description : Raw converter single dialog + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +} + +// C++ includes. + +#include <cstdio> + +// Qt includes. + +#include <qtimer.h> +#include <qframe.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qfileinfo.h> +#include <qevent.h> +#include <qpushbutton.h> +#include <qfile.h> + +// KDE includes. + +#include <kcursor.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kconfig.h> +#include <kio/renamedlg.h> +#include <kapplication.h> +#include <khelpmenu.h> +#include <kiconloader.h> +#include <kpopupmenu.h> +#include <kstandarddirs.h> +#include <kdebug.h> + +// LibKDcraw includes. + +#include <libkdcraw/version.h> +#include <libkdcraw/dcrawsettingswidget.h> + +// Local includes. + +#include "kpaboutdata.h" +#include "pluginsversion.h" +#include "previewwidget.h" +#include "rawdecodingiface.h" +#include "savesettingswidget.h" +#include "actionthread.h" +#include "singledialog.h" +#include "singledialog.moc" + +namespace KIPIRawConverterPlugin +{ + +SingleDialog::SingleDialog(const QString& file, QWidget */*parent*/) + : KDialogBase(0, 0, false, i18n("Raw Image Converter"), + Help|Default|User1|User2|User3|Close, Close, true, + i18n("&Preview"), i18n("Con&vert"), i18n("&Abort")) +{ + m_inputFile = file; + m_inputFileName = QFileInfo(file).fileName(); + + QWidget *page = new QWidget( this ); + setMainWidget( page ); + QGridLayout *mainLayout = new QGridLayout(page, 1, 1, 0, spacingHint()); + + m_previewWidget = new PreviewWidget(page); + + // --------------------------------------------------------------- + + m_decodingSettingsBox = new KDcrawIface::DcrawSettingsWidget(page, false, true, true); + m_saveSettingsBox = new SaveSettingsWidget(m_decodingSettingsBox); + +#if KDCRAW_VERSION >= 0x000105 + m_decodingSettingsBox->addItem(m_saveSettingsBox, i18n("Save settings")); + m_decodingSettingsBox->updateMinimumWidth(); +#else + m_decodingSettingsBox->insertTab(m_saveSettingsBox, i18n("Save settings")); +#endif + + mainLayout->addMultiCellWidget(m_previewWidget, 0, 1, 0, 0); + mainLayout->addMultiCellWidget(m_decodingSettingsBox, 0, 0, 1, 1); + mainLayout->setColStretch(0, 10); + mainLayout->setRowStretch(1, 10); + + // --------------------------------------------------------------- + // About data and help button. + + m_about = new KIPIPlugins::KPAboutData(I18N_NOOP("RAW Image Converter"), + 0, + KAboutData::License_GPL, + I18N_NOOP("A Kipi plugin to convert a Raw image"), + "(c) 2003-2005, Renchi Raju\n" + "(c) 2006-2008, Gilles Caulier"); + + m_about->addAuthor("Renchi Raju", I18N_NOOP("Original author"), + "renchi at pooh dot tam dot uiuc dot edu"); + + m_about->addAuthor("Gilles Caulier", I18N_NOOP("Maintainer"), + "caulier dot gilles at gmail dot com"); + + KHelpMenu* helpMenu = new KHelpMenu(this, m_about, false); + helpMenu->menu()->removeItemAt(0); + helpMenu->menu()->insertItem(i18n("Plugin Handbook"), this, SLOT(slotHelp()), 0, -1, 0); + actionButton(Help)->setPopup( helpMenu->menu() ); + + // --------------------------------------------------------------- + + setButtonTip( User1, i18n("<p>Generate a Preview from current settings. " + "Uses a simple bilinear interpolation for " + "quick results.")); + + setButtonTip( User2, i18n("<p>Convert the Raw Image from current settings. " + "This uses a high-quality adaptive algorithm.")); + + setButtonTip( User3, i18n("<p>Abort the current Raw file conversion")); + + setButtonTip( Close, i18n("<p>Exit Raw Converter")); + + m_blinkPreviewTimer = new QTimer(this); + m_blinkConvertTimer = new QTimer(this); + m_thread = new ActionThread(this); + + // --------------------------------------------------------------- + + connect(m_blinkPreviewTimer, SIGNAL(timeout()), + this, SLOT(slotPreviewBlinkTimerDone())); + + connect(m_blinkConvertTimer, SIGNAL(timeout()), + this, SLOT(slotConvertBlinkTimerDone())); + + // --------------------------------------------------------------- + + busy(false); + readSettings(); + QTimer::singleShot(0, this, SLOT( slotIdentify() ) ); +} + +SingleDialog::~SingleDialog() +{ + delete m_about; + delete m_thread; +} + +void SingleDialog::closeEvent(QCloseEvent *e) +{ + if (!e) return; + m_blinkPreviewTimer->stop(); + m_blinkConvertTimer->stop(); + m_thread->cancel(); + saveSettings(); + e->accept(); +} + +void SingleDialog::slotClose() +{ + m_blinkPreviewTimer->stop(); + m_blinkConvertTimer->stop(); + m_thread->cancel(); + saveSettings(); + KDialogBase::slotClose(); +} + +void SingleDialog::slotDefault() +{ + m_decodingSettingsBox->setDefaultSettings(); + m_saveSettingsBox->setDefaultSettings(); +} + +void SingleDialog::readSettings() +{ + KConfig config("kipirc"); + config.setGroup("RawConverter Settings"); + + m_decodingSettingsBox->setWhiteBalance((KDcrawIface::RawDecodingSettings::WhiteBalance) + config.readNumEntry("White Balance", + KDcrawIface::RawDecodingSettings::CAMERA)); + m_decodingSettingsBox->setCustomWhiteBalance(config.readNumEntry("Custom White Balance", 6500)); + m_decodingSettingsBox->setCustomWhiteBalanceGreen(config.readDoubleNumEntry("Custom White Balance Green", 1.0)); + m_decodingSettingsBox->setFourColor(config.readBoolEntry("Four Color RGB", false)); + m_decodingSettingsBox->setUnclipColor(config.readNumEntry("Unclip Color", 0)); + m_decodingSettingsBox->setDontStretchPixels(config.readBoolEntry("Dont Stretch Pixels", false)); + m_decodingSettingsBox->setNoiseReduction(config.readBoolEntry("Use Noise Reduction", false)); + m_decodingSettingsBox->setBrightness(config.readDoubleNumEntry("Brightness Multiplier", 1.0)); + m_decodingSettingsBox->setUseBlackPoint(config.readBoolEntry("Use Black Point", false)); + m_decodingSettingsBox->setBlackPoint(config.readNumEntry("Black Point", 0)); +#if KDCRAW_VERSION >= 0x000105 + m_decodingSettingsBox->setUseWhitePoint(config.readBoolEntry("Use White Point", false)); + m_decodingSettingsBox->setWhitePoint(config.readNumEntry("White Point", 0)); + m_decodingSettingsBox->setMedianFilterPasses(config.readNumEntry("Median Filter Passes", 0)); +#endif + m_decodingSettingsBox->setNRThreshold(config.readNumEntry("NR Threshold", 100)); + m_decodingSettingsBox->setUseCACorrection(config.readBoolEntry("EnableCACorrection", false)); + m_decodingSettingsBox->setcaRedMultiplier(config.readDoubleNumEntry("caRedMultiplier", 1.0)); + m_decodingSettingsBox->setcaBlueMultiplier(config.readDoubleNumEntry("caBlueMultiplier", 1.0)); + + m_decodingSettingsBox->setQuality( + (KDcrawIface::RawDecodingSettings::DecodingQuality)config.readNumEntry("Decoding Quality", + (int)(KDcrawIface::RawDecodingSettings::BILINEAR))); + + m_decodingSettingsBox->setOutputColorSpace( + (KDcrawIface::RawDecodingSettings::OutputColorSpace)config.readNumEntry("Output Color Space", + (int)(KDcrawIface::RawDecodingSettings::SRGB))); + + m_saveSettingsBox->setFileFormat( + (SaveSettingsWidget::OutputFormat)config.readNumEntry("Output Format", + (int)(SaveSettingsWidget::OUTPUT_PNG))); + + m_saveSettingsBox->setConflictRule( + (SaveSettingsWidget::ConflictRule)config.readNumEntry("Conflict", + (int)(SaveSettingsWidget::OVERWRITE))); + + resize(configDialogSize(config, QString("Single Raw Converter Dialog"))); +} + +void SingleDialog::saveSettings() +{ + KConfig config("kipirc"); + config.setGroup("RawConverter Settings"); + + config.writeEntry("White Balance", m_decodingSettingsBox->whiteBalance()); + config.writeEntry("Custom White Balance", m_decodingSettingsBox->customWhiteBalance()); + config.writeEntry("Custom White Balance Green", m_decodingSettingsBox->customWhiteBalanceGreen()); + config.writeEntry("Four Color RGB", m_decodingSettingsBox->useFourColor()); + config.writeEntry("Unclip Color", m_decodingSettingsBox->unclipColor()); + config.writeEntry("Dont Stretch Pixels", m_decodingSettingsBox->useDontStretchPixels()); + config.writeEntry("Use Noise Reduction", m_decodingSettingsBox->useNoiseReduction()); + config.writeEntry("Brightness Multiplier", m_decodingSettingsBox->brightness()); + config.writeEntry("Use Black Point", m_decodingSettingsBox->useBlackPoint()); + config.writeEntry("Black Point", m_decodingSettingsBox->blackPoint()); +#if KDCRAW_VERSION >= 0x000105 + config.writeEntry("Use White Point", m_decodingSettingsBox->useWhitePoint()); + config.writeEntry("White Point", m_decodingSettingsBox->whitePoint()); + config.writeEntry("Median Filter Passes", m_decodingSettingsBox->medianFilterPasses()); +#endif + config.writeEntry("NR Threshold", m_decodingSettingsBox->NRThreshold()); + config.writeEntry("EnableCACorrection", m_decodingSettingsBox->useCACorrection()); + config.writeEntry("caRedMultiplier", m_decodingSettingsBox->caRedMultiplier()); + config.writeEntry("caBlueMultiplier", m_decodingSettingsBox->caBlueMultiplier()); + config.writeEntry("Decoding Quality", (int)m_decodingSettingsBox->quality()); + config.writeEntry("Output Color Space", (int)m_decodingSettingsBox->outputColorSpace()); + + config.writeEntry("Output Format", (int)m_saveSettingsBox->fileFormat()); + config.writeEntry("Conflict", (int)m_saveSettingsBox->conflictRule()); + + saveDialogSize(config, QString("Single Raw Converter Dialog")); + config.sync(); +} + +void SingleDialog::slotHelp() +{ + KApplication::kApplication()->invokeHelp("rawconverter", "kipi-plugins"); +} + +// 'Preview' dialog button. +void SingleDialog::slotUser1() +{ + KDcrawIface::RawDecodingSettings rawDecodingSettings; + rawDecodingSettings.whiteBalance = m_decodingSettingsBox->whiteBalance(); + rawDecodingSettings.customWhiteBalance = m_decodingSettingsBox->customWhiteBalance(); + rawDecodingSettings.customWhiteBalanceGreen = m_decodingSettingsBox->customWhiteBalanceGreen(); + rawDecodingSettings.RGBInterpolate4Colors = m_decodingSettingsBox->useFourColor(); + rawDecodingSettings.unclipColors = m_decodingSettingsBox->unclipColor(); + rawDecodingSettings.DontStretchPixels = m_decodingSettingsBox->useDontStretchPixels(); + rawDecodingSettings.enableNoiseReduction = m_decodingSettingsBox->useNoiseReduction(); + rawDecodingSettings.brightness = m_decodingSettingsBox->brightness(); + rawDecodingSettings.enableBlackPoint = m_decodingSettingsBox->useBlackPoint(); + rawDecodingSettings.blackPoint = m_decodingSettingsBox->blackPoint(); +#if KDCRAW_VERSION >= 0x000105 + rawDecodingSettings.enableWhitePoint = m_decodingSettingsBox->useWhitePoint(); + rawDecodingSettings.whitePoint = m_decodingSettingsBox->whitePoint(); + rawDecodingSettings.medianFilterPasses = m_decodingSettingsBox->medianFilterPasses(); +#endif + rawDecodingSettings.NRThreshold = m_decodingSettingsBox->NRThreshold(); + rawDecodingSettings.enableCACorrection = m_decodingSettingsBox->useCACorrection(); + rawDecodingSettings.caMultiplier[0] = m_decodingSettingsBox->caRedMultiplier(); + rawDecodingSettings.caMultiplier[1] = m_decodingSettingsBox->caBlueMultiplier(); + rawDecodingSettings.RAWQuality = m_decodingSettingsBox->quality(); + rawDecodingSettings.outputColorSpace = m_decodingSettingsBox->outputColorSpace(); + + m_thread->setRawDecodingSettings(rawDecodingSettings, SaveSettingsWidget::OUTPUT_PPM); + m_thread->processHalfRawFile(KURL(m_inputFile)); + if (!m_thread->running()) + m_thread->start(); +} + +// 'Convert' dialog button. +void SingleDialog::slotUser2() +{ + KDcrawIface::RawDecodingSettings rawDecodingSettings; + rawDecodingSettings.whiteBalance = m_decodingSettingsBox->whiteBalance(); + rawDecodingSettings.customWhiteBalance = m_decodingSettingsBox->customWhiteBalance(); + rawDecodingSettings.customWhiteBalanceGreen = m_decodingSettingsBox->customWhiteBalanceGreen(); + rawDecodingSettings.RGBInterpolate4Colors = m_decodingSettingsBox->useFourColor(); + rawDecodingSettings.unclipColors = m_decodingSettingsBox->unclipColor(); + rawDecodingSettings.DontStretchPixels = m_decodingSettingsBox->useDontStretchPixels(); + rawDecodingSettings.enableNoiseReduction = m_decodingSettingsBox->useNoiseReduction(); + rawDecodingSettings.brightness = m_decodingSettingsBox->brightness(); + rawDecodingSettings.enableBlackPoint = m_decodingSettingsBox->useBlackPoint(); + rawDecodingSettings.blackPoint = m_decodingSettingsBox->blackPoint(); +#if KDCRAW_VERSION >= 0x000105 + rawDecodingSettings.enableWhitePoint = m_decodingSettingsBox->useWhitePoint(); + rawDecodingSettings.whitePoint = m_decodingSettingsBox->whitePoint(); + rawDecodingSettings.medianFilterPasses = m_decodingSettingsBox->medianFilterPasses(); +#endif + rawDecodingSettings.NRThreshold = m_decodingSettingsBox->NRThreshold(); + rawDecodingSettings.enableCACorrection = m_decodingSettingsBox->useCACorrection(); + rawDecodingSettings.caMultiplier[0] = m_decodingSettingsBox->caRedMultiplier(); + rawDecodingSettings.caMultiplier[1] = m_decodingSettingsBox->caBlueMultiplier(); + rawDecodingSettings.RAWQuality = m_decodingSettingsBox->quality(); + rawDecodingSettings.outputColorSpace = m_decodingSettingsBox->outputColorSpace(); + + m_thread->setRawDecodingSettings(rawDecodingSettings, m_saveSettingsBox->fileFormat()); + m_thread->processRawFile(KURL(m_inputFile)); + if (!m_thread->running()) + m_thread->start(); +} + +// 'Abort' dialog button. +void SingleDialog::slotUser3() +{ + m_thread->cancel(); +} + +void SingleDialog::slotIdentify() +{ + m_thread->identifyRawFile(KURL(m_inputFile), true); + if (!m_thread->running()) + m_thread->start(); +} + +void SingleDialog::busy(bool val) +{ + m_decodingSettingsBox->setEnabled(!val); + m_saveSettingsBox->setEnabled(!val); + enableButton (User1, !val); + enableButton (User2, !val); + enableButton (User3, val); + enableButton (Close, !val); +} + +void SingleDialog::identified(const QString&, const QString& identity, const QPixmap& preview) +{ + m_previewWidget->setInfo(m_inputFileName + QString(" :\n") + identity, Qt::white, preview); +} + +void SingleDialog::previewing(const QString&) +{ + m_previewBlink = false; + m_previewWidget->setCursor( KCursor::waitCursor() ); + m_blinkPreviewTimer->start(200); +} + +void SingleDialog::previewed(const QString&, const QString& tmpFile) +{ + m_previewWidget->unsetCursor(); + m_blinkPreviewTimer->stop(); + m_previewWidget->load(tmpFile); + ::remove(QFile::encodeName(tmpFile)); +} + +void SingleDialog::previewFailed(const QString&) +{ + m_previewWidget->unsetCursor(); + m_blinkPreviewTimer->stop(); + m_previewWidget->setInfo(i18n("Failed to generate preview"), Qt::red); +} + +void SingleDialog::processing(const QString&) +{ + m_convertBlink = false; + m_previewWidget->setCursor( KCursor::waitCursor() ); + m_blinkConvertTimer->start(200); +} + +void SingleDialog::processed(const QString&, const QString& tmpFile) +{ + m_previewWidget->unsetCursor(); + m_blinkConvertTimer->stop(); + m_previewWidget->load(tmpFile); + QString filter("*."); + QString ext; + + switch(m_saveSettingsBox->fileFormat()) + { + case SaveSettingsWidget::OUTPUT_JPEG: + ext = "jpg"; + break; + case SaveSettingsWidget::OUTPUT_TIFF: + ext = "tif"; + break; + case SaveSettingsWidget::OUTPUT_PPM: + ext = "ppm"; + break; + case SaveSettingsWidget::OUTPUT_PNG: + ext = "png"; + break; + } + + filter += ext; + QFileInfo fi(m_inputFile); + QString destFile = fi.dirPath(true) + QString("/") + fi.baseName() + QString(".") + ext; + + if (m_saveSettingsBox->conflictRule() != SaveSettingsWidget::OVERWRITE) + { + struct stat statBuf; + if (::stat(QFile::encodeName(destFile), &statBuf) == 0) + { + KIO::RenameDlg dlg(this, i18n("Save Raw Image converted from '%1' as").arg(fi.fileName()), + tmpFile, destFile, + KIO::RenameDlg_Mode(KIO::M_SINGLE | KIO::M_OVERWRITE | KIO::M_SKIP)); + + switch (dlg.exec()) + { + case KIO::R_CANCEL: + case KIO::R_SKIP: + { + destFile = QString(); + break; + } + case KIO::R_RENAME: + { + destFile = dlg.newDestURL().path(); + break; + } + default: // Overwrite. + break; + } + } + } + + if (!destFile.isEmpty()) + { + if (::rename(QFile::encodeName(tmpFile), QFile::encodeName(destFile)) != 0) + { + KMessageBox::error(this, i18n("Failed to save image %1").arg( destFile )); + } + } +} + +void SingleDialog::processingFailed(const QString&) +{ + m_previewWidget->unsetCursor(); + m_blinkConvertTimer->stop(); + m_previewWidget->setInfo(i18n("Failed to convert Raw image"), Qt::red); +} + +void SingleDialog::slotPreviewBlinkTimerDone() +{ + QString preview = i18n("Generating Preview..."); + + if (m_previewBlink) + m_previewWidget->setInfo(preview, Qt::green); + else + m_previewWidget->setInfo(preview, Qt::darkGreen); + + m_previewBlink = !m_previewBlink; + m_blinkPreviewTimer->start(200); +} + +void SingleDialog::slotConvertBlinkTimerDone() +{ + QString convert = i18n("Converting Raw Image..."); + + if (m_convertBlink) + m_previewWidget->setInfo(convert, Qt::green); + else + m_previewWidget->setInfo(convert, Qt::darkGreen); + + m_convertBlink = !m_convertBlink; + m_blinkConvertTimer->start(200); +} + +void SingleDialog::customEvent(QCustomEvent *event) +{ + if (!event) return; + + EventData *d = (EventData*) event->data(); + if (!d) return; + + QString text; + + if (d->starting) // Something have been started... + { + switch (d->action) + { + case(IDENTIFY_FULL): + break; + case(PREVIEW): + { + busy(true); + previewing(d->filePath); + break; + } + case(PROCESS): + { + busy(true); + processing(d->filePath); + break; + } + default: + { + kdWarning( 51000 ) << "KIPIRawConverterPlugin: Unknown event" << endl; + break; + } + } + } + else + { + if (!d->success) // Something is failed... + { + switch (d->action) + { + case(IDENTIFY_FULL): + break; + case(PREVIEW): + { + previewFailed(d->filePath); + busy(false); + break; + } + case(PROCESS): + { + processingFailed(d->filePath); + busy(false); + break; + } + default: + { + kdWarning( 51000 ) << "KIPIRawConverterPlugin: Unknown event" << endl; + break; + } + } + } + else // Something is done... + { + switch (d->action) + { + case(IDENTIFY_FULL): + { + QPixmap pix = QPixmap(d->image.scale(256, 256, QImage::ScaleMin)); + identified(d->filePath, d->message, pix); + busy(false); + break; + } + case(PREVIEW): + { + previewed(d->filePath, d->destPath); + busy(false); + break; + } + case(PROCESS): + { + processed(d->filePath, d->destPath); + busy(false); + break; + } + default: + { + kdWarning( 51000 ) << "KIPIRawConverterPlugin: Unknown event" << endl; + break; + } + } + } + } + + delete d; +} + +} // NameSpace KIPIRawConverterPlugin diff --git a/kipi-plugins/rawconverter/singledialog.h b/kipi-plugins/rawconverter/singledialog.h new file mode 100644 index 0000000..2038031 --- /dev/null +++ b/kipi-plugins/rawconverter/singledialog.h @@ -0,0 +1,124 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2003-10-22 + * Description : Raw converter single dialog + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef SINGLEDIALOG_H +#define SINGLEDIALOG_H + +// Qt includes. + +#include <qstring.h> + +// KDE includes. + +#include <kdialogbase.h> + +// Local includes + +#include "kpaboutdata.h" + +class QCloseEvent; +class QCustomEvent; +class QTimer; + +namespace KDcrawIface +{ +class DcrawSettingsWidget; +} + +namespace KIPIRawConverterPlugin +{ + +class PreviewWidget; +class ActionThread; +class SaveSettingsWidget; + +class SingleDialog : public KDialogBase +{ + Q_OBJECT + +public: + + SingleDialog(const QString& file, QWidget *parent); + ~SingleDialog(); + +protected: + + void customEvent(QCustomEvent *event); + void closeEvent(QCloseEvent *e); + +private: + + void readSettings(); + void saveSettings(); + + void busy(bool busy); + + void identified(const QString&, const QString& identity, const QPixmap& preview); + + void previewing(const QString&); + void previewed(const QString&, const QString& tmpFile); + void previewFailed(const QString&); + + void processing(const QString&); + void processed(const QString&, const QString& tmpFile); + void processingFailed(const QString&); + +private slots: + + void slotDefault(); + void slotClose(); + void slotHelp(); + void slotUser1(); + void slotUser2(); + void slotUser3(); + + void slotIdentify(); + + void slotPreviewBlinkTimerDone(); + void slotConvertBlinkTimerDone(); + +private: + + bool m_previewBlink; + bool m_convertBlink; + + QString m_inputFile; + QString m_inputFileName; + + QTimer *m_blinkPreviewTimer; + QTimer *m_blinkConvertTimer; + + PreviewWidget *m_previewWidget; + + ActionThread *m_thread; + + SaveSettingsWidget *m_saveSettingsBox; + + KDcrawIface::DcrawSettingsWidget *m_decodingSettingsBox; + + KIPIPlugins::KPAboutData *m_about; +}; + +} // NameSpace KIPIRawConverterPlugin + +#endif // SINGLEDIALOG_H |