filters

kis_raw_import.cpp
1/*
2 * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19#include "config.h"
20
21#ifdef HAVE_SYS_TYPES_H
22 #include <sys/types.h>
23#endif
24
25#include <sys/types.h>
26#include <netinet/in.h>
27
28#include <tqstring.h>
29#include <tqfile.h>
30#include <tqimage.h>
31#include <tqradiobutton.h>
32#include <tqgroupbox.h>
33#include <tqbuttongroup.h>
34#include <tqpushbutton.h>
35#include <tqlabel.h>
36#include <tqcheckbox.h>
37#include <tqapplication.h>
38#include <tqcursor.h>
39#include <tqeventloop.h>
40#include <tqprogressdialog.h>
41#include <tqtimer.h>
42
43#include <tdeglobal.h>
44#include <tdeconfig.h>
45#include <knuminput.h>
46#include <kgenericfactory.h>
47#include <kdialogbase.h>
48#include <kdialog.h>
49#include <tdemessagebox.h>
50#include <tdelocale.h>
51#include <kprocess.h>
52
53#include <KoDocument.h>
54#include <KoFilterChain.h>
55
56#include "imageviewer.h"
57#include "kis_config.h"
58#include "kis_cmb_idlist.h"
59#include "kis_types.h"
60#include "kis_raw_import.h"
61#include "kis_doc.h"
62#include "kis_image.h"
63#include "kis_meta_registry.h"
64#include "kis_layer.h"
65#include "kis_annotation.h"
66#include "kis_profile.h"
67#include "kis_colorspace_factory_registry.h"
68#include "kis_iterators_pixel.h"
69#include "kis_abstract_colorspace.h"
70#include "kis_paint_device.h"
71#include "kis_paint_layer.h"
72#include "wdgrawimport.h"
73
74typedef KGenericFactory<KisRawImport, KoFilter> KisRawImportFactory;
75K_EXPORT_COMPONENT_FACTORY(libchalk_raw_import, KisRawImportFactory("kofficefilters"))
76
77KisRawImport::KisRawImport(KoFilter *, const char *, const TQStringList&)
78 : KoFilter()
79 , m_data(0)
80 , m_process(0)
81 , m_progress(0)
82 , m_err(false)
83{
84 m_dialog = new KDialogBase();
85 m_dialog->enableButtonApply(false);
86 m_page = new WdgRawImport(m_dialog);
87 m_dialog -> setMainWidget(m_page);
88
89 connect(m_page->bnPreview, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotUpdatePreview()));
90 connect(m_page->grpColorSpace, TQ_SIGNAL(clicked( int )), this, TQ_SLOT(slotFillCmbProfiles()));
91 connect(m_page->grpChannelDepth, TQ_SIGNAL(clicked( int )), this, TQ_SLOT(slotFillCmbProfiles()));
92
93 KisConfig cfg;
94 TQString monitorProfileName = cfg.monitorProfile();
95 m_monitorProfile = KisMetaRegistry::instance()->csRegistry()->getProfileByName(monitorProfileName);
96
97 slotFillCmbProfiles();
98}
99
100KisRawImport::~KisRawImport()
101{
102 delete m_dialog;
103 delete m_process;
104}
105
106KoFilter::ConversionStatus KisRawImport::convert(const TQCString& from, const TQCString& to)
107{
108 if (from != "image/x-raw" || to != "application/x-chalk") {
109 return KoFilter::NotImplemented;
110 }
111
112 if (m_err) {
113 return KoFilter::CreationError;
114 }
115
116 kdDebug(41008) << "Chalk importing from Raw\n";
117
118 KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument());
119 if (!doc) {
120 return KoFilter::CreationError;
121 }
122
123 doc -> prepareForImport();
124
125 TQString filename = m_chain -> inputFile();
126
127 if (filename.isEmpty()) {
128 return KoFilter::FileNotFound;
129 }
130
131 slotUpdatePreview();
132
133 // Show dialog
134 m_dialog->setCursor(TQt::ArrowCursor);
135 TQApplication::setOverrideCursor(TQt::ArrowCursor);
136
137 TDEConfig * cfg = TDEGlobal::config();
138 cfg->setGroup("rawimport");
139
140 m_page->radioGray->setChecked(cfg->readBoolEntry("gray", false));
141 m_page->radioRGB->setChecked(cfg->readBoolEntry("rgb", true));
142 m_page->radio8->setChecked(cfg->readBoolEntry("8bit", false));
143 m_page->radio16->setChecked(cfg->readBoolEntry("16bit", true));
144 m_page->chkFourColorRGB->setChecked( cfg->readBoolEntry("four_color_rgb", false));
145 m_page->chkCameraColors->setChecked( cfg->readBoolEntry("camera_colors", false));
146 m_page->chkBrightness->setChecked( cfg->readBoolEntry("do_brightness", false));
147 m_page->chkBlackpoint->setChecked( cfg->readBoolEntry("do_blackpoint", false));
148 m_page->chkRed->setChecked( cfg->readBoolEntry("do_red", false));
149 m_page->chkBlue->setChecked( cfg->readBoolEntry("do_blue", false));
150 m_page->radioFixed->setChecked( cfg->readBoolEntry("fixed_wb", true));
151 m_page->radioAutomatic->setChecked( cfg->readBoolEntry("automatic_wb", false));
152 m_page->radioCamera->setChecked( cfg->readBoolEntry("camera_wb", false));
153 m_page->chkClip->setChecked( cfg->readBoolEntry("clip", true));
154 m_page->chkProfile->setChecked(cfg->readBoolEntry("useprofile", false));
155 m_page->dblBrightness->setValue(cfg->readDoubleNumEntry("brightness", 1.0));
156 m_page->dblBlackpoint->setValue(cfg->readDoubleNumEntry("blackpoint", 0));
157 m_page->dblRed->setValue(cfg->readDoubleNumEntry("red", 1.0));
158 m_page->dblBlue->setValue(cfg->readDoubleNumEntry("blue", 1.0));
159
160 if (m_dialog->exec() == TQDialog::Accepted) {
161
162 cfg->writeEntry("gray", m_page->radioGray->isChecked());
163 cfg->writeEntry("rgb", m_page->radioRGB->isChecked());
164 cfg->writeEntry("8bit", m_page->radio8->isChecked());
165 cfg->writeEntry("16bit", m_page->radio16->isChecked());
166 cfg->writeEntry("four_color_rgb", m_page->chkFourColorRGB -> isChecked());
167 cfg->writeEntry("camera_colors", m_page->chkCameraColors -> isChecked());
168 cfg->writeEntry("do_brightness", m_page->chkBrightness -> isChecked());
169 cfg->writeEntry("do_blackpoint", m_page->chkBlackpoint -> isChecked());
170 cfg->writeEntry("do_red", m_page->chkRed -> isChecked());
171 cfg->writeEntry("do_blue", m_page->chkBlue -> isChecked());
172 cfg->writeEntry("fixed_wb", m_page->radioFixed -> isChecked());
173 cfg->writeEntry("automatic_wb", m_page->radioAutomatic -> isChecked());
174 cfg->writeEntry("camera_wb", m_page->radioCamera -> isChecked());
175 cfg->writeEntry("clip", m_page->chkClip->isChecked());
176 cfg->writeEntry("useprofile", m_page->chkProfile->isChecked());
177 cfg->writeEntry("brightness", m_page->dblBrightness->value());
178 cfg->writeEntry("blackpoint", m_page->dblBlackpoint->value());
179 cfg->writeEntry("red", m_page->dblRed->value());
180 cfg->writeEntry("blue", m_page->dblBlue->value());
181
182 TQApplication::setOverrideCursor(TQt::waitCursor);
183 // Create a busy indicator to show that we didn't die or so
184 m_progress = new TQProgressDialog();
185 m_progress -> setTotalSteps(0);
186 m_progress -> setCancelButton(0);
187 TQTimer timer;
188 connect(&timer, TQ_SIGNAL(timeout()), this, TQ_SLOT(incrementProgress()));
189 timer.start(200);
190
191 doc -> undoAdapter() -> setUndo(false);
192
193 getImageData(createArgumentList(false));
194
195 KisImageSP image = 0;
196 KisPaintLayerSP layer = 0;
197 KisPaintDeviceSP device = 0;
198
199 TQApplication::restoreOverrideCursor();
200
201 delete m_progress;
202 m_progress = 0;
203
204 if (m_page->radio8->isChecked()) {
205 // 8 bits
206
207 TQImage img;
208 img.loadFromData(*m_data);
209
210 KisColorSpace * cs = 0;
211 if (m_page->radioGray->isChecked()) {
212 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("GRAYA"), profile() );
213 }
214 else {
215 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("RGBA"), profile() );
216 }
217 if (cs == 0) { kdDebug() << "No CS\n"; return KoFilter::InternalError; }
218
219 image = new KisImage(doc->undoAdapter(), img.width(), img.height(), cs, filename);
220 if (image == 0) return KoFilter::CreationError;
221 image->blockSignals(true); // Don't send out signals while we're building the image
222
223 layer = dynamic_cast<KisPaintLayer*>( image->newLayer(image -> nextLayerName(), OPACITY_OPAQUE).data() );
224 if (layer == 0) return KoFilter::CreationError;
225
226 device = layer->paintDevice();
227 if (device == 0) return KoFilter::CreationError;
228
229 device->convertFromTQImage(img, "");
230
231 } else {
232 // 16 bits
233
234 TQ_UINT32 startOfImagedata = 0;
235 TQSize sz = determineSize(startOfImagedata);
236
237 kdDebug(41008) << "Total bytes: " << m_data->size()
238 << "\n start of image data: " << startOfImagedata
239 << "\n bytes for pixels left: " << m_data->size() - startOfImagedata
240 << "\n total pixels: " << sz.width() * sz.height()
241 << "\n total pixel bytes: " << sz.width() * sz.height() * 6
242 << "\n total necessary bytes: " << (sz.width() * sz.height() * 6) + startOfImagedata
243 << "\n";
244
245
246 char * data = m_data->data() + startOfImagedata;
247
248 KisColorSpace * cs = 0;
249 if (m_page->radioGray->isChecked()) {
250 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("GRAYA16"), profile() );
251 }
252 else {
253 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("RGBA16"), profile() );
254 }
255 if (cs == 0) return KoFilter::InternalError;
256
257 image = new KisImage( doc->undoAdapter(), sz.width(), sz.height(), cs, filename);
258 if (image == 0)return KoFilter::CreationError;
259
260 layer = dynamic_cast<KisPaintLayer*> (image->newLayer(image -> nextLayerName(), OPACITY_OPAQUE).data());
261 if (layer == 0) return KoFilter::CreationError;
262
263 device = layer->paintDevice();
264 if (device == 0) return KoFilter::CreationError;
265
266 // Copy the colordata to the pixels
267 int pos = 0;
268
269 for (int line = 0; line < sz.height(); ++line) {
270 KisHLineIterator it = device->createHLineIterator(0, line, sz.width(), true);
271
272 while (!it.isDone()) {
273 if (m_page->radioGray->isChecked()) {
274 TQ_UINT16 d = (TQ_INT16)*(data + pos);
275 d = ntohs(d);
276 memcpy(it.rawData(), &d, 2);
277 pos += 2;
278 }
279 else {
280 // Red
281 TQ_UINT16 d = (TQ_INT16)*(data + pos);
282 d = ntohs(d);
283 memcpy(it.rawData() + 4, &d, 2);
284
285 // Green
286 pos += 2;
287 d = (TQ_INT16)*(data + pos );
288 d = ntohs(d);
289 memcpy(it.rawData() + 2, &d, 2);
290
291 // Blue
292 pos += 2;
293 d = (TQ_INT16)*(data + pos );
294 d = ntohs(d);
295 memcpy(it.rawData(), &d, 2);
296
297 pos += 2;
298 }
299 cs->setAlpha(it.rawData(), OPACITY_OPAQUE, 1);
300 ++it;
301 }
302 }
303 }
304 layer->setDirty();
305 kdDebug() << "going to set image\n";
306 doc -> setCurrentImage(image);
307 doc -> undoAdapter() -> setUndo(true);
308 doc -> setModified(false);
309 kdDebug() << "everything ok\n";
310
311 TQApplication::restoreOverrideCursor();
312 return KoFilter::OK;
313 }
314
315 TQApplication::restoreOverrideCursor();
316 return KoFilter::UserCancelled;
317}
318
319void KisRawImport::incrementProgress()
320{
321 m_progress -> setProgress(m_progress -> progress() + 10);
322}
323
324
325void KisRawImport::slotUpdatePreview()
326{
327 TQApplication::setOverrideCursor(TQt::waitCursor);
328 getImageData(createArgumentList(true));
329
330 kdDebug(41008) << "Retrieved " << m_data->size() << " bytes of image data\n";
331
332 if (m_data->isNull()) return;
333
334 TQImage img;
335
336 if (m_page->radio8->isChecked()) {
337 // 8 bits
338 img.loadFromData(*m_data);
339
340 } else {
341 // 16 bits
342
343 TQ_UINT32 startOfImagedata = 0;
344 TQSize sz = determineSize(startOfImagedata);
345
346 kdDebug(41008) << "Total bytes: " << m_data->size()
347 << "\n start of image data: " << startOfImagedata
348 << "\n bytes for pixels left: " << m_data->size() - startOfImagedata
349 << "\n total pixels: " << sz.width() * sz.height()
350 << "\n total pixel bytes: " << sz.width() * sz.height() * 6
351 << "\n total necessary bytes: " << (sz.width() * sz.height() * 6) + startOfImagedata
352 << "\n";
353
354 char * data = m_data->data() + startOfImagedata;
355
356 KisColorSpace * cs = 0;
357 if (m_page->radioGray->isChecked()) {
358 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("GRAYA16"), profile() );
359 }
360 else {
361 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace( KisID("RGBA16"), profile() );
362 }
363 KisPaintDevice * dev = new KisPaintDevice(cs, "preview");
364 // Copy the colordata to the pixels
365 int pos = 0;
366
367 for (int line = 0; line < sz.height(); ++line) {
368 KisHLineIterator it = dev->createHLineIterator(0, line, sz.width(), true);
369
370 while (!it.isDone()) {
371 if (m_page->radioGray->isChecked()) {
372 TQ_UINT16 d = (TQ_INT16)*(data + pos);
373 d = ntohs(d);
374 memcpy(it.rawData(), &d, 2);
375 pos += 2;
376 }
377 else {
378 // Red
379 TQ_UINT16 d = (TQ_INT16)*(data + pos);
380 d = ntohs(d);
381 memcpy(it.rawData() + 4, &d, 2);
382
383 // Green
384 pos += 2;
385 d = (TQ_INT16)*(data + pos );
386 d = ntohs(d);
387 memcpy(it.rawData() + 2, &d, 2);
388
389 // Blue
390 pos += 2;
391 d = (TQ_INT16)*(data + pos );
392 d = ntohs(d);
393 memcpy(it.rawData(), &d, 2);
394
395 pos += 2;
396 }
397 cs->setAlpha(it.rawData(), OPACITY_OPAQUE, 1);
398 ++it;
399 }
400 }
401
402 img = dev->convertToTQImage(m_monitorProfile);
403 }
404
405 m_page->lblPreview->setImage(img);
406 TQApplication::restoreOverrideCursor();
407}
408
409
410void KisRawImport::getImageData( TQStringList arguments )
411{
412 // delete m_process;
413 delete m_data;
414
415 kdDebug(41008) << "getImageData " << arguments.join(" ") << "\n";
416 TDEProcess process (this);
417 m_data = new TQByteArray(0);
418
419 for (TQStringList::iterator it = arguments.begin(); it != arguments.end(); ++it) {
420 process << *it;
421 }
422
423 process.setUseShell(true);
424 connect(&process, TQ_SIGNAL(receivedStdout(TDEProcess *, char *, int)), this, TQ_SLOT(slotReceivedStdout(TDEProcess *, char *, int)));
425 connect(&process, TQ_SIGNAL(receivedStderr(TDEProcess *, char *, int)), this, TQ_SLOT(slotReceivedStderr(TDEProcess *, char *, int)));
426 connect(&process, TQ_SIGNAL(processExited(TDEProcess *)), this, TQ_SLOT(slotProcessDone()));
427
428
429 kdDebug(41008) << "Starting process\n";
430
431 if (!process.start(TDEProcess::NotifyOnExit, TDEProcess::AllOutput)) {
432 KMessageBox::error( 0, i18n("Cannot convert RAW files because the dcraw executable could not be started."));
433 }
434 while (process.isRunning()) {
435 //kdDebug(41008) << "Waiting...\n";
436 tqApp->eventLoop()->processEvents(TQEventLoop::ExcludeUserInput);
437 //process.wait(2);
438 }
439
440 if (process.normalExit()) {
441 kdDebug(41008) << "Return value of process: " << process.exitStatus() << "\n";
442 }
443 else {
444 kdDebug(41008) << "Process did not exit normally. Exit signal: " << process.exitSignal() << "\n";
445 m_err = true;
446 }
447
448}
449
450
451void KisRawImport::slotProcessDone()
452{
453 kdDebug(41008) << "process done!\n";
454}
455
456void KisRawImport::slotReceivedStdout(TDEProcess *, char *buffer, int buflen)
457{
458 //kdDebug(41008) << "stdout received " << buflen << " bytes on stdout.\n";
459 //kdDebug(41008) << TQString::fromAscii(buffer, buflen) << "\n";
460 int oldSize = m_data->size();
461 m_data->resize(oldSize + buflen, TQGArray::SpeedOptim);
462 memcpy(m_data->data() + oldSize, buffer, buflen);
463}
464
465void KisRawImport::slotReceivedStderr(TDEProcess *, char *buffer, int buflen)
466{
467 TQByteArray b(buflen);
468 memcpy(b.data(), buffer, buflen);
469 kdDebug(41008) << TQString(b) << "\n";
470 KMessageBox::error(0, i18n("Error: Dcraw cannot load this image. Message: ") + TQString(b));
471 m_err = true;
472}
473
474
475TQStringList KisRawImport::createArgumentList(bool forPreview)
476{
477 TQStringList args;
478
479 args.append("dcraw"); // XXX: Create a chalkdcraw so we can count on it being available
480
481 //args.append("-v"); // Verbose
482
483 args.append("-c"); // Write to stdout
484
485 if (forPreview) {
486 args.append("-h"); // Fast, half size image
487 }
488
489 if (m_page->radio8->isChecked()) {
490 args.append("-2"); // 8 bits
491 }
492 else {
493 args.append("-4"); // 16 bits
494 }
495
496 if (m_page->radioGray->isChecked()) {
497 args.append("-d"); // Create grayscale image
498 }
499
500 if (m_page->chkCameraColors->isChecked()) {
501 args.append("-m"); // Use camera raw colors instead of sRGB
502 }
503
504 if (m_page->radioAutomatic->isChecked()) {
505 args.append("-a"); // Automatic white balancing
506 }
507
508 if (m_page->radioCamera->isChecked()) {
509 args.append("-w"); // Use camera white balance, if present
510 }
511
512 if (m_page->chkFourColorRGB->isChecked()) {
513 args.append("-f"); // Interpolate RGB as four colors
514 }
515
516 if (!m_page->chkClip->isChecked()) {
517 args.append("-n"); // Do not clip colors
518 }
519
520 if (m_page->chkBrightness->isChecked()) {
521 args.append("-b " + TQString::number(m_page->dblBrightness->value()));
522 }
523
524 if (m_page->chkBlackpoint->isChecked()) {
525 args.append("-k " + TQString::number(m_page->dblBlackpoint->value()));
526 }
527
528 if (m_page->chkRed->isChecked()) {
529 args.append("-r " + TQString::number(m_page->dblRed->value()));
530 }
531
532 if (m_page->chkBlue->isChecked()) {
533 args.append("-l " + TQString::number(m_page->dblBlue->value()));
534 }
535
536
537 KisProfile * pf = profile();
538 if (m_page->chkProfile->isChecked()) {
539 if (!pf->filename().isNull()) {
540 // Use the user-set profile, if it's not an lcms internal
541 // profile. This does not add the profile to the image, we
542 // need to do that later.
543 args.append("-p \"" + pf->filename() + "\"");
544 }
545 }
546
547 // Don't forget the filename
548 args.append("\"" + m_chain -> inputFile() + "\"");
549
550 return args;
551}
552
553TQSize KisRawImport::determineSize(TQ_UINT32& startOfImageData)
554{
555 if (m_data->isNull() || m_data->size() < 2048) {
556 startOfImageData = 0;
557 return TQSize(0,0);
558 }
559
560 TQString magick = TQString::fromAscii(m_data->data(), 2);
561 if (magick != "P6") {
562 kdDebug(41008) << " Bad magick! " << magick << "\n";
563 startOfImageData = 0;
564 return TQSize(0,0);
565 }
566
567 // Find the third newline that marks the header end in a dcraw generated ppm.
568 TQ_UINT32 i = 0;
569 TQ_UINT32 counter = 0;
570
571 while (true) {
572 if (counter == 3) break;
573 if (m_data->data()[i] == '\n') {
574 counter++;
575 }
576 ++i;
577 }
578
579 TQString size = TQStringList::split("\n", TQString::fromAscii(m_data->data(), i))[1];
580 kdDebug(41008) << "Header: " << TQString(TQString::fromAscii(m_data->data(), i)) << "\n";
581 TQStringList sizelist = TQStringList::split(" ", size);
582 TQ_INT32 w = sizelist[0].toInt();
583 TQ_INT32 h = sizelist[1].toInt();
584
585 startOfImageData = i;
586 return TQSize(w, h);
587
588}
589
590KisProfile * KisRawImport::profile()
591{
592 if (m_page->chkProfile->isChecked()) {
593 return KisMetaRegistry::instance()->csRegistry()->getProfileByName(m_page->cmbProfile->currentText());
594 }
595 else
596 return 0;
597}
598
599void KisRawImport::slotFillCmbProfiles()
600{
601 KisID s = getColorSpace();
602
603 KisColorSpaceFactory * csf = KisMetaRegistry::instance()->csRegistry() -> get(s);
604 m_page -> cmbProfile -> clear();
605 TQValueVector<KisProfile *> profileList = KisMetaRegistry::instance()->csRegistry()->profilesFor( csf );
606 TQValueVector<KisProfile *> ::iterator it;
607 for ( it = profileList.begin(); it != profileList.end(); ++it ) {
608 m_page -> cmbProfile -> insertItem((*it) -> productName());
609 }
610}
611
612KisID KisRawImport::getColorSpace()
613{
614 if (m_page->radioRGB->isChecked()) {
615 if (m_page->radio16->isChecked()) {
616 return KisID( "RGBA16" );
617 }
618 }
619 else {
620 if (m_page->radio16->isChecked()) {
621 return KisID( "GRAYA16" );
622 }
623 else {
624 return KisID( "GRAYA" );
625 }
626 }
627 return KisID("RGBA");
628}
629
630#include "kis_raw_import.moc"