You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/filters/chalk/raw/kis_raw_import.cpp

631 lines
21 KiB

/*
* Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
*
* 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 of the License, 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#include <sys/types.h>
#include <netinet/in.h>
#include <tqstring.h>
#include <tqfile.h>
#include <tqimage.h>
#include <tqradiobutton.h>
#include <tqgroupbox.h>
#include <tqbuttongroup.h>
#include <tqpushbutton.h>
#include <tqlabel.h>
#include <tqcheckbox.h>
#include <tqapplication.h>
#include <tqcursor.h>
#include <tqeventloop.h>
#include <tqprogressdialog.h>
#include <tqtimer.h>
#include <kglobal.h>
#include <tdeconfig.h>
#include <knuminput.h>
#include <kgenericfactory.h>
#include <kdialogbase.h>
#include <kdialog.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <kprocess.h>
#include <KoDocument.h>
#include <KoFilterChain.h>
#include "imageviewer.h"
#include "kis_config.h"
#include "kis_cmb_idlist.h"
#include "kis_types.h"
#include "kis_raw_import.h"
#include "kis_doc.h"
#include "kis_image.h"
#include "kis_meta_registry.h"
#include "kis_layer.h"
#include "kis_annotation.h"
#include "kis_profile.h"
#include "kis_colorspace_factory_registry.h"
#include "kis_iterators_pixel.h"
#include "kis_abstract_colorspace.h"
#include "kis_paint_device.h"
#include "kis_paint_layer.h"
#include "wdgrawimport.h"
typedef KGenericFactory<KisRawImport, KoFilter> KisRawImportFactory;
K_EXPORT_COMPONENT_FACTORY(libchalk_raw_import, KisRawImportFactory("kofficefilters"))
KisRawImport::KisRawImport(KoFilter *, const char *, const TQStringList&)
: KoFilter()
, m_data(0)
, m_process(0)
, m_progress(0)
, m_err(false)
{
m_dialog = new KDialogBase();
m_dialog->enableButtonApply(false);
m_page = new WdgRawImport(m_dialog);
m_dialog -> setMainWidget(m_page);
connect(m_page->bnPreview, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotUpdatePreview()));
connect(m_page->grpColorSpace, TQT_SIGNAL(clicked( int )), this, TQT_SLOT(slotFillCmbProfiles()));
connect(m_page->grpChannelDepth, TQT_SIGNAL(clicked( int )), this, TQT_SLOT(slotFillCmbProfiles()));
KisConfig cfg;
TQString monitorProfileName = cfg.monitorProfile();
m_monitorProfile = KisMetaRegistry::instance()->csRegistry()->getProfileByName(monitorProfileName);
slotFillCmbProfiles();
}
KisRawImport::~KisRawImport()
{
delete m_dialog;
delete m_process;
}
KoFilter::ConversionStatus KisRawImport::convert(const TQCString& from, const TQCString& to)
{
if (from != "image/x-raw" || to != "application/x-chalk") {
return KoFilter::NotImplemented;
}
if (m_err) {
return KoFilter::CreationError;
}
kdDebug(41008) << "Chalk importing from Raw\n";
KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument());
if (!doc) {
return KoFilter::CreationError;
}
doc -> prepareForImport();
TQString filename = m_chain -> inputFile();
if (filename.isEmpty()) {
return KoFilter::FileNotFound;
}
slotUpdatePreview();
// Show dialog
m_dialog->setCursor(TQt::ArrowCursor);
TQApplication::setOverrideCursor(TQt::ArrowCursor);
TDEConfig * cfg = TDEGlobal::config();
cfg->setGroup("rawimport");
m_page->radioGray->setChecked(cfg->readBoolEntry("gray", false));
m_page->radioRGB->setChecked(cfg->readBoolEntry("rgb", true));
m_page->radio8->setChecked(cfg->readBoolEntry("8bit", false));
m_page->radio16->setChecked(cfg->readBoolEntry("16bit", true));
m_page->chkFourColorRGB->setChecked( cfg->readBoolEntry("four_color_rgb", false));
m_page->chkCameraColors->setChecked( cfg->readBoolEntry("camera_colors", false));
m_page->chkBrightness->setChecked( cfg->readBoolEntry("do_brightness", false));
m_page->chkBlackpoint->setChecked( cfg->readBoolEntry("do_blackpoint", false));
m_page->chkRed->setChecked( cfg->readBoolEntry("do_red", false));
m_page->chkBlue->setChecked( cfg->readBoolEntry("do_blue", false));
m_page->radioFixed->setChecked( cfg->readBoolEntry("fixed_wb", true));
m_page->radioAutomatic->setChecked( cfg->readBoolEntry("automatic_wb", false));
m_page->radioCamera->setChecked( cfg->readBoolEntry("camera_wb", false));
m_page->chkClip->setChecked( cfg->readBoolEntry("clip", true));
m_page->chkProfile->setChecked(cfg->readBoolEntry("useprofile", false));
m_page->dblBrightness->setValue(cfg->readDoubleNumEntry("brightness", 1.0));
m_page->dblBlackpoint->setValue(cfg->readDoubleNumEntry("blackpoint", 0));
m_page->dblRed->setValue(cfg->readDoubleNumEntry("red", 1.0));
m_page->dblBlue->setValue(cfg->readDoubleNumEntry("blue", 1.0));
if (m_dialog->exec() == TQDialog::Accepted) {
cfg->writeEntry("gray", m_page->radioGray->isChecked());
cfg->writeEntry("rgb", m_page->radioRGB->isChecked());
cfg->writeEntry("8bit", m_page->radio8->isChecked());
cfg->writeEntry("16bit", m_page->radio16->isChecked());
cfg->writeEntry("four_color_rgb", m_page->chkFourColorRGB -> isChecked());
cfg->writeEntry("camera_colors", m_page->chkCameraColors -> isChecked());
cfg->writeEntry("do_brightness", m_page->chkBrightness -> isChecked());
cfg->writeEntry("do_blackpoint", m_page->chkBlackpoint -> isChecked());
cfg->writeEntry("do_red", m_page->chkRed -> isChecked());
cfg->writeEntry("do_blue", m_page->chkBlue -> isChecked());
cfg->writeEntry("fixed_wb", m_page->radioFixed -> isChecked());
cfg->writeEntry("automatic_wb", m_page->radioAutomatic -> isChecked());
cfg->writeEntry("camera_wb", m_page->radioCamera -> isChecked());
cfg->writeEntry("clip", m_page->chkClip->isChecked());
cfg->writeEntry("useprofile", m_page->chkProfile->isChecked());
cfg->writeEntry("brightness", m_page->dblBrightness->value());
cfg->writeEntry("blackpoint", m_page->dblBlackpoint->value());
cfg->writeEntry("red", m_page->dblRed->value());
cfg->writeEntry("blue", m_page->dblBlue->value());
TQApplication::setOverrideCursor(TQt::waitCursor);
// Create a busy indicator to show that we didn't die or so
m_progress = new TQProgressDialog();
m_progress -> setTotalSteps(0);
m_progress -> setCancelButton(0);
TQTimer timer;
connect(&timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(incrementProgress()));
timer.start(200);
doc -> undoAdapter() -> setUndo(false);
getImageData(createArgumentList(false));
KisImageSP image = 0;
KisPaintLayerSP layer = 0;
KisPaintDeviceSP device = 0;
TQApplication::restoreOverrideCursor();
delete m_progress;
m_progress = 0;
if (m_page->radio8->isChecked()) {
// 8 bits
TQImage img;
img.loadFromData(*m_data);
KisColorSpace * cs = 0;
if (m_page->radioGray->isChecked()) {
cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("GRAYA"), profile() );
}
else {
cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("RGBA"), profile() );
}
if (cs == 0) { kdDebug() << "No CS\n"; return KoFilter::InternalError; }
image = new KisImage(doc->undoAdapter(), img.width(), img.height(), cs, filename);
if (image == 0) return KoFilter::CreationError;
image->blockSignals(true); // Don't send out signals while we're building the image
layer = dynamic_cast<KisPaintLayer*>( image->newLayer(image -> nextLayerName(), OPACITY_OPAQUE).data() );
if (layer == 0) return KoFilter::CreationError;
device = layer->paintDevice();
if (device == 0) return KoFilter::CreationError;
device->convertFromTQImage(img, "");
} else {
// 16 bits
TQ_UINT32 startOfImagedata = 0;
TQSize sz = determineSize(startOfImagedata);
kdDebug(41008) << "Total bytes: " << m_data->size()
<< "\n start of image data: " << startOfImagedata
<< "\n bytes for pixels left: " << m_data->size() - startOfImagedata
<< "\n total pixels: " << sz.width() * sz.height()
<< "\n total pixel bytes: " << sz.width() * sz.height() * 6
<< "\n total necessary bytes: " << (sz.width() * sz.height() * 6) + startOfImagedata
<< "\n";
char * data = m_data->data() + startOfImagedata;
KisColorSpace * cs = 0;
if (m_page->radioGray->isChecked()) {
cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("GRAYA16"), profile() );
}
else {
cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("RGBA16"), profile() );
}
if (cs == 0) return KoFilter::InternalError;
image = new KisImage( doc->undoAdapter(), sz.width(), sz.height(), cs, filename);
if (image == 0)return KoFilter::CreationError;
layer = dynamic_cast<KisPaintLayer*> (image->newLayer(image -> nextLayerName(), OPACITY_OPAQUE).data());
if (layer == 0) return KoFilter::CreationError;
device = layer->paintDevice();
if (device == 0) return KoFilter::CreationError;
// Copy the colordata to the pixels
int pos = 0;
for (int line = 0; line < sz.height(); ++line) {
KisHLineIterator it = device->createHLineIterator(0, line, sz.width(), true);
while (!it.isDone()) {
if (m_page->radioGray->isChecked()) {
TQ_UINT16 d = (TQ_INT16)*(data + pos);
d = ntohs(d);
memcpy(it.rawData(), &d, 2);
pos += 2;
}
else {
// Red
TQ_UINT16 d = (TQ_INT16)*(data + pos);
d = ntohs(d);
memcpy(it.rawData() + 4, &d, 2);
// Green
pos += 2;
d = (TQ_INT16)*(data + pos );
d = ntohs(d);
memcpy(it.rawData() + 2, &d, 2);
// Blue
pos += 2;
d = (TQ_INT16)*(data + pos );
d = ntohs(d);
memcpy(it.rawData(), &d, 2);
pos += 2;
}
cs->setAlpha(it.rawData(), OPACITY_OPAQUE, 1);
++it;
}
}
}
layer->setDirty();
kdDebug() << "going to set image\n";
doc -> setCurrentImage(image);
doc -> undoAdapter() -> setUndo(true);
doc -> setModified(false);
kdDebug() << "everything ok\n";
TQApplication::restoreOverrideCursor();
return KoFilter::OK;
}
TQApplication::restoreOverrideCursor();
return KoFilter::UserCancelled;
}
void KisRawImport::incrementProgress()
{
m_progress -> setProgress(m_progress -> progress() + 10);
}
void KisRawImport::slotUpdatePreview()
{
TQApplication::setOverrideCursor(TQt::waitCursor);
getImageData(createArgumentList(true));
kdDebug(41008) << "Retrieved " << m_data->size() << " bytes of image data\n";
if (m_data->isNull()) return;
TQImage img;
if (m_page->radio8->isChecked()) {
// 8 bits
img.loadFromData(*m_data);
} else {
// 16 bits
TQ_UINT32 startOfImagedata = 0;
TQSize sz = determineSize(startOfImagedata);
kdDebug(41008) << "Total bytes: " << m_data->size()
<< "\n start of image data: " << startOfImagedata
<< "\n bytes for pixels left: " << m_data->size() - startOfImagedata
<< "\n total pixels: " << sz.width() * sz.height()
<< "\n total pixel bytes: " << sz.width() * sz.height() * 6
<< "\n total necessary bytes: " << (sz.width() * sz.height() * 6) + startOfImagedata
<< "\n";
char * data = m_data->data() + startOfImagedata;
KisColorSpace * cs = 0;
if (m_page->radioGray->isChecked()) {
cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("GRAYA16"), profile() );
}
else {
cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("RGBA16"), profile() );
}
KisPaintDevice * dev = new KisPaintDevice(cs, "preview");
// Copy the colordata to the pixels
int pos = 0;
for (int line = 0; line < sz.height(); ++line) {
KisHLineIterator it = dev->createHLineIterator(0, line, sz.width(), true);
while (!it.isDone()) {
if (m_page->radioGray->isChecked()) {
TQ_UINT16 d = (TQ_INT16)*(data + pos);
d = ntohs(d);
memcpy(it.rawData(), &d, 2);
pos += 2;
}
else {
// Red
TQ_UINT16 d = (TQ_INT16)*(data + pos);
d = ntohs(d);
memcpy(it.rawData() + 4, &d, 2);
// Green
pos += 2;
d = (TQ_INT16)*(data + pos );
d = ntohs(d);
memcpy(it.rawData() + 2, &d, 2);
// Blue
pos += 2;
d = (TQ_INT16)*(data + pos );
d = ntohs(d);
memcpy(it.rawData(), &d, 2);
pos += 2;
}
cs->setAlpha(it.rawData(), OPACITY_OPAQUE, 1);
++it;
}
}
img = dev->convertToTQImage(m_monitorProfile);
}
m_page->lblPreview->setImage(img);
TQApplication::restoreOverrideCursor();
}
void KisRawImport::getImageData( TQStringList arguments )
{
// delete m_process;
delete m_data;
kdDebug(41008) << "getImageData " << arguments.join(" ") << "\n";
TDEProcess process (this);
m_data = new TQByteArray(0);
for (TQStringList::iterator it = arguments.begin(); it != arguments.end(); ++it) {
process << *it;
}
process.setUseShell(true);
connect(&process, TQT_SIGNAL(receivedStdout(TDEProcess *, char *, int)), this, TQT_SLOT(slotReceivedStdout(TDEProcess *, char *, int)));
connect(&process, TQT_SIGNAL(receivedStderr(TDEProcess *, char *, int)), this, TQT_SLOT(slotReceivedStderr(TDEProcess *, char *, int)));
connect(&process, TQT_SIGNAL(processExited(TDEProcess *)), this, TQT_SLOT(slotProcessDone()));
kdDebug(41008) << "Starting process\n";
if (!process.start(TDEProcess::NotifyOnExit, TDEProcess::AllOutput)) {
KMessageBox::error( 0, i18n("Cannot convert RAW files because the dcraw executable could not be started."));
}
while (process.isRunning()) {
//kdDebug(41008) << "Waiting...\n";
tqApp->eventLoop()->processEvents(TQEventLoop::ExcludeUserInput);
//process.wait(2);
}
if (process.normalExit()) {
kdDebug(41008) << "Return value of process: " << process.exitStatus() << "\n";
}
else {
kdDebug(41008) << "Process did not exit normally. Exit signal: " << process.exitSignal() << "\n";
m_err = true;
}
}
void KisRawImport::slotProcessDone()
{
kdDebug(41008) << "process done!\n";
}
void KisRawImport::slotReceivedStdout(TDEProcess *, char *buffer, int buflen)
{
//kdDebug(41008) << "stdout received " << buflen << " bytes on stdout.\n";
//kdDebug(41008) << TQString::fromAscii(buffer, buflen) << "\n";
int oldSize = m_data->size();
m_data->resize(oldSize + buflen, TQGArray::SpeedOptim);
memcpy(m_data->data() + oldSize, buffer, buflen);
}
void KisRawImport::slotReceivedStderr(TDEProcess *, char *buffer, int buflen)
{
TQByteArray b(buflen);
memcpy(b.data(), buffer, buflen);
kdDebug(41008) << TQString(b) << "\n";
KMessageBox::error(0, i18n("Error: Dcraw cannot load this image. Message: ") + TQString(b));
m_err = true;
}
TQStringList KisRawImport::createArgumentList(bool forPreview)
{
TQStringList args;
args.append("dcraw"); // XXX: Create a chalkdcraw so we can count on it being available
//args.append("-v"); // Verbose
args.append("-c"); // Write to stdout
if (forPreview) {
args.append("-h"); // Fast, half size image
}
if (m_page->radio8->isChecked()) {
args.append("-2"); // 8 bits
}
else {
args.append("-4"); // 16 bits
}
if (m_page->radioGray->isChecked()) {
args.append("-d"); // Create grayscale image
}
if (m_page->chkCameraColors->isChecked()) {
args.append("-m"); // Use camera raw colors instead of sRGB
}
if (m_page->radioAutomatic->isChecked()) {
args.append("-a"); // Automatic white balancing
}
if (m_page->radioCamera->isChecked()) {
args.append("-w"); // Use camera white balance, if present
}
if (m_page->chkFourColorRGB->isChecked()) {
args.append("-f"); // Interpolate RGB as four colors
}
if (!m_page->chkClip->isChecked()) {
args.append("-n"); // Do not clip colors
}
if (m_page->chkBrightness->isChecked()) {
args.append("-b " + TQString::number(m_page->dblBrightness->value()));
}
if (m_page->chkBlackpoint->isChecked()) {
args.append("-k " + TQString::number(m_page->dblBlackpoint->value()));
}
if (m_page->chkRed->isChecked()) {
args.append("-r " + TQString::number(m_page->dblRed->value()));
}
if (m_page->chkBlue->isChecked()) {
args.append("-l " + TQString::number(m_page->dblBlue->value()));
}
KisProfile * pf = profile();
if (m_page->chkProfile->isChecked()) {
if (!pf->filename().isNull()) {
// Use the user-set profile, if it's not an lcms internal
// profile. This does not add the profile to the image, we
// need to do that later.
args.append("-p \"" + pf->filename() + "\"");
}
}
// Don't forget the filename
args.append("\"" + m_chain -> inputFile() + "\"");
return args;
}
TQSize KisRawImport::determineSize(TQ_UINT32& startOfImageData)
{
if (m_data->isNull() || m_data->size() < 2048) {
startOfImageData = 0;
return TQSize(0,0);
}
TQString magick = TQString::fromAscii(m_data->data(), 2);
if (magick != "P6") {
kdDebug(41008) << " Bad magick! " << magick << "\n";
startOfImageData = 0;
return TQSize(0,0);
}
// Find the third newline that marks the header end in a dcraw generated ppm.
TQ_UINT32 i = 0;
TQ_UINT32 counter = 0;
while (true) {
if (counter == 3) break;
if (m_data->data()[i] == '\n') {
counter++;
}
++i;
}
TQString size = TQStringList::split("\n", TQString::fromAscii(m_data->data(), i))[1];
kdDebug(41008) << "Header: " << TQString(TQString::fromAscii(m_data->data(), i)) << "\n";
TQStringList sizelist = TQStringList::split(" ", size);
TQ_INT32 w = sizelist[0].toInt();
TQ_INT32 h = sizelist[1].toInt();
startOfImageData = i;
return TQSize(w, h);
}
KisProfile * KisRawImport::profile()
{
if (m_page->chkProfile->isChecked()) {
return KisMetaRegistry::instance()->csRegistry()->getProfileByName(m_page->cmbProfile->currentText());
}
else
return 0;
}
void KisRawImport::slotFillCmbProfiles()
{
KisID s = getColorSpace();
KisColorSpaceFactory * csf = KisMetaRegistry::instance()->csRegistry() -> get(s);
m_page -> cmbProfile -> clear();
TQValueVector<KisProfile *> profileList = KisMetaRegistry::instance()->csRegistry()->profilesFor( csf );
TQValueVector<KisProfile *> ::iterator it;
for ( it = profileList.begin(); it != profileList.end(); ++it ) {
m_page -> cmbProfile -> insertItem((*it) -> productName());
}
}
KisID KisRawImport::getColorSpace()
{
if (m_page->radioRGB->isChecked()) {
if (m_page->radio16->isChecked()) {
return KisID( "RGBA16" );
}
}
else {
if (m_page->radio16->isChecked()) {
return KisID( "GRAYA16" );
}
else {
return KisID( "GRAYA" );
}
}
return KisID("RGBA");
}
#include "kis_raw_import.moc"