File indexing completed on 2024-06-16 04:17:02

0001 /*
0002  *  SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
0003  *  SPDX-FileCopyrightText: 2005 C. Boemann <cbo@boemann.dk>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "kis_kra_savexml_visitor.h"
0009 #include "kis_kra_tags.h"
0010 #include "kis_kra_utils.h"
0011 #include "kis_layer_properties_icons.h"
0012 
0013 #include <QDir>
0014 
0015 #include <KoProperties.h>
0016 #include <KoColorSpace.h>
0017 #include <KoCompositeOp.h>
0018 
0019 #include <kis_debug.h>
0020 #include <filter/kis_filter_configuration.h>
0021 #include <generator/kis_generator_layer.h>
0022 #include <kis_adjustment_layer.h>
0023 #include <kis_clone_layer.h>
0024 #include <kis_filter_mask.h>
0025 #include <kis_transform_mask.h>
0026 #include <kis_group_layer.h>
0027 #include <kis_image.h>
0028 #include <kis_layer.h>
0029 #include <kis_paint_device.h>
0030 #include <kis_paint_layer.h>
0031 #include <kis_selection_mask.h>
0032 #include <kis_shape_layer.h>
0033 #include <kis_transparency_mask.h>
0034 #include <lazybrush/kis_colorize_mask.h>
0035 #include <kis_file_layer.h>
0036 #include <kis_psd_layer_style.h>
0037 #include <KisReferenceImage.h>
0038 #include <KisReferenceImagesLayer.h>
0039 #include "kis_keyframe_channel.h"
0040 #include "kis_dom_utils.h"
0041 
0042 using namespace KRA;
0043 
0044 KisSaveXmlVisitor::KisSaveXmlVisitor(QDomDocument doc, const QDomElement & element, quint32 &count, const QString &url, bool root)
0045     : KisNodeVisitor()
0046     , m_doc(doc)
0047     , m_count(count)
0048     , m_url(url)
0049     , m_root(root)
0050 {
0051     Q_ASSERT(!element.isNull());
0052     m_elem = element;
0053 }
0054 
0055 void KisSaveXmlVisitor::setSelectedNodes(vKisNodeSP selectedNodes)
0056 {
0057     m_selectedNodes = selectedNodes;
0058 }
0059 
0060 QStringList KisSaveXmlVisitor::errorMessages() const
0061 {
0062     return m_errorMessages;
0063 }
0064 
0065 bool KisSaveXmlVisitor::visit(KisExternalLayer * layer)
0066 {
0067     if (layer->inherits("KisReferenceImagesLayer")) {
0068         return saveReferenceImagesLayer(layer);
0069     } else if (layer->inherits("KisShapeLayer")) {
0070         QDomElement layerElement = m_doc.createElement(LAYER);
0071         saveLayer(layerElement, SHAPE_LAYER, layer);
0072         m_elem.appendChild(layerElement);
0073         m_count++;
0074         return saveMasks(layer, layerElement);
0075     }
0076     else if (layer->inherits("KisFileLayer")) {
0077         QDomElement layerElement = m_doc.createElement(LAYER);
0078         saveLayer(layerElement, FILE_LAYER, layer);
0079 
0080         KisFileLayer *fileLayer = dynamic_cast<KisFileLayer*>(layer);
0081         KIS_ASSERT(fileLayer);
0082 
0083         QString path = fileLayer->path();
0084 
0085         QDir d(QFileInfo(m_url).absolutePath());
0086 
0087 #ifndef Q_OS_ANDROID
0088         layerElement.setAttribute("source", d.relativeFilePath(path));
0089 #else
0090         layerElement.setAttribute("source", path);
0091 #endif
0092 
0093         if (fileLayer->scalingMethod() == KisFileLayer::ToImagePPI) {
0094             layerElement.setAttribute("scale", "true");
0095         }
0096         else {
0097             layerElement.setAttribute("scale", "false");
0098         }
0099         layerElement.setAttribute("scalingmethod", (int)fileLayer->scalingMethod());
0100         layerElement.setAttribute(COLORSPACE_NAME, layer->original()->colorSpace()->id());
0101         layerElement.setAttribute("scalingfilter", fileLayer->scalingFilter());
0102 
0103         m_elem.appendChild(layerElement);
0104         m_count++;
0105         return saveMasks(layer, layerElement);
0106     }
0107     return false;
0108 }
0109 
0110 QDomElement KisSaveXmlVisitor::savePaintLayerAttributes(KisPaintLayer *layer, QDomDocument &doc)
0111 {
0112     QDomElement element = doc.createElement(LAYER);
0113     saveLayer(element, PAINT_LAYER, layer);
0114     element.setAttribute(CHANNEL_LOCK_FLAGS, flagsToString(layer->channelLockFlags()));
0115     element.setAttribute(COLORSPACE_NAME, layer->paintDevice()->colorSpace()->id());
0116 
0117     element.setAttribute(ONION_SKIN_ENABLED, layer->onionSkinEnabled());
0118     element.setAttribute(VISIBLE_IN_TIMELINE, layer->isPinnedToTimeline());
0119 
0120     return element;
0121 }
0122 
0123 void KisSaveXmlVisitor::loadPaintLayerAttributes(const QDomElement &el, KisPaintLayer *layer)
0124 {
0125     loadLayerAttributes(el, layer);
0126 
0127     if (el.hasAttribute(CHANNEL_LOCK_FLAGS)) {
0128         layer->setChannelLockFlags(stringToFlags(el.attribute(CHANNEL_LOCK_FLAGS)));
0129     }
0130 }
0131 
0132 bool KisSaveXmlVisitor::visit(KisPaintLayer *layer)
0133 {
0134     QDomElement layerElement = savePaintLayerAttributes(layer, m_doc);
0135     m_elem.appendChild(layerElement);
0136     m_count++;
0137     return saveMasks(layer, layerElement);
0138 }
0139 
0140 bool KisSaveXmlVisitor::visit(KisGroupLayer *layer)
0141 {
0142     QDomElement layerElement;
0143 
0144     if (m_root) // if this is the root we fake so not to save it
0145         layerElement = m_elem;
0146     else {
0147         layerElement = m_doc.createElement(LAYER);
0148         saveLayer(layerElement, GROUP_LAYER, layer);
0149         layerElement.setAttribute(PASS_THROUGH_MODE, layer->passThroughMode());
0150         m_elem.appendChild(layerElement);
0151     }
0152     QDomElement elem = m_doc.createElement(LAYERS);
0153     Q_ASSERT(!layerElement.isNull());
0154     layerElement.appendChild(elem);
0155     KisSaveXmlVisitor visitor(m_doc, elem, m_count, m_url, false);
0156     visitor.setSelectedNodes(m_selectedNodes);
0157     m_count++;
0158     bool success = visitor.visitAllInverse(layer);
0159 
0160     m_errorMessages.append(visitor.errorMessages());
0161     if (!m_errorMessages.isEmpty()) {
0162         return false;
0163     }
0164 
0165     QMapIterator<const KisNode*, QString> i(visitor.nodeFileNames());
0166     while (i.hasNext()) {
0167         i.next();
0168         m_nodeFileNames[i.key()] = i.value();
0169     }
0170 
0171     i = QMapIterator<const KisNode*, QString>(visitor.keyframeFileNames());
0172     while (i.hasNext()) {
0173         i.next();
0174         m_keyframeFileNames[i.key()] = i.value();
0175     }
0176 
0177     return success;
0178 }
0179 
0180 bool KisSaveXmlVisitor::visit(KisAdjustmentLayer* layer)
0181 {
0182     if (!layer->filter()) {
0183         return false;
0184     }
0185     QDomElement layerElement = m_doc.createElement(LAYER);
0186     saveLayer(layerElement, ADJUSTMENT_LAYER, layer);
0187     layerElement.setAttribute(FILTER_NAME, layer->filter()->name());
0188     layerElement.setAttribute(FILTER_VERSION, layer->filter()->version());
0189     m_elem.appendChild(layerElement);
0190 
0191     m_count++;
0192     return saveMasks(layer, layerElement);
0193 }
0194 
0195 bool KisSaveXmlVisitor::visit(KisGeneratorLayer *layer)
0196 {
0197     QDomElement layerElement = m_doc.createElement(LAYER);
0198     saveLayer(layerElement, GENERATOR_LAYER, layer);
0199     layerElement.setAttribute(GENERATOR_NAME, layer->filter()->name());
0200     layerElement.setAttribute(GENERATOR_VERSION, layer->filter()->version());
0201     m_elem.appendChild(layerElement);
0202 
0203     m_count++;
0204     return saveMasks(layer, layerElement);
0205 }
0206 
0207 bool KisSaveXmlVisitor::visit(KisCloneLayer *layer)
0208 {
0209     QDomElement layerElement = m_doc.createElement(LAYER);
0210     saveLayer(layerElement, CLONE_LAYER, layer);
0211     layerElement.setAttribute(CLONE_FROM, layer->copyFromInfo().name());
0212     layerElement.setAttribute(CLONE_FROM_UUID, layer->copyFromInfo().uuid().toString());
0213     layerElement.setAttribute(CLONE_TYPE, layer->copyType());
0214     m_elem.appendChild(layerElement);
0215 
0216     m_count++;
0217     return saveMasks(layer, layerElement);
0218 }
0219 
0220 bool KisSaveXmlVisitor::visit(KisFilterMask *mask)
0221 {
0222     Q_ASSERT(mask);
0223     if (!mask->filter()) {
0224         return false;
0225     }
0226     QDomElement el = m_doc.createElement(MASK);
0227     saveMask(el, FILTER_MASK, mask);
0228     el.setAttribute(FILTER_NAME, mask->filter()->name());
0229     el.setAttribute(FILTER_VERSION, mask->filter()->version());
0230 
0231     m_elem.appendChild(el);
0232 
0233     m_count++;
0234     return true;
0235 }
0236 
0237 bool KisSaveXmlVisitor::visit(KisTransformMask *mask)
0238 {
0239     Q_ASSERT(mask);
0240 
0241     QDomElement el = m_doc.createElement(MASK);
0242     saveMask(el, TRANSFORM_MASK, mask);
0243 
0244     m_elem.appendChild(el);
0245 
0246     m_count++;
0247     return true;
0248 }
0249 
0250 bool KisSaveXmlVisitor::visit(KisTransparencyMask *mask)
0251 {
0252     Q_ASSERT(mask);
0253     QDomElement el = m_doc.createElement(MASK);
0254     saveMask(el, TRANSPARENCY_MASK, mask);
0255     m_elem.appendChild(el);
0256     m_count++;
0257     return true;
0258 }
0259 
0260 bool KisSaveXmlVisitor::visit(KisColorizeMask *mask)
0261 {
0262     Q_ASSERT(mask);
0263     QDomElement el = m_doc.createElement(MASK);
0264     saveMask(el, COLORIZE_MASK, mask);
0265     m_elem.appendChild(el);
0266     m_count++;
0267     return true;
0268 }
0269 
0270 bool KisSaveXmlVisitor::visit(KisSelectionMask *mask)
0271 {
0272     Q_ASSERT(mask);
0273 
0274     QDomElement el = m_doc.createElement(MASK);
0275     saveMask(el, SELECTION_MASK, mask);
0276     m_elem.appendChild(el);
0277     m_count++;
0278     return true;
0279 }
0280 
0281 
0282 void KisSaveXmlVisitor::loadLayerAttributes(const QDomElement &el, KisLayer *layer)
0283 {
0284     if (el.hasAttribute(NAME)) {
0285         QString layerName = el.attribute(NAME);
0286         if (layerName != layer->name()) {
0287             // Make the EXR layername leading in case of conflicts
0288             layer->setName(layerName);
0289         }
0290     }
0291 
0292     if (el.hasAttribute(CHANNEL_FLAGS)) {
0293         layer->setChannelFlags(stringToFlags(el.attribute(CHANNEL_FLAGS)));
0294     }
0295 
0296     if (el.hasAttribute(OPACITY)) {
0297         layer->setOpacity(el.attribute(OPACITY).toInt());
0298     }
0299 
0300     if (el.hasAttribute(COMPOSITE_OP)) {
0301         layer->setCompositeOpId(el.attribute(COMPOSITE_OP));
0302     }
0303 
0304     if (el.hasAttribute(VISIBLE)) {
0305         layer->setVisible(el.attribute(VISIBLE).toInt());
0306     }
0307 
0308     if (el.hasAttribute(LOCKED)) {
0309         layer->setUserLocked(el.attribute(LOCKED).toInt());
0310     }
0311 
0312     if (el.hasAttribute(X)) {
0313         layer->setX(el.attribute(X).toInt());
0314     }
0315 
0316     if (el.hasAttribute(Y)) {
0317         layer->setY(el.attribute(Y).toInt());
0318     }
0319 
0320     if (el.hasAttribute(UUID)) {
0321         layer->setUuid(el.attribute(UUID));
0322     }
0323 
0324     if (el.hasAttribute(COLLAPSED)) {
0325         layer->setCollapsed(el.attribute(COLLAPSED).toInt());
0326     }
0327 
0328     if (el.hasAttribute(COLOR_LABEL)) {
0329         layer->setColorLabelIndex(el.attribute(COLOR_LABEL).toInt());
0330     }
0331 
0332     if (el.hasAttribute(VISIBLE_IN_TIMELINE)) {
0333         layer->setPinnedToTimeline(el.attribute(VISIBLE_IN_TIMELINE).toInt());
0334     }
0335 
0336     if (el.hasAttribute(LAYER_STYLE_UUID)) {
0337         QString uuidString = el.attribute(LAYER_STYLE_UUID);
0338         QUuid uuid(uuidString);
0339         if (!uuid.isNull()) {
0340             KisPSDLayerStyleSP dumbLayerStyle(new KisPSDLayerStyle());
0341             dumbLayerStyle->setUuid(uuid);
0342             layer->setLayerStyle(dumbLayerStyle);
0343         } else {
0344             warnKrita << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString;
0345         }
0346     }
0347 }
0348 
0349 void KisSaveXmlVisitor::saveNodeKeyframes(const KisNode* node, QString nodeFilename, QDomElement& nodeElement)
0350 {
0351     if (node->isAnimated()) {
0352         QString keyframeFile = nodeFilename + ".keyframes.xml";
0353         m_keyframeFileNames[node] = keyframeFile;
0354         nodeElement.setAttribute(KEYFRAME_FILE, keyframeFile);
0355     }
0356 }
0357 
0358 void KisSaveXmlVisitor::saveLayer(QDomElement & el, const QString & layerType, const KisLayer * layer)
0359 {
0360     QString filename = LAYER + QString::number(m_count);
0361 
0362     el.setAttribute(CHANNEL_FLAGS, flagsToString(layer->channelFlags()));
0363     el.setAttribute(NAME, layer->name());
0364     el.setAttribute(OPACITY, layer->opacity());
0365     el.setAttribute(COMPOSITE_OP, layer->compositeOp()->id());
0366     el.setAttribute(VISIBLE, layer->visible());
0367     el.setAttribute(LOCKED, layer->userLocked());
0368     el.setAttribute(NODE_TYPE, layerType);
0369     el.setAttribute(FILE_NAME, filename);
0370     el.setAttribute(X, layer->x());
0371     el.setAttribute(Y, layer->y());
0372     el.setAttribute(UUID, layer->uuid().toString());
0373     el.setAttribute(COLLAPSED, layer->collapsed());
0374     el.setAttribute(COLOR_LABEL, layer->colorLabelIndex());
0375     el.setAttribute(VISIBLE_IN_TIMELINE, layer->isPinnedToTimeline());
0376 
0377     if (layer->layerStyle()) {
0378         el.setAttribute(LAYER_STYLE_UUID, layer->layerStyle()->uuid().toString());
0379     }
0380 
0381     Q_FOREACH (KisNodeSP node, m_selectedNodes) {
0382         if (node.data() == layer) {
0383             el.setAttribute("selected", "true");
0384             break;
0385         }
0386     }
0387 
0388     saveNodeKeyframes(layer, filename, el);
0389 
0390     m_nodeFileNames[layer] = filename;
0391 
0392     dbgFile << "Saved layer "
0393             << layer->name()
0394             << " of type " << layerType
0395             << " with filename " << LAYER + QString::number(m_count);
0396 }
0397 
0398 void KisSaveXmlVisitor::saveMask(QDomElement & el, const QString & maskType, const KisMaskSP mask)
0399 {
0400     QString filename = MASK + QString::number(m_count);
0401 
0402     el.setAttribute(NAME, mask->name());
0403     el.setAttribute(VISIBLE, mask->visible());
0404     el.setAttribute(LOCKED, mask->userLocked());
0405     el.setAttribute(NODE_TYPE, maskType);
0406     el.setAttribute(FILE_NAME, filename);
0407     el.setAttribute(X, mask->x());
0408     el.setAttribute(Y, mask->y());
0409     el.setAttribute(UUID, mask->uuid().toString());
0410     el.setAttribute(COLOR_LABEL, mask->colorLabelIndex());
0411     el.setAttribute(VISIBLE_IN_TIMELINE, mask->isPinnedToTimeline());
0412 
0413     if (maskType == SELECTION_MASK) {
0414         el.setAttribute(ACTIVE, mask->nodeProperties().boolProperty("active"));
0415     } else if (maskType == COLORIZE_MASK) {
0416         el.setAttribute(COLORSPACE_NAME, mask->colorSpace()->id());
0417         el.setAttribute(COMPOSITE_OP, mask->compositeOpId());
0418         el.setAttribute(COLORIZE_EDIT_KEYSTROKES, KisLayerPropertiesIcons::nodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, true).toBool());
0419         el.setAttribute(COLORIZE_SHOW_COLORING, KisLayerPropertiesIcons::nodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, true).toBool());
0420 
0421         const KisColorizeMask *colorizeMask = dynamic_cast<const KisColorizeMask*>(mask.data());
0422         KIS_SAFE_ASSERT_RECOVER_NOOP(colorizeMask);
0423 
0424         if (colorizeMask) {
0425             el.setAttribute(COLORIZE_USE_EDGE_DETECTION, colorizeMask->useEdgeDetection());
0426             el.setAttribute(COLORIZE_EDGE_DETECTION_SIZE, KisDomUtils::toString(colorizeMask->edgeDetectionSize()));
0427             el.setAttribute(COLORIZE_FUZZY_RADIUS, KisDomUtils::toString(colorizeMask->fuzzyRadius()));
0428             el.setAttribute(COLORIZE_CLEANUP, int(100 * colorizeMask->cleanUpAmount()));
0429             el.setAttribute(COLORIZE_LIMIT_TO_DEVICE, colorizeMask->limitToDeviceBounds());
0430         }
0431     }
0432 
0433     saveNodeKeyframes(mask, filename, el);
0434 
0435     m_nodeFileNames[mask] = filename;
0436 
0437     dbgFile << "Saved mask "
0438             << mask->name()
0439             << " of type " << maskType
0440             << " with filename " << filename;
0441 }
0442 
0443 bool KisSaveXmlVisitor::saveMasks(KisNode * node, QDomElement & layerElement)
0444 {
0445     if (node->childCount() > 0) {
0446         QDomElement elem = m_doc.createElement(MASKS);
0447         Q_ASSERT(!layerElement.isNull());
0448         layerElement.appendChild(elem);
0449         KisSaveXmlVisitor visitor(m_doc, elem, m_count, m_url, false);
0450         visitor.setSelectedNodes(m_selectedNodes);
0451         bool success = visitor.visitAllInverse(node);
0452         m_errorMessages.append(visitor.errorMessages());
0453         if (!m_errorMessages.isEmpty()) {
0454             return false;
0455         }
0456 
0457         QMapIterator<const KisNode*, QString> i(visitor.nodeFileNames());
0458         while (i.hasNext()) {
0459             i.next();
0460             m_nodeFileNames[i.key()] = i.value();
0461         }
0462 
0463         i = QMapIterator<const KisNode*, QString>(visitor.keyframeFileNames());
0464         while (i.hasNext()) {
0465             i.next();
0466             m_keyframeFileNames[i.key()] = i.value();
0467         }
0468 
0469         return success;
0470     }
0471     return true;
0472 }
0473 
0474 bool KisSaveXmlVisitor::saveReferenceImagesLayer(KisExternalLayer *layer)
0475 {
0476     auto *referencesLayer = dynamic_cast<KisReferenceImagesLayer*>(layer);
0477     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(referencesLayer, false);
0478 
0479     QDomElement layerElement = m_doc.createElement(LAYER);
0480     layerElement.setAttribute(NODE_TYPE, REFERENCE_IMAGES_LAYER);
0481 
0482     int nextId = 0;
0483     Q_FOREACH(KoShape *shape, referencesLayer->shapes()) {
0484         auto *reference = dynamic_cast<KisReferenceImage*>(shape);
0485         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(reference, false);
0486         reference->saveXml(m_doc, layerElement, nextId);
0487         nextId++;
0488     }
0489 
0490     m_elem.appendChild(layerElement);
0491     m_count++;
0492     return true;
0493 }
0494 
0495