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                     if (!ProfileImage(dst, (*it) -> type().ascii(),
00293                                     (unsigned char*)(*it) -> annotation() . data(),
00294                                     (*it) -> annotation() . size(), MagickFalse)) {
00295                         kdDebug(41008) << "Storing failed!" << endl;
00296                     }
00297             }
00298             ++it;
00299         }
00300     }
00301 
00302 
00303     void InitGlobalMagick()
00304     {
00305         static bool init = false;
00306 
00307         if (!init) {
00308             KApplication *app = KApplication::kApplication();
00309 
00310             InitializeMagick(*app -> argv());
00311             atexit(DestroyMagick);
00312             init = true;
00313         }
00314     }
00315 
00316     /*
00317      * ImageMagick progress monitor callback.  Unfortunately it doesn't support passing in some user
00318      * data which complicates things quite a bit.  The plan was to allow the user start multiple
00319      * import/scans if he/she so wished.  However, without passing user data it's not possible to tell
00320      * on which task we have made progress on.
00321      *
00322      * Additionally, ImageMagick is thread-safe, not re-entrant... i.e. IM does not relinquish held
00323      * locks when calling user defined callbacks, this means that the same thread going back into IM
00324      * would deadlock since it would try to acquire locks it already holds.
00325      */
00326 #if 0
00327     MagickBooleanType monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *)
00328     {
00329         KApplication *app = KApplication::kApplication();
00330 
00331         Q_ASSERT(app);
00332 
00333         if (app -> hasPendingEvents())
00334             app -> processEvents();
00335 
00336         printf("%s\n", text);
00337         return MagickTrue;
00338     }
00339 #else
00340     unsigned int monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *)
00341     {
00342         KApplication *app = KApplication::kApplication();
00343 
00344         Q_ASSERT(app);
00345 
00346         if (app -> hasPendingEvents())
00347             app -> processEvents();
00348 
00349         printf("%s\n", text);
00350         return true;
00351     }
00352 #endif
00353 
00354 
00355 
00356 KisImageMagickConverter::KisImageMagickConverter(KisDoc *doc, KisUndoAdapter *adapter)
00357 {
00358     InitGlobalMagick();
00359     init(doc, adapter);
00360     SetMonitorHandler(monitor);
00361     m_stop = false;
00362 }
00363 
00364 KisImageMagickConverter::~KisImageMagickConverter()
00365 {
00366 }
00367 
00368 KisImageBuilder_Result KisImageMagickConverter::decode(const KURL& uri, bool isBlob)
00369 {
00370     Image *image;
00371     Image *images;
00372     ExceptionInfo ei;
00373     ImageInfo *ii;
00374 
00375     if (m_stop) {
00376         m_img = 0;
00377         return KisImageBuilder_RESULT_INTR;
00378     }
00379 
00380     GetExceptionInfo(&ei);
00381     ii = CloneImageInfo(0);
00382 
00383     if (isBlob) {
00384 
00385         // TODO : Test.  Does BlobToImage even work?
00386         Q_ASSERT(uri.isEmpty());
00387         images = BlobToImage(ii, &m_data[0], m_data.size(), &ei);
00388     } else {
00389 
00390         qstrncpy(ii -> filename, QFile::encodeName(uri.path()), MaxTextExtent - 1);
00391 
00392         if (ii -> filename[MaxTextExtent - 1]) {
00393             emit notifyProgressError();
00394             return KisImageBuilder_RESULT_PATH;
00395         }
00396 
00397         images = ReadImage(ii, &ei);
00398 
00399     }
00400 
00401     if (ei.severity != UndefinedException)
00402         CatchException(&ei);
00403 
00404     if (images == 0) {
00405         DestroyImageInfo(ii);
00406         DestroyExceptionInfo(&ei);
00407         emit notifyProgressError();
00408         return KisImageBuilder_RESULT_FAILURE;
00409     }
00410 
00411     emit notifyProgressStage(i18n("Importing..."), 0);
00412 
00413     m_img = 0;
00414 
00415     while ((image = RemoveFirstImageFromList(&images))) {
00416         ViewInfo *vi = OpenCacheView(image);
00417 
00418         // Determine image depth -- for now, all channels of an imported image are of the same depth
00419         unsigned long imageDepth = image->depth;
00420         kdDebug(41008) << "Image depth: " << imageDepth << "\n";
00421 
00422         QString csName;
00423         KisColorSpace * cs = 0;
00424         ColorspaceType colorspaceType;
00425 
00426         // Determine image type -- rgb, grayscale or cmyk
00427         if (GetImageType(image, &ei) == GrayscaleType || GetImageType(image, &ei) == GrayscaleMatteType) {
00428             if (imageDepth == 8)
00429                 csName = "GRAYA";
00430             else if ( imageDepth == 16 )
00431                 csName = "GRAYA16" ;
00432             colorspaceType = GRAYColorspace;
00433         }
00434         else {
00435             colorspaceType = image->colorspace;
00436             csName = getColorSpaceName(image -> colorspace, imageDepth);
00437         }
00438 
00439         kdDebug(41008) << "image has " << csName << " colorspace\n";
00440         
00441         KisProfile * profile = getProfileForProfileInfo(image);
00442         if (profile)
00443         {
00444             kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n";
00445             cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile);
00446         }
00447         else
00448             cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),"");
00449 
00450         if (!cs) {
00451             kdDebug(41008) << "Krita does not support colorspace " << image -> colorspace << "\n";
00452             CloseCacheView(vi);
00453             DestroyImage(image);
00454             DestroyExceptionInfo(&ei);
00455             DestroyImageList(images);
00456             DestroyImageInfo(ii);
00457             emit notifyProgressError();
00458             return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
00459         }
00460 
00461         if( ! m_img) {
00462             m_img = new KisImage(m_doc->undoAdapter(), image -> columns, image -> rows, cs, "built image");
00463             Q_CHECK_PTR(m_img);
00464             m_img->blockSignals(true); // Don't send out signals while we're building the image
00465             
00466             // XXX I'm assuming seperate layers won't have other profile things like EXIF
00467             setAnnotationsForImage(image, m_img);
00468         }
00469 
00470         if (image -> columns && image -> rows) {
00471 
00472             // Opacity (set by the photoshop import filter)
00473             Q_UINT8 opacity = OPACITY_OPAQUE;
00474             const ImageAttribute * attr = GetImageAttribute(image, "[layer-opacity]");
00475             if (attr != 0) {
00476                 opacity = Q_UINT8_MAX - Downscale(QString(attr->value).toInt());
00477             }
00478 
00479             KisPaintLayerSP layer = 0;
00480 
00481             attr = GetImageAttribute(image, "[layer-name]");
00482             if (attr != 0) {
00483                 layer = new KisPaintLayer(m_img, attr->value, opacity);
00484             }
00485             else {
00486                 layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), opacity);
00487             }
00488 
00489             Q_ASSERT(layer);
00490 
00491             // Layerlocation  (set by the photoshop import filter)
00492             Q_INT32 x_offset = 0;
00493             Q_INT32 y_offset = 0;
00494 
00495             attr = GetImageAttribute(image, "[layer-xpos]");
00496             if (attr != 0) {
00497                 x_offset = QString(attr->value).toInt();
00498             }
00499 
00500             attr = GetImageAttribute(image, "[layer-ypos]");
00501             if (attr != 0) {
00502                 y_offset = QString(attr->value).toInt();
00503             }
00504 
00505 
00506             for (Q_UINT32 y = 0; y < image->rows; y ++)
00507             {
00508                 const PixelPacket *pp = AcquireCacheView(vi, 0, y, image->columns, 1, &ei);
00509 
00510                 if(!pp)
00511                 {
00512                     CloseCacheView(vi);
00513                     DestroyImageList(images);
00514                     DestroyImageInfo(ii);
00515                     DestroyExceptionInfo(&ei);
00516                     emit notifyProgressError();
00517                     return KisImageBuilder_RESULT_FAILURE;
00518                 }
00519 
00520                 IndexPacket * indexes = GetCacheViewIndexes(vi);
00521 
00522                 KisHLineIteratorPixel hiter = layer->paintDevice()->createHLineIterator(0, y, image->columns, true);
00523 
00524                 if (colorspaceType== CMYKColorspace) {
00525                     if (imageDepth == 8) {
00526                         int x = 0;
00527                         while (!hiter.isDone())
00528                         {
00529                             Q_UINT8 *ptr= hiter.rawData();
00530                             *(ptr++) = Downscale(pp->red); // cyan
00531                             *(ptr++) = Downscale(pp->green); // magenta
00532                             *(ptr++) = Downscale(pp->blue); // yellow
00533                             *(ptr++) = Downscale(indexes[x]); // Black
00534 // XXX: Warning! This ifdef messes up the paren matching big-time!
00535 #ifdef HAVE_MAGICK6
00536                             if (image->matte != MagickFalse) {
00537 #else
00538                             if (image->matte == true) {
00539 #endif
00540                                 *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity);
00541                             }
00542                             else {
00543                                 *(ptr++) = OPACITY_OPAQUE;
00544                             }
00545                             ++x;
00546                             pp++;
00547                             ++hiter;
00548                         }
00549                     }
00550                 }
00551                 else if (colorspaceType == LABColorspace) {
00552                     while(! hiter.isDone())
00553                     {
00554                         Q_UINT16 *ptr = reinterpret_cast<Q_UINT16 *>(hiter.rawData());
00555                         
00556                         *(ptr++) = ScaleQuantumToShort(pp->red);
00557                         *(ptr++) = ScaleQuantumToShort(pp->green);
00558                         *(ptr++) = ScaleQuantumToShort(pp->blue);
00559                         *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
00560 
00561                         pp++;
00562                         ++hiter;
00563                     }
00564                 }
00565                 else if (colorspaceType == RGBColorspace ||
00566                              colorspaceType == sRGBColorspace ||
00567                              colorspaceType == TransparentColorspace)
00568                     {
00569                         if (imageDepth == 8) {
00570                             while(! hiter.isDone())
00571                             {
00572                                 Q_UINT8 *ptr= hiter.rawData();
00573                                 // XXX: not colorstrategy and bitdepth independent
00574                                 *(ptr++) = Downscale(pp->blue);
00575                                 *(ptr++) = Downscale(pp->green);
00576                                 *(ptr++) = Downscale(pp->red);
00577                                 *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity);
00578 
00579                                 pp++;
00580                                 ++hiter;
00581                             }
00582                         }
00583                         else if (imageDepth == 16) {
00584                             while(! hiter.isDone())
00585                             {
00586                                 Q_UINT16 *ptr = reinterpret_cast<Q_UINT16 *>(hiter.rawData());
00587                                 // XXX: not colorstrategy independent
00588                                 *(ptr++) = ScaleQuantumToShort(pp->blue);
00589                                 *(ptr++) = ScaleQuantumToShort(pp->green);
00590                                 *(ptr++) = ScaleQuantumToShort(pp->red);
00591                                 *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
00592 
00593                                 pp++;
00594                                 ++hiter;
00595                             }
00596                         }
00597                     }
00598                     else if ( colorspaceType == GRAYColorspace) {
00599                         if (imageDepth == 8) {
00600                             while(! hiter.isDone())
00601                             {
00602                                 Q_UINT8 *ptr= hiter.rawData();
00603                                 // XXX: not colorstrategy and bitdepth independent
00604                                 *(ptr++) = Downscale(pp->blue);
00605                                 *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity);
00606 
00607                                 pp++;
00608                                 ++hiter;
00609                             }
00610                         }
00611                         else if (imageDepth == 16) {
00612                             while(! hiter.isDone())
00613                             {
00614                                 Q_UINT16 *ptr = reinterpret_cast<Q_UINT16 *>(hiter.rawData());
00615                                 // XXX: not colorstrategy independent
00616                                 *(ptr++) = ScaleQuantumToShort(pp->blue);
00617                                 *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);
00618 
00619                                 pp++;
00620                                 ++hiter;
00621                             }
00622                         }
00623                     }
00624 
00625                     emit notifyProgress(y * 100 / image->rows);
00626 
00627                     if (m_stop) {
00628                         CloseCacheView(vi);
00629                         DestroyImage(image);
00630                         DestroyImageList(images);
00631                         DestroyImageInfo(ii);
00632                         DestroyExceptionInfo(&ei);
00633                         m_img = 0;
00634                         return KisImageBuilder_RESULT_INTR;
00635                     }
00636                 }
00637                 m_img->addLayer(layer.data(), m_img->rootLayer());
00638                 layer->paintDevice()->move(x_offset, y_offset);
00639             }
00640 
00641             emit notifyProgressDone();
00642             CloseCacheView(vi);
00643             DestroyImage(image);
00644         }
00645 
00646         emit notifyProgressDone();
00647         DestroyImageList(images);
00648         DestroyImageInfo(ii);
00649         DestroyExceptionInfo(&ei);
00650         return KisImageBuilder_RESULT_OK;
00651     }
00652 
00653     KisImageBuilder_Result KisImageMagickConverter::buildImage(const KURL& uri)
00654     {
00655         if (uri.isEmpty())
00656             return KisImageBuilder_RESULT_NO_URI;
00657 
00658         if (!KIO::NetAccess::exists(uri, false, qApp -> mainWidget())) {
00659             return KisImageBuilder_RESULT_NOT_EXIST;
00660         }
00661 
00662         KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE;
00663         QString tmpFile;
00664 
00665         if (KIO::NetAccess::download(uri, tmpFile, qApp -> mainWidget())) {
00666             KURL uriTF;
00667             uriTF.setPath( tmpFile );
00668             result = decode(uriTF, false);
00669             KIO::NetAccess::removeTempFile(tmpFile);
00670         }
00671 
00672         return result;
00673     }
00674 
00675 
00676     KisImageSP KisImageMagickConverter::image()
00677     {
00678         return m_img;
00679     }
00680 
00681     void KisImageMagickConverter::init(KisDoc *doc, KisUndoAdapter *adapter)
00682     {
00683         m_doc = doc;
00684         m_adapter = adapter;
00685         m_job = 0;
00686     }
00687 
00688     KisImageBuilder_Result KisImageMagickConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd)
00689     {
00690         Image *image;
00691         ExceptionInfo ei;
00692         ImageInfo *ii;
00693 
00694         if (!layer)
00695             return KisImageBuilder_RESULT_INVALID_ARG;
00696 
00697         KisImageSP img = layer->image();
00698         if (!img)
00699             return KisImageBuilder_RESULT_EMPTY;
00700 
00701         if (uri.isEmpty())
00702             return KisImageBuilder_RESULT_NO_URI;
00703 
00704         if (!uri.isLocalFile())
00705             return KisImageBuilder_RESULT_NOT_LOCAL;
00706 
00707 
00708         Q_UINT32 layerBytesPerChannel = layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels();
00709 
00710         GetExceptionInfo(&ei);
00711 
00712         ii = CloneImageInfo(0);
00713 
00714         qstrncpy(ii -> filename, QFile::encodeName(uri.path()), MaxTextExtent - 1);
00715 
00716         if (ii -> filename[MaxTextExtent - 1]) {
00717             emit notifyProgressError();
00718             return KisImageBuilder_RESULT_PATH;
00719         }
00720 
00721         if (!img -> width() || !img -> height())
00722             return KisImageBuilder_RESULT_EMPTY;
00723 
00724         if (layerBytesPerChannel < 2) {
00725             ii->depth = 8;
00726         }
00727         else {
00728             ii->depth = 16;
00729         }
00730 
00731         ii->colorspace = getColorTypeforColorSpace(layer->paintDevice()->colorSpace());
00732 
00733         image = AllocateImage(ii);
00734 //         SetImageColorspace(image, ii->colorspace);
00735         image -> columns = img -> width();
00736         image -> rows = img -> height();
00737 
00738         kdDebug(41008) << "Saving with colorspace " << image->colorspace << ", (" << layer->paintDevice()->colorSpace()->id().name() << ")\n";
00739         kdDebug(41008) << "IM Image thinks it has depth: " << image->depth << "\n";
00740 
00741 #ifdef HAVE_MAGICK6
00742         //    if ( layer-> hasAlpha() )
00743         image -> matte = MagickTrue;
00744         //    else
00745         //        image -> matte = MagickFalse;
00746 #else
00747         //    image -> matte = layer -> hasAlpha();
00748         image -> matte = true;
00749 #endif
00750 
00751         Q_INT32 y, height, width;
00752 
00753         height = img -> height();
00754         width = img -> width();
00755 
00756         bool alpha = true;
00757         QString ext = QFileInfo(QFile::encodeName(uri.path())).extension(false).upper();
00758         if (ext == "BMP") {
00759             alpha = false;
00760             qstrncpy(ii->magick, "BMP2", MaxTextExtent - 1);
00761         }
00762         else if (ext == "RGB") {
00763             qstrncpy(ii->magick, "SGI", MaxTextExtent - 1);
00764         }
00765 
00766         for (y = 0; y < height; y++) {
00767 
00768             // Allocate pixels for this scanline
00769             PixelPacket * pp = SetImagePixels(image, 0, y, width, 1);
00770 
00771             if (!pp) {
00772                 DestroyExceptionInfo(&ei);
00773                 DestroyImage(image);
00774                 emit notifyProgressError();
00775                 return KisImageBuilder_RESULT_FAILURE;
00776 
00777             }
00778 
00779             KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, y, width, false);
00780             if (alpha)
00781                 SetImageType(image, TrueColorMatteType);
00782             else
00783                 SetImageType(image,  TrueColorType);
00784 
00785             if (image->colorspace== CMYKColorspace) {
00786 
00787                 IndexPacket * indexes = GetIndexes(image);
00788                 int x = 0;
00789                 if (layerBytesPerChannel == 2) {
00790                     while (!it.isDone()) {
00791 
00792                         const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData());
00793                         pp -> red = ScaleShortToQuantum(d[PIXEL_CYAN]);
00794                         pp -> green = ScaleShortToQuantum(d[PIXEL_MAGENTA]);
00795                         pp -> blue = ScaleShortToQuantum(d[PIXEL_YELLOW]);
00796                         if (alpha)
00797                             pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_CMYK_ALPHA]);
00798                         indexes[x] = ScaleShortToQuantum(d[PIXEL_BLACK]);
00799                         x++;
00800                         pp++;
00801                         ++it;
00802                     }
00803                 }
00804                 else {
00805                     while (!it.isDone()) {
00806 
00807                         Q_UINT8 * d = it.rawData();
00808                         pp -> red = Upscale(d[PIXEL_CYAN]);
00809                         pp -> green = Upscale(d[PIXEL_MAGENTA]);
00810                         pp -> blue = Upscale(d[PIXEL_YELLOW]);
00811                         if (alpha)
00812                             pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_CMYK_ALPHA]);
00813 
00814                         indexes[x]= Upscale(d[PIXEL_BLACK]);
00815 
00816                         x++;
00817                         pp++;
00818                         ++it;
00819                     }
00820                 }
00821             }
00822             else if (image->colorspace== RGBColorspace ||
00823                      image->colorspace == sRGBColorspace ||
00824                      image->colorspace == TransparentColorspace)
00825             {
00826                 if (layerBytesPerChannel == 2) {
00827                     while (!it.isDone()) {
00828 
00829                         const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData());
00830                         pp -> red = ScaleShortToQuantum(d[PIXEL_RED]);
00831                         pp -> green = ScaleShortToQuantum(d[PIXEL_GREEN]);
00832                         pp -> blue = ScaleShortToQuantum(d[PIXEL_BLUE]);
00833                         if (alpha)
00834                             pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_ALPHA]);
00835 
00836                         pp++;
00837                         ++it;
00838                     }
00839                 }
00840                 else {
00841                     while (!it.isDone()) {
00842 
00843                         Q_UINT8 * d = it.rawData();
00844                         pp -> red = Upscale(d[PIXEL_RED]);
00845                         pp -> green = Upscale(d[PIXEL_GREEN]);
00846                         pp -> blue = Upscale(d[PIXEL_BLUE]);
00847                         if (alpha)
00848                             pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_ALPHA]);
00849 
00850                         pp++;
00851                         ++it;
00852                     }
00853                 }
00854             }
00855             else if (image->colorspace == GRAYColorspace)
00856             {
00857                 SetImageType(image, GrayscaleMatteType);
00858                 if (layerBytesPerChannel == 2) {
00859                     while (!it.isDone()) {
00860 
00861                         const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData());
00862                         pp -> red = ScaleShortToQuantum(d[PIXEL_GRAY]);
00863                         pp -> green = ScaleShortToQuantum(d[PIXEL_GRAY]);
00864                         pp -> blue = ScaleShortToQuantum(d[PIXEL_GRAY]);
00865                         if (alpha)
00866                             pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_GRAY_ALPHA]);
00867 
00868                         pp++;
00869                         ++it;
00870                     }
00871                 }
00872                 else {
00873                     while (!it.isDone()) {
00874                         Q_UINT8 * d = it.rawData();
00875                         pp -> red = Upscale(d[PIXEL_GRAY]);
00876                         pp -> green = Upscale(d[PIXEL_GRAY]);
00877                         pp -> blue = Upscale(d[PIXEL_GRAY]);
00878                         if (alpha)
00879                             pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_GRAY_ALPHA]);
00880 
00881                         pp++;
00882                         ++it;
00883                     }
00884                 }
00885             }
00886             else {
00887                 kdDebug(41008) << "Unsupported image format\n";
00888                 return KisImageBuilder_RESULT_INVALID_ARG;
00889             }
00890 
00891             emit notifyProgressStage(i18n("Saving..."), y * 100 / height);
00892 
00893 #ifdef HAVE_MAGICK6
00894             if (SyncImagePixels(image) == MagickFalse)
00895                 kdDebug(41008) << "Syncing pixels failed\n";
00896 #else
00897             if (!SyncImagePixels(image))
00898                 kdDebug(41008) << "Syncing pixels failed\n";
00899 #endif
00900         }
00901 
00902         // set the annotations
00903         exportAnnotationsForImage(image, annotationsStart, annotationsEnd);
00904 
00905         // XXX: Write to a temp file, then have Krita use KIO to copy temp
00906         // image to remote location.
00907 
00908         WriteImage(ii, image);
00909         DestroyExceptionInfo(&ei);
00910         DestroyImage(image);
00911         emit notifyProgressDone();
00912         return KisImageBuilder_RESULT_OK;
00913     }
00914 
00915     void KisImageMagickConverter::ioData(KIO::Job *job, const QByteArray& data)
00916     {
00917         if (data.isNull() || data.isEmpty()) {
00918             emit notifyProgressStage(i18n("Loading..."), 0);
00919             return;
00920         }
00921 
00922         if (m_data.empty()) {
00923             Image *image;
00924             ImageInfo *ii;
00925             ExceptionInfo ei;
00926 
00927             ii = CloneImageInfo(0);
00928             GetExceptionInfo(&ei);
00929             image = PingBlob(ii, data.data(), data.size(), &ei);
00930 
00931             if (image == 0 || ei.severity == BlobError) {
00932                 DestroyExceptionInfo(&ei);
00933                 DestroyImageInfo(ii);
00934                 job -> kill();
00935                 emit notifyProgressError();
00936                 return;
00937             }
00938 
00939             DestroyImage(image);
00940             DestroyExceptionInfo(&ei);
00941             DestroyImageInfo(ii);
00942             emit notifyProgressStage(i18n("Loading..."), 0);
00943         }
00944 
00945         Q_ASSERT(data.size() + m_data.size() <= m_size);
00946         memcpy(&m_data[m_data.size()], data.data(), data.count());
00947         m_data.resize(m_data.size() + data.count());
00948         emit notifyProgressStage(i18n("Loading..."), m_data.size() * 100 / m_size);
00949 
00950         if (m_stop)
00951             job -> kill();
00952     }
00953 
00954     void KisImageMagickConverter::ioResult(KIO::Job *job)
00955     {
00956         m_job = 0;
00957 
00958         if (job -> error())
00959             emit notifyProgressError();
00960 
00961         decode(KURL(), true);
00962     }
00963 
00964     void KisImageMagickConverter::ioTotalSize(KIO::Job * /*job*/, KIO::filesize_t size)
00965     {
00966         m_size = size;
00967         m_data.reserve(size);
00968         emit notifyProgressStage(i18n("Loading..."), 0);
00969     }
00970 
00971     void KisImageMagickConverter::cancel()
00972     {
00973         m_stop = true;
00974     }
00975 
00980     QString KisImageMagickConverter::readFilters()
00981     {
00982         QString s;
00983         QString all;
00984         QString name;
00985         QString description;
00986         unsigned long matches;
00987 
00988 /*#ifdef HAVE_MAGICK6
00989 #ifdef HAVE_OLD_GETMAGICKINFOLIST
00990         const MagickInfo **mi;
00991         mi = GetMagickInfoList("*", &matches);
00992 #else // HAVE_OLD_GETMAGICKINFOLIST
00993         ExceptionInfo ei;
00994         GetExceptionInfo(&ei);
00995         const MagickInfo **mi;
00996         mi = GetMagickInfoList("*", &matches, &ei);
00997         DestroyExceptionInfo(&ei);
00998 #endif // HAVE_OLD_GETMAGICKINFOLIST
00999 #else // HAVE_MAGICK6*/
01000         const MagickInfo *mi;
01001         ExceptionInfo ei;
01002         GetExceptionInfo(&ei);
01003         mi = GetMagickInfo("*", &ei);
01004         DestroyExceptionInfo(&ei);
01005 // #endif // HAVE_MAGICK6
01006 
01007         if (!mi)
01008             return s;
01009 
01010 /*#ifdef HAVE_MAGICK6
01011         for (unsigned long i = 0; i < matches; i++) {
01012             const MagickInfo *info = mi[i];
01013             if (info -> stealth)
01014                 continue;
01015 
01016             if (info -> decoder) {
01017                 name = info -> name;
01018                 description = info -> description;
01019                 kdDebug(41008) << "Found import filter for: " << name << "\n";
01020 
01021                 if (!description.isEmpty() && !description.contains('/')) {
01022                     all += "*." + name.lower() + " *." + name + " ";
01023                     s += "*." + name.lower() + " *." + name + "|";
01024                     s += i18n(description.utf8());
01025                     s += "\n";
01026                 }
01027             }
01028         }
01029 #else*/
01030         for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) {
01031             if (mi -> stealth)
01032                 continue;
01033             if (mi -> decoder) {
01034                 name = mi -> name;
01035                 description = mi -> description;
01036                 kdDebug(41008) << "Found import filter for: " << name << "\n";
01037 
01038                 if (!description.isEmpty() && !description.contains('/')) {
01039                     all += "*." + name.lower() + " *." + name + " ";
01040                     s += "*." + name.lower() + " *." + name + "|";
01041                     s += i18n(description.utf8());
01042                     s += "\n";
01043                 }
01044             }
01045         }
01046 // #endif
01047 
01048         all += "|" + i18n("All Images");
01049         all += "\n";
01050 
01051         return all + s;
01052     }
01053 
01054     QString KisImageMagickConverter::writeFilters()
01055     {
01056         QString s;
01057         QString all;
01058         QString name;
01059         QString description;
01060         unsigned long matches;
01061 
01062 /*#ifdef HAVE_MAGICK6
01063 #ifdef HAVE_OLD_GETMAGICKINFOLIST
01064         const MagickInfo **mi;
01065         mi = GetMagickInfoList("*", &matches);
01066 #else // HAVE_OLD_GETMAGICKINFOLIST
01067         ExceptionInfo ei;
01068         GetExceptionInfo(&ei);
01069         const MagickInfo **mi;
01070         mi = GetMagickInfoList("*", &matches, &ei);
01071         DestroyExceptionInfo(&ei);
01072 #endif // HAVE_OLD_GETMAGICKINFOLIST
01073 #else // HAVE_MAGICK6*/
01074         const MagickInfo *mi;
01075         ExceptionInfo ei;
01076         GetExceptionInfo(&ei);
01077         mi = GetMagickInfo("*", &ei);
01078         DestroyExceptionInfo(&ei);
01079 // #endif // HAVE_MAGICK6
01080 
01081         if (!mi) {
01082             kdDebug(41008) << "Eek, no magick info!\n";
01083             return s;
01084         }
01085 
01086 /*#ifdef HAVE_MAGICK6
01087         for (unsigned long i = 0; i < matches; i++) {
01088             const MagickInfo *info = mi[i];
01089             kdDebug(41008) << "Found export filter for: " << info -> name << "\n";
01090             if (info -> stealth)
01091                 continue;
01092 
01093             if (info -> encoder) {
01094                 name = info -> name;
01095 
01096                 description = info -> description;
01097 
01098                 if (!description.isEmpty() && !description.contains('/')) {
01099                     all += "*." + name.lower() + " *." + name + " ";
01100                     s += "*." + name.lower() + " *." + name + "|";
01101                     s += i18n(description.utf8());
01102                     s += "\n";
01103                 }
01104             }
01105         }
01106 #else*/
01107         for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) {
01108             kdDebug(41008) << "Found export filter for: " << mi -> name << "\n";
01109             if (mi -> stealth)
01110                 continue;
01111 
01112             if (mi -> encoder) {
01113                 name = mi -> name;
01114 
01115                 description = mi -> description;
01116 
01117                 if (!description.isEmpty() && !description.contains('/')) {
01118                     all += "*." + name.lower() + " *." + name + " ";
01119                     s += "*." + name.lower() + " *." + name + "|";
01120                     s += i18n(description.utf8());
01121                     s += "\n";
01122                 }
01123             }
01124         }
01125 // #endif
01126 
01127 
01128         all += "|" + i18n("All Images");
01129         all += "\n";
01130 
01131         return all + s;
01132     }
01133 
01134 #include "kis_image_magick_converter.moc"
01135 
KDE Home | KDE Accessibility Home | Description of Access Keys