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