filters

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