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