diff --git a/libs/ui/KisImportExportFilter.cpp b/libs/ui/KisImportExportFilter.cpp index 5fd237a095..c3dbcbd503 100644 --- a/libs/ui/KisImportExportFilter.cpp +++ b/libs/ui/KisImportExportFilter.cpp @@ -1,202 +1,207 @@ /* This file is part of the KDE libraries Copyright (C) 2001 Werner Trobin 2002 Werner Trobin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisImportExportFilter.h" #include #include #include #include "KisImportExportManager.h" #include "KoUpdater.h" #include class Q_DECL_HIDDEN KisImportExportFilter::Private { public: QPointer updater; Private() : updater(0) {} /** * Use this pointer to access all information about input/output * during the conversion. @em Don't use it in the constructor - * it's invalid while constructing the object! */ KisFilterChainSP chain; }; KisImportExportFilter::KisImportExportFilter(QObject *parent) : QObject(parent) , d(new Private) { } KisDocument *KisImportExportFilter::inputDocument() const { return d->chain->inputDocument(); } KisDocument *KisImportExportFilter::outputDocument() const { return d->chain->outputDocument(); } QString KisImportExportFilter::inputFile() const { return d->chain->inputFile(); } QString KisImportExportFilter::outputFile() const { return d->chain->outputFile(); } bool KisImportExportFilter::getBatchMode() const { return d->chain->manager()->getBatchMode(); } KisImportExportFilter::~KisImportExportFilter() { Q_ASSERT(d->updater); if (d->updater) d->updater->setProgress(100); delete d; } void KisImportExportFilter::setChain(KisFilterChainSP chain) { d->chain = chain; } QString KisImportExportFilter::conversionStatusString(ConversionStatus status) { QString msg; switch (status) { case OK: break; case FilterCreationError: msg = i18n("Could not create the filter plugin"); break; case CreationError: msg = i18n("Could not create the output document"); break; case FileNotFound: msg = i18n("File not found"); break; case StorageCreationError: msg = i18n("Cannot create storage"); break; case BadMimeType: msg = i18n("Bad MIME type"); break; case EmbeddedDocError: msg = i18n("Error in embedded document"); break; case WrongFormat: msg = i18n("Format not recognized"); break; case NotImplemented: msg = i18n("Not implemented"); break; case ParsingError: msg = i18n("Parsing error"); break; case PasswordProtected: msg = i18n("Document is password protected"); break; case InvalidFormat: msg = i18n("Invalid file format"); break; case InternalError: case UnexpectedEOF: case UnexpectedOpcode: case StupidError: // ?? what is this ?? case UsageError: msg = i18n("Internal error"); break; case OutOfMemory: msg = i18n("Out of memory"); break; case FilterEntryNull: msg = i18n("Empty Filter Plugin"); break; case NoDocumentCreated: msg = i18n("Trying to load into the wrong kind of document"); break; case DownloadFailed: msg = i18n("Failed to download remote file"); break; case ProgressCancelled: msg = i18n("Cancelled by user"); break; case BadConversionGraph: msg = i18n("Unknown file type"); break; + case UnsupportedVersion: + + msg = i18n("Unsupported file version"); break; + case UserCancelled: // intentionally we do not prompt the error message here break; + default: msg = i18n("Unknown error"); break; } return msg; } KisPropertiesConfigurationSP KisImportExportFilter::defaultConfiguration(const QByteArray &from, const QByteArray &to) const { Q_UNUSED(from); Q_UNUSED(to); return 0; } KisPropertiesConfigurationSP KisImportExportFilter::lastSavedConfiguration(const QByteArray &from, const QByteArray &to) const { return defaultConfiguration(from, to); } KisConfigWidget *KisImportExportFilter::createConfigurationWidget(QWidget *, const QByteArray &from, const QByteArray &to) const { Q_UNUSED(from); Q_UNUSED(to); return 0; } void KisImportExportFilter::setUpdater(const QPointer& updater) { Q_ASSERT(updater); if (d->updater && !updater) { disconnect(this, SLOT(slotProgress(int))); } else if (!d->updater && updater) { connect(this, SIGNAL(sigProgress(int)), SLOT(slotProgress(int))); } d->updater = updater; } void KisImportExportFilter::slotProgress(int value) { Q_ASSERT(d->updater); if (d->updater) { d->updater->setValue(value); } } diff --git a/libs/ui/KisImportExportFilter.h b/libs/ui/KisImportExportFilter.h index c5bc6e6e17..3ffdb394b7 100644 --- a/libs/ui/KisImportExportFilter.h +++ b/libs/ui/KisImportExportFilter.h @@ -1,193 +1,194 @@ /* This file is part of the Calligra libraries Copyright (C) 2001 Werner Trobin 2002 Werner Trobin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_IMPORT_EXPORT_FILTER_H #define KIS_IMPORT_EXPORT_FILTER_H #include #include #include #include #include #include #include class KoUpdater; class KisDocument; class KisConfigWidget; #include "kritaui_export.h" /** * @brief The base class for import and export filters. * * Derive your filter class from this base class and implement * the @ref convert() method. Don't forget to specify the Q_OBJECT * macro in your class even if you don't use signals or slots. * This is needed as filters are created on the fly. * The m_chain member allows access to the @ref KisFilterChain * which invokes the filter to query for input/output. * * @note Take care: The m_chain pointer is invalid while the constructor * runs due to the implementation -- @em don't use it in the constructor. * After the constructor, when running the @ref convert() method it's * guaranteed to be valid, so no need to check against 0. * * @note If the code is compiled in debug mode, setting CALLIGRA_DEBUG_FILTERS * environment variable to any value disables deletion of temporary files while * importing/exporting. This is useful for testing purposes. * * @author Werner Trobin * @todo the class has no constructor and therefore cannot initialize its private class */ class KRITAUI_EXPORT KisImportExportFilter : public QObject { Q_OBJECT public: /** * This enum is used to signal the return state of your filter. * Return OK in @ref convert() in case everything worked as expected. * Feel free to add some more error conditions @em before the last item * if it's needed. */ enum ConversionStatus { OK, StupidError, UsageError, CreationError, FileNotFound, StorageCreationError, BadMimeType, BadConversionGraph, EmbeddedDocError, WrongFormat, NotImplemented, ParsingError, InternalError, UnexpectedEOF, UnexpectedOpcode, UserCancelled, OutOfMemory, PasswordProtected, InvalidFormat, FilterEntryNull, NoDocumentCreated, DownloadFailed, FilterCreationError, ProgressCancelled, + UnsupportedVersion, JustInCaseSomeBrokenCompilerUsesLessThanAByte = 255 }; virtual ~KisImportExportFilter(); /** * @brief setChain set the chain information on the filter. The chain information * lets the filter know what document it's working on. The filter will not delete * @param chain the actual filter chain */ void setChain(KisFilterChainSP chain); /** * The filter chain calls this method to perform the actual conversion. * The passed mimetypes should be a pair of those you specified in your * .desktop file. * You @em have to implement this method to make the filter work. * * @param from The mimetype of the source file/document * @param to The mimetype of the destination file/document * @return The error status, see the @ref #ConversionStatus enum. * KisImportExportFilter::OK means that everything is alright. */ virtual ConversionStatus convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration = 0) = 0; /** * Set the updater to which the filter will report progress. * Every emit of the sigProgress signal is reported to the updater. */ void setUpdater(const QPointer& updater); /** * Get the text version of the status value */ static QString conversionStatusString(ConversionStatus status); /** * @brief defaultConfiguration defines the default settings for the given import export filter * @param from The mimetype of the source file/document * @param to The mimetype of the destination file/document * @return a serializable KisPropertiesConfiguration object */ virtual KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from = "", const QByteArray& to = "") const; /** * @brief lastSavedConfiguration return the last saved configuration for this filter * @param from The mimetype of the source file/document * @param to The mimetype of the destination file/document * @return a serializable KisPropertiesConfiguration object */ virtual KisPropertiesConfigurationSP lastSavedConfiguration(const QByteArray &from = "", const QByteArray &to = "") const; /** * @brief createConfigurationWidget creates a widget that can be used to define the settings for a given import/export filter * @param parent the ownder of the widget; the caller is responsible for deleting * @param from The mimetype of the source file/document * @param to The mimetype of the destination file/document * * @return the widget */ virtual KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const; Q_SIGNALS: /** * Emit this signal with a value in the range of 1...100 to have some * progress feedback for the user in the statusbar of the application. * * @param value The actual progress state. Should always remain in * the range 1..100. */ void sigProgress(int value); protected: /** * This is the constructor your filter has to call, obviously. */ KisImportExportFilter(QObject *parent = 0); KisDocument *inputDocument() const; KisDocument *outputDocument() const; QString inputFile() const; QString outputFile() const; bool getBatchMode() const; private: KisImportExportFilter(const KisImportExportFilter& rhs); KisImportExportFilter& operator=(const KisImportExportFilter& rhs); class Private; Private *const d; private Q_SLOTS: void slotProgress(int value); }; #endif diff --git a/plugins/impex/xcf/3rdparty/xcftools/xcf-general.c b/plugins/impex/xcf/3rdparty/xcftools/xcf-general.c index b23c260509..371691782f 100644 --- a/plugins/impex/xcf/3rdparty/xcftools/xcf-general.c +++ b/plugins/impex/xcf/3rdparty/xcftools/xcf-general.c @@ -1,307 +1,305 @@ /* Generic functions for reading XCF files * * This file was written by Henning Makholm * It is hereby in the public domain. - * + * * In jurisdictions that do not recognise grants of copyright to the * public domain: I, the author and (presumably, in those jurisdictions) * copyright holder, hereby permit anyone to distribute and use this code, * in source code or binary form, with or without modifications. This * permission is world-wide and irrevocable. * * Of course, I will not be liable for any errors or shortcomings in the * code, since I give it away without asking any compenstations. * * If you use or distribute this code, I would appreciate receiving * credit for writing it, in whichever way you find proper and customary. */ #include "xcftools.h" #include #include #ifdef HAVE_ICONV # include #elif !defined(ICONV_CONST) # define ICONV_CONST const #endif uint8_t *xcf_file = 0 ; size_t xcf_length ; int use_utf8 = 0 ; uint32_t xcfOffset(uint32_t addr,int spaceafter) { uint32_t apparent ; xcfCheckspace(addr,4,"(xcfOffset)"); apparent = xcfL(addr); xcfCheckspace(apparent,spaceafter, "Too large offset (%" PRIX32 ") at position %" PRIX32, apparent,addr); return apparent ; } int xcfNextprop(uint32_t *master,uint32_t *body) { uint32_t ptr, length, total, minlength ; PropType type ; ptr = *master ; xcfCheckspace(ptr,8,"(property header)"); type = xcfL(ptr); length = xcfL(ptr+4); *body = ptr+8 ; switch(type) { case PROP_COLORMAP: { uint32_t ncolors ; xcfCheckspace(ptr+8,4,"(colormap length)"); ncolors = xcfL(ptr+8) ; if( ncolors > 256 ) FatalBadXCF("Colormap has %" PRIu32 " entries",ncolors); /* Surprise! Some older verion of the Gimp computed the wrong length * word, and the _reader_ always just reads three bytes per color * and ignores the length tag! Duplicate this so we too can read * the buggy XCF files. */ length = minlength = 4+3*ncolors; break; } case PROP_COMPRESSION: minlength = 1; break; case PROP_OPACITY: minlength = 4; break; case PROP_APPLY_MASK: minlength = 4; break; case PROP_OFFSETS: minlength = 8; break; case PROP_MODE: minlength = 4; break; default: minlength = 0; break; } if( length < minlength ) FatalBadXCF("Short %s property at %" PRIX32 " (%" PRIu32 "<%" PRIu32 ")", showPropType(type),ptr,length,minlength); *master = ptr+8+length ; total = 8 + length + (type != PROP_END ? 8 : 0) ; if( total < length ) /* Check overwrap */ FatalBadXCF("Overlong property at %" PRIX32, ptr); xcfCheckspace(ptr,total,"Overlong property at %" PRIX32,ptr) ; return type ; } const char* xcfString(uint32_t ptr,uint32_t *after) { uint32_t length ; unsigned i ; ICONV_CONST char *utf8master ; - + xcfCheckspace(ptr,4,"(string length)"); length = xcfL(ptr) ; ptr += 4 ; xcfCheckspace(ptr,length,"(string)"); utf8master = (ICONV_CONST char*)(xcf_file+ptr) ; if( after ) *after = ptr + length ; if( length == 0 || utf8master[length-1] != 0 ) FatalBadXCF("String at %" PRIX32 " not zero-terminated",ptr-4); length-- ; if( use_utf8 ) return utf8master ; /* We assume that the local character set includes ASCII... * Check if conversion is needed at all */ for( i=0 ; ; i++ ) { if( i == length ) return utf8master ; /* Only ASCII after all */ if( utf8master[i] == 0 ) FatalBadXCF("String at %" PRIX32 " has embedded zeroes",ptr-4); if( (int8_t) utf8master[i] < 0 ) break ; } #ifdef HAVE_ICONV { size_t targetsize = length+1 ; int sloppy_translation = 0 ; iconv_t cd = iconv_open("//TRANSLIT","UTF-8"); if( cd == (iconv_t) -1 ) { cd = iconv_open("","UTF-8"); sloppy_translation = 1 ; } if( cd == (iconv_t) -1 ) iconv_close(cd) ; /* Give up; perhaps iconv doesn't know UTF-8 */ else while(1) { char *buffer = xcfmalloc(targetsize) ; ICONV_CONST char *inbuf = utf8master ; char *outbuf = buffer ; size_t incount = length ; size_t outcount = targetsize ; while(1) { /* Loop for systems without //ICONV support */ size_t result = iconv(cd,&inbuf,&incount,&outbuf,&outcount) ; if( result == (size_t)-1 && errno == EILSEQ && sloppy_translation && outcount > 0 ) { *outbuf++ = '?' ; outcount-- ; while( (int8_t)*inbuf < 0 ) inbuf++, incount-- ; continue ; } if( result != (size_t)-1 ) { if( outcount == 0 ) errno = E2BIG ; else { *outbuf = 0 ; iconv_close(cd) ; return buffer ; } } break ; } if( errno == EILSEQ || errno == EINVAL ) FatalBadXCF("Bad UTF-8 encoding '%s' at %" PRIXPTR, inbuf,(uintptr_t)((inbuf-utf8master)+ptr)); if( errno == E2BIG ) { targetsize += 1+incount ; xcffree(buffer) ; continue ; } FatalUnexpected("!iconv on layer name at %"PRIX32,ptr); } } #endif { static int warned = 0 ; if( !warned ) { fprintf(stderr,_("Warning: one or more layer names could not be\n" " translated to the local character set.\n")); warned = 1 ; } } return utf8master ; } /* ****************************************************************** */ void computeDimensions(struct tileDimensions *d) { d->c.r = d->c.l + d->width ; d->c.b = d->c.t + d->height ; d->tilesx = (d->width+TILE_WIDTH-1)/TILE_WIDTH ; d->tilesy = (d->height+TILE_HEIGHT-1)/TILE_HEIGHT ; d->ntiles = d->tilesx * d->tilesy ; } struct xcfImage XCF ; void getBasicXcfInfo(void) { uint32_t ptr, data, layerfile ; PropType type ; int i, j ; - + xcfCheckspace(0,14+7*4,"(very short)"); if( strcmp((char*)xcf_file,"gimp xcf file") == 0 ) XCF.version = 0 ; else if( xcf_file[13] == 0 && sscanf((char*)xcf_file,"gimp xcf v%d",&XCF.version) == 1 ) ; else FatalBadXCF(_("Not an XCF file at all (magic not recognized)")); - if( XCF.version < 0 || XCF.version > 3 ) { - fprintf(stderr, - _("Warning: XCF version %d not supported (trying anyway...)\n"), - XCF.version); + if (XCF.version < 0 || XCF.version > 3) { + return; } - + XCF.compression = COMPRESS_NONE ; XCF.colormapptr = 0 ; - + ptr = 14 ; XCF.width = xcfL(ptr); ptr += 4 ; XCF.height = xcfL(ptr); ptr += 4 ; XCF.type = xcfL(ptr); ptr += 4 ; while( (type = xcfNextprop(&ptr,&data)) != PROP_END ) { switch(type) { case PROP_COLORMAP: XCF.colormapptr = data ; break ; case PROP_COMPRESSION: XCF.compression = xcf_file[data] ; break ; default: /* Ignore unknown properties */ break ; } } layerfile = ptr ; for( XCF.numLayers = 0 ; xcfOffset(ptr,8*4) ; XCF.numLayers++, ptr+=4 ) ; XCF.layers = xcfmalloc(XCF.numLayers * sizeof(struct xcfLayer)) ; for( i = 0 ; i < XCF.numLayers ; i++ ) { struct xcfLayer *L = XCF.layers + i ; ptr = xcfL(layerfile+4*(XCF.numLayers-1-i)) ; L->mode = GIMP_NORMAL_MODE ; L->opacity = 255 ; L->isVisible = 1 ; L->hasMask = 0 ; L->dim.width = xcfL(ptr); ptr+=4 ; L->dim.height = xcfL(ptr); ptr+=4 ; L->type = xcfL(ptr); ptr+=4 ; L->name = xcfString(ptr,&ptr); L->propptr = ptr ; L->isGroup = 0; L->pathLength = 0; L->path = NULL; while( (type = xcfNextprop(&ptr,&data)) != PROP_END ) { switch(type) { case PROP_OPACITY: L->opacity = xcfL(data); if( L->opacity > 255 ) L->opacity = 255 ; break ; case PROP_VISIBLE: L->isVisible = xcfL(data) != 0 ; break ; case PROP_APPLY_MASK: L->hasMask = xcfL(data) != 0 ; break ; case PROP_OFFSETS: L->dim.c.l = (int32_t)(xcfL(data )) ; L->dim.c.t = (int32_t)(xcfL(data+4)) ; break ; case PROP_MODE: L->mode = xcfL(data); break ; case PROP_GROUP_ITEM: L->isGroup = 1 ; break; case PROP_ITEM_PATH: L->pathLength = (ptr - data - 2) / 4 ; if ( L->pathLength != 0 ) { - + L->path = xcfmalloc( L->pathLength * sizeof(unsigned) ) ; - + for ( j = 0; j!=L->pathLength; j++ ) *(L->path + j) = (unsigned)xcfL(data + 4 * j); } break; default: /* Ignore unknown properties */ break ; } } xcfCheckspace(ptr,8,"(end of layer %s)",L->name); L->pixels.tileptrs = 0 ; L->pixels.hierarchy = xcfOffset(ptr ,4*4); L->mask.tileptrs = 0 ; L->mask.hierarchy = xcfOffset(ptr+4,4*4); computeDimensions(&L->dim); } } diff --git a/plugins/impex/xcf/kis_xcf_import.cpp b/plugins/impex/xcf/kis_xcf_import.cpp index d926632a57..939003bef1 100644 --- a/plugins/impex/xcf/kis_xcf_import.cpp +++ b/plugins/impex/xcf/kis_xcf_import.cpp @@ -1,344 +1,350 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_xcf_import.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_iterator_ng.h" #include "kis_types.h" #include extern "C" { #include "xcftools.h" #include "pixels.h" #define GET_RED(x) (x >> RED_SHIFT) #define GET_GREEN(x) (x >> GREEN_SHIFT) #define GET_BLUE(x) (x >> BLUE_SHIFT) #define GET_ALPHA(x) (x >> ALPHA_SHIFT) } struct Layer { KisLayerSP layer; int depth; KisMaskSP mask; }; KisGroupLayerSP findGroup(const QVector &layers, const Layer& layer, int i) { for (; i < layers.size(); ++i) { KisGroupLayerSP group = dynamic_cast(const_cast(layers[i].layer.data())); if (group && (layers[i].depth == layer.depth -1)) { return group; } } return 0; } void addLayers(const QVector &layers, KisImageSP image, int depth) { for(int i = 0; i < layers.size(); i++) { const Layer &layer = layers[i]; if (layer.depth == depth) { KisGroupLayerSP group = (depth == 0 ? image->rootLayer() : findGroup(layers, layer, i)); image->addNode(layer.layer, group); if (layer.mask) { image->addNode(layer.mask, layer.layer); } } } } K_PLUGIN_FACTORY_WITH_JSON(XCFImportFactory, "krita_xcf_import.json", registerPlugin();) KisXCFImport::KisXCFImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } KisXCFImport::~KisXCFImport() { } KisImportExportFilter::ConversionStatus KisXCFImport::convert(const QByteArray& from, const QByteArray& to, KisPropertiesConfigurationSP configuration) { Q_UNUSED(from); Q_UNUSED(configuration); dbgFile << "Importing using XCFImport!"; if (to != "application/x-krita") return KisImportExportFilter::BadMimeType; KisDocument * doc = outputDocument(); if (!doc) return KisImportExportFilter::NoDocumentCreated; QString filename = inputFile(); if (filename.isEmpty()) { return KisImportExportFilter::FileNotFound; } QFile fp(filename); if (fp.exists()) { doc->prepareForImport(); return loadFromDevice(&fp, doc); } return KisImportExportFilter::CreationError; } QString layerModeG2K(GimpLayerModeEffects mode) { switch (mode) { case GIMP_NORMAL_MODE: return COMPOSITE_OVER; case GIMP_DISSOLVE_MODE: return COMPOSITE_DISSOLVE; case GIMP_MULTIPLY_MODE: return COMPOSITE_MULT; case GIMP_SCREEN_MODE: return COMPOSITE_SCREEN; case GIMP_OVERLAY_MODE: case GIMP_SOFTLIGHT_MODE: return COMPOSITE_OVERLAY; case GIMP_DIFFERENCE_MODE: return COMPOSITE_DIFF; case GIMP_ADDITION_MODE: return COMPOSITE_ADD; case GIMP_SUBTRACT_MODE: return COMPOSITE_SUBTRACT; case GIMP_DARKEN_ONLY_MODE: return COMPOSITE_DARKEN; case GIMP_LIGHTEN_ONLY_MODE: return COMPOSITE_LIGHTEN; case GIMP_HUE_MODE: return COMPOSITE_HUE_HSL; case GIMP_SATURATION_MODE: return COMPOSITE_SATURATION_HSV; case GIMP_COLOR_MODE: return COMPOSITE_COLOR_HSL; case GIMP_VALUE_MODE: return COMPOSITE_VALUE; case GIMP_DIVIDE_MODE: return COMPOSITE_DIVIDE; case GIMP_DODGE_MODE: return COMPOSITE_DODGE; case GIMP_BURN_MODE: return COMPOSITE_BURN; case GIMP_ERASE_MODE: return COMPOSITE_ERASE; case GIMP_REPLACE_MODE: return COMPOSITE_COPY; case GIMP_HARDLIGHT_MODE: return COMPOSITE_HARD_LIGHT; case GIMP_COLOR_ERASE_MODE: case GIMP_NORMAL_NOPARTIAL_MODE: case GIMP_ANTI_ERASE_MODE: case GIMP_GRAIN_EXTRACT_MODE: return COMPOSITE_GRAIN_EXTRACT; case GIMP_GRAIN_MERGE_MODE: return COMPOSITE_GRAIN_MERGE; case GIMP_BEHIND_MODE: break; } dbgFile << "Unknown mode: " << mode; return COMPOSITE_OVER; } KisImportExportFilter::ConversionStatus KisXCFImport::loadFromDevice(QIODevice* device, KisDocument* doc) { dbgFile << "Start decoding file"; // Read the file into memory device->open(QIODevice::ReadOnly); QByteArray data = device->readAll(); xcf_file = (uint8_t*)data.data(); xcf_length = data.size(); device->close(); // Decode the data getBasicXcfInfo() ; + + if (XCF.version < 0 || XCF.version > 3) { + doc->setErrorMessage(i18n("This XCF file is too new; Krita cannot support XCF files written by GIMP 2.9 or newer.")); + return KisImportExportFilter::UnsupportedVersion; + } + initColormap(); dbgFile << XCF.version << "width = " << XCF.width << "height = " << XCF.height << "layers = " << XCF.numLayers; // Create the image KisImageSP image = new KisImage(doc->createUndoStore(), XCF.width, XCF.height, KoColorSpaceRegistry::instance()->rgb8(), "built image"); QVector layers; uint maxDepth = 0; // Read layers for (int i = 0; i < XCF.numLayers; ++i) { Layer layer; xcfLayer& xcflayer = XCF.layers[i]; dbgFile << i << " name = " << xcflayer.name << " opacity = " << xcflayer.opacity << "group:" << xcflayer.isGroup << xcflayer.pathLength; dbgFile << ppVar(xcflayer.dim.width) << ppVar(xcflayer.dim.height) << ppVar(xcflayer.dim.tilesx) << ppVar(xcflayer.dim.tilesy) << ppVar(xcflayer.dim.ntiles) << ppVar(xcflayer.dim.c.t) << ppVar(xcflayer.dim.c.l) << ppVar(xcflayer.dim.c.r) << ppVar(xcflayer.dim.c.b); maxDepth = qMax(maxDepth, xcflayer.pathLength); bool isRgbA = false; // Select the color space const KoColorSpace* colorSpace = 0; switch (xcflayer.type) { case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE: case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE: colorSpace = KoColorSpaceRegistry::instance()->rgb8(); isRgbA = true; break; case GIMP_GRAY_IMAGE: case GIMP_GRAYA_IMAGE: colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), ""); isRgbA = false; break; } // Create the layer KisLayerSP kisLayer; if (xcflayer.isGroup) { kisLayer = new KisGroupLayer(image, QString::fromUtf8(xcflayer.name), xcflayer.opacity); } else { kisLayer = new KisPaintLayer(image, QString::fromUtf8(xcflayer.name), xcflayer.opacity, colorSpace); } // Set some properties kisLayer->setCompositeOpId(layerModeG2K(xcflayer.mode)); kisLayer->setVisible(xcflayer.isVisible); kisLayer->disableAlphaChannel(xcflayer.mode != GIMP_NORMAL_MODE); layer.layer = kisLayer; layer.depth = xcflayer.pathLength; // Copy the data in the image initLayer(&xcflayer); int left = xcflayer.dim.c.l; int top = xcflayer.dim.c.t; if (!xcflayer.isGroup) { // Copy the data; for (unsigned int x = 0; x < xcflayer.dim.width; x += TILE_WIDTH) { for (unsigned int y = 0; y < xcflayer.dim.height; y += TILE_HEIGHT) { rect want; want.l = x + left; want.t = y + top; want.b = want.t + TILE_HEIGHT; want.r = want.l + TILE_WIDTH; Tile* tile = getMaskOrLayerTile(&xcflayer.dim, &xcflayer.pixels, want); KisHLineIteratorSP it = kisLayer->paintDevice()->createHLineIteratorNG(x, y, TILE_WIDTH); rgba* data = tile->pixels; for (int v = 0; v < TILE_HEIGHT; ++v) { if (isRgbA) { // RGB image do { KoBgrTraits::setRed(it->rawData(), GET_RED(*data)); KoBgrTraits::setGreen(it->rawData(), GET_GREEN(*data)); KoBgrTraits::setBlue(it->rawData(), GET_BLUE(*data)); KoBgrTraits::setOpacity(it->rawData(), quint8(GET_ALPHA(*data)), 1); ++data; } while (it->nextPixel()); } else { // Grayscale image do { it->rawData()[0] = GET_RED(*data); it->rawData()[1] = GET_ALPHA(*data); ++data; } while (it->nextPixel()); } it->nextRow(); } } } // Move the layer to its position kisLayer->paintDevice()->setX(left); kisLayer->paintDevice()->setY(top); } // Create the mask if (xcflayer.hasMask) { KisTransparencyMaskSP mask = new KisTransparencyMask(); layer.mask = mask; mask->initSelection(kisLayer); for (unsigned int x = 0; x < xcflayer.dim.width; x += TILE_WIDTH) { for (unsigned int y = 0; y < xcflayer.dim.height; y += TILE_HEIGHT) { rect want; want.l = x + left; want.t = y + top; want.b = want.t + TILE_HEIGHT; want.r = want.l + TILE_WIDTH; Tile* tile = getMaskOrLayerTile(&xcflayer.dim, &xcflayer.mask, want); KisHLineIteratorSP it = mask->paintDevice()->createHLineIteratorNG(x, y, TILE_WIDTH); rgba* data = tile->pixels; for (int v = 0; v < TILE_HEIGHT; ++v) { do { it->rawData()[0] = GET_ALPHA(*data); ++data; } while (it->nextPixel()); it->nextRow(); } } } mask->paintDevice()->setX(left); mask->paintDevice()->setY(top); image->addNode(mask, kisLayer); } dbgFile << xcflayer.pixels.tileptrs; layers.append(layer); } for (int i = 0; i <= maxDepth; ++i) { addLayers(layers, image, i); } doc->setCurrentImage(image); return KisImportExportFilter::OK; } #include "kis_xcf_import.moc"