summaryrefslogtreecommitdiffstats
path: root/filters/chalk/magick/kis_image_magick_converter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'filters/chalk/magick/kis_image_magick_converter.cpp')
-rw-r--r--filters/chalk/magick/kis_image_magick_converter.cpp1087
1 files changed, 1087 insertions, 0 deletions
diff --git a/filters/chalk/magick/kis_image_magick_converter.cpp b/filters/chalk/magick/kis_image_magick_converter.cpp
new file mode 100644
index 000000000..5f8bafa3a
--- /dev/null
+++ b/filters/chalk/magick/kis_image_magick_converter.cpp
@@ -0,0 +1,1087 @@
+/*
+ * Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <magick/api.h>
+
+#include <tqfile.h>
+#include <tqfileinfo.h>
+#include <tqstring.h>
+
+#include <tdeversion.h>
+#include <kdebug.h>
+#include <tdeapplication.h>
+#include <tdelocale.h>
+#include <kurl.h>
+#include <tdeio/netaccess.h>
+
+#include <tqcolor.h>
+
+#include "kis_types.h"
+#include "kis_global.h"
+#include "kis_doc.h"
+#include "kis_image.h"
+#include "kis_layer.h"
+#include "kis_undo_adapter.h"
+#include "kis_image_magick_converter.h"
+#include "kis_meta_registry.h"
+#include "kis_colorspace_factory_registry.h"
+#include "kis_iterators_pixel.h"
+#include "kis_colorspace.h"
+#include "kis_profile.h"
+#include "kis_annotation.h"
+#include "kis_paint_layer.h"
+#include "kis_group_layer.h"
+#include "kis_paint_device.h"
+
+#include "../../../config.h"
+
+namespace {
+
+ const TQ_UINT8 PIXEL_BLUE = 0;
+ const TQ_UINT8 PIXEL_GREEN = 1;
+ const TQ_UINT8 PIXEL_RED = 2;
+ const TQ_UINT8 PIXEL_ALPHA = 3;
+
+ static const TQ_UINT8 PIXEL_CYAN = 0;
+ static const TQ_UINT8 PIXEL_MAGENTA = 1;
+ static const TQ_UINT8 PIXEL_YELLOW = 2;
+ static const TQ_UINT8 PIXEL_BLACK = 3;
+ static const TQ_UINT8 PIXEL_CMYK_ALPHA = 4;
+
+ static const TQ_UINT8 PIXEL_GRAY = 0;
+ static const TQ_UINT8 PIXEL_GRAY_ALPHA = 1;
+
+ /**
+ * Make this more flexible -- although... ImageMagick
+ * isn't that flexible either.
+ */
+ TQString getColorSpaceName(ColorspaceType type, unsigned long imageDepth = 8)
+ {
+
+ if (type == GRAYColorspace) {
+ if (imageDepth == 8)
+ return "GRAYA";
+ else if ( imageDepth == 16 )
+ return "GRAYA16" ;
+ }
+ else if (type == CMYKColorspace) {
+ if (imageDepth == 8)
+ return "CMYK";
+ else if ( imageDepth == 16 ) {
+ return "CMYK16";
+ }
+ }
+ else if (type == LABColorspace) {
+ kdDebug(41008) << "Lab!\n";
+ return "LABA";
+ }
+ else if (type == RGBColorspace || type == sRGBColorspace || type == TransparentColorspace) {
+ if (imageDepth == 8)
+ return "RGBA";
+ else if (imageDepth == 16)
+ return "RGBA16";
+ }
+ return "";
+
+ }
+
+ ColorspaceType getColorTypeforColorSpace( KisColorSpace * cs )
+ {
+ if ( cs->id() == KisID("GRAYA") || cs->id() == KisID("GRAYA16") ) return GRAYColorspace;
+ if ( cs->id() == KisID("RGBA") || cs->id() == KisID("RGBA16") ) return RGBColorspace;
+ if ( cs->id() == KisID("CMYK") || cs->id() == KisID("CMYK16") ) return CMYKColorspace;
+ if ( cs->id() == KisID("LABA") ) return LABColorspace;
+
+ kdDebug(41008) << "Cannot export images in " + cs->id().name() + " yet.\n";
+ return RGBColorspace;
+
+ }
+
+ KisProfile * getProfileForProfileInfo(const Image * image)
+ {
+#ifndef HAVE_MAGICK6
+ return 0;
+#else
+
+ if (image->profiles == NULL)
+ return 0;
+
+ const char *name;
+ const StringInfo *profile;
+
+ KisProfile * p = 0;
+
+ ResetImageProfileIterator(image);
+ for (name = GetNextImageProfile(image); name != (char *) NULL; )
+ {
+ profile = GetImageProfile(image, name);
+ if (profile == (StringInfo *) NULL)
+ continue;
+
+ // XXX: Hardcoded for icc type -- is that correct for us?
+ if (TQString::compare(name, "icc") == 0) {
+ TQByteArray rawdata;
+ rawdata.resize(profile->length);
+ memcpy(rawdata.data(), profile->datum, profile->length);
+
+ p = new KisProfile(rawdata);
+ if (p == 0)
+ return 0;
+ }
+ name = GetNextImageProfile(image);
+ }
+ return p;
+#endif
+ }
+
+ void setAnnotationsForImage(const Image * src, KisImageSP image)
+ {
+#ifndef HAVE_MAGICK6
+ return;
+#else
+ if (src->profiles == NULL)
+ return;
+
+ const char *name = 0;
+ const StringInfo *profile;
+ KisAnnotation* annotation = 0;
+
+ // Profiles and so
+ ResetImageProfileIterator(src);
+ while((name = GetNextImageProfile(src))) {
+ profile = GetImageProfile(src, name);
+ if (profile == (StringInfo *) NULL)
+ continue;
+
+ // XXX: icc will be written seperately?
+ if (TQString::compare(name, "icc") == 0)
+ continue;
+
+ TQByteArray rawdata;
+ rawdata.resize(profile->length);
+ memcpy(rawdata.data(), profile->datum, profile->length);
+
+ annotation = new KisAnnotation(TQString(name), "", rawdata);
+ TQ_CHECK_PTR(annotation);
+
+ image -> addAnnotation(annotation);
+ }
+
+ // Attributes, since we have no hint on if this is an attribute or a profile
+ // annotation, we prefix it with 'chalk_attribute:'. XXX This needs to be rethought!
+ // The joys of imagemagick. From at version 6.2.1 (dfaure has 6.2.0 and confirms the
+ // old way of doing things) they changed the src -> attributes
+ // to void* and require us to use the iterator functions. So we #if around that, *sigh*
+#if MagickLibVersion >= 0x621
+ const ImageAttribute * attr;
+ ResetImageAttributeIterator(src);
+ while ( (attr = GetNextImageAttribute(src)) ) {
+#else
+ ImageAttribute * attr = src -> attributes;
+ while (attr) {
+#endif
+ TQByteArray rawdata;
+ int len = strlen(attr -> value) + 1;
+ rawdata.resize(len);
+ memcpy(rawdata.data(), attr -> value, len);
+
+ annotation = new KisAnnotation(
+ TQString("chalk_attribute:%1").arg(TQString(attr -> key)), "", rawdata);
+ TQ_CHECK_PTR(annotation);
+
+ image -> addAnnotation(annotation);
+#if MagickLibVersion < 0x620
+ attr = attr -> next;
+#endif
+ }
+
+#endif
+ }
+ }
+
+ void exportAnnotationsForImage(Image * dst, vKisAnnotationSP_it& it, vKisAnnotationSP_it& annotationsEnd)
+ {
+#ifndef HAVE_MAGICK6
+ return;
+#else
+ while(it != annotationsEnd) {
+ if (!(*it) || (*it) -> type() == TQString()) {
+ kdDebug(41008) << "Warning: empty annotation" << endl;
+ ++it;
+ continue;
+ }
+
+ kdDebug(41008) << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size() << endl;
+
+ if ((*it) -> type().startsWith("chalk_attribute:")) { // Attribute
+ if (!SetImageAttribute(dst,
+ (*it) -> type().mid(strlen("chalk_attribute:")).ascii(),
+ (*it) -> annotation() . data()) ) {
+ kdDebug(41008) << "Storing of attribute " << (*it) -> type() << "failed!\n";
+ }
+ } else { // Profile
+ if (!ProfileImage(dst, (*it) -> type().ascii(),
+ (unsigned char*)(*it) -> annotation() . data(),
+ (*it) -> annotation() . size(), MagickFalse)) {
+ kdDebug(41008) << "Storing failed!" << endl;
+ }
+ }
+ ++it;
+ }
+#endif
+ }
+
+
+ void InitGlobalMagick()
+ {
+ static bool init = false;
+
+ if (!init) {
+ TDEApplication *app = TDEApplication::kApplication();
+
+ InitializeMagick(*app -> argv());
+ atexit(DestroyMagick);
+ init = true;
+ }
+ }
+
+ /*
+ * ImageMagick progress monitor callback. Unfortunately it doesn't support passing in some user
+ * data which complicates things quite a bit. The plan was to allow the user start multiple
+ * import/scans if he/she so wished. However, without passing user data it's not possible to tell
+ * on which task we have made progress on.
+ *
+ * Additionally, ImageMagick is thread-safe, not re-entrant... i.e. IM does not relinquish held
+ * locks when calling user defined callbacks, this means that the same thread going back into IM
+ * would deadlock since it would try to acquire locks it already holds.
+ */
+#ifdef HAVE_MAGICK6
+ MagickBooleanType monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *)
+ {
+ TDEApplication *app = TDEApplication::kApplication();
+
+ Q_ASSERT(app);
+
+ if (app -> hasPendingEvents())
+ app -> processEvents();
+
+ printf("%s\n", text);
+ return MagickTrue;
+ }
+#else
+ unsigned int monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *)
+ {
+ TDEApplication *app = TDEApplication::kApplication();
+
+ Q_ASSERT(app);
+
+ if (app -> hasPendingEvents())
+ app -> processEvents();
+
+ printf("%s\n", text);
+ return true;
+ }
+#endif
+
+
+
+KisImageMagickConverter::KisImageMagickConverter(KisDoc *doc, KisUndoAdapter *adapter)
+{
+ InitGlobalMagick();
+ init(doc, adapter);
+ SetMonitorHandler(monitor);
+ m_stop = false;
+}
+
+KisImageMagickConverter::~KisImageMagickConverter()
+{
+}
+
+KisImageBuilder_Result KisImageMagickConverter::decode(const KURL& uri, bool isBlob)
+{
+ Image *image;
+ Image *images;
+ ExceptionInfo ei;
+ ImageInfo *ii;
+
+ if (m_stop) {
+ m_img = 0;
+ return KisImageBuilder_RESULT_INTR;
+ }
+
+ GetExceptionInfo(&ei);
+ ii = CloneImageInfo(0);
+
+ if (isBlob) {
+
+ // TODO : Test. Does BlobToImage even work?
+ Q_ASSERT(uri.isEmpty());
+ images = BlobToImage(ii, &m_data[0], m_data.size(), &ei);
+ } else {
+
+ tqstrncpy(ii -> filename, TQFile::encodeName(uri.path()), MaxTextExtent - 1);
+
+ if (ii -> filename[MaxTextExtent - 1]) {
+ emit notifyProgressError();
+ return KisImageBuilder_RESULT_PATH;
+ }
+
+ images = ReadImage(ii, &ei);
+
+ }
+
+ if (ei.severity != UndefinedException)
+ CatchException(&ei);
+
+ if (images == 0) {
+ DestroyImageInfo(ii);
+ DestroyExceptionInfo(&ei);
+ emit notifyProgressError();
+ return KisImageBuilder_RESULT_FAILURE;
+ }
+
+ emit notifyProgressStage(i18n("Importing..."), 0);
+
+ m_img = 0;
+
+ while ((image = RemoveFirstImageFromList(&images))) {
+ ViewInfo *vi = OpenCacheView(image);
+
+ // Determine image depth -- for now, all channels of an imported image are of the same depth
+ unsigned long imageDepth = image->depth;
+ kdDebug(41008) << "Image depth: " << imageDepth << "\n";
+
+ TQString csName;
+ KisColorSpace * cs = 0;
+ ColorspaceType colorspaceType;
+
+ // Determine image type -- rgb, grayscale or cmyk
+ if (GetImageType(image, &ei) == GrayscaleType || GetImageType(image, &ei) == GrayscaleMatteType) {
+ if (imageDepth == 8)
+ csName = "GRAYA";
+ else if ( imageDepth == 16 )
+ csName = "GRAYA16" ;
+ colorspaceType = GRAYColorspace;
+ }
+ else {
+ colorspaceType = image->colorspace;
+ csName = getColorSpaceName(image -> colorspace, imageDepth);
+ }
+
+ kdDebug(41008) << "image has " << csName << " colorspace\n";
+
+ KisProfile * profile = getProfileForProfileInfo(image);
+ if (profile)
+ {
+ kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n";
+ cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile);
+ }
+ else
+ cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),"");
+
+ if (!cs) {
+ kdDebug(41008) << "Chalk does not support colorspace " << image -> colorspace << "\n";
+ CloseCacheView(vi);
+ DestroyImage(image);
+ DestroyExceptionInfo(&ei);
+ DestroyImageList(images);
+ DestroyImageInfo(ii);
+ emit notifyProgressError();
+ return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
+ }
+
+ if( ! m_img) {
+ m_img = new KisImage(m_doc->undoAdapter(), image -> columns, image -> rows, cs, "built image");
+ TQ_CHECK_PTR(m_img);
+ m_img->blockSignals(true); // Don't send out signals while we're building the image
+
+ // XXX I'm assuming seperate layers won't have other profile things like EXIF
+ setAnnotationsForImage(image, m_img);
+ }
+
+ if (image -> columns && image -> rows) {
+
+ // Opacity (set by the photoshop import filter)
+ TQ_UINT8 opacity = OPACITY_OPAQUE;
+ const ImageAttribute * attr = GetImageAttribute(image, "[layer-opacity]");
+ if (attr != 0) {
+ opacity = TQ_UINT8_MAX - Downscale(TQString(attr->value).toInt());
+ }
+
+ KisPaintLayerSP layer = 0;
+
+ attr = GetImageAttribute(image, "[layer-name]");
+ if (attr != 0) {
+ layer = new KisPaintLayer(m_img, attr->value, opacity);
+ }
+ else {
+ layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), opacity);
+ }
+
+ Q_ASSERT(layer);
+
+ // Layerlocation (set by the photoshop import filter)
+ TQ_INT32 x_offset = 0;
+ TQ_INT32 y_offset = 0;
+
+ attr = GetImageAttribute(image, "[layer-xpos]");
+ if (attr != 0) {
+ x_offset = TQString(attr->value).toInt();
+ }
+
+ attr = GetImageAttribute(image, "[layer-ypos]");
+ if (attr != 0) {
+ y_offset = TQString(attr->value).toInt();
+ }
+
+
+ for (TQ_UINT32 y = 0; y < image->rows; y ++)
+ {
+ const PixelPacket *pp = AcquireCacheView(vi, 0, y, image->columns, 1, &ei);
+
+ if(!pp)
+ {
+ CloseCacheView(vi);
+ DestroyImageList(images);
+ DestroyImageInfo(ii);
+ DestroyExceptionInfo(&ei);
+ emit notifyProgressError();
+ return KisImageBuilder_RESULT_FAILURE;
+ }
+
+ IndexPacket * indexes = GetCacheViewIndexes(vi);
+
+ KisHLineIteratorPixel hiter = layer->paintDevice()->createHLineIterator(0, y, image->columns, true);
+
+ if (colorspaceType== CMYKColorspace) {
+ if (imageDepth == 8) {
+ int x = 0;
+ while (!hiter.isDone())
+ {
+ TQ_UINT8 *ptr= hiter.rawData();
+ *(ptr++) = Downscale(pp->red); // cyan
+ *(ptr++) = Downscale(pp->green); // magenta
+ *(ptr++) = Downscale(pp->blue); // yellow
+ *(ptr++) = Downscale(indexes[x]); // Black
+// XXX: Warning! This ifdef messes up the paren matching big-time!
+#ifdef HAVE_MAGICK6
+ if (image->matte != MagickFalse) {
+#else
+ if (image->matte == true) {
+#endif
+ *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity);
+ }
+ else {
+ *(ptr++) = OPACITY_OPAQUE;
+ }
+ ++x;
+ pp++;
+ ++hiter;
+ }
+ }
+ }
+ else if (colorspaceType == LABColorspace) {
+ while(! hiter.isDone())
+ {
+ TQ_UINT16 *ptr = reinterpret_cast<TQ_UINT16 *>(hiter.rawData());
+
+ *(ptr++) = ScaleQuantumToShort(pp->red);
+ *(ptr++) = ScaleQuantumToShort(pp->green);
+ *(ptr++) = ScaleQuantumToShort(pp->blue);
+ *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
+
+ pp++;
+ ++hiter;
+ }
+ }
+ else if (colorspaceType == RGBColorspace ||
+ colorspaceType == sRGBColorspace ||
+ colorspaceType == TransparentColorspace)
+ {
+ if (imageDepth == 8) {
+ while(! hiter.isDone())
+ {
+ TQ_UINT8 *ptr= hiter.rawData();
+ // XXX: not colorstrategy and bitdepth independent
+ *(ptr++) = Downscale(pp->blue);
+ *(ptr++) = Downscale(pp->green);
+ *(ptr++) = Downscale(pp->red);
+ *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity);
+
+ pp++;
+ ++hiter;
+ }
+ }
+ else if (imageDepth == 16) {
+ while(! hiter.isDone())
+ {
+ TQ_UINT16 *ptr = reinterpret_cast<TQ_UINT16 *>(hiter.rawData());
+ // XXX: not colorstrategy independent
+ *(ptr++) = ScaleQuantumToShort(pp->blue);
+ *(ptr++) = ScaleQuantumToShort(pp->green);
+ *(ptr++) = ScaleQuantumToShort(pp->red);
+ *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
+
+ pp++;
+ ++hiter;
+ }
+ }
+ }
+ else if ( colorspaceType == GRAYColorspace) {
+ if (imageDepth == 8) {
+ while(! hiter.isDone())
+ {
+ TQ_UINT8 *ptr= hiter.rawData();
+ // XXX: not colorstrategy and bitdepth independent
+ *(ptr++) = Downscale(pp->blue);
+ *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity);
+
+ pp++;
+ ++hiter;
+ }
+ }
+ else if (imageDepth == 16) {
+ while(! hiter.isDone())
+ {
+ TQ_UINT16 *ptr = reinterpret_cast<TQ_UINT16 *>(hiter.rawData());
+ // XXX: not colorstrategy independent
+ *(ptr++) = ScaleQuantumToShort(pp->blue);
+ *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
+
+ pp++;
+ ++hiter;
+ }
+ }
+ }
+
+ emit notifyProgress(y * 100 / image->rows);
+
+ if (m_stop) {
+ CloseCacheView(vi);
+ DestroyImage(image);
+ DestroyImageList(images);
+ DestroyImageInfo(ii);
+ DestroyExceptionInfo(&ei);
+ m_img = 0;
+ return KisImageBuilder_RESULT_INTR;
+ }
+ }
+ m_img->addLayer(layer.data(), m_img->rootLayer());
+ layer->paintDevice()->move(x_offset, y_offset);
+ }
+
+ emit notifyProgressDone();
+ CloseCacheView(vi);
+ DestroyImage(image);
+ }
+
+ emit notifyProgressDone();
+ DestroyImageList(images);
+ DestroyImageInfo(ii);
+ DestroyExceptionInfo(&ei);
+ return KisImageBuilder_RESULT_OK;
+ }
+
+ KisImageBuilder_Result KisImageMagickConverter::buildImage(const KURL& uri)
+ {
+ if (uri.isEmpty())
+ return KisImageBuilder_RESULT_NO_URI;
+
+ if (!TDEIO::NetAccess::exists(uri, false, tqApp -> mainWidget())) {
+ return KisImageBuilder_RESULT_NOT_EXIST;
+ }
+
+ KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE;
+ TQString tmpFile;
+
+ if (TDEIO::NetAccess::download(uri, tmpFile, tqApp -> mainWidget())) {
+ KURL uriTF;
+ uriTF.setPath( tmpFile );
+ result = decode(uriTF, false);
+ TDEIO::NetAccess::removeTempFile(tmpFile);
+ }
+
+ return result;
+ }
+
+
+ KisImageSP KisImageMagickConverter::image()
+ {
+ return m_img;
+ }
+
+ void KisImageMagickConverter::init(KisDoc *doc, KisUndoAdapter *adapter)
+ {
+ m_doc = doc;
+ m_adapter = adapter;
+ m_job = 0;
+ }
+
+ KisImageBuilder_Result KisImageMagickConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd)
+ {
+ Image *image;
+ ExceptionInfo ei;
+ ImageInfo *ii;
+
+ if (!layer)
+ return KisImageBuilder_RESULT_INVALID_ARG;
+
+ KisImageSP img = layer->image();
+ if (!img)
+ return KisImageBuilder_RESULT_EMPTY;
+
+ if (uri.isEmpty())
+ return KisImageBuilder_RESULT_NO_URI;
+
+ if (!uri.isLocalFile())
+ return KisImageBuilder_RESULT_NOT_LOCAL;
+
+
+ TQ_UINT32 layerBytesPerChannel = layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels();
+
+ GetExceptionInfo(&ei);
+
+ ii = CloneImageInfo(0);
+
+ tqstrncpy(ii -> filename, TQFile::encodeName(uri.path()), MaxTextExtent - 1);
+
+ if (ii -> filename[MaxTextExtent - 1]) {
+ emit notifyProgressError();
+ return KisImageBuilder_RESULT_PATH;
+ }
+
+ if (!img -> width() || !img -> height())
+ return KisImageBuilder_RESULT_EMPTY;
+
+ if (layerBytesPerChannel < 2) {
+ ii->depth = 8;
+ }
+ else {
+ ii->depth = 16;
+ }
+
+ ii->colorspace = getColorTypeforColorSpace(layer->paintDevice()->colorSpace());
+
+ image = AllocateImage(ii);
+ SetImageColorspace(image, ii->colorspace);
+ image -> columns = img -> width();
+ image -> rows = img -> height();
+
+ kdDebug(41008) << "Saving with colorspace " << image->colorspace << ", (" << layer->paintDevice()->colorSpace()->id().name() << ")\n";
+ kdDebug(41008) << "IM Image thinks it has depth: " << image->depth << "\n";
+
+#ifdef HAVE_MAGICK6
+ // if ( layer-> hasAlpha() )
+ image -> matte = MagickTrue;
+ // else
+ // image -> matte = MagickFalse;
+#else
+ // image -> matte = layer -> hasAlpha();
+ image -> matte = true;
+#endif
+
+ TQ_INT32 y, height, width;
+
+ height = img -> height();
+ width = img -> width();
+
+ bool alpha = true;
+ TQString ext = TQFileInfo(TQFile::encodeName(uri.path())).extension(false).upper();
+ if (ext == "BMP") {
+ alpha = false;
+ tqstrncpy(ii->magick, "BMP2", MaxTextExtent - 1);
+ }
+ else if (ext == "RGB") {
+ tqstrncpy(ii->magick, "SGI", MaxTextExtent - 1);
+ }
+
+ for (y = 0; y < height; y++) {
+
+ // Allocate pixels for this scanline
+ PixelPacket * pp = SetImagePixels(image, 0, y, width, 1);
+
+ if (!pp) {
+ DestroyExceptionInfo(&ei);
+ DestroyImage(image);
+ emit notifyProgressError();
+ return KisImageBuilder_RESULT_FAILURE;
+
+ }
+
+ KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, y, width, false);
+ if (alpha)
+ SetImageType(image, TrueColorMatteType);
+ else
+ SetImageType(image, TrueColorType);
+
+ if (image->colorspace== CMYKColorspace) {
+
+ IndexPacket * indexes = GetIndexes(image);
+ int x = 0;
+ if (layerBytesPerChannel == 2) {
+ while (!it.isDone()) {
+
+ const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
+ pp -> red = ScaleShortToQuantum(d[PIXEL_CYAN]);
+ pp -> green = ScaleShortToQuantum(d[PIXEL_MAGENTA]);
+ pp -> blue = ScaleShortToQuantum(d[PIXEL_YELLOW]);
+ if (alpha)
+ pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_CMYK_ALPHA]);
+ indexes[x] = ScaleShortToQuantum(d[PIXEL_BLACK]);
+ x++;
+ pp++;
+ ++it;
+ }
+ }
+ else {
+ while (!it.isDone()) {
+
+ TQ_UINT8 * d = it.rawData();
+ pp -> red = Upscale(d[PIXEL_CYAN]);
+ pp -> green = Upscale(d[PIXEL_MAGENTA]);
+ pp -> blue = Upscale(d[PIXEL_YELLOW]);
+ if (alpha)
+ pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_CMYK_ALPHA]);
+
+ indexes[x]= Upscale(d[PIXEL_BLACK]);
+
+ x++;
+ pp++;
+ ++it;
+ }
+ }
+ }
+ else if (image->colorspace== RGBColorspace ||
+ image->colorspace == sRGBColorspace ||
+ image->colorspace == TransparentColorspace)
+ {
+ if (layerBytesPerChannel == 2) {
+ while (!it.isDone()) {
+
+ const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
+ pp -> red = ScaleShortToQuantum(d[PIXEL_RED]);
+ pp -> green = ScaleShortToQuantum(d[PIXEL_GREEN]);
+ pp -> blue = ScaleShortToQuantum(d[PIXEL_BLUE]);
+ if (alpha)
+ pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_ALPHA]);
+
+ pp++;
+ ++it;
+ }
+ }
+ else {
+ while (!it.isDone()) {
+
+ TQ_UINT8 * d = it.rawData();
+ pp -> red = Upscale(d[PIXEL_RED]);
+ pp -> green = Upscale(d[PIXEL_GREEN]);
+ pp -> blue = Upscale(d[PIXEL_BLUE]);
+ if (alpha)
+ pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_ALPHA]);
+
+ pp++;
+ ++it;
+ }
+ }
+ }
+ else if (image->colorspace == GRAYColorspace)
+ {
+ SetImageType(image, GrayscaleMatteType);
+ if (layerBytesPerChannel == 2) {
+ while (!it.isDone()) {
+
+ const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
+ pp -> red = ScaleShortToQuantum(d[PIXEL_GRAY]);
+ pp -> green = ScaleShortToQuantum(d[PIXEL_GRAY]);
+ pp -> blue = ScaleShortToQuantum(d[PIXEL_GRAY]);
+ if (alpha)
+ pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_GRAY_ALPHA]);
+
+ pp++;
+ ++it;
+ }
+ }
+ else {
+ while (!it.isDone()) {
+ TQ_UINT8 * d = it.rawData();
+ pp -> red = Upscale(d[PIXEL_GRAY]);
+ pp -> green = Upscale(d[PIXEL_GRAY]);
+ pp -> blue = Upscale(d[PIXEL_GRAY]);
+ if (alpha)
+ pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_GRAY_ALPHA]);
+
+ pp++;
+ ++it;
+ }
+ }
+ }
+ else {
+ kdDebug(41008) << "Unsupported image format\n";
+ return KisImageBuilder_RESULT_INVALID_ARG;
+ }
+
+ emit notifyProgressStage(i18n("Saving..."), y * 100 / height);
+
+#ifdef HAVE_MAGICK6
+ if (SyncImagePixels(image) == MagickFalse)
+ kdDebug(41008) << "Syncing pixels failed\n";
+#else
+ if (!SyncImagePixels(image))
+ kdDebug(41008) << "Syncing pixels failed\n";
+#endif
+ }
+
+ // set the annotations
+ exportAnnotationsForImage(image, annotationsStart, annotationsEnd);
+
+ // XXX: Write to a temp file, then have Chalk use TDEIO to copy temp
+ // image to remote location.
+
+ WriteImage(ii, image);
+ DestroyExceptionInfo(&ei);
+ DestroyImage(image);
+ emit notifyProgressDone();
+ return KisImageBuilder_RESULT_OK;
+ }
+
+ void KisImageMagickConverter::ioData(TDEIO::Job *job, const TQByteArray& data)
+ {
+ if (data.isNull() || data.isEmpty()) {
+ emit notifyProgressStage(i18n("Loading..."), 0);
+ return;
+ }
+
+ if (m_data.empty()) {
+ Image *image;
+ ImageInfo *ii;
+ ExceptionInfo ei;
+
+ ii = CloneImageInfo(0);
+ GetExceptionInfo(&ei);
+ image = PingBlob(ii, data.data(), data.size(), &ei);
+
+ if (image == 0 || ei.severity == BlobError) {
+ DestroyExceptionInfo(&ei);
+ DestroyImageInfo(ii);
+ job -> kill();
+ emit notifyProgressError();
+ return;
+ }
+
+ DestroyImage(image);
+ DestroyExceptionInfo(&ei);
+ DestroyImageInfo(ii);
+ emit notifyProgressStage(i18n("Loading..."), 0);
+ }
+
+ Q_ASSERT(data.size() + m_data.size() <= m_size);
+ memcpy(&m_data[m_data.size()], data.data(), data.count());
+ m_data.resize(m_data.size() + data.count());
+ emit notifyProgressStage(i18n("Loading..."), m_data.size() * 100 / m_size);
+
+ if (m_stop)
+ job -> kill();
+ }
+
+ void KisImageMagickConverter::ioResult(TDEIO::Job *job)
+ {
+ m_job = 0;
+
+ if (job -> error())
+ emit notifyProgressError();
+
+ decode(KURL(), true);
+ }
+
+ void KisImageMagickConverter::ioTotalSize(TDEIO::Job * /*job*/, TDEIO::filesize_t size)
+ {
+ m_size = size;
+ m_data.reserve(size);
+ emit notifyProgressStage(i18n("Loading..."), 0);
+ }
+
+ void KisImageMagickConverter::cancel()
+ {
+ m_stop = true;
+ }
+
+ /**
+ * @name readFilters
+ * @return Provide a list of file formats the application can read.
+ */
+ TQString KisImageMagickConverter::readFilters()
+ {
+ TQString s;
+ TQString all;
+ TQString name;
+ TQString description;
+ unsigned long matches;
+
+#ifdef HAVE_MAGICK6
+#ifdef HAVE_OLD_GETMAGICKINFOLIST
+ const MagickInfo **mi;
+ mi = GetMagickInfoList("*", &matches);
+#else // HAVE_OLD_GETMAGICKINFOLIST
+ ExceptionInfo ei;
+ GetExceptionInfo(&ei);
+ const MagickInfo **mi;
+ mi = GetMagickInfoList("*", &matches, &ei);
+ DestroyExceptionInfo(&ei);
+#endif // HAVE_OLD_GETMAGICKINFOLIST
+#else // HAVE_MAGICK6
+ const MagickInfo *mi;
+ ExceptionInfo ei;
+ GetExceptionInfo(&ei);
+ mi = GetMagickInfo("*", &ei);
+ DestroyExceptionInfo(&ei);
+#endif // HAVE_MAGICK6
+
+ if (!mi)
+ return s;
+
+#ifdef HAVE_MAGICK6
+ for (unsigned long i = 0; i < matches; i++) {
+ const MagickInfo *info = mi[i];
+ if (info -> stealth)
+ continue;
+
+ if (info -> decoder) {
+ name = info -> name;
+ description = info -> description;
+ kdDebug(41008) << "Found import filter for: " << name << "\n";
+
+ if (!description.isEmpty() && !description.contains('/')) {
+ all += "*." + name.lower() + " *." + name + " ";
+ s += "*." + name.lower() + " *." + name + "|";
+ s += i18n(description.utf8());
+ s += "\n";
+ }
+ }
+ }
+#else
+ for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) {
+ if (mi -> stealth)
+ continue;
+ if (mi -> decoder) {
+ name = mi -> name;
+ description = mi -> description;
+ kdDebug(41008) << "Found import filter for: " << name << "\n";
+
+ if (!description.isEmpty() && !description.contains('/')) {
+ all += "*." + name.lower() + " *." + name + " ";
+ s += "*." + name.lower() + " *." + name + "|";
+ s += i18n(description.utf8());
+ s += "\n";
+ }
+ }
+ }
+#endif
+
+ all += "|" + i18n("All Images");
+ all += "\n";
+
+ return all + s;
+ }
+
+ TQString KisImageMagickConverter::writeFilters()
+ {
+ TQString s;
+ TQString all;
+ TQString name;
+ TQString description;
+ unsigned long matches;
+
+#ifdef HAVE_MAGICK6
+#ifdef HAVE_OLD_GETMAGICKINFOLIST
+ const MagickInfo **mi;
+ mi = GetMagickInfoList("*", &matches);
+#else // HAVE_OLD_GETMAGICKINFOLIST
+ ExceptionInfo ei;
+ GetExceptionInfo(&ei);
+ const MagickInfo **mi;
+ mi = GetMagickInfoList("*", &matches, &ei);
+ DestroyExceptionInfo(&ei);
+#endif // HAVE_OLD_GETMAGICKINFOLIST
+#else // HAVE_MAGICK6
+ const MagickInfo *mi;
+ ExceptionInfo ei;
+ GetExceptionInfo(&ei);
+ mi = GetMagickInfo("*", &ei);
+ DestroyExceptionInfo(&ei);
+#endif // HAVE_MAGICK6
+
+ if (!mi) {
+ kdDebug(41008) << "Eek, no magick info!\n";
+ return s;
+ }
+
+#ifdef HAVE_MAGICK6
+ for (unsigned long i = 0; i < matches; i++) {
+ const MagickInfo *info = mi[i];
+ kdDebug(41008) << "Found export filter for: " << info -> name << "\n";
+ if (info -> stealth)
+ continue;
+
+ if (info -> encoder) {
+ name = info -> name;
+
+ description = info -> description;
+
+ if (!description.isEmpty() && !description.contains('/')) {
+ all += "*." + name.lower() + " *." + name + " ";
+ s += "*." + name.lower() + " *." + name + "|";
+ s += i18n(description.utf8());
+ s += "\n";
+ }
+ }
+ }
+#else
+ for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) {
+ kdDebug(41008) << "Found export filter for: " << mi -> name << "\n";
+ if (mi -> stealth)
+ continue;
+
+ if (mi -> encoder) {
+ name = mi -> name;
+
+ description = mi -> description;
+
+ if (!description.isEmpty() && !description.contains('/')) {
+ all += "*." + name.lower() + " *." + name + " ";
+ s += "*." + name.lower() + " *." + name + "|";
+ s += i18n(description.utf8());
+ s += "\n";
+ }
+ }
+ }
+#endif
+
+
+ all += "|" + i18n("All Images");
+ all += "\n";
+
+ return all + s;
+ }
+
+#include "kis_image_magick_converter.moc"
+