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