filters

kis_image_magick_converter.cc

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