/* * Copyright (c) 2005 Boudewijn Rempt * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 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(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( 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 (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 profileList = KisMetaRegistry::instance()->csRegistry()->profilesFor( csf ); TQValueVector ::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"