File indexing completed on 2024-12-22 04:15:55

0001 /*
0002  *  SPDX-FileCopyrightText: 2016 Boudewijn Rempt <boud@valdyas.org>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_spriter_export.h"
0008 
0009 #include <QApplication>
0010 #include <QCheckBox>
0011 #include <QDomDocument>
0012 #include <QFileInfo>
0013 #include <QSlider>
0014 #include <QDir>
0015 
0016 #include <kpluginfactory.h>
0017 
0018 #include <KoColorSpaceConstants.h>
0019 #include <KoColorSpaceRegistry.h>
0020 
0021 #include <KisExportCheckRegistry.h>
0022 #include <KisImportExportManager.h>
0023 
0024 #include <KisDocument.h>
0025 #include <kis_group_layer.h>
0026 #include <kis_image.h>
0027 #include <kis_layer.h>
0028 #include <kis_node.h>
0029 #include <kis_painter.h>
0030 #include <kis_paint_layer.h>
0031 #include <kis_shape_layer.h>
0032 #include <kis_file_layer.h>
0033 #include <kis_clone_layer.h>
0034 #include <kis_generator_layer.h>
0035 #include <kis_adjustment_layer.h>
0036 #include <KisPart.h>
0037 #include <kis_types.h>
0038 #include <kis_png_converter.h>
0039 #include <kis_global.h> // for KisDegreesToRadians
0040 #include <kis_fast_math.h>
0041 #include <math.h>
0042 #include <kis_dom_utils.h>
0043 #include <kis_layer_utils.h>
0044 
0045 K_PLUGIN_FACTORY_WITH_JSON(KisSpriterExportFactory, "krita_spriter_export.json", registerPlugin<KisSpriterExport>();)
0046 
0047 KisSpriterExport::KisSpriterExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
0048 {
0049 }
0050 
0051 KisSpriterExport::~KisSpriterExport()
0052 {
0053 }
0054 
0055 KisImportExportErrorCode KisSpriterExport::savePaintDevice(KisPaintDeviceSP dev, const QString &fileName)
0056 {
0057     QFileInfo fi(fileName);
0058 
0059     QDir d = fi.absoluteDir();
0060     d.mkpath(d.path());
0061     QRect rc = m_image->bounds().intersected(dev->exactBounds());
0062 
0063     if (!KisPNGConverter::isColorSpaceSupported(dev->colorSpace())) {
0064         dev = new KisPaintDevice(*dev.data());
0065         dev->convertTo(KoColorSpaceRegistry::instance()->rgb8());
0066     }
0067 
0068     KisPNGOptions options;
0069     options.forceSRGB = true;
0070 
0071     vKisAnnotationSP_it beginIt = m_image->beginAnnotations();
0072     vKisAnnotationSP_it endIt = m_image->endAnnotations();
0073 
0074     KisPNGConverter converter(0);
0075     KisImportExportErrorCode res = converter.buildFile(fileName, rc, m_image->xRes(), m_image->yRes(), dev, beginIt, endIt, options, 0);
0076 
0077     return res;
0078 }
0079 
0080 KisImportExportErrorCode KisSpriterExport::parseFolder(KisGroupLayerSP parentGroup, const QString &folderName, const QString &basePath, int *folderId)
0081 {
0082 //    qDebug() << "parseFolder: parent" << parentGroup->name()
0083 //                << "folderName" << folderName
0084 //                << "basepath" << basePath;
0085 
0086     int currentFolder=0;
0087     if(folderId == 0)
0088     {
0089         folderId = &currentFolder;
0090     }
0091     QString pathName;
0092     if (!folderName.isEmpty()) {
0093         pathName = folderName + "/";
0094     }
0095 
0096 
0097     KisNodeSP child = parentGroup->lastChild();
0098     while (child) {
0099         if (child->visible() && child->inherits("KisGroupLayer")) {
0100             KisImportExportErrorCode res = parseFolder(qobject_cast<KisGroupLayer*>(child.data()), child->name().split(" ").first(), basePath + "/" + pathName, folderId);
0101             if (!res.isOk()) {
0102                 return res;
0103             }
0104         }
0105         child = child->prevSibling();
0106     }
0107 
0108     Folder folder;
0109     folder.id = *folderId;
0110     folder.name = folderName;
0111     folder.groupName = parentGroup->name();
0112 
0113     int fileId = 0;
0114     child = parentGroup->lastChild();
0115 
0116     while (child) {
0117         if (child->visible() && !child->inherits("KisGroupLayer") && !child->inherits("KisMask")) {
0118             QRectF rc = m_image->bounds().intersected(child->exactBounds());
0119             QString layerBaseName = child->name().split(" ").first();
0120             SpriterFile file;
0121             file.id = fileId++;
0122             file.pathName = pathName;
0123             file.baseName = layerBaseName;
0124             file.layerName = child->name();
0125             file.name = folderName + "/" + layerBaseName + ".png";
0126 
0127             qreal xmin = rc.left();
0128             qreal ymin = rc.top();
0129             qreal xmax = rc.right();
0130             qreal ymax = rc.bottom();
0131 
0132             file.width = xmax - xmin;
0133             file.height = ymax - ymin;
0134             file.x = xmin;
0135             file.y = ymin;
0136             //qDebug() << "Created file" << file.id << file.name << file.pathName << file.baseName << file.width << file.height << file.layerName;
0137             KisImportExportErrorCode result = savePaintDevice(child->projection(), basePath + file.name);
0138             if (result.isOk()) {
0139                 folder.files.append(file);
0140             } else {
0141                 return result;
0142             }
0143         }
0144 
0145         child = child->prevSibling();
0146     }
0147 
0148     if (folder.files.size() > 0) {
0149         //qDebug() << "Adding folder" << folder.id << folder.name << folder.groupName << folder.files.length();
0150         m_folders.append(folder);
0151         (*folderId)++;
0152     }
0153 
0154     return ImportExportCodes::OK;
0155 }
0156 
0157 Bone *KisSpriterExport::parseBone(const Bone *parent, KisGroupLayerSP groupLayer)
0158 {
0159     static int boneId = 0;
0160     QString groupBaseName = groupLayer->name().split(" ").first();
0161     Bone *bone = new Bone;
0162     bone->id = boneId++;
0163     bone->parentBone = parent;
0164     bone->name = groupBaseName;
0165 
0166     if (m_boneLayer) {
0167         QRectF rc = m_image->bounds().intersected(m_boneLayer->exactBounds());
0168 
0169         qreal xmin = rc.left();
0170         qreal ymin = rc.top();
0171         qreal xmax = rc.right();
0172         qreal ymax = rc.bottom();
0173 
0174         bone->x = (xmin + xmax) / 2;
0175         bone->y = -(ymin + ymax) / 2;
0176         bone->width = xmax - xmin;
0177         bone->height = ymax - ymin;
0178     }
0179     else {
0180         bone->x = 0.0;
0181         bone->y = 0.0;
0182         bone->width = 0.0;
0183         bone->height = 0.0;
0184     }
0185 
0186     if (parent) {
0187         bone->localX = bone->x - parent->x;
0188         bone->localY = bone->y - parent->y;
0189     }
0190     else {
0191         bone->localX = bone->x;
0192         bone->localY = bone->y;
0193     }
0194 
0195     bone->localAngle = 0.0;
0196     bone->localScaleX = 1.0;
0197     bone->localScaleY = 1.0;
0198 
0199     KisNodeSP child = groupLayer->lastChild();
0200     while (child) {
0201         if (child->visible() && child->inherits("KisGroupLayer")) {
0202             bone->bones.append(parseBone(bone, qobject_cast<KisGroupLayer*>(child.data())));
0203         }
0204         child = child->prevSibling();
0205     }
0206 
0207     //qDebug() << "Created bone" << bone->id << "with" << bone->bones.size() << "bones";
0208     return bone;
0209 }
0210 
0211 void copyBone(Bone *startBone)
0212 {
0213     startBone->fixLocalX = startBone->localX;
0214     startBone->fixLocalY = startBone->localY;
0215     startBone->fixLocalAngle = startBone->localAngle;
0216     startBone->fixLocalScaleX= startBone->localScaleX;
0217     startBone->fixLocalScaleY= startBone->localScaleY;
0218 
0219     Q_FOREACH(Bone *child, startBone->bones) {
0220         copyBone(child);
0221     }
0222 }
0223 
0224 void KisSpriterExport::fixBone(Bone *bone)
0225 {
0226     qreal boneLocalAngle = 0;
0227     qreal boneLocalScaleX = 1;
0228 
0229     if (bone->bones.length() >= 1) {
0230         // if a bone has one or more children, point at first child
0231         Bone *childBone = bone->bones[0];
0232         qreal dx = childBone->x - bone->x;
0233         qreal dy = childBone->y - bone->y;
0234         if (qAbs(dx) > 0 || qAbs(dy) > 0) {
0235             boneLocalAngle = KisFastMath::atan2(dy, dx);
0236             boneLocalScaleX = sqrt(dx * dx + dy * dy) / 200;
0237         }
0238     }
0239     else if (bone->parentBone) {
0240         // else, if bone has parent, point away from parent
0241         qreal dx = bone->x - bone->parentBone->x;
0242         qreal dy = bone->y - bone->parentBone->y;
0243         if (qAbs(dx) > 0 || qAbs(dy) > 0) {
0244             boneLocalAngle = KisFastMath::atan2(dy, dx);
0245             boneLocalScaleX = sqrt(dx * dx + dy * dy) / 200;
0246         }
0247     }
0248     // adjust bone angle
0249     bone->fixLocalAngle += boneLocalAngle;
0250     bone->fixLocalScaleX *= boneLocalScaleX;
0251 
0252     // rotate all the child bones back to world position
0253     for (int i = 0; i < bone->bones.length(); ++i) {
0254         Bone *childBone = bone->bones[i];
0255 
0256         qreal tx = childBone->fixLocalX;
0257         qreal ty = childBone->fixLocalY;
0258 
0259         childBone->fixLocalX = tx * cos(-boneLocalAngle) - ty * sin(-boneLocalAngle);
0260         childBone->fixLocalY = tx * sin(-boneLocalAngle) + ty * cos(-boneLocalAngle);
0261 
0262         childBone->fixLocalX /= boneLocalScaleX;
0263         childBone->fixLocalAngle -= boneLocalAngle;
0264         childBone->fixLocalScaleX /= boneLocalScaleX;
0265     }
0266 
0267     // rotate all the child objects back to world position
0268     for (int i = 0; i < m_objects.length(); ++i) {
0269         if (m_objects[i].bone == bone) {
0270             m_objects[i].fixLocalAngle -= boneLocalAngle;
0271             m_objects[i].fixLocalScaleX /= boneLocalScaleX;
0272         }
0273     }
0274 
0275     // process all child bones
0276     for (int i = 0; i < bone->bones.length(); ++i) {
0277         fixBone(bone->bones[i]);
0278     }
0279 }
0280 
0281 void KisSpriterExport::writeBoneRef(const Bone *bone, QDomElement &key, QDomDocument &scml)
0282 {
0283     if (!bone) return;
0284     QDomElement boneRef = scml.createElement("bone_ref");
0285     key.appendChild(boneRef);
0286     boneRef.setAttribute("id", bone->id);
0287     if (bone->parentBone) {
0288         boneRef.setAttribute("parent", bone->parentBone->id);
0289     }
0290     boneRef.setAttribute("timeline", m_timelineid++);
0291     boneRef.setAttribute("key", "0");
0292     Q_FOREACH(const Bone *childBone, bone->bones) {
0293         writeBoneRef(childBone, key, scml);
0294     }
0295 }
0296 
0297 void KisSpriterExport::writeBone(const Bone *bone, QDomElement &animation, QDomDocument &scml)
0298 {
0299     if (!bone) return;
0300     QDomElement timeline = scml.createElement("timeline");
0301     animation.appendChild(timeline);
0302     timeline.setAttribute("id", m_timelineid);
0303     timeline.setAttribute("name", bone->name);
0304     timeline.setAttribute("object_type", "bone");
0305 
0306     QDomElement key = scml.createElement("key");
0307     timeline.appendChild(key);
0308     key.setAttribute("id", "0");
0309     key.setAttribute("spin", 0);
0310 
0311     QDomElement boneEl = scml.createElement("bone");
0312     key.appendChild(boneEl);
0313     boneEl.setAttribute("x", QString::number(bone->fixLocalX, 'f', 2));
0314     boneEl.setAttribute("y", QString::number(bone->fixLocalY, 'f', 2));
0315     boneEl.setAttribute("angle", QString::number(bone->fixLocalAngle, 'f', 2));
0316     boneEl.setAttribute("scale_x", QString::number(bone->fixLocalScaleX, 'f', 2));
0317     boneEl.setAttribute("scale_y", QString::number(bone->fixLocalScaleY, 'f', 2));
0318 
0319     m_timelineid++;
0320 
0321     Q_FOREACH(const Bone *childBone, bone->bones) {
0322         writeBone(childBone, animation, scml);
0323     }
0324 }
0325 
0326 void KisSpriterExport::fillScml(QDomDocument &scml, const QString &entityName)
0327 {
0328     //qDebug() << "Creating scml" << entityName;
0329 
0330     QDomElement root = scml.createElement("spriter_data");
0331     scml.appendChild(root);
0332     root.setAttribute("scml_version", 1);
0333     root.setAttribute("generator", "krita");
0334     root.setAttribute("generator_version", qApp->applicationVersion());
0335 
0336     Q_FOREACH(const Folder &folder, m_folders) {
0337         QDomElement fe = scml.createElement("folder");
0338         root.appendChild(fe);
0339         fe.setAttribute("id", folder.id);
0340         fe.setAttribute("name", folder.name);
0341         Q_FOREACH(const SpriterFile &file, folder.files) {
0342             QDomElement fileElement = scml.createElement("file");
0343             fe.appendChild(fileElement);
0344             fileElement.setAttribute("id", file.id);
0345             fileElement.setAttribute("name", file.name);
0346             fileElement.setAttribute("width", QString::number(file.width, 'f', 2));
0347             fileElement.setAttribute("height", QString::number(file.height, 'f', 2));
0348             // qreal pivotX=0;
0349             // qreal pivotY=1;
0350             // Q_FOREACH(const SpriterObject &object, m_objects) {
0351                 // if(file.id == object.fileId)
0352                 // {
0353                     // pivotX = (0.0 -(object.fixLocalX / file.width));
0354                     // pivotY = (1.0 -(object.fixLocalY / file.height));
0355                     // break;
0356                 // }
0357             // }
0358             // fileElement.setAttribute("pivot_x", QString::number(pivotX, 'f', 2));
0359             // fileElement.setAttribute("pivot_y", QString::number(pivotY, 'f', 2));
0360         }
0361     }
0362 
0363     // entity
0364     QDomElement entity = scml.createElement("entity");
0365     root.appendChild(entity);
0366     entity.setAttribute("id", "0");
0367     entity.setAttribute("name", entityName);
0368 
0369     // entity/animation
0370     QDomElement animation = scml.createElement("animation");
0371     entity.appendChild(animation);
0372     animation.setAttribute("id", "0");
0373     animation.setAttribute("name", "default");
0374     animation.setAttribute("length", "1000");
0375     animation.setAttribute("looping", "false");
0376 
0377     // entity/animation/mainline
0378     QDomElement mainline = scml.createElement("mainline");
0379     animation.appendChild(mainline);
0380 
0381     QDomElement key = scml.createElement("key");
0382     mainline.appendChild(key);
0383     key.setAttribute("id", "0");
0384 
0385     m_timelineid = 0;
0386     writeBoneRef(m_rootBone, key, scml);
0387 
0388     Q_FOREACH(const SpriterObject &object, m_objects) {
0389         QDomElement oe = scml.createElement("object_ref");
0390         key.appendChild(oe);
0391         oe.setAttribute("id", object.id);
0392         if (object.bone) {
0393             oe.setAttribute("parent", object.bone->id);
0394         }
0395         oe.setAttribute("timeline", m_timelineid++);
0396         oe.setAttribute("key", "0");
0397         oe.setAttribute("z_index", object.id);
0398     }
0399 
0400     // entity/animation/timeline
0401     m_timelineid = 0;
0402     if (m_rootBone) {
0403         writeBone(m_rootBone, animation, scml);
0404     }
0405 
0406     Q_FOREACH(const SpriterObject &object, m_objects) {
0407         Folder folder;
0408         Q_FOREACH(const Folder & f, m_folders) {
0409             if (f.id == object.folderId) {
0410                 folder = f;
0411                 break;
0412             }
0413         }
0414         SpriterFile file;
0415         file.id = -1;
0416         Q_FOREACH(const SpriterFile &f, folder.files) {
0417             if (f.id == object.fileId) {
0418                 file = f;
0419                 break;
0420             }
0421         }
0422         Q_ASSERT(file.id >= 0);
0423 
0424         QString objectName = "object-" + file.baseName;
0425 
0426         QDomElement timeline = scml.createElement("timeline");
0427         animation.appendChild(timeline);
0428         timeline.setAttribute("id", m_timelineid++);
0429         timeline.setAttribute("name", objectName);
0430 
0431         QDomElement key = scml.createElement("key");
0432         timeline.appendChild(key);
0433         key.setAttribute("id", "0");
0434         key.setAttribute("spin", "0");
0435 
0436         QDomElement objectEl = scml.createElement("object");
0437         key.appendChild(objectEl);
0438         objectEl.setAttribute("folder", object.folderId);
0439         objectEl.setAttribute("file", object.fileId);
0440         objectEl.setAttribute("x", object.fixLocalX);
0441         objectEl.setAttribute("y", object.fixLocalY);
0442         objectEl.setAttribute("angle", QString::number(kisRadiansToDegrees(object.fixLocalAngle), 'f', 2));
0443         objectEl.setAttribute("scale_x", QString::number(object.fixLocalScaleX, 'f', 2));
0444         objectEl.setAttribute("scale_y", QString::number(object.fixLocalScaleY, 'f', 2));
0445     }
0446 }
0447 
0448 Bone *findBoneByName(Bone *startBone, const QString &name)
0449 {
0450     if (!startBone) return 0;
0451     //qDebug() << "findBoneByName" << name << "starting with" << startBone->name;
0452 
0453     if (startBone->name == name) {
0454         return startBone;
0455     }
0456     Q_FOREACH(Bone *child, startBone->bones) {
0457         //qDebug() << "looking for" << name << "found" << child->name;
0458         if (child->name == name) {
0459             return child;
0460         }
0461         Bone *grandChild = findBoneByName(child, name);
0462         if (grandChild){
0463             return grandChild;
0464         }
0465     }
0466     return 0;
0467 }
0468 
0469 KisImportExportErrorCode KisSpriterExport::convert(KisDocument *document, QIODevice *io,  KisPropertiesConfigurationSP /*configuration*/)
0470 {
0471     QFileInfo fi(filename());
0472 
0473     m_image = document->savingImage();
0474 
0475     if (m_image->rootLayer()->childCount() == 0) {
0476         return ImportExportCodes::Failure;
0477     }
0478 
0479     KisGroupLayerSP root = m_image->rootLayer();
0480 
0481     m_boneLayer = qobject_cast<KisLayer*>(KisLayerUtils::findNodeByName(root,"bone").data());
0482     //qDebug() << "Found boneLayer" << m_boneLayer;
0483 
0484     m_rootLayer= qobject_cast<KisGroupLayer*>(KisLayerUtils::findNodeByName(root,"root").data());
0485     //qDebug() << "Fond rootLayer" << m_rootLayer;
0486 
0487     KisImportExportErrorCode result = parseFolder(m_image->rootLayer(), "", fi.absolutePath());
0488     if (!result.isOk()) {
0489         dbgFile << "There were errors encountered while using the spriter exporter.";
0490         return result;
0491     }
0492 
0493     m_rootBone = 0;
0494 
0495     if (m_rootLayer) {
0496         m_rootBone = parseBone(0, m_rootLayer);
0497     }
0498     // Generate objects
0499     int objectId = 0;
0500     for (int folderIndex = 0, folderCount = m_folders.size(); folderIndex < folderCount; ++folderIndex) {
0501         Folder folder = m_folders[folderCount - 1 - folderIndex];
0502         for (int fileIndex = 0, fileCount = folder.files.size(); fileIndex < fileCount; ++ fileIndex) {
0503             SpriterFile file = folder.files[fileCount - 1 - fileIndex];
0504             SpriterObject spriterObject;
0505             spriterObject.id = objectId++;
0506             spriterObject.folderId = folder.id;
0507             spriterObject.fileId = file.id;
0508             spriterObject.x = file.x;
0509             spriterObject.y = -file.y;
0510             Bone *bone = 0;
0511 
0512             //qDebug() << "file layername" << file.layerName;
0513             // layer.name format: "base_name bone(bone_name) slot(slot_name)"
0514             if (file.layerName.contains("bone(")) {
0515                 int start = file.layerName.indexOf("bone(") + 5;
0516                 int end = file.layerName.indexOf(')', start);
0517                 QString boneName = file.layerName.mid(start, end - start);
0518                 bone = findBoneByName(m_rootBone, boneName);
0519             }
0520 
0521 
0522             // layer.name format: "base_name"
0523             if (!bone && m_rootBone) {
0524                 bone = findBoneByName(m_rootBone, file.layerName);
0525             }
0526             // group.name format: "base_name bone(bone_name)"
0527             if (!bone && m_rootBone) {
0528                 if (folder.groupName.contains("bone(")) {
0529                     int start = folder.groupName.indexOf("bone(") + 5;
0530                     int end = folder.groupName.indexOf(')', start);
0531                     QString boneName = folder.groupName.mid(start, end - start);
0532                     bone = findBoneByName(m_rootBone, boneName);
0533                 }
0534 
0535                 // group.name format: "base_name"
0536                 if (!bone) {
0537                     bone = findBoneByName(m_rootBone, folder.groupName);
0538                 }
0539             }
0540 
0541             if (!bone) {
0542                 bone = m_rootBone;
0543             }
0544 
0545             if (bone) {
0546                 spriterObject.bone = bone;
0547                 spriterObject.localX = spriterObject.x - bone->x;
0548                 spriterObject.localY = spriterObject.y - bone->y;
0549             }
0550             else {
0551                 spriterObject.bone = 0;
0552                 spriterObject.localX = spriterObject.x;
0553                 spriterObject.localY = spriterObject.y;
0554             }
0555 
0556             spriterObject.localAngle = 0;
0557             spriterObject.localScaleX = 1.0;
0558             spriterObject.localScaleY = 1.0;
0559 
0560             SpriterSlot *slot = 0;
0561 
0562             // layer.name format: "base_name bone(bone_name) slot(slot_name)"
0563             if (file.layerName.contains("slot(")) {
0564                 int start = file.layerName.indexOf("slot(") + 5;
0565                 int end = file.layerName.indexOf(')', start);
0566                 slot = new SpriterSlot();
0567                 slot->name = file.layerName.mid(start, end - start);
0568                 slot->defaultAttachmentFlag = file.layerName.contains("*");
0569             }
0570 
0571             spriterObject.slot = slot;
0572 
0573 //            qDebug() << "Created object" << spriterObject.id << spriterObject.folderId
0574 //                     << spriterObject.fileId << spriterObject.x << spriterObject.y
0575 //                     << spriterObject.localX << spriterObject.localY;
0576 
0577             m_objects.append(spriterObject);
0578         }
0579     }
0580 
0581     // Copy object transforms
0582     for (int i = 0; i < m_objects.size(); ++i) {
0583         m_objects[i].fixLocalX = m_objects[i].localX;
0584         m_objects[i].fixLocalY = m_objects[i].localY;
0585         m_objects[i].fixLocalAngle = m_objects[i].localAngle;
0586         m_objects[i].fixLocalScaleX = m_objects[i].localScaleX;
0587         m_objects[i].fixLocalScaleY = m_objects[i].localScaleY;
0588     }
0589 
0590     // Calculate bone angles
0591     if (m_rootBone) {
0592         copyBone(m_rootBone);
0593         fixBone(m_rootBone);
0594     }
0595 
0596     // Generate scml
0597     QDomDocument scml;
0598     fillScml(scml, fi.completeBaseName());
0599 
0600     bool openedHere = false;
0601     if (!io->isOpen()) {
0602         openedHere = io->open(QIODevice::WriteOnly);
0603         if (!openedHere) {
0604             // unsuccessful open
0605             return ImportExportCodes::NoAccessToWrite;
0606         }
0607     }
0608 
0609     QString towrite = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
0610     if (io->write(towrite.toUtf8()) != towrite.length()) {
0611         return ImportExportCodes::ErrorWhileWriting;
0612     }
0613     towrite = scml.toString(4).toUtf8();
0614     if (io->write(towrite.toUtf8()) != towrite.length()) {
0615         return ImportExportCodes::ErrorWhileWriting;
0616     }
0617 
0618     delete m_rootBone;
0619 
0620     if (openedHere) {
0621         // FIXME: causes crash...
0622         //io->close();
0623     }
0624 
0625     return ImportExportCodes::OK;
0626 }
0627 
0628 void KisSpriterExport::initializeCapabilities()
0629 {
0630     addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED));
0631     addCapability(KisExportCheckRegistry::instance()->get("LayerOpacityCheck")->create(KisExportCheckBase::PARTIALLY));
0632     QList<QPair<KoID, KoID> > supportedColorModels;
0633     supportedColorModels << QPair<KoID, KoID>()
0634             << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID);
0635     addSupportedColorModels(supportedColorModels, "Spriter");
0636 }
0637 
0638 
0639 
0640 #include "kis_spriter_export.moc"