filters

gmagick/kis_image_magick_converter.cpp
1/*
2 * Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
3 * Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24
25#include <magick/api.h>
26
27#include <tqfile.h>
28#include <tqfileinfo.h>
29#include <tqstring.h>
30
31#include <tdeapplication.h>
32#include <tdelocale.h>
33#include <kurl.h>
34#include <tdeio/netaccess.h>
35
36#include <tqcolor.h>
37
38#include "kis_types.h"
39#include "kis_global.h"
40#include "kis_doc.h"
41#include "kis_image.h"
42#include "kis_layer.h"
43#include "kis_undo_adapter.h"
44#include "kis_image_magick_converter.h"
45#include "kis_meta_registry.h"
46#include "kis_colorspace_factory_registry.h"
47#include "kis_iterators_pixel.h"
48#include "kis_colorspace.h"
49#include "kis_profile.h"
50#include "kis_annotation.h"
51#include "kis_paint_layer.h"
52#include "kis_group_layer.h"
53#include "kis_paint_device.h"
54
55#include "../../../config.h"
56
57namespace {
58
59 const TQ_UINT8 PIXEL_BLUE = 0;
60 const TQ_UINT8 PIXEL_GREEN = 1;
61 const TQ_UINT8 PIXEL_RED = 2;
62 const TQ_UINT8 PIXEL_ALPHA = 3;
63
64 static const TQ_UINT8 PIXEL_CYAN = 0;
65 static const TQ_UINT8 PIXEL_MAGENTA = 1;
66 static const TQ_UINT8 PIXEL_YELLOW = 2;
67 static const TQ_UINT8 PIXEL_BLACK = 3;
68 static const TQ_UINT8 PIXEL_CMYK_ALPHA = 4;
69
70 static const TQ_UINT8 PIXEL_GRAY = 0;
71 static const TQ_UINT8 PIXEL_GRAY_ALPHA = 1;
72
77 TQString getColorSpaceName(ColorspaceType type, unsigned long imageDepth = 8)
78 {
79
80 if (type == GRAYColorspace) {
81 if (imageDepth == 8)
82 return "GRAYA";
83 else if ( imageDepth == 16 )
84 return "GRAYA16" ;
85 }
86 else if (type == CMYKColorspace) {
87 if (imageDepth == 8)
88 return "CMYK";
89 else if ( imageDepth == 16 ) {
90 return "CMYK16";
91 }
92 }
93 else if (type == LABColorspace) {
94 kdDebug(41008) << "Lab!\n";
95 return "LABA";
96 }
97 else if (type == RGBColorspace || type == sRGBColorspace || type == TransparentColorspace) {
98 if (imageDepth == 8)
99 return "RGBA";
100 else if (imageDepth == 16)
101 return "RGBA16";
102 }
103 return "";
104
105 }
106
107 ColorspaceType getColorTypeforColorSpace( KisColorSpace * cs )
108 {
109 if ( cs->id() == KisID("GRAYA") || cs->id() == KisID("GRAYA16") ) return GRAYColorspace;
110 if ( cs->id() == KisID("RGBA") || cs->id() == KisID("RGBA16") ) return RGBColorspace;
111 if ( cs->id() == KisID("CMYK") || cs->id() == KisID("CMYK16") ) return CMYKColorspace;
112 if ( cs->id() == KisID("LABA") ) return LABColorspace;
113
114 kdDebug(41008) << "Cannot export images in " + cs->id().name() + " yet.\n";
115 return RGBColorspace;
116
117 }
118
119 KisProfile * getProfileForProfileInfo(const Image * image)
120 {
121 size_t length;
122
123 const unsigned char * profiledata = GetImageProfile(image, "ICM", &length);
124 if( profiledata == NULL )
125 return 0;
126 TQByteArray rawdata;
127 rawdata.resize(length);
128 memcpy(rawdata.data(), profiledata, length);
129
130 KisProfile* p = new KisProfile(rawdata);
131 return p;
132
133#if 0
134 return 0;
135
136 if (image->profiles == NULL)
137 return 0;
138
139 const char *name;
140 const StringInfo *profile;
141
142 KisProfile * p = 0;
143
144 ResetImageProfileIterator(image);
145 for (name = GetNextImageProfile(image); name != (char *) NULL; )
146 {
147 profile = GetImageProfile(image, name);
148 if (profile == (StringInfo *) NULL)
149 continue;
150
151 // XXX: Hardcoded for icc type -- is that correct for us?
152 if (TQString::compare(name, "icc") == 0) {
153 TQByteArray rawdata;
154 rawdata.resize(profile->length);
155 memcpy(rawdata.data(), profile->datum, profile->length);
156
157 p = new KisProfile(rawdata);
158 if (p == 0)
159 return 0;
160 }
161 name = GetNextImageProfile(image);
162 }
163 return p;
164#endif
165 }
166
167 void setAnnotationsForImage(const Image * src, KisImageSP image)
168 {
169 size_t length;
170
171 const unsigned char * profiledata = GetImageProfile(src, "IPTC", &length);
172 if( profiledata != NULL )
173 {
174 TQByteArray rawdata;
175 rawdata.resize(length);
176 memcpy(rawdata.data(), profiledata, length);
177
178 KisAnnotation* annotation = new KisAnnotation(TQString("IPTC"), "", rawdata);
179 TQ_CHECK_PTR(annotation);
180
181 image -> addAnnotation(annotation);
182 }
183/* This code is no longer needed or supported by graphicsmagick */
184#if 0
185 for(int i = 0; i < src->generic_profiles; i++)
186 {
187 TQByteArray rawdata;
188 rawdata.resize(length);
189 memcpy(rawdata.data(), src->generic_profile[i].info, src->generic_profile[i].length);
190
191 KisAnnotation* annotation = new KisAnnotation(TQString(src->generic_profile[i].name), "", rawdata);
192 TQ_CHECK_PTR(annotation);
193
194 image -> addAnnotation(annotation);
195 }
196#endif
197
198 const ImageAttribute* imgAttr = GetImageAttribute(src, NULL);
199 while(imgAttr)
200 {
201 TQByteArray rawdata;
202 int len = strlen(imgAttr -> value) + 1;
203 rawdata.resize(len);
204 memcpy(rawdata.data(), imgAttr -> value, len);
205
206 KisAnnotation* annotation = new KisAnnotation( TQString("chalk_attribute:%1").arg(TQString(imgAttr -> key)), "", rawdata );
207 TQ_CHECK_PTR(annotation);
208
209 image -> addAnnotation(annotation);
210
211 imgAttr = imgAttr->next;
212 }
213#if 0
214 return;
215 if (src->profiles == NULL)
216 return;
217
218 const char *name = 0;
219 const StringInfo *profile;
220 KisAnnotation* annotation = 0;
221
222 // Profiles and so
223 ResetImageProfileIterator(src);
224 while((name = GetNextImageProfile(src))) {
225 profile = GetImageProfile(src, name);
226 if (profile == (StringInfo *) NULL)
227 continue;
228
229 // XXX: icc will be written seperately?
230 if (TQString::compare(name, "icc") == 0)
231 continue;
232
233 TQByteArray rawdata;
234 rawdata.resize(profile->length);
235 memcpy(rawdata.data(), profile->datum, profile->length);
236
237 annotation = new KisAnnotation(TQString(name), "", rawdata);
238 TQ_CHECK_PTR(annotation);
239
240 image -> addAnnotation(annotation);
241 }
242
243 // Attributes, since we have no hint on if this is an attribute or a profile
244 // annotation, we prefix it with 'chalk_attribute:'. XXX This needs to be rethought!
245 // The joys of imagemagick. From at version 6.2.1 (dfaure has 6.2.0 and confirms the
246 // old way of doing things) they changed the src -> attributes
247 // to void* and require us to use the iterator functions. So we #if around that, *sigh*
248#if MagickLibVersion >= 0x621
249 const ImageAttribute * attr;
250 ResetImageAttributeIterator(src);
251 while ( (attr = GetNextImageAttribute(src)) ) {
252#else
253 ImageAttribute * attr = src -> attributes;
254 while (attr) {
255#endif
256 TQByteArray rawdata;
257 int len = strlen(attr -> value) + 1;
258 rawdata.resize(len);
259 memcpy(rawdata.data(), attr -> value, len);
260
261 annotation = new KisAnnotation(
262 TQString("chalk_attribute:%1").arg(TQString(attr -> key)), "", rawdata);
263 TQ_CHECK_PTR(annotation);
264
265 image -> addAnnotation(annotation);
266#if MagickLibVersion < 0x620
267 attr = attr -> next;
268#endif
269 }
270
271#endif
272 }
273 }
274
275 void exportAnnotationsForImage(Image * dst, vKisAnnotationSP_it& it, vKisAnnotationSP_it& annotationsEnd)
276 {
277 while(it != annotationsEnd) {
278 if (!(*it) || (*it) -> type() == TQString()) {
279 kdDebug(41008) << "Warning: empty annotation" << endl;
280 ++it;
281 continue;
282 }
283
284 kdDebug(41008) << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size() << endl;
285
286 if ((*it) -> type().startsWith("chalk_attribute:")) { // Attribute
287 if (!SetImageAttribute(dst,
288 (*it) -> type().mid(strlen("chalk_attribute:")).ascii(),
289 (*it) -> annotation() . data()) ) {
290 kdDebug(41008) << "Storing of attribute " << (*it) -> type() << "failed!\n";
291 }
292 } else { // Profile
293 unsigned char * profiledata = new unsigned char[(*it) -> annotation() . size()];
294 memcpy( profiledata, (*it) -> annotation() . data(), (*it) -> annotation() . size());
295 if (!ProfileImage(dst, (*it) -> type().ascii(),
296 profiledata, (*it) -> annotation() . size(), MagickFalse)) {
297 kdDebug(41008) << "Storing failed!" << endl;
298 }
299 }
300 ++it;
301 }
302 }
303
304
305 void InitGlobalMagick()
306 {
307 static bool init = false;
308
309 if (!init) {
310 TDEApplication *app = TDEApplication::kApplication();
311
312 InitializeMagick(*app -> argv());
313 atexit(DestroyMagick);
314 init = true;
315 }
316 }
317
318 /*
319 * ImageMagick progress monitor callback. Unfortunately it doesn't support passing in some user
320 * data which complicates things quite a bit. The plan was to allow the user start multiple
321 * import/scans if he/she so wished. However, without passing user data it's not possible to tell
322 * on which task we have made progress on.
323 *
324 * Additionally, ImageMagick is thread-safe, not re-entrant... i.e. IM does not relinquish held
325 * locks when calling user defined callbacks, this means that the same thread going back into IM
326 * would deadlock since it would try to acquire locks it already holds.
327 */
328#if 0
329 MagickBooleanType monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *)
330 {
331 TDEApplication *app = TDEApplication::kApplication();
332
333 Q_ASSERT(app);
334
335 if (app -> hasPendingEvents())
336 app -> processEvents();
337
338 printf("%s\n", text);
339 return MagickTrue;
340 }
341#else
342 unsigned int monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *)
343 {
344 TDEApplication *app = TDEApplication::kApplication();
345
346 Q_ASSERT(app);
347
348 if (app -> hasPendingEvents())
349 app -> processEvents();
350
351 printf("%s\n", text);
352 return true;
353 }
354#endif
355
356
357
358KisImageMagickConverter::KisImageMagickConverter(KisDoc *doc, KisUndoAdapter *adapter)
359{
360 InitGlobalMagick();
361 init(doc, adapter);
362 SetMonitorHandler(monitor);
363 m_stop = false;
364}
365
366KisImageMagickConverter::~KisImageMagickConverter()
367{
368}
369
370KisImageBuilder_Result KisImageMagickConverter::decode(const KURL& uri, bool isBlob)
371{
372 Image *image;
373 Image *images;
374 ExceptionInfo ei;
375 ImageInfo *ii;
376
377 if (m_stop) {
378 m_img = 0;
379 return KisImageBuilder_RESULT_INTR;
380 }
381
382 GetExceptionInfo(&ei);
383 ii = CloneImageInfo(0);
384
385 if (isBlob) {
386
387 // TODO : Test. Does BlobToImage even work?
388 Q_ASSERT(uri.isEmpty());
389 images = BlobToImage(ii, &m_data[0], m_data.size(), &ei);
390 } else {
391
392 tqstrncpy(ii -> filename, TQFile::encodeName(uri.path()), MaxTextExtent - 1);
393
394 if (ii -> filename[MaxTextExtent - 1]) {
395 emit notifyProgressError();
396 return KisImageBuilder_RESULT_PATH;
397 }
398
399 images = ReadImage(ii, &ei);
400
401 }
402
403 if (ei.severity != UndefinedException)
404 {
405 CatchException(&ei);
406 kdDebug(41008) << "Exceptions happen when loading" << endl;
407 return KisImageBuilder_RESULT_FAILURE;
408 }
409
410
411 if (images == 0) {
412 DestroyImageInfo(ii);
413 DestroyExceptionInfo(&ei);
414 emit notifyProgressError();
415 return KisImageBuilder_RESULT_FAILURE;
416 }
417
418 emit notifyProgressStage(i18n("Importing..."), 0);
419
420 m_img = 0;
421
422 while ((image = RemoveFirstImageFromList(&images))) {
423 if(image->rows == 0 or image->columns == 0) return KisImageBuilder_RESULT_FAILURE;
424 ViewInfo *vi = OpenCacheView(image);
425
426 // Determine image depth -- for now, all channels of an imported image are of the same depth
427 unsigned long imageDepth = image->depth;
428 kdDebug(41008) << "Image depth: " << imageDepth << "\n";
429
430 TQString csName;
431 KisColorSpace * cs = 0;
432 ColorspaceType colorspaceType;
433
434 // Determine image type -- rgb, grayscale or cmyk
435 if (GetImageType(image, &ei) == GrayscaleType || GetImageType(image, &ei) == GrayscaleMatteType) {
436 if (imageDepth == 8)
437 csName = "GRAYA";
438 else if ( imageDepth == 16 )
439 csName = "GRAYA16" ;
440 colorspaceType = GRAYColorspace;
441 }
442 else {
443 colorspaceType = image->colorspace;
444 csName = getColorSpaceName(image -> colorspace, imageDepth);
445 }
446
447 kdDebug(41008) << "image has " << csName << " colorspace\n";
448
449 KisProfile * profile = getProfileForProfileInfo(image);
450 if (profile)
451 {
452 kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n";
453 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile);
454 }
455 else
456 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),"");
457
458 if (!cs) {
459 kdDebug(41008) << "Chalk does not support colorspace " << image -> colorspace << "\n";
460 CloseCacheView(vi);
461 DestroyImage(image);
462 DestroyExceptionInfo(&ei);
463 DestroyImageList(images);
464 DestroyImageInfo(ii);
465 emit notifyProgressError();
466 return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
467 }
468
469 if( ! m_img) {
470 m_img = new KisImage(m_doc->undoAdapter(), image -> columns, image -> rows, cs, "built image");
471 TQ_CHECK_PTR(m_img);
472 m_img->blockSignals(true); // Don't send out signals while we're building the image
473
474 // XXX I'm assuming seperate layers won't have other profile things like EXIF
475 setAnnotationsForImage(image, m_img);
476 }
477
478 if (image -> columns && image -> rows) {
479
480 // Opacity (set by the photoshop import filter)
481 TQ_UINT8 opacity = OPACITY_OPAQUE;
482 const ImageAttribute * attr = GetImageAttribute(image, "[layer-opacity]");
483 if (attr != 0) {
484 opacity = TQ_UINT8_MAX - ScaleQuantumToChar(TQString(attr->value).toInt());
485 }
486
487 KisPaintLayerSP layer = 0;
488
489 attr = GetImageAttribute(image, "[layer-name]");
490 if (attr != 0) {
491 layer = new KisPaintLayer(m_img, attr->value, opacity);
492 }
493 else {
494 layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), opacity);
495 }
496
497 Q_ASSERT(layer);
498
499 // Layerlocation (set by the photoshop import filter)
500 TQ_INT32 x_offset = 0;
501 TQ_INT32 y_offset = 0;
502
503 attr = GetImageAttribute(image, "[layer-xpos]");
504 if (attr != 0) {
505 x_offset = TQString(attr->value).toInt();
506 }
507
508 attr = GetImageAttribute(image, "[layer-ypos]");
509 if (attr != 0) {
510 y_offset = TQString(attr->value).toInt();
511 }
512
513
514 for (TQ_UINT32 y = 0; y < image->rows; y ++)
515 {
516 const PixelPacket *pp = AcquireCacheView(vi, 0, y, image->columns, 1, &ei);
517
518 if(!pp)
519 {
520 CloseCacheView(vi);
521 DestroyImageList(images);
522 DestroyImageInfo(ii);
523 DestroyExceptionInfo(&ei);
524 emit notifyProgressError();
525 return KisImageBuilder_RESULT_FAILURE;
526 }
527
528 IndexPacket * indexes = GetCacheViewIndexes(vi);
529
530 KisHLineIteratorPixel hiter = layer->paintDevice()->createHLineIterator(0, y, image->columns, true);
531
532 if (colorspaceType== CMYKColorspace) {
533 if (imageDepth == 8) {
534 int x = 0;
535 while (!hiter.isDone())
536 {
537 TQ_UINT8 *ptr= hiter.rawData();
538 *(ptr++) = ScaleQuantumToChar(pp->red); // cyan
539 *(ptr++) = ScaleQuantumToChar(pp->green); // magenta
540 *(ptr++) = ScaleQuantumToChar(pp->blue); // yellow
541 *(ptr++) = ScaleQuantumToChar(indexes[x]); // Black
542// XXX: Warning! This ifdef messes up the paren matching big-time!
543#ifdef HAVE_MAGICK6
544 if (image->matte != MagickFalse) {
545#else
546 if (image->matte == true) {
547#endif
548 *(ptr++) = OPACITY_OPAQUE - ScaleQuantumToChar(pp->opacity);
549 }
550 else {
551 *(ptr++) = OPACITY_OPAQUE;
552 }
553 ++x;
554 pp++;
555 ++hiter;
556 }
557 }
558 }
559 else if (colorspaceType == LABColorspace) {
560 while(! hiter.isDone())
561 {
562 TQ_UINT16 *ptr = reinterpret_cast<TQ_UINT16 *>(hiter.rawData());
563
564 *(ptr++) = ScaleQuantumToShort(pp->red);
565 *(ptr++) = ScaleQuantumToShort(pp->green);
566 *(ptr++) = ScaleQuantumToShort(pp->blue);
567 *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
568
569 pp++;
570 ++hiter;
571 }
572 }
573 else if (colorspaceType == RGBColorspace ||
574 colorspaceType == sRGBColorspace ||
575 colorspaceType == TransparentColorspace)
576 {
577 if (imageDepth == 8) {
578 while(! hiter.isDone())
579 {
580 TQ_UINT8 *ptr= hiter.rawData();
581 // XXX: not colorstrategy and bitdepth independent
582 *(ptr++) = ScaleQuantumToChar(pp->blue);
583 *(ptr++) = ScaleQuantumToChar(pp->green);
584 *(ptr++) = ScaleQuantumToChar(pp->red);
585 *(ptr++) = OPACITY_OPAQUE - ScaleQuantumToChar(pp->opacity);
586
587 pp++;
588 ++hiter;
589 }
590 }
591 else if (imageDepth == 16) {
592 while(! hiter.isDone())
593 {
594 TQ_UINT16 *ptr = reinterpret_cast<TQ_UINT16 *>(hiter.rawData());
595 // XXX: not colorstrategy independent
596 *(ptr++) = ScaleQuantumToShort(pp->blue);
597 *(ptr++) = ScaleQuantumToShort(pp->green);
598 *(ptr++) = ScaleQuantumToShort(pp->red);
599 *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
600
601 pp++;
602 ++hiter;
603 }
604 }
605 }
606 else if ( colorspaceType == GRAYColorspace) {
607 if (imageDepth == 8) {
608 while(! hiter.isDone())
609 {
610 TQ_UINT8 *ptr= hiter.rawData();
611 // XXX: not colorstrategy and bitdepth independent
612 *(ptr++) = ScaleQuantumToChar(pp->blue);
613 *(ptr++) = OPACITY_OPAQUE - ScaleQuantumToChar(pp->opacity);
614
615 pp++;
616 ++hiter;
617 }
618 }
619 else if (imageDepth == 16) {
620 while(! hiter.isDone())
621 {
622 TQ_UINT16 *ptr = reinterpret_cast<TQ_UINT16 *>(hiter.rawData());
623 // XXX: not colorstrategy independent
624 *(ptr++) = ScaleQuantumToShort(pp->blue);
625 *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
626
627 pp++;
628 ++hiter;
629 }
630 }
631 }
632
633 emit notifyProgress(y * 100 / image->rows);
634
635 if (m_stop) {
636 CloseCacheView(vi);
637 DestroyImage(image);
638 DestroyImageList(images);
639 DestroyImageInfo(ii);
640 DestroyExceptionInfo(&ei);
641 m_img = 0;
642 return KisImageBuilder_RESULT_INTR;
643 }
644 }
645 m_img->addLayer(layer.data(), m_img->rootLayer());
646 layer->paintDevice()->move(x_offset, y_offset);
647 }
648
649 emit notifyProgressDone();
650 CloseCacheView(vi);
651 DestroyImage(image);
652 }
653
654 emit notifyProgressDone();
655 DestroyImageList(images);
656 DestroyImageInfo(ii);
657 DestroyExceptionInfo(&ei);
658 return KisImageBuilder_RESULT_OK;
659 }
660
661 KisImageBuilder_Result KisImageMagickConverter::buildImage(const KURL& uri)
662 {
663 if (uri.isEmpty())
664 return KisImageBuilder_RESULT_NO_URI;
665
666 if (!TDEIO::NetAccess::exists(uri, false, tqApp -> mainWidget())) {
667 return KisImageBuilder_RESULT_NOT_EXIST;
668 }
669
670 KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE;
671 TQString tmpFile;
672
673 if (TDEIO::NetAccess::download(uri, tmpFile, tqApp -> mainWidget())) {
674 KURL uriTF;
675 uriTF.setPath( tmpFile );
676 result = decode(uriTF, false);
677 TDEIO::NetAccess::removeTempFile(tmpFile);
678 }
679
680 return result;
681 }
682
683
684 KisImageSP KisImageMagickConverter::image()
685 {
686 return m_img;
687 }
688
689 void KisImageMagickConverter::init(KisDoc *doc, KisUndoAdapter *adapter)
690 {
691 m_doc = doc;
692 m_adapter = adapter;
693 m_job = 0;
694 }
695
696 KisImageBuilder_Result KisImageMagickConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd)
697 {
698 Image *image;
699 ExceptionInfo ei;
700 ImageInfo *ii;
701
702 if (!layer)
703 return KisImageBuilder_RESULT_INVALID_ARG;
704
705 KisImageSP img = layer->image();
706 if (!img)
707 return KisImageBuilder_RESULT_EMPTY;
708
709 if (uri.isEmpty())
710 return KisImageBuilder_RESULT_NO_URI;
711
712 if (!uri.isLocalFile())
713 return KisImageBuilder_RESULT_NOT_LOCAL;
714
715
716 TQ_UINT32 layerBytesPerChannel = layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels();
717
718 GetExceptionInfo(&ei);
719
720 ii = CloneImageInfo(0);
721
722 tqstrncpy(ii -> filename, TQFile::encodeName(uri.path()), MaxTextExtent - 1);
723
724 if (ii -> filename[MaxTextExtent - 1]) {
725 emit notifyProgressError();
726 return KisImageBuilder_RESULT_PATH;
727 }
728
729 if (!img -> width() || !img -> height())
730 return KisImageBuilder_RESULT_EMPTY;
731
732 if (layerBytesPerChannel < 2) {
733 ii->depth = 8;
734 }
735 else {
736 ii->depth = 16;
737 }
738
739 ii->colorspace = getColorTypeforColorSpace(layer->paintDevice()->colorSpace());
740
741 image = AllocateImage(ii);
742// SetImageColorspace(image, ii->colorspace);
743 image -> columns = img -> width();
744 image -> rows = img -> height();
745
746 kdDebug(41008) << "Saving with colorspace " << image->colorspace << ", (" << layer->paintDevice()->colorSpace()->id().name() << ")\n";
747 kdDebug(41008) << "IM Image thinks it has depth: " << image->depth << "\n";
748
749#ifdef HAVE_MAGICK6
750 // if ( layer-> hasAlpha() )
751 image -> matte = MagickTrue;
752 // else
753 // image -> matte = MagickFalse;
754#else
755 // image -> matte = layer -> hasAlpha();
756 image -> matte = true;
757#endif
758
759 TQ_INT32 y, height, width;
760
761 height = img -> height();
762 width = img -> width();
763
764 bool alpha = true;
765 TQString ext = TQFileInfo(TQFile::encodeName(uri.path())).extension(false).upper();
766 if (ext == "BMP") {
767 alpha = false;
768 tqstrncpy(ii->magick, "BMP2", MaxTextExtent - 1);
769 }
770 else if (ext == "RGB") {
771 tqstrncpy(ii->magick, "SGI", MaxTextExtent - 1);
772 }
773
774 for (y = 0; y < height; y++) {
775
776 // Allocate pixels for this scanline
777 PixelPacket * pp = SetImagePixels(image, 0, y, width, 1);
778
779 if (!pp) {
780 DestroyExceptionInfo(&ei);
781 DestroyImage(image);
782 emit notifyProgressError();
783 return KisImageBuilder_RESULT_FAILURE;
784
785 }
786
787 KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, y, width, false);
788 if (alpha)
789 SetImageType(image, TrueColorMatteType);
790 else
791 SetImageType(image, TrueColorType);
792
793 if (image->colorspace== CMYKColorspace) {
794
795 IndexPacket * indexes = GetIndexes(image);
796 int x = 0;
797 if (layerBytesPerChannel == 2) {
798 while (!it.isDone()) {
799
800 const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
801 pp -> red = ScaleShortToQuantum(d[PIXEL_CYAN]);
802 pp -> green = ScaleShortToQuantum(d[PIXEL_MAGENTA]);
803 pp -> blue = ScaleShortToQuantum(d[PIXEL_YELLOW]);
804 if (alpha)
805 pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_CMYK_ALPHA]);
806 indexes[x] = ScaleShortToQuantum(d[PIXEL_BLACK]);
807 x++;
808 pp++;
809 ++it;
810 }
811 }
812 else {
813 while (!it.isDone()) {
814
815 TQ_UINT8 * d = it.rawData();
816 pp -> red = ScaleCharToQuantum(d[PIXEL_CYAN]);
817 pp -> green = ScaleCharToQuantum(d[PIXEL_MAGENTA]);
818 pp -> blue = ScaleCharToQuantum(d[PIXEL_YELLOW]);
819 if (alpha)
820 pp -> opacity = ScaleCharToQuantum(OPACITY_OPAQUE - d[PIXEL_CMYK_ALPHA]);
821 indexes[x]= ScaleCharToQuantum(d[PIXEL_BLACK]);
822
823 x++;
824 pp++;
825 ++it;
826 }
827 }
828 }
829 else if (image->colorspace== RGBColorspace ||
830 image->colorspace == sRGBColorspace ||
831 image->colorspace == TransparentColorspace)
832 {
833 if (layerBytesPerChannel == 2) {
834 while (!it.isDone()) {
835
836 const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
837 pp -> red = ScaleShortToQuantum(d[PIXEL_RED]);
838 pp -> green = ScaleShortToQuantum(d[PIXEL_GREEN]);
839 pp -> blue = ScaleShortToQuantum(d[PIXEL_BLUE]);
840 if (alpha)
841 pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_ALPHA]);
842
843 pp++;
844 ++it;
845 }
846 }
847 else {
848 while (!it.isDone()) {
849
850 TQ_UINT8 * d = it.rawData();
851 pp -> red = ScaleCharToQuantum(d[PIXEL_RED]);
852 pp -> green = ScaleCharToQuantum(d[PIXEL_GREEN]);
853 pp -> blue = ScaleCharToQuantum(d[PIXEL_BLUE]);
854 if (alpha)
855 pp -> opacity = ScaleCharToQuantum(OPACITY_OPAQUE - d[PIXEL_ALPHA]);
856
857 pp++;
858 ++it;
859 }
860 }
861 }
862 else if (image->colorspace == GRAYColorspace)
863 {
864 SetImageType(image, GrayscaleMatteType);
865 if (layerBytesPerChannel == 2) {
866 while (!it.isDone()) {
867
868 const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
869 pp -> red = ScaleShortToQuantum(d[PIXEL_GRAY]);
870 pp -> green = ScaleShortToQuantum(d[PIXEL_GRAY]);
871 pp -> blue = ScaleShortToQuantum(d[PIXEL_GRAY]);
872 if (alpha)
873 pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_GRAY_ALPHA]);
874
875 pp++;
876 ++it;
877 }
878 }
879 else {
880 while (!it.isDone()) {
881 TQ_UINT8 * d = it.rawData();
882 pp -> red = ScaleCharToQuantum(d[PIXEL_GRAY]);
883 pp -> green = ScaleCharToQuantum(d[PIXEL_GRAY]);
884 pp -> blue = ScaleCharToQuantum(d[PIXEL_GRAY]);
885 if (alpha)
886 pp -> opacity = ScaleCharToQuantum(OPACITY_OPAQUE - d[PIXEL_GRAY_ALPHA]);
887
888 pp++;
889 ++it;
890 }
891 }
892 }
893 else {
894 kdDebug(41008) << "Unsupported image format\n";
895 return KisImageBuilder_RESULT_INVALID_ARG;
896 }
897
898 emit notifyProgressStage(i18n("Saving..."), y * 100 / height);
899
900#ifdef HAVE_MAGICK6
901 if (SyncImagePixels(image) == MagickFalse)
902 kdDebug(41008) << "Syncing pixels failed\n";
903#else
904 if (!SyncImagePixels(image))
905 kdDebug(41008) << "Syncing pixels failed\n";
906#endif
907 }
908
909 // set the annotations
910 exportAnnotationsForImage(image, annotationsStart, annotationsEnd);
911
912 // XXX: Write to a temp file, then have Chalk use TDEIO to copy temp
913 // image to remote location.
914
915 WriteImage(ii, image);
916 DestroyExceptionInfo(&ei);
917 DestroyImage(image);
918 emit notifyProgressDone();
919 return KisImageBuilder_RESULT_OK;
920 }
921
922 void KisImageMagickConverter::ioData(TDEIO::Job *job, const TQByteArray& data)
923 {
924 if (data.isNull() || data.isEmpty()) {
925 emit notifyProgressStage(i18n("Loading..."), 0);
926 return;
927 }
928
929 if (m_data.empty()) {
930 Image *image;
931 ImageInfo *ii;
932 ExceptionInfo ei;
933
934 ii = CloneImageInfo(0);
935 GetExceptionInfo(&ei);
936 image = PingBlob(ii, data.data(), data.size(), &ei);
937
938 if (image == 0 || ei.severity == BlobError) {
939 DestroyExceptionInfo(&ei);
940 DestroyImageInfo(ii);
941 job -> kill();
942 emit notifyProgressError();
943 return;
944 }
945
946 DestroyImage(image);
947 DestroyExceptionInfo(&ei);
948 DestroyImageInfo(ii);
949 emit notifyProgressStage(i18n("Loading..."), 0);
950 }
951
952 Q_ASSERT(data.size() + m_data.size() <= m_size);
953 memcpy(&m_data[m_data.size()], data.data(), data.count());
954 m_data.resize(m_data.size() + data.count());
955 emit notifyProgressStage(i18n("Loading..."), m_data.size() * 100 / m_size);
956
957 if (m_stop)
958 job -> kill();
959 }
960
961 void KisImageMagickConverter::ioResult(TDEIO::Job *job)
962 {
963 m_job = 0;
964
965 if (job -> error())
966 emit notifyProgressError();
967
968 decode(KURL(), true);
969 }
970
971 void KisImageMagickConverter::ioTotalSize(TDEIO::Job * /*job*/, TDEIO::filesize_t size)
972 {
973 m_size = size;
974 m_data.reserve(size);
975 emit notifyProgressStage(i18n("Loading..."), 0);
976 }
977
978 void KisImageMagickConverter::cancel()
979 {
980 m_stop = true;
981 }
982
987 TQString KisImageMagickConverter::readFilters()
988 {
989 TQString s;
990 TQString all;
991 TQString name;
992 TQString description;
993 unsigned long matches;
994
995/*#ifdef HAVE_MAGICK6
996#ifdef HAVE_OLD_GETMAGICKINFOLIST
997 const MagickInfo **mi;
998 mi = GetMagickInfoList("*", &matches);
999#else // HAVE_OLD_GETMAGICKINFOLIST
1000 ExceptionInfo ei;
1001 GetExceptionInfo(&ei);
1002 const MagickInfo **mi;
1003 mi = GetMagickInfoList("*", &matches, &ei);
1004 DestroyExceptionInfo(&ei);
1005#endif // HAVE_OLD_GETMAGICKINFOLIST
1006#else // HAVE_MAGICK6*/
1007 const MagickInfo *mi;
1008 ExceptionInfo ei;
1009 GetExceptionInfo(&ei);
1010 mi = GetMagickInfo("*", &ei);
1011 DestroyExceptionInfo(&ei);
1012// #endif // HAVE_MAGICK6
1013
1014 if (!mi)
1015 return s;
1016
1017/*#ifdef HAVE_MAGICK6
1018 for (unsigned long i = 0; i < matches; i++) {
1019 const MagickInfo *info = mi[i];
1020 if (info -> stealth)
1021 continue;
1022
1023 if (info -> decoder) {
1024 name = info -> name;
1025 description = info -> description;
1026 kdDebug(41008) << "Found import filter for: " << name << "\n";
1027
1028 if (!description.isEmpty() && !description.contains('/')) {
1029 all += "*." + name.lower() + " *." + name + " ";
1030 s += "*." + name.lower() + " *." + name + "|";
1031 s += i18n(description.utf8());
1032 s += "\n";
1033 }
1034 }
1035 }
1036#else*/
1037 for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) {
1038 if (mi -> stealth)
1039 continue;
1040 if (mi -> decoder) {
1041 name = mi -> name;
1042 description = mi -> description;
1043 kdDebug(41008) << "Found import filter for: " << name << "\n";
1044
1045 if (!description.isEmpty() && !description.contains('/')) {
1046 all += "*." + name.lower() + " *." + name + " ";
1047 s += "*." + name.lower() + " *." + name + "|";
1048 s += i18n(description.utf8());
1049 s += "\n";
1050 }
1051 }
1052 }
1053// #endif
1054
1055 all += "|" + i18n("All Images");
1056 all += "\n";
1057
1058 return all + s;
1059 }
1060
1061 TQString KisImageMagickConverter::writeFilters()
1062 {
1063 TQString s;
1064 TQString all;
1065 TQString name;
1066 TQString description;
1067 unsigned long matches;
1068
1069/*#ifdef HAVE_MAGICK6
1070#ifdef HAVE_OLD_GETMAGICKINFOLIST
1071 const MagickInfo **mi;
1072 mi = GetMagickInfoList("*", &matches);
1073#else // HAVE_OLD_GETMAGICKINFOLIST
1074 ExceptionInfo ei;
1075 GetExceptionInfo(&ei);
1076 const MagickInfo **mi;
1077 mi = GetMagickInfoList("*", &matches, &ei);
1078 DestroyExceptionInfo(&ei);
1079#endif // HAVE_OLD_GETMAGICKINFOLIST
1080#else // HAVE_MAGICK6*/
1081 const MagickInfo *mi;
1082 ExceptionInfo ei;
1083 GetExceptionInfo(&ei);
1084 mi = GetMagickInfo("*", &ei);
1085 DestroyExceptionInfo(&ei);
1086// #endif // HAVE_MAGICK6
1087
1088 if (!mi) {
1089 kdDebug(41008) << "Eek, no magick info!\n";
1090 return s;
1091 }
1092
1093/*#ifdef HAVE_MAGICK6
1094 for (unsigned long i = 0; i < matches; i++) {
1095 const MagickInfo *info = mi[i];
1096 kdDebug(41008) << "Found export filter for: " << info -> name << "\n";
1097 if (info -> stealth)
1098 continue;
1099
1100 if (info -> encoder) {
1101 name = info -> name;
1102
1103 description = info -> description;
1104
1105 if (!description.isEmpty() && !description.contains('/')) {
1106 all += "*." + name.lower() + " *." + name + " ";
1107 s += "*." + name.lower() + " *." + name + "|";
1108 s += i18n(description.utf8());
1109 s += "\n";
1110 }
1111 }
1112 }
1113#else*/
1114 for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) {
1115 kdDebug(41008) << "Found export filter for: " << mi -> name << "\n";
1116 if (mi -> stealth)
1117 continue;
1118
1119 if (mi -> encoder) {
1120 name = mi -> name;
1121
1122 description = mi -> description;
1123
1124 if (!description.isEmpty() && !description.contains('/')) {
1125 all += "*." + name.lower() + " *." + name + " ";
1126 s += "*." + name.lower() + " *." + name + "|";
1127 s += i18n(description.utf8());
1128 s += "\n";
1129 }
1130 }
1131 }
1132// #endif
1133
1134
1135 all += "|" + i18n("All Images");
1136 all += "\n";
1137
1138 return all + s;
1139 }
1140
1141#include "kis_image_magick_converter.moc"
1142