filters

kis_image_magick_converter.cc

00001 /*
00002  *  Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
00003  *  Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019  */
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <unistd.h>
00024 
00025 #include <magick/api.h>
00026 
00027 #include <qfile.h>
00028 #include <qfileinfo.h>
00029 #include <qstring.h>
00030 
00031 #include <kdeversion.h>
00032 #include <kdebug.h>
00033 #include <kapplication.h>
00034 #include <klocale.h>
00035 #include <kurl.h>
00036 #include <kio/netaccess.h>
00037 
00038 #include <qcolor.h>
00039 
00040 #include "kis_types.h"
00041 #include "kis_global.h"
00042 #include "kis_doc.h"
00043 #include "kis_image.h"
00044 #include "kis_layer.h"
00045 #include "kis_undo_adapter.h"
00046 #include "kis_image_magick_converter.h"
00047 #include "kis_meta_registry.h"
00048 #include "kis_colorspace_factory_registry.h"
00049 #include "kis_iterators_pixel.h"
00050 #include "kis_colorspace.h"
00051 #include "kis_profile.h"
00052 #include "kis_annotation.h"
00053 #include "kis_paint_layer.h"
00054 #include "kis_group_layer.h"
00055 #include "kis_paint_device.h"
00056 
00057 #include "../../../config.h"
00058 
00059 namespace {
00060 
00061     const Q_UINT8 PIXEL_BLUE = 0;
00062     const Q_UINT8 PIXEL_GREEN = 1;
00063     const Q_UINT8 PIXEL_RED = 2;
00064     const Q_UINT8 PIXEL_ALPHA = 3;
00065 
00066     static const Q_UINT8 PIXEL_CYAN = 0;
00067     static const Q_UINT8 PIXEL_MAGENTA = 1;
00068     static const Q_UINT8 PIXEL_YELLOW = 2;
00069     static const Q_UINT8 PIXEL_BLACK = 3;
00070     static const Q_UINT8 PIXEL_CMYK_ALPHA = 4;
00071 
00072     static const Q_UINT8 PIXEL_GRAY = 0;
00073     static const Q_UINT8 PIXEL_GRAY_ALPHA = 1;
00074 
00079     QString getColorSpaceName(ColorspaceType type, unsigned long imageDepth = 8)
00080     {
00081 
00082         if (type == GRAYColorspace) {
00083             if (imageDepth == 8)
00084                 return "GRAYA";
00085             else if ( imageDepth == 16 )
00086                 return "GRAYA16" ;
00087         }
00088         else if (type == CMYKColorspace) {
00089             if (imageDepth == 8)
00090                 return "CMYK";
00091             else if ( imageDepth == 16 ) {
00092                 return "CMYK16";
00093             }
00094         }
00095         else if (type == LABColorspace) {
00096             kdDebug(41008) << "Lab!\n";
00097             return "LABA";
00098         }
00099         else if (type == RGBColorspace || type == sRGBColorspace || type == TransparentColorspace) {
00100             if (imageDepth == 8)
00101                 return "RGBA";
00102             else if (imageDepth == 16)
00103                 return "RGBA16";
00104         }
00105         return "";
00106 
00107     }
00108 
00109     ColorspaceType getColorTypeforColorSpace( KisColorSpace * cs )
00110     {
00111         if ( cs->id() == KisID("GRAYA") || cs->id() == KisID("GRAYA16") ) return GRAYColorspace;
00112         if ( cs->id() == KisID("RGBA") || cs->id() == KisID("RGBA16") ) return RGBColorspace;
00113         if ( cs->id() == KisID("CMYK") || cs->id() == KisID("CMYK16") ) return CMYKColorspace;
00114         if ( cs->id() == KisID("LABA") ) return LABColorspace;
00115 
00116         kdDebug(41008) << "Cannot export images in " + cs->id().name() + " yet.\n";
00117         return RGBColorspace;
00118 
00119     }
00120 
00121     KisProfile * getProfileForProfileInfo(const Image * image)
00122     {
00123         size_t length;
00124         
00125         const unsigned char * profiledata = GetImageProfile(image, "ICM", &length);
00126         if( profiledata == NULL )
00127             return 0;
00128         QByteArray rawdata;
00129         rawdata.resize(length);
00130         memcpy(rawdata.data(), profiledata, length);
00131         
00132         KisProfile* p = new KisProfile(rawdata);
00133         return p;
00134 
00135 #if 0
00136         return 0;
00137 
00138         if (image->profiles == NULL)
00139             return  0;
00140 
00141         const char *name;
00142         const StringInfo *profile;
00143 
00144         KisProfile * p = 0;
00145 
00146         ResetImageProfileIterator(image);
00147         for (name = GetNextImageProfile(image); name != (char *) NULL; )
00148         {
00149             profile = GetImageProfile(image, name);
00150             if (profile == (StringInfo *) NULL)
00151                 continue;
00152 
00153             // XXX: Hardcoded for icc type -- is that correct for us?
00154             if (QString::compare(name, "icc") == 0) {
00155                 QByteArray rawdata;
00156                 rawdata.resize(profile->length);
00157                 memcpy(rawdata.data(), profile->datum, profile->length);
00158 
00159                 p = new KisProfile(rawdata);
00160                 if (p == 0)
00161                     return 0;
00162             }
00163             name = GetNextImageProfile(image);
00164         }
00165         return p;
00166 #endif
00167     }
00168 
00169     void setAnnotationsForImage(const Image * src, KisImageSP image)
00170     {
00171         size_t length;
00172         
00173         const unsigned char * profiledata = GetImageProfile(src, "IPTC", &length);
00174         if( profiledata != NULL )
00175         {
00176             QByteArray rawdata;
00177             rawdata.resize(length);
00178             memcpy(rawdata.data(), profiledata, length);
00179             
00180             KisAnnotation* annotation = new KisAnnotation(QString("IPTC"), "", rawdata);
00181             Q_CHECK_PTR(annotation);
00182 
00183             image -> addAnnotation(annotation);
00184         }
00185         for(int i = 0; i < src->generic_profiles; i++)
00186         {
00187             QByteArray rawdata;
00188             rawdata.resize(length);
00189             memcpy(rawdata.data(), src->generic_profile[i].info, src->generic_profile[i].length);
00190             
00191             KisAnnotation* annotation = new KisAnnotation(QString(src->generic_profile[i].name), "", rawdata);
00192             Q_CHECK_PTR(annotation);
00193 
00194             image -> addAnnotation(annotation);
00195         }
00196         
00197         const ImageAttribute* imgAttr = GetImageAttribute(src, NULL);
00198         while(imgAttr)
00199         {
00200             QByteArray rawdata;
00201             int len = strlen(imgAttr -> value) + 1;
00202             rawdata.resize(len);
00203             memcpy(rawdata.data(), imgAttr -> value, len);
00204             
00205             KisAnnotation* annotation = new KisAnnotation( QString("krita_attribute:%1").arg(QString(imgAttr -> key)), "", rawdata );
00206             Q_CHECK_PTR(annotation);
00207 
00208             image -> addAnnotation(annotation);
00209 
00210             imgAttr = imgAttr->next;
00211         }
00212 #if 0
00213         return;
00214         if (src->profiles == NULL)
00215             return;
00216 
00217         const char *name = 0;
00218         const StringInfo *profile;
00219         KisAnnotation* annotation = 0;
00220 
00221         // Profiles and so
00222         ResetImageProfileIterator(src);
00223         while((name = GetNextImageProfile(src))) {
00224             profile = GetImageProfile(src, name);
00225             if (profile == (StringInfo *) NULL)
00226                 continue;
00227 
00228             // XXX: icc will be written seperately?
00229             if (QString::compare(name, "icc") == 0)
00230                 continue;
00231 
00232             QByteArray rawdata;
00233             rawdata.resize(profile->length);
00234             memcpy(rawdata.data(), profile->datum, profile->length);
00235 
00236             annotation = new KisAnnotation(QString(name), "", rawdata);
00237             Q_CHECK_PTR(annotation);
00238 
00239             image -> addAnnotation(annotation);
00240         }
00241 
00242         // Attributes, since we have no hint on if this is an attribute or a profile
00243         // annotation, we prefix it with 'krita_attribute:'. XXX This needs to be rethought!
00244         // The joys of imagemagick. From at version 6.2.1 (dfaure has 6.2.0 and confirms the
00245         // old way of doing things) they changed the src -> attributes
00246         // to void* and require us to use the iterator functions. So we #if around that, *sigh*
00247 #if MagickLibVersion >= 0x621
00248         const ImageAttribute * attr;
00249         ResetImageAttributeIterator(src);
00250         while ( (attr = GetNextImageAttribute(src)) ) {
00251 #else
00252             ImageAttribute * attr = src -> attributes;
00253             while (attr) {
00254 #endif
00255                 QByteArray rawdata;
00256                 int len = strlen(attr -> value) + 1;
00257                 rawdata.resize(len);
00258                 memcpy(rawdata.data(), attr -> value, len);
00259 
00260                 annotation = new KisAnnotation(
00261                     QString("krita_attribute:%1").arg(QString(attr -> key)), "", rawdata);
00262                 Q_CHECK_PTR(annotation);
00263 
00264                 image -> addAnnotation(annotation);
00265 #if MagickLibVersion < 0x620
00266                 attr = attr -> next;
00267 #endif
00268             }
00269 
00270 #endif
00271         }
00272     }
00273 
00274     void exportAnnotationsForImage(Image * dst, vKisAnnotationSP_it& it, vKisAnnotationSP_it& annotationsEnd)
00275     {
00276         while(it != annotationsEnd) {
00277             if (!(*it) || (*it) -> type() == QString()) {
00278                     kdDebug(41008) << "Warning: empty annotation" << endl;
00279                     ++it;
00280                     continue;
00281             }
00282 
00283             kdDebug(41008) << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size() << endl;
00284 
00285             if ((*it) -> type().startsWith("krita_attribute:")) { // Attribute
00286                 if (!SetImageAttribute(dst,
00287                                         (*it) -> type().mid(strlen("krita_attribute:")).ascii(),
00288                                         (*it) -> annotation() . data()) ) {
00289                         kdDebug(41008) << "Storing of attribute " << (*it) -> type() << "failed!\n";
00290                     }
00291             } else { // Profile
00292                     unsigned char * profiledata = new unsigned char[(*it) -> annotation() . size()];
00293                     memcpy( profiledata, (*it) -> annotation() . data(), (*it) -> annotation() . size());
00294                     if (!ProfileImage(dst, (*it) -> type().ascii(),
00295                                     profiledata, (*it) -> annotation() . size(), MagickFalse)) {
00296                         kdDebug(41008) << "Storing failed!" << endl;
00297                     }
00298             }
00299             ++it;
00300         }
00301     }
00302 
00303 
00304     void InitGlobalMagick()
00305     {
00306         static bool init = false;
00307 
00308         if (!init) {
00309             KApplication *app = KApplication::kApplication();
00310 
00311             InitializeMagick(*app -> argv());
00312             atexit(DestroyMagick);
00313             init = true;
00314         }
00315     }
00316 
00317     /*
00318      * ImageMagick progress monitor callback.  Unfortunately it doesn't support passing in some user
00319      * data which complicates things quite a bit.  The plan was to allow the user start multiple
00320      * import/scans if he/she so wished.  However, without passing user data it's not possible to tell
00321      * on which task we have made progress on.
00322      *
00323      * Additionally, ImageMagick is thread-safe, not re-entrant... i.e. IM does not relinquish held
00324      * locks when calling user defined callbacks, this means that the same thread going back into IM
00325      * would deadlock since it would try to acquire locks it already holds.
00326      */
00327 #if 0
00328     MagickBooleanType monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *)
00329     {
00330         KApplication *app = KApplication::kApplication();
00331 
00332         Q_ASSERT(app);
00333 
00334         if (app -> hasPendingEvents())
00335             app -> processEvents();
00336 
00337         printf("%s\n", text);
00338         return MagickTrue;
00339     }
00340 #else
00341     unsigned int monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *)
00342     {
00343         KApplication *app = KApplication::kApplication();
00344 
00345         Q_ASSERT(app);
00346 
00347         if (app -> hasPendingEvents())
00348             app -> processEvents();
00349 
00350         printf("%s\n", text);
00351         return true;
00352     }
00353 #endif
00354 
00355 
00356 
00357 KisImageMagickConverter::KisImageMagickConverter(KisDoc *doc, KisUndoAdapter *adapter)
00358 {
00359     InitGlobalMagick();
00360     init(doc, adapter);
00361     SetMonitorHandler(monitor);
00362     m_stop = false;
00363 }
00364 
00365 KisImageMagickConverter::~KisImageMagickConverter()
00366 {
00367 }
00368 
00369 KisImageBuilder_Result KisImageMagickConverter::decode(const KURL& uri, bool isBlob)
00370 {
00371     Image *image;
00372     Image *images;
00373     ExceptionInfo ei;
00374     ImageInfo *ii;
00375 
00376     if (m_stop) {
00377         m_img = 0;
00378         return KisImageBuilder_RESULT_INTR;
00379     }
00380 
00381     GetExceptionInfo(&ei);
00382     ii = CloneImageInfo(0);
00383 
00384     if (isBlob) {
00385 
00386         // TODO : Test.  Does BlobToImage even work?
00387         Q_ASSERT(uri.isEmpty());
00388         images = BlobToImage(ii, &m_data[0], m_data.size(), &ei);
00389     } else {
00390 
00391         qstrncpy(ii -> filename, QFile::encodeName(uri.path()), MaxTextExtent - 1);
00392 
00393         if (ii -> filename[MaxTextExtent - 1]) {
00394             emit notifyProgressError();
00395             return KisImageBuilder_RESULT_PATH;
00396         }
00397 
00398         images = ReadImage(ii, &ei);
00399 
00400     }
00401 
00402     if (ei.severity != UndefinedException)
00403     {
00404         CatchException(&ei);
00405         kdDebug(41008) << "Exceptions happen when loading" << endl;
00406         return KisImageBuilder_RESULT_FAILURE;
00407     }
00408     
00409 
00410     if (images == 0) {
00411         DestroyImageInfo(ii);
00412         DestroyExceptionInfo(&ei);
00413         emit notifyProgressError();
00414         return KisImageBuilder_RESULT_FAILURE;
00415     }
00416 
00417     emit notifyProgressStage(i18n("Importing..."), 0);
00418 
00419     m_img = 0;
00420 
00421     while ((image = RemoveFirstImageFromList(&images))) {
00422         if(image->rows == 0 or image->columns == 0) return KisImageBuilder_RESULT_FAILURE;
00423         ViewInfo *vi = OpenCacheView(image);
00424 
00425         // Determine image depth -- for now, all channels of an imported image are of the same depth
00426         unsigned long imageDepth = image->depth;
00427         kdDebug(41008) << "Image depth: " << imageDepth << "\n";
00428 
00429         QString csName;
00430         KisColorSpace * cs = 0;
00431         ColorspaceType colorspaceType;
00432 
00433         // Determine image type -- rgb, grayscale or cmyk
00434         if (GetImageType(image, &ei) == GrayscaleType || GetImageType(image, &ei) == GrayscaleMatteType) {
00435             if (imageDepth == 8)
00436                 csName = "GRAYA";
00437             else if ( imageDepth == 16 )
00438                 csName = "GRAYA16" ;
00439             colorspaceType = GRAYColorspace;
00440         }
00441         else {
00442             colorspaceType = image->colorspace;
00443             csName = getColorSpaceName(image -> colorspace, imageDepth);
00444         }
00445 
00446         kdDebug(41008) << "image has " << csName << " colorspace\n";
00447         
00448         KisProfile * profile = getProfileForProfileInfo(image);
00449         if (profile)
00450         {
00451             kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n";
00452             cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile);
00453         }
00454         else
00455             cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),"");
00456 
00457         if (!cs) {
00458             kdDebug(41008) << "Krita does not support colorspace " << image -> colorspace << "\n";
00459             CloseCacheView(vi);
00460             DestroyImage(image);
00461             DestroyExceptionInfo(&ei);
00462             DestroyImageList(images);
00463             DestroyImageInfo(ii);
00464             emit notifyProgressError();
00465             return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
00466         }
00467 
00468         if( ! m_img) {
00469             m_img = new KisImage(m_doc->undoAdapter(), image -> columns, image -> rows, cs, "built image");
00470             Q_CHECK_PTR(m_img);
00471             m_img->blockSignals(true); // Don't send out signals while we're building the image
00472             
00473             // XXX I'm assuming seperate layers won't have other profile things like EXIF
00474             setAnnotationsForImage(image, m_img);
00475         }
00476 
00477         if (image -> columns && image -> rows) {
00478 
00479             // Opacity (set by the photoshop import filter)
00480             Q_UINT8 opacity = OPACITY_OPAQUE;
00481             const ImageAttribute * attr = GetImageAttribute(image, "[layer-opacity]");
00482             if (attr != 0) {
00483                 opacity = Q_UINT8_MAX - Downscale(QString(attr->value).toInt());
00484             }
00485 
00486             KisPaintLayerSP layer = 0;
00487 
00488             attr = GetImageAttribute(image, "[layer-name]");
00489             if (attr != 0) {
00490                 layer = new KisPaintLayer(m_img, attr->value, opacity);
00491             }
00492             else {
00493                 layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), opacity);
00494             }
00495 
00496             Q_ASSERT(layer);
00497 
00498             // Layerlocation  (set by the photoshop import filter)
00499             Q_INT32 x_offset = 0;
00500             Q_INT32 y_offset = 0;
00501 
00502             attr = GetImageAttribute(image, "[layer-xpos]");
00503             if (attr != 0) {
00504                 x_offset = QString(attr->value).toInt();
00505             }
00506 
00507             attr = GetImageAttribute(image, "[layer-ypos]");
00508             if (attr != 0) {
00509                 y_offset = QString(attr->value).toInt();
00510             }
00511 
00512 
00513             for (Q_UINT32 y = 0; y < image->rows; y ++)
00514             {
00515                 const PixelPacket *pp = AcquireCacheView(vi, 0, y, image->columns, 1, &ei);
00516 
00517                 if(!pp)
00518                 {
00519                     CloseCacheView(vi);
00520                     DestroyImageList(images);
00521                     DestroyImageInfo(ii);
00522                     DestroyExceptionInfo(&ei);
00523                     emit notifyProgressError();
00524                     return KisImageBuilder_RESULT_FAILURE;
00525                 }
00526 
00527                 IndexPacket * indexes = GetCacheViewIndexes(vi);
00528 
00529                 KisHLineIteratorPixel hiter = layer->paintDevice()->createHLineIterator(0, y, image->columns, true);
00530 
00531                 if (colorspaceType== CMYKColorspace) {
00532                     if (imageDepth == 8) {
00533                         int x = 0;
00534                         while (!hiter.isDone())
00535                         {
00536                             Q_UINT8 *ptr= hiter.rawData();
00537                             *(ptr++) = Downscale(pp->red); // cyan
00538                             *(ptr++) = Downscale(pp->green); // magenta
00539                             *(ptr++) = Downscale(pp->blue); // yellow
00540                             *(ptr++) = Downscale(indexes[x]); // Black
00541 // XXX: Warning! This ifdef messes up the paren matching big-time!
00542 #ifdef HAVE_MAGICK6
00543                             if (image->matte != MagickFalse) {
00544 #else
00545                             if (image->matte == true) {
00546 #endif
00547                                 *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity);
00548                             }
00549                             else {
00550                                 *(ptr++) = OPACITY_OPAQUE;
00551                             }
00552                             ++x;
00553                             pp++;
00554                             ++hiter;
00555                         }
00556                     }
00557                 }
00558                 else if (colorspaceType == LABColorspace) {
00559                     while(! hiter.isDone())
00560                     {
00561                         Q_UINT16 *ptr = reinterpret_cast<Q_UINT16 *>(hiter.rawData());
00562                         
00563                         *(ptr++) = ScaleQuantumToShort(pp->red);
00564                         *(ptr++) = ScaleQuantumToShort(pp->green);
00565                         *(ptr++) = ScaleQuantumToShort(pp->blue);
00566                         *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
00567 
00568                         pp++;
00569                         ++hiter;
00570                     }
00571                 }
00572                 else if (colorspaceType == RGBColorspace ||
00573                              colorspaceType == sRGBColorspace ||
00574                              colorspaceType == TransparentColorspace)
00575                     {
00576                         if (imageDepth == 8) {
00577                             while(! hiter.isDone())
00578                             {
00579                                 Q_UINT8 *ptr= hiter.rawData();
00580                                 // XXX: not colorstrategy and bitdepth independent
00581                                 *(ptr++) = Downscale(pp->blue);
00582                                 *(ptr++) = Downscale(pp->green);
00583                                 *(ptr++) = Downscale(pp->red);
00584                                 *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity);
00585 
00586                                 pp++;
00587                                 ++hiter;
00588                             }
00589                         }
00590                         else if (imageDepth == 16) {
00591                             while(! hiter.isDone())
00592                             {
00593                                 Q_UINT16 *ptr = reinterpret_cast<Q_UINT16 *>(hiter.rawData());
00594                                 // XXX: not colorstrategy independent
00595                                 *(ptr++) = ScaleQuantumToShort(pp->blue);
00596                                 *(ptr++) = ScaleQuantumToShort(pp->green);
00597                                 *(ptr++) = ScaleQuantumToShort(pp->red);
00598                                 *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
00599 
00600                                 pp++;
00601                                 ++hiter;
00602                             }
00603                         }
00604                     }
00605                     else if ( colorspaceType == GRAYColorspace) {
00606                         if (imageDepth == 8) {
00607                             while(! hiter.isDone())
00608                             {
00609                                 Q_UINT8 *ptr= hiter.rawData();
00610                                 // XXX: not colorstrategy and bitdepth independent
00611                                 *(ptr++) = Downscale(pp->blue);
00612                                 *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity);
00613 
00614                                 pp++;
00615                                 ++hiter;
00616                             }
00617                         }
00618                         else if (imageDepth == 16) {
00619                             while(! hiter.isDone())
00620                             {
00621                                 Q_UINT16 *ptr = reinterpret_cast<Q_UINT16 *>(hiter.rawData());
00622                                 // XXX: not colorstrategy independent
00623                                 *(ptr++) = ScaleQuantumToShort(pp->blue);
00624                                 *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
00625 
00626                                 pp++;
00627                                 ++hiter;
00628                             }
00629                         }
00630                     }
00631 
00632                     emit notifyProgress(y * 100 / image->rows);
00633 
00634                     if (m_stop) {
00635                         CloseCacheView(vi);
00636                         DestroyImage(image);
00637                         DestroyImageList(images);
00638                         DestroyImageInfo(ii);
00639                         DestroyExceptionInfo(&ei);
00640                         m_img = 0;
00641                         return KisImageBuilder_RESULT_INTR;
00642                     }
00643                 }
00644                 m_img->addLayer(layer.data(), m_img->rootLayer());
00645                 layer->paintDevice()->move(x_offset, y_offset);
00646             }
00647 
00648             emit notifyProgressDone();
00649             CloseCacheView(vi);
00650             DestroyImage(image);
00651         }
00652 
00653         emit notifyProgressDone();
00654         DestroyImageList(images);
00655         DestroyImageInfo(ii);
00656         DestroyExceptionInfo(&ei);
00657         return KisImageBuilder_RESULT_OK;
00658     }
00659 
00660     KisImageBuilder_Result KisImageMagickConverter::buildImage(const KURL& uri)
00661     {
00662         if (uri.isEmpty())
00663             return KisImageBuilder_RESULT_NO_URI;
00664 
00665         if (!KIO::NetAccess::exists(uri, false, qApp -> mainWidget())) {
00666             return KisImageBuilder_RESULT_NOT_EXIST;
00667         }
00668 
00669         KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE;
00670         QString tmpFile;
00671 
00672         if (KIO::NetAccess::download(uri, tmpFile, qApp -> mainWidget())) {
00673             KURL uriTF;
00674             uriTF.setPath( tmpFile );
00675             result = decode(uriTF, false);
00676             KIO::NetAccess::removeTempFile(tmpFile);
00677         }
00678 
00679         return result;
00680     }
00681 
00682 
00683     KisImageSP KisImageMagickConverter::image()
00684     {
00685         return m_img;
00686     }
00687 
00688     void KisImageMagickConverter::init(KisDoc *doc, KisUndoAdapter *adapter)
00689     {
00690         m_doc = doc;
00691         m_adapter = adapter;
00692         m_job = 0;
00693     }
00694 
00695     KisImageBuilder_Result KisImageMagickConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd)
00696     {
00697         Image *image;
00698         ExceptionInfo ei;
00699         ImageInfo *ii;
00700 
00701         if (!layer)
00702             return KisImageBuilder_RESULT_INVALID_ARG;
00703 
00704         KisImageSP img = layer->image();
00705         if (!img)
00706             return KisImageBuilder_RESULT_EMPTY;
00707 
00708         if (uri.isEmpty())
00709             return KisImageBuilder_RESULT_NO_URI;
00710 
00711         if (!uri.isLocalFile())
00712             return KisImageBuilder_RESULT_NOT_LOCAL;
00713 
00714 
00715         Q_UINT32 layerBytesPerChannel = layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels();
00716 
00717         GetExceptionInfo(&ei);
00718 
00719         ii = CloneImageInfo(0);
00720 
00721         qstrncpy(ii -> filename, QFile::encodeName(uri.path()), MaxTextExtent - 1);
00722 
00723         if (ii -> filename[MaxTextExtent - 1]) {
00724             emit notifyProgressError();
00725             return KisImageBuilder_RESULT_PATH;
00726         }
00727 
00728         if (!img -> width() || !img -> height())
00729             return KisImageBuilder_RESULT_EMPTY;
00730 
00731         if (layerBytesPerChannel < 2) {
00732             ii->depth = 8;
00733         }
00734         else {
00735             ii->depth = 16;
00736         }
00737 
00738         ii->colorspace = getColorTypeforColorSpace(layer->paintDevice()->colorSpace());
00739 
00740         image = AllocateImage(ii);
00741 //         SetImageColorspace(image, ii->colorspace);
00742         image -> columns = img -> width();
00743         image -> rows = img -> height();
00744 
00745         kdDebug(41008) << "Saving with colorspace " << image->colorspace << ", (" << layer->paintDevice()->colorSpace()->id().name() << ")\n";
00746         kdDebug(41008) << "IM Image thinks it has depth: " << image->depth << "\n";
00747 
00748 #ifdef HAVE_MAGICK6
00749         //    if ( layer-> hasAlpha() )
00750         image -> matte = MagickTrue;
00751         //    else
00752         //        image -> matte = MagickFalse;
00753 #else
00754         //    image -> matte = layer -> hasAlpha();
00755         image -> matte = true;
00756 #endif
00757 
00758         Q_INT32 y, height, width;
00759 
00760         height = img -> height();
00761         width = img -> width();
00762 
00763         bool alpha = true;
00764         QString ext = QFileInfo(QFile::encodeName(uri.path())).extension(false).upper();
00765         if (ext == "BMP") {
00766             alpha = false;
00767             qstrncpy(ii->magick, "BMP2", MaxTextExtent - 1);
00768         }
00769         else if (ext == "RGB") {
00770             qstrncpy(ii->magick, "SGI", MaxTextExtent - 1);
00771         }
00772 
00773         for (y = 0; y < height; y++) {
00774 
00775             // Allocate pixels for this scanline
00776             PixelPacket * pp = SetImagePixels(image, 0, y, width, 1);
00777 
00778             if (!pp) {
00779                 DestroyExceptionInfo(&ei);
00780                 DestroyImage(image);
00781                 emit notifyProgressError();
00782                 return KisImageBuilder_RESULT_FAILURE;
00783 
00784             }
00785 
00786             KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, y, width, false);
00787             if (alpha)
00788                 SetImageType(image, TrueColorMatteType);
00789             else
00790                 SetImageType(image,  TrueColorType);
00791 
00792             if (image->colorspace== CMYKColorspace) {
00793 
00794                 IndexPacket * indexes = GetIndexes(image);
00795                 int x = 0;
00796                 if (layerBytesPerChannel == 2) {
00797                     while (!it.isDone()) {
00798 
00799                         const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData());
00800                         pp -> red = ScaleShortToQuantum(d[PIXEL_CYAN]);
00801                         pp -> green = ScaleShortToQuantum(d[PIXEL_MAGENTA]);
00802                         pp -> blue = ScaleShortToQuantum(d[PIXEL_YELLOW]);
00803                         if (alpha)
00804                             pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_CMYK_ALPHA]);
00805                         indexes[x] = ScaleShortToQuantum(d[PIXEL_BLACK]);
00806                         x++;
00807                         pp++;
00808                         ++it;
00809                     }
00810                 }
00811                 else {
00812                     while (!it.isDone()) {
00813 
00814                         Q_UINT8 * d = it.rawData();
00815                         pp -> red = Upscale(d[PIXEL_CYAN]);
00816                         pp -> green = Upscale(d[PIXEL_MAGENTA]);
00817                         pp -> blue = Upscale(d[PIXEL_YELLOW]);
00818                         if (alpha)
00819                             pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_CMYK_ALPHA]);
00820 
00821                         indexes[x]= Upscale(d[PIXEL_BLACK]);
00822 
00823                         x++;
00824                         pp++;
00825                         ++it;
00826                     }
00827                 }
00828             }
00829             else if (image->colorspace== RGBColorspace ||
00830                      image->colorspace == sRGBColorspace ||
00831                      image->colorspace == TransparentColorspace)
00832             {
00833                 if (layerBytesPerChannel == 2) {
00834                     while (!it.isDone()) {
00835 
00836                         const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData());
00837                         pp -> red = ScaleShortToQuantum(d[PIXEL_RED]);
00838                         pp -> green = ScaleShortToQuantum(d[PIXEL_GREEN]);
00839                         pp -> blue = ScaleShortToQuantum(d[PIXEL_BLUE]);
00840                         if (alpha)
00841                             pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_ALPHA]);
00842 
00843                         pp++;
00844                         ++it;
00845                     }
00846                 }
00847                 else {
00848                     while (!it.isDone()) {
00849 
00850                         Q_UINT8 * d = it.rawData();
00851                         pp -> red = Upscale(d[PIXEL_RED]);
00852                         pp -> green = Upscale(d[PIXEL_GREEN]);
00853                         pp -> blue = Upscale(d[PIXEL_BLUE]);
00854                         if (alpha)
00855                             pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_ALPHA]);
00856 
00857                         pp++;
00858                         ++it;
00859                     }
00860                 }
00861             }
00862             else if (image->colorspace == GRAYColorspace)
00863             {
00864                 SetImageType(image, GrayscaleMatteType);
00865                 if (layerBytesPerChannel == 2) {
00866                     while (!it.isDone()) {
00867 
00868                         const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData());
00869                         pp -> red = ScaleShortToQuantum(d[PIXEL_GRAY]);
00870                         pp -> green = ScaleShortToQuantum(d[PIXEL_GRAY]);
00871                         pp -> blue = ScaleShortToQuantum(d[PIXEL_GRAY]);
00872                         if (alpha)
00873                             pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_GRAY_ALPHA]);
00874 
00875                         pp++;
00876                         ++it;
00877                     }
00878                 }
00879                 else {
00880                     while (!it.isDone()) {
00881                         Q_UINT8 * d = it.rawData();
00882                         pp -> red = Upscale(d[PIXEL_GRAY]);
00883                         pp -> green = Upscale(d[PIXEL_GRAY]);
00884                         pp -> blue = Upscale(d[PIXEL_GRAY]);
00885                         if (alpha)
00886                             pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_GRAY_ALPHA]);
00887 
00888                         pp++;
00889                         ++it;
00890                     }
00891                 }
00892             }
00893             else {
00894                 kdDebug(41008) << "Unsupported image format\n";
00895                 return KisImageBuilder_RESULT_INVALID_ARG;
00896             }
00897 
00898             emit notifyProgressStage(i18n("Saving..."), y * 100 / height);
00899 
00900 #ifdef HAVE_MAGICK6
00901             if (SyncImagePixels(image) == MagickFalse)
00902                 kdDebug(41008) << "Syncing pixels failed\n";
00903 #else
00904             if (!SyncImagePixels(image))
00905                 kdDebug(41008) << "Syncing pixels failed\n";
00906 #endif
00907         }
00908 
00909         // set the annotations
00910         exportAnnotationsForImage(image, annotationsStart, annotationsEnd);
00911 
00912         // XXX: Write to a temp file, then have Krita use KIO to copy temp
00913         // image to remote location.
00914 
00915         WriteImage(ii, image);
00916         DestroyExceptionInfo(&ei);
00917         DestroyImage(image);
00918         emit notifyProgressDone();
00919         return KisImageBuilder_RESULT_OK;
00920     }
00921 
00922     void KisImageMagickConverter::ioData(KIO::Job *job, const QByteArray& data)
00923     {
00924         if (data.isNull() || data.isEmpty()) {
00925             emit notifyProgressStage(i18n("Loading..."), 0);
00926             return;
00927         }
00928 
00929         if (m_data.empty()) {
00930             Image *image;
00931             ImageInfo *ii;
00932             ExceptionInfo ei;
00933 
00934             ii = CloneImageInfo(0);
00935             GetExceptionInfo(&ei);
00936             image = PingBlob(ii, data.data(), data.size(), &ei);
00937 
00938             if (image == 0 || ei.severity == BlobError) {
00939                 DestroyExceptionInfo(&ei);
00940                 DestroyImageInfo(ii);
00941                 job -> kill();
00942                 emit notifyProgressError();
00943                 return;
00944             }
00945 
00946             DestroyImage(image);
00947             DestroyExceptionInfo(&ei);
00948             DestroyImageInfo(ii);
00949             emit notifyProgressStage(i18n("Loading..."), 0);
00950         }
00951 
00952         Q_ASSERT(data.size() + m_data.size() <= m_size);
00953         memcpy(&m_data[m_data.size()], data.data(), data.count());
00954         m_data.resize(m_data.size() + data.count());
00955         emit notifyProgressStage(i18n("Loading..."), m_data.size() * 100 / m_size);
00956 
00957         if (m_stop)
00958             job -> kill();
00959     }
00960 
00961     void KisImageMagickConverter::ioResult(KIO::Job *job)
00962     {
00963         m_job = 0;
00964 
00965         if (job -> error())
00966             emit notifyProgressError();
00967 
00968         decode(KURL(), true);
00969     }
00970 
00971     void KisImageMagickConverter::ioTotalSize(KIO::Job * /*job*/, KIO::filesize_t size)
00972     {
00973         m_size = size;
00974         m_data.reserve(size);
00975         emit notifyProgressStage(i18n("Loading..."), 0);
00976     }
00977 
00978     void KisImageMagickConverter::cancel()
00979     {
00980         m_stop = true;
00981     }
00982 
00987     QString KisImageMagickConverter::readFilters()
00988     {
00989         QString s;
00990         QString all;
00991         QString name;
00992         QString description;
00993         unsigned long matches;
00994 
00995 /*#ifdef HAVE_MAGICK6
00996 #ifdef HAVE_OLD_GETMAGICKINFOLIST
00997         const MagickInfo **mi;
00998         mi = GetMagickInfoList("*", &matches);
00999 #else // HAVE_OLD_GETMAGICKINFOLIST
01000         ExceptionInfo ei;
01001         GetExceptionInfo(&ei);
01002         const MagickInfo **mi;
01003         mi = GetMagickInfoList("*", &matches, &ei);
01004         DestroyExceptionInfo(&ei);
01005 #endif // HAVE_OLD_GETMAGICKINFOLIST
01006 #else // HAVE_MAGICK6*/
01007         const MagickInfo *mi;
01008         ExceptionInfo ei;
01009         GetExceptionInfo(&ei);
01010         mi = GetMagickInfo("*", &ei);
01011         DestroyExceptionInfo(&ei);
01012 // #endif // HAVE_MAGICK6
01013 
01014         if (!mi)
01015             return s;
01016 
01017 /*#ifdef HAVE_MAGICK6
01018         for (unsigned long i = 0; i < matches; i++) {
01019             const MagickInfo *info = mi[i];
01020             if (info -> stealth)
01021                 continue;
01022 
01023             if (info -> decoder) {
01024                 name = info -> name;
01025                 description = info -> description;
01026                 kdDebug(41008) << "Found import filter for: " << name << "\n";
01027 
01028                 if (!description.isEmpty() && !description.contains('/')) {
01029                     all += "*." + name.lower() + " *." + name + " ";
01030                     s += "*." + name.lower() + " *." + name + "|";
01031                     s += i18n(description.utf8());
01032                     s += "\n";
01033                 }
01034             }
01035         }
01036 #else*/
01037         for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) {
01038             if (mi -> stealth)
01039                 continue;
01040             if (mi -> decoder) {
01041                 name = mi -> name;
01042                 description = mi -> description;
01043                 kdDebug(41008) << "Found import filter for: " << name << "\n";
01044 
01045                 if (!description.isEmpty() && !description.contains('/')) {
01046                     all += "*." + name.lower() + " *." + name + " ";
01047                     s += "*." + name.lower() + " *." + name + "|";
01048                     s += i18n(description.utf8());
01049                     s += "\n";
01050                 }
01051             }
01052         }
01053 // #endif
01054 
01055         all += "|" + i18n("All Images");
01056         all += "\n";
01057 
01058         return all + s;
01059     }
01060 
01061     QString KisImageMagickConverter::writeFilters()
01062     {
01063         QString s;
01064         QString all;
01065         QString name;
01066         QString description;
01067         unsigned long matches;
01068 
01069 /*#ifdef HAVE_MAGICK6
01070 #ifdef HAVE_OLD_GETMAGICKINFOLIST
01071         const MagickInfo **mi;
01072         mi = GetMagickInfoList("*", &matches);
01073 #else // HAVE_OLD_GETMAGICKINFOLIST
01074         ExceptionInfo ei;
01075         GetExceptionInfo(&ei);
01076         const MagickInfo **mi;
01077         mi = GetMagickInfoList("*", &matches, &ei);
01078         DestroyExceptionInfo(&ei);
01079 #endif // HAVE_OLD_GETMAGICKINFOLIST
01080 #else // HAVE_MAGICK6*/
01081         const MagickInfo *mi;
01082         ExceptionInfo ei;
01083         GetExceptionInfo(&ei);
01084         mi = GetMagickInfo("*", &ei);
01085         DestroyExceptionInfo(&ei);
01086 // #endif // HAVE_MAGICK6
01087 
01088         if (!mi) {
01089             kdDebug(41008) << "Eek, no magick info!\n";
01090             return s;
01091         }
01092 
01093 /*#ifdef HAVE_MAGICK6
01094         for (unsigned long i = 0; i < matches; i++) {
01095             const MagickInfo *info = mi[i];
01096             kdDebug(41008) << "Found export filter for: " << info -> name << "\n";
01097             if (info -> stealth)
01098                 continue;
01099 
01100             if (info -> encoder) {
01101                 name = info -> name;
01102 
01103                 description = info -> description;
01104 
01105                 if (!description.isEmpty() && !description.contains('/')) {
01106                     all += "*." + name.lower() + " *." + name + " ";
01107                     s += "*." + name.lower() + " *." + name + "|";
01108                     s += i18n(description.utf8());
01109                     s += "\n";
01110                 }
01111             }
01112         }
01113 #else*/
01114         for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) {
01115             kdDebug(41008) << "Found export filter for: " << mi -> name << "\n";
01116             if (mi -> stealth)
01117                 continue;
01118 
01119             if (mi -> encoder) {
01120                 name = mi -> name;
01121 
01122                 description = mi -> description;
01123 
01124                 if (!description.isEmpty() && !description.contains('/')) {
01125                     all += "*." + name.lower() + " *." + name + " ";
01126                     s += "*." + name.lower() + " *." + name + "|";
01127                     s += i18n(description.utf8());
01128                     s += "\n";
01129                 }
01130             }
01131         }
01132 // #endif
01133 
01134 
01135         all += "|" + i18n("All Images");
01136         all += "\n";
01137 
01138         return all + s;
01139     }
01140 
01141 #include "kis_image_magick_converter.moc"
01142 
KDE Home | KDE Accessibility Home | Description of Access Keys