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