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 InitializeMagick(*app -> argv());
312 atexit(DestroyMagick);
313 init = true;
314 }
315 }
316
317 /*
318 * ImageMagick progress monitor callback. Unfortunately it doesn't support passing in some user
319 * data which complicates things quite a bit. The plan was to allow the user start multiple
320 * import/scans if he/she so wished. However, without passing user data it's not possible to tell
321 * on which task we have made progress on.
322 *
323 * Additionally, ImageMagick is thread-safe, not re-entrant... i.e. IM does not relinquish held
324 * locks when calling user defined callbacks, this means that the same thread going back into IM
325 * would deadlock since it would try to acquire locks it already holds.
326 */
327#if 0
328 MagickBooleanType monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *)
329 {
330 TDEApplication *app = TDEApplication::kApplication();
331 Q_ASSERT(app);
332
333 if (app -> hasPendingEvents())
334 app -> processEvents();
335
336 printf("%s\n", text);
337 return MagickTrue;
338 }
339#else
340 unsigned int monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *)
341 {
342 TDEApplication *app = TDEApplication::kApplication();
343
344 Q_ASSERT(app);
345
346 if (app -> hasPendingEvents())
347 app -> processEvents();
348
349 printf("%s\n", text);
350 return true;
351 }
352#endif
353
354
355
356KisImageMagickConverter::KisImageMagickConverter(KisDoc *doc, KisUndoAdapter *adapter)
357{
358 InitGlobalMagick();
359 init(doc, adapter);
360 SetMonitorHandler(monitor);
361 m_stop = false;
362}
363
364KisImageMagickConverter::~KisImageMagickConverter()
365{
366}
367
368KisImageBuilder_Result KisImageMagickConverter::decode(const KURL& uri, bool isBlob)
369{
370 Image *image;
371 Image *images;
372 ExceptionInfo ei;
373 ImageInfo *ii;
374
375 if (m_stop) {
376 m_img = 0;
377 return KisImageBuilder_RESULT_INTR;
378 }
379
380 GetExceptionInfo(&ei);
381 ii = CloneImageInfo(0);
382
383 if (isBlob) {
384
385 // TODO : Test. Does BlobToImage even work?
386 Q_ASSERT(uri.isEmpty());
387 images = BlobToImage(ii, &m_data[0], m_data.size(), &ei);
388 } else {
389
390 tqstrncpy(ii -> filename, TQFile::encodeName(uri.path()), MaxTextExtent - 1);
391
392 if (ii -> filename[MaxTextExtent - 1]) {
393 emit notifyProgressError();
394 return KisImageBuilder_RESULT_PATH;
395 }
396
397 images = ReadImage(ii, &ei);
398
399 }
400
401 if (ei.severity != UndefinedException)
402 {
403 CatchException(&ei);
404 kdDebug(41008) << "Exceptions happen when loading" << endl;
405 return KisImageBuilder_RESULT_FAILURE;
406 }
407
408
409 if (images == 0) {
410 DestroyImageInfo(ii);
411 DestroyExceptionInfo(&ei);
412 emit notifyProgressError();
413 return KisImageBuilder_RESULT_FAILURE;
414 }
415
416 emit notifyProgressStage(i18n("Importing..."), 0);
417
418 m_img = 0;
419
420 while ((image = RemoveFirstImageFromList(&images))) {
421 if(image->rows == 0 or image->columns == 0) return KisImageBuilder_RESULT_FAILURE;
422 ViewInfo *vi = OpenCacheView(image);
423
424 // Determine image depth -- for now, all channels of an imported image are of the same depth
425 unsigned long imageDepth = image->depth;
426 kdDebug(41008) << "Image depth: " << imageDepth << "\n";
427
428 TQString csName;
429 KisColorSpace * cs = 0;
430 ColorspaceType colorspaceType;
431
432 // Determine image type -- rgb, grayscale or cmyk
433 if (GetImageType(image, &ei) == GrayscaleType || GetImageType(image, &ei) == GrayscaleMatteType) {
434 if (imageDepth == 8)
435 csName = "GRAYA";
436 else if ( imageDepth == 16 )
437 csName = "GRAYA16" ;
438 colorspaceType = GRAYColorspace;
439 }
440 else {
441 colorspaceType = image->colorspace;
442 csName = getColorSpaceName(image -> colorspace, imageDepth);
443 }
444
445 kdDebug(41008) << "image has " << csName << " colorspace\n";
446
447 KisProfile * profile = getProfileForProfileInfo(image);
448 if (profile)
449 {
450 kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n";
451 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile);
452 }
453 else
454 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),"");
455
456 if (!cs) {
457 kdDebug(41008) << "Chalk does not support colorspace " << image -> colorspace << "\n";
458 CloseCacheView(vi);
459 DestroyImage(image);
460 DestroyExceptionInfo(&ei);
461 DestroyImageList(images);
462 DestroyImageInfo(ii);
463 emit notifyProgressError();
464 return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
465 }
466
467 if( ! m_img) {
468 m_img = new KisImage(m_doc->undoAdapter(), image -> columns, image -> rows, cs, "built image");
469 TQ_CHECK_PTR(m_img);
470 m_img->blockSignals(true); // Don't send out signals while we're building the image
471
472 // XXX I'm assuming seperate layers won't have other profile things like EXIF
473 setAnnotationsForImage(image, m_img);
474 }
475
476 if (image -> columns && image -> rows) {
477
478 // Opacity (set by the photoshop import filter)
479 TQ_UINT8 opacity = OPACITY_OPAQUE;
480 const ImageAttribute * attr = GetImageAttribute(image, "[layer-opacity]");
481 if (attr != 0) {
482 opacity = TQ_UINT8_MAX - ScaleQuantumToChar(TQString(attr->value).toInt());
483 }
484
485 KisPaintLayerSP layer = 0;
486
487 attr = GetImageAttribute(image, "[layer-name]");
488 if (attr != 0) {
489 layer = new KisPaintLayer(m_img, attr->value, opacity);
490 }
491 else {
492 layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), opacity);
493 }
494
495 Q_ASSERT(layer);
496
497 // Layerlocation (set by the photoshop import filter)
498 TQ_INT32 x_offset = 0;
499 TQ_INT32 y_offset = 0;
500
501 attr = GetImageAttribute(image, "[layer-xpos]");
502 if (attr != 0) {
503 x_offset = TQString(attr->value).toInt();
504 }
505
506 attr = GetImageAttribute(image, "[layer-ypos]");
507 if (attr != 0) {
508 y_offset = TQString(attr->value).toInt();
509 }
510
511
512 for (TQ_UINT32 y = 0; y < image->rows; y ++)
513 {
514 const PixelPacket *pp = AcquireCacheView(vi, 0, y, image->columns, 1, &ei);
515
516 if(!pp)
517 {
518 CloseCacheView(vi);
519 DestroyImageList(images);
520 DestroyImageInfo(ii);
521 DestroyExceptionInfo(&ei);
522 emit notifyProgressError();
523 return KisImageBuilder_RESULT_FAILURE;
524 }
525
526 IndexPacket * indexes = GetCacheViewIndexes(vi);
527
528 KisHLineIteratorPixel hiter = layer->paintDevice()->createHLineIterator(0, y, image->columns, true);
529
530 if (colorspaceType== CMYKColorspace) {
531 if (imageDepth == 8) {
532 int x = 0;
533 while (!hiter.isDone())
534 {
535 TQ_UINT8 *ptr= hiter.rawData();
536 *(ptr++) = ScaleQuantumToChar(pp->red); // cyan
537 *(ptr++) = ScaleQuantumToChar(pp->green); // magenta
538 *(ptr++) = ScaleQuantumToChar(pp->blue); // yellow
539 *(ptr++) = ScaleQuantumToChar(indexes[x]); // Black
540// XXX: Warning! This ifdef messes up the paren matching big-time!
541#ifdef HAVE_MAGICK6
542 if (image->matte != MagickFalse) {
543#else
544 if (image->matte == true) {
545#endif
546 *(ptr++) = OPACITY_OPAQUE - ScaleQuantumToChar(pp->opacity);
547 }
548 else {
549 *(ptr++) = OPACITY_OPAQUE;
550 }
551 ++x;
552 pp++;
553 ++hiter;
554 }
555 }
556 }
557 else if (colorspaceType == LABColorspace) {
558 while(! hiter.isDone())
559 {
560 TQ_UINT16 *ptr = reinterpret_cast<TQ_UINT16 *>(hiter.rawData());
561
562 *(ptr++) = ScaleQuantumToShort(pp->red);
563 *(ptr++) = ScaleQuantumToShort(pp->green);
564 *(ptr++) = ScaleQuantumToShort(pp->blue);
565 *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
566
567 pp++;
568 ++hiter;
569 }
570 }
571 else if (colorspaceType == RGBColorspace ||
572 colorspaceType == sRGBColorspace ||
573 colorspaceType == TransparentColorspace)
574 {
575 if (imageDepth == 8) {
576 while(! hiter.isDone())
577 {
578 TQ_UINT8 *ptr= hiter.rawData();
579 // XXX: not colorstrategy and bitdepth independent
580 *(ptr++) = ScaleQuantumToChar(pp->blue);
581 *(ptr++) = ScaleQuantumToChar(pp->green);
582 *(ptr++) = ScaleQuantumToChar(pp->red);
583 *(ptr++) = OPACITY_OPAQUE - ScaleQuantumToChar(pp->opacity);
584
585 pp++;
586 ++hiter;
587 }
588 }
589 else if (imageDepth == 16) {
590 while(! hiter.isDone())
591 {
592 TQ_UINT16 *ptr = reinterpret_cast<TQ_UINT16 *>(hiter.rawData());
593 // XXX: not colorstrategy independent
594 *(ptr++) = ScaleQuantumToShort(pp->blue);
595 *(ptr++) = ScaleQuantumToShort(pp->green);
596 *(ptr++) = ScaleQuantumToShort(pp->red);
597 *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
598
599 pp++;
600 ++hiter;
601 }
602 }
603 }
604 else if ( colorspaceType == GRAYColorspace) {
605 if (imageDepth == 8) {
606 while(! hiter.isDone())
607 {
608 TQ_UINT8 *ptr= hiter.rawData();
609 // XXX: not colorstrategy and bitdepth independent
610 *(ptr++) = ScaleQuantumToChar(pp->blue);
611 *(ptr++) = OPACITY_OPAQUE - ScaleQuantumToChar(pp->opacity);
612
613 pp++;
614 ++hiter;
615 }
616 }
617 else if (imageDepth == 16) {
618 while(! hiter.isDone())
619 {
620 TQ_UINT16 *ptr = reinterpret_cast<TQ_UINT16 *>(hiter.rawData());
621 // XXX: not colorstrategy independent
622 *(ptr++) = ScaleQuantumToShort(pp->blue);
623 *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
624
625 pp++;
626 ++hiter;
627 }
628 }
629 }
630
631 emit notifyProgress(y * 100 / image->rows);
632
633 if (m_stop) {
634 CloseCacheView(vi);
635 DestroyImage(image);
636 DestroyImageList(images);
637 DestroyImageInfo(ii);
638 DestroyExceptionInfo(&ei);
639 m_img = 0;
640 return KisImageBuilder_RESULT_INTR;
641 }
642 }
643 m_img->addLayer(layer.data(), m_img->rootLayer());
644 layer->paintDevice()->move(x_offset, y_offset);
645 }
646
647 emit notifyProgressDone();
648 CloseCacheView(vi);
649 DestroyImage(image);
650 }
651
652 emit notifyProgressDone();
653 DestroyImageList(images);
654 DestroyImageInfo(ii);
655 DestroyExceptionInfo(&ei);
656 return KisImageBuilder_RESULT_OK;
657 }
658
659 KisImageBuilder_Result KisImageMagickConverter::buildImage(const KURL& uri)
660 {
661 if (uri.isEmpty())
662 return KisImageBuilder_RESULT_NO_URI;
663
664 if (!TDEIO::NetAccess::exists(uri, false, tqApp -> mainWidget())) {
665 return KisImageBuilder_RESULT_NOT_EXIST;
666 }
667
668 KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE;
669 TQString tmpFile;
670
671 if (TDEIO::NetAccess::download(uri, tmpFile, tqApp -> mainWidget())) {
672 KURL uriTF;
673 uriTF.setPath( tmpFile );
674 result = decode(uriTF, false);
675 TDEIO::NetAccess::removeTempFile(tmpFile);
676 }
677
678 return result;
679 }
680
681
682 KisImageSP KisImageMagickConverter::image()
683 {
684 return m_img;
685 }
686
687 void KisImageMagickConverter::init(KisDoc *doc, KisUndoAdapter *adapter)
688 {
689 m_doc = doc;
690 m_adapter = adapter;
691 m_job = 0;
692 }
693
694 KisImageBuilder_Result KisImageMagickConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd)
695 {
696 Image *image;
697 ExceptionInfo ei;
698 ImageInfo *ii;
699
700 if (!layer)
701 return KisImageBuilder_RESULT_INVALID_ARG;
702
703 KisImageSP img = layer->image();
704 if (!img)
705 return KisImageBuilder_RESULT_EMPTY;
706
707 if (uri.isEmpty())
708 return KisImageBuilder_RESULT_NO_URI;
709
710 if (!uri.isLocalFile())
711 return KisImageBuilder_RESULT_NOT_LOCAL;
712
713
714 TQ_UINT32 layerBytesPerChannel = layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels();
715
716 GetExceptionInfo(&ei);
717
718 ii = CloneImageInfo(0);
719
720 tqstrncpy(ii -> filename, TQFile::encodeName(uri.path()), MaxTextExtent - 1);
721
722 if (ii -> filename[MaxTextExtent - 1]) {
723 emit notifyProgressError();
724 return KisImageBuilder_RESULT_PATH;
725 }
726
727 if (!img -> width() || !img -> height())
728 return KisImageBuilder_RESULT_EMPTY;
729
730 if (layerBytesPerChannel < 2) {
731 ii->depth = 8;
732 }
733 else {
734 ii->depth = 16;
735 }
736
737 ii->colorspace = getColorTypeforColorSpace(layer->paintDevice()->colorSpace());
738
739 image = AllocateImage(ii);
740// SetImageColorspace(image, ii->colorspace);
741 image -> columns = img -> width();
742 image -> rows = img -> height();
743
744 kdDebug(41008) << "Saving with colorspace " << image->colorspace << ", (" << layer->paintDevice()->colorSpace()->id().name() << ")\n";
745 kdDebug(41008) << "IM Image thinks it has depth: " << image->depth << "\n";
746
747#ifdef HAVE_MAGICK6
748 // if ( layer-> hasAlpha() )
749 image -> matte = MagickTrue;
750 // else
751 // image -> matte = MagickFalse;
752#else
753 // image -> matte = layer -> hasAlpha();
754 image -> matte = true;
755#endif
756
757 TQ_INT32 y, height, width;
758
759 height = img -> height();
760 width = img -> width();
761
762 bool alpha = true;
763 TQString ext = TQFileInfo(TQFile::encodeName(uri.path())).extension(false).upper();
764 if (ext == "BMP") {
765 alpha = false;
766 tqstrncpy(ii->magick, "BMP2", MaxTextExtent - 1);
767 }
768 else if (ext == "RGB") {
769 tqstrncpy(ii->magick, "SGI", MaxTextExtent - 1);
770 }
771
772 for (y = 0; y < height; y++) {
773
774 // Allocate pixels for this scanline
775 PixelPacket * pp = SetImagePixels(image, 0, y, width, 1);
776
777 if (!pp) {
778 DestroyExceptionInfo(&ei);
779 DestroyImage(image);
780 emit notifyProgressError();
781 return KisImageBuilder_RESULT_FAILURE;
782
783 }
784
785 KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, y, width, false);
786 if (alpha)
787 SetImageType(image, TrueColorMatteType);
788 else
789 SetImageType(image, TrueColorType);
790
791 if (image->colorspace== CMYKColorspace) {
792
793 IndexPacket * indexes = GetIndexes(image);
794 int x = 0;
795 if (layerBytesPerChannel == 2) {
796 while (!it.isDone()) {
797
798 const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
799 pp -> red = ScaleShortToQuantum(d[PIXEL_CYAN]);
800 pp -> green = ScaleShortToQuantum(d[PIXEL_MAGENTA]);
801 pp -> blue = ScaleShortToQuantum(d[PIXEL_YELLOW]);
802 if (alpha)
803 pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_CMYK_ALPHA]);
804 indexes[x] = ScaleShortToQuantum(d[PIXEL_BLACK]);
805 x++;
806 pp++;
807 ++it;
808 }
809 }
810 else {
811 while (!it.isDone()) {
812
813 TQ_UINT8 * d = it.rawData();
814 pp -> red = ScaleCharToQuantum(d[PIXEL_CYAN]);
815 pp -> green = ScaleCharToQuantum(d[PIXEL_MAGENTA]);
816 pp -> blue = ScaleCharToQuantum(d[PIXEL_YELLOW]);
817 if (alpha)
818 pp -> opacity = ScaleCharToQuantum(OPACITY_OPAQUE - d[PIXEL_CMYK_ALPHA]);
819 indexes[x]= ScaleCharToQuantum(d[PIXEL_BLACK]);
820
821 x++;
822 pp++;
823 ++it;
824 }
825 }
826 }
827 else if (image->colorspace== RGBColorspace ||
828 image->colorspace == sRGBColorspace ||
829 image->colorspace == TransparentColorspace)
830 {
831 if (layerBytesPerChannel == 2) {
832 while (!it.isDone()) {
833
834 const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
835 pp -> red = ScaleShortToQuantum(d[PIXEL_RED]);
836 pp -> green = ScaleShortToQuantum(d[PIXEL_GREEN]);
837 pp -> blue = ScaleShortToQuantum(d[PIXEL_BLUE]);
838 if (alpha)
839 pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_ALPHA]);
840
841 pp++;
842 ++it;
843 }
844 }
845 else {
846 while (!it.isDone()) {
847
848 TQ_UINT8 * d = it.rawData();
849 pp -> red = ScaleCharToQuantum(d[PIXEL_RED]);
850 pp -> green = ScaleCharToQuantum(d[PIXEL_GREEN]);
851 pp -> blue = ScaleCharToQuantum(d[PIXEL_BLUE]);
852 if (alpha)
853 pp -> opacity = ScaleCharToQuantum(OPACITY_OPAQUE - d[PIXEL_ALPHA]);
854
855 pp++;
856 ++it;
857 }
858 }
859 }
860 else if (image->colorspace == GRAYColorspace)
861 {
862 SetImageType(image, GrayscaleMatteType);
863 if (layerBytesPerChannel == 2) {
864 while (!it.isDone()) {
865
866 const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
867 pp -> red = ScaleShortToQuantum(d[PIXEL_GRAY]);
868 pp -> green = ScaleShortToQuantum(d[PIXEL_GRAY]);
869 pp -> blue = ScaleShortToQuantum(d[PIXEL_GRAY]);
870 if (alpha)
871 pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_GRAY_ALPHA]);
872
873 pp++;
874 ++it;
875 }
876 }
877 else {
878 while (!it.isDone()) {
879 TQ_UINT8 * d = it.rawData();
880 pp -> red = ScaleCharToQuantum(d[PIXEL_GRAY]);
881 pp -> green = ScaleCharToQuantum(d[PIXEL_GRAY]);
882 pp -> blue = ScaleCharToQuantum(d[PIXEL_GRAY]);
883 if (alpha)
884 pp -> opacity = ScaleCharToQuantum(OPACITY_OPAQUE - d[PIXEL_GRAY_ALPHA]);
885
886 pp++;
887 ++it;
888 }
889 }
890 }
891 else {
892 kdDebug(41008) << "Unsupported image format\n";
893 return KisImageBuilder_RESULT_INVALID_ARG;
894 }
895
896 emit notifyProgressStage(i18n("Saving..."), y * 100 / height);
897
898#ifdef HAVE_MAGICK6
899 if (SyncImagePixels(image) == MagickFalse)
900 kdDebug(41008) << "Syncing pixels failed\n";
901#else
902 if (!SyncImagePixels(image))
903 kdDebug(41008) << "Syncing pixels failed\n";
904#endif
905 }
906
907 // set the annotations
908 exportAnnotationsForImage(image, annotationsStart, annotationsEnd);
909
910 // XXX: Write to a temp file, then have Chalk use TDEIO to copy temp
911 // image to remote location.
912
913 WriteImage(ii, image);
914 DestroyExceptionInfo(&ei);
915 DestroyImage(image);
916 emit notifyProgressDone();
917 return KisImageBuilder_RESULT_OK;
918 }
919
920 void KisImageMagickConverter::ioData(TDEIO::Job *job, const TQByteArray& data)
921 {
922 if (data.isNull() || data.isEmpty()) {
923 emit notifyProgressStage(i18n("Loading..."), 0);
924 return;
925 }
926
927 if (m_data.empty()) {
928 Image *image;
929 ImageInfo *ii;
930 ExceptionInfo ei;
931
932 ii = CloneImageInfo(0);
933 GetExceptionInfo(&ei);
934 image = PingBlob(ii, data.data(), data.size(), &ei);
935
936 if (image == 0 || ei.severity == BlobError) {
937 DestroyExceptionInfo(&ei);
938 DestroyImageInfo(ii);
939 job -> kill();
940 emit notifyProgressError();
941 return;
942 }
943
944 DestroyImage(image);
945 DestroyExceptionInfo(&ei);
946 DestroyImageInfo(ii);
947 emit notifyProgressStage(i18n("Loading..."), 0);
948 }
949
950 Q_ASSERT(data.size() + m_data.size() <= m_size);
951 memcpy(&m_data[m_data.size()], data.data(), data.count());
952 m_data.resize(m_data.size() + data.count());
953 emit notifyProgressStage(i18n("Loading..."), m_data.size() * 100 / m_size);
954
955 if (m_stop)
956 job -> kill();
957 }
958
959 void KisImageMagickConverter::ioResult(TDEIO::Job *job)
960 {
961 m_job = 0;
962
963 if (job -> error())
964 emit notifyProgressError();
965
966 decode(KURL(), true);
967 }
968
969 void KisImageMagickConverter::ioTotalSize(TDEIO::Job * /*job*/, TDEIO::filesize_t size)
970 {
971 m_size = size;
972 m_data.reserve(size);
973 emit notifyProgressStage(i18n("Loading..."), 0);
974 }
975
976 void KisImageMagickConverter::cancel()
977 {
978 m_stop = true;
979 }
980
985 TQString KisImageMagickConverter::readFilters()
986 {
987 TQString s;
988 TQString all;
989 TQString name;
990 TQString description;
991 unsigned long matches;
992
993/*#ifdef HAVE_MAGICK6
994#ifdef HAVE_OLD_GETMAGICKINFOLIST
995 const MagickInfo **mi;
996 mi = GetMagickInfoList("*", &matches);
997#else // HAVE_OLD_GETMAGICKINFOLIST
998 ExceptionInfo ei;
999 GetExceptionInfo(&ei);
1000 const MagickInfo **mi;
1001 mi = GetMagickInfoList("*", &matches, &ei);
1002 DestroyExceptionInfo(&ei);
1003#endif // HAVE_OLD_GETMAGICKINFOLIST
1004#else // HAVE_MAGICK6*/
1005 const MagickInfo *mi;
1006 ExceptionInfo ei;
1007 GetExceptionInfo(&ei);
1008 mi = GetMagickInfo("*", &ei);
1009 DestroyExceptionInfo(&ei);
1010// #endif // HAVE_MAGICK6
1011
1012 if (!mi)
1013 return s;
1014
1015/*#ifdef HAVE_MAGICK6
1016 for (unsigned long i = 0; i < matches; i++) {
1017 const MagickInfo *info = mi[i];
1018 if (info -> stealth)
1019 continue;
1020
1021 if (info -> decoder) {
1022 name = info -> name;
1023 description = info -> description;
1024 kdDebug(41008) << "Found import filter for: " << name << "\n";
1025
1026 if (!description.isEmpty() && !description.contains('/')) {
1027 all += "*." + name.lower() + " *." + name + " ";
1028 s += "*." + name.lower() + " *." + name + "|";
1029 s += i18n(description.utf8());
1030 s += "\n";
1031 }
1032 }
1033 }
1034#else*/
1035 for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) {
1036 if (mi -> stealth)
1037 continue;
1038 if (mi -> decoder) {
1039 name = mi -> name;
1040 description = mi -> description;
1041 kdDebug(41008) << "Found import filter for: " << name << "\n";
1042
1043 if (!description.isEmpty() && !description.contains('/')) {
1044 all += "*." + name.lower() + " *." + name + " ";
1045 s += "*." + name.lower() + " *." + name + "|";
1046 s += i18n(description.utf8());
1047 s += "\n";
1048 }
1049 }
1050 }
1051// #endif
1052
1053 all += "|" + i18n("All Images");
1054 all += "\n";
1055
1056 return all + s;
1057 }
1058
1059 TQString KisImageMagickConverter::writeFilters()
1060 {
1061 TQString s;
1062 TQString all;
1063 TQString name;
1064 TQString description;
1065 unsigned long matches;
1066
1067/*#ifdef HAVE_MAGICK6
1068#ifdef HAVE_OLD_GETMAGICKINFOLIST
1069 const MagickInfo **mi;
1070 mi = GetMagickInfoList("*", &matches);
1071#else // HAVE_OLD_GETMAGICKINFOLIST
1072 ExceptionInfo ei;
1073 GetExceptionInfo(&ei);
1074 const MagickInfo **mi;
1075 mi = GetMagickInfoList("*", &matches, &ei);
1076 DestroyExceptionInfo(&ei);
1077#endif // HAVE_OLD_GETMAGICKINFOLIST
1078#else // HAVE_MAGICK6*/
1079 const MagickInfo *mi;
1080 ExceptionInfo ei;
1081 GetExceptionInfo(&ei);
1082 mi = GetMagickInfo("*", &ei);
1083 DestroyExceptionInfo(&ei);
1084// #endif // HAVE_MAGICK6
1085
1086 if (!mi) {
1087 kdDebug(41008) << "Eek, no magick info!\n";
1088 return s;
1089 }
1090
1091/*#ifdef HAVE_MAGICK6
1092 for (unsigned long i = 0; i < matches; i++) {
1093 const MagickInfo *info = mi[i];
1094 kdDebug(41008) << "Found export filter for: " << info -> name << "\n";
1095 if (info -> stealth)
1096 continue;
1097
1098 if (info -> encoder) {
1099 name = info -> name;
1100
1101 description = info -> description;
1102
1103 if (!description.isEmpty() && !description.contains('/')) {
1104 all += "*." + name.lower() + " *." + name + " ";
1105 s += "*." + name.lower() + " *." + name + "|";
1106 s += i18n(description.utf8());
1107 s += "\n";
1108 }
1109 }
1110 }
1111#else*/
1112 for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) {
1113 kdDebug(41008) << "Found export filter for: " << mi -> name << "\n";
1114 if (mi -> stealth)
1115 continue;
1116
1117 if (mi -> encoder) {
1118 name = mi -> name;
1119
1120 description = mi -> description;
1121
1122 if (!description.isEmpty() && !description.contains('/')) {
1123 all += "*." + name.lower() + " *." + name + " ";
1124 s += "*." + name.lower() + " *." + name + "|";
1125 s += i18n(description.utf8());
1126 s += "\n";
1127 }
1128 }
1129 }
1130// #endif
1131
1132
1133 all += "|" + i18n("All Images");
1134 all += "\n";
1135
1136 return all + s;
1137 }
1138
1139#include "kis_image_magick_converter.moc"
1140