/* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2006 Cyrille Berger * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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) { size_t length; const unsigned char * profiledata = GetImageProfile(image, "ICM", &length); if( profiledata == NULL ) return 0; TQByteArray rawdata; rawdata.resize(length); memcpy(rawdata.data(), profiledata, length); KisProfile* p = new KisProfile(rawdata); return p; #if 0 return 0; 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) { size_t length; const unsigned char * profiledata = GetImageProfile(src, "IPTC", &length); if( profiledata != NULL ) { TQByteArray rawdata; rawdata.resize(length); memcpy(rawdata.data(), profiledata, length); KisAnnotation* annotation = new KisAnnotation(TQString("IPTC"), "", rawdata); TQ_CHECK_PTR(annotation); image -> addAnnotation(annotation); } /* This code is no longer needed or supported by graphicsmagick */ #if 0 for(int i = 0; i < src->generic_profiles; i++) { TQByteArray rawdata; rawdata.resize(length); memcpy(rawdata.data(), src->generic_profile[i].info, src->generic_profile[i].length); KisAnnotation* annotation = new KisAnnotation(TQString(src->generic_profile[i].name), "", rawdata); TQ_CHECK_PTR(annotation); image -> addAnnotation(annotation); } #endif const ImageAttribute* imgAttr = GetImageAttribute(src, NULL); while(imgAttr) { TQByteArray rawdata; int len = strlen(imgAttr -> value) + 1; rawdata.resize(len); memcpy(rawdata.data(), imgAttr -> value, len); KisAnnotation* annotation = new KisAnnotation( TQString("chalk_attribute:%1").arg(TQString(imgAttr -> key)), "", rawdata ); TQ_CHECK_PTR(annotation); image -> addAnnotation(annotation); imgAttr = imgAttr->next; } #if 0 return; 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) { 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 unsigned char * profiledata = new unsigned char[(*it) -> annotation() . size()]; memcpy( profiledata, (*it) -> annotation() . data(), (*it) -> annotation() . size()); if (!ProfileImage(dst, (*it) -> type().ascii(), profiledata, (*it) -> annotation() . size(), MagickFalse)) { kdDebug(41008) << "Storing failed!" << endl; } } ++it; } } 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. */ #if 0 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); kdDebug(41008) << "Exceptions happen when loading" << endl; return KisImageBuilder_RESULT_FAILURE; } 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))) { if(image->rows == 0 or image->columns == 0) return KisImageBuilder_RESULT_FAILURE; 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 - ScaleQuantumToChar(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++) = ScaleQuantumToChar(pp->red); // cyan *(ptr++) = ScaleQuantumToChar(pp->green); // magenta *(ptr++) = ScaleQuantumToChar(pp->blue); // yellow *(ptr++) = ScaleQuantumToChar(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 - ScaleQuantumToChar(pp->opacity); } else { *(ptr++) = OPACITY_OPAQUE; } ++x; pp++; ++hiter; } } } else if (colorspaceType == LABColorspace) { while(! hiter.isDone()) { TQ_UINT16 *ptr = reinterpret_cast(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++) = ScaleQuantumToChar(pp->blue); *(ptr++) = ScaleQuantumToChar(pp->green); *(ptr++) = ScaleQuantumToChar(pp->red); *(ptr++) = OPACITY_OPAQUE - ScaleQuantumToChar(pp->opacity); pp++; ++hiter; } } else if (imageDepth == 16) { while(! hiter.isDone()) { TQ_UINT16 *ptr = reinterpret_cast(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++) = ScaleQuantumToChar(pp->blue); *(ptr++) = OPACITY_OPAQUE - ScaleQuantumToChar(pp->opacity); pp++; ++hiter; } } else if (imageDepth == 16) { while(! hiter.isDone()) { TQ_UINT16 *ptr = reinterpret_cast(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(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 = ScaleCharToQuantum(d[PIXEL_CYAN]); pp -> green = ScaleCharToQuantum(d[PIXEL_MAGENTA]); pp -> blue = ScaleCharToQuantum(d[PIXEL_YELLOW]); if (alpha) pp -> opacity = ScaleCharToQuantum(OPACITY_OPAQUE - d[PIXEL_CMYK_ALPHA]); indexes[x]= ScaleCharToQuantum(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(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 = ScaleCharToQuantum(d[PIXEL_RED]); pp -> green = ScaleCharToQuantum(d[PIXEL_GREEN]); pp -> blue = ScaleCharToQuantum(d[PIXEL_BLUE]); if (alpha) pp -> opacity = ScaleCharToQuantum(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(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 = ScaleCharToQuantum(d[PIXEL_GRAY]); pp -> green = ScaleCharToQuantum(d[PIXEL_GRAY]); pp -> blue = ScaleCharToQuantum(d[PIXEL_GRAY]); if (alpha) pp -> opacity = ScaleCharToQuantum(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(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(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"