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