1 /*
2  *  Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
3  *  Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #include "kis_kra_savexml_visitor.h"
21 #include "kis_kra_tags.h"
22 #include "kis_kra_utils.h"
23 #include "kis_layer_properties_icons.h"
24 
25 #include <QTextStream>
26 #include <QDir>
27 
28 #include <KoProperties.h>
29 #include <KoColorSpace.h>
30 #include <KoCompositeOp.h>
31 
32 #include <kis_debug.h>
33 #include <filter/kis_filter_configuration.h>
34 #include <generator/kis_generator_layer.h>
35 #include <kis_adjustment_layer.h>
36 #include <kis_clone_layer.h>
37 #include <kis_filter_mask.h>
38 #include <kis_transform_mask.h>
39 #include <kis_group_layer.h>
40 #include <kis_image.h>
41 #include <kis_layer.h>
42 #include <kis_paint_device.h>
43 #include <kis_paint_layer.h>
44 #include <kis_selection_mask.h>
45 #include <kis_shape_layer.h>
46 #include <kis_transparency_mask.h>
47 #include <lazybrush/kis_colorize_mask.h>
48 #include <kis_file_layer.h>
49 #include <kis_psd_layer_style.h>
50 #include <KisReferenceImage.h>
51 #include <KisReferenceImagesLayer.h>
52 #include "kis_keyframe_channel.h"
53 #include "kis_dom_utils.h"
54 
55 using namespace KRA;
56 
KisSaveXmlVisitor(QDomDocument doc,const QDomElement & element,quint32 & count,const QString & url,bool root)57 KisSaveXmlVisitor::KisSaveXmlVisitor(QDomDocument doc, const QDomElement & element, quint32 &count, const QString &url, bool root)
58     : KisNodeVisitor()
59     , m_doc(doc)
60     , m_count(count)
61     , m_url(url)
62     , m_root(root)
63 {
64     Q_ASSERT(!element.isNull());
65     m_elem = element;
66 }
67 
setSelectedNodes(vKisNodeSP selectedNodes)68 void KisSaveXmlVisitor::setSelectedNodes(vKisNodeSP selectedNodes)
69 {
70     m_selectedNodes = selectedNodes;
71 }
72 
errorMessages() const73 QStringList KisSaveXmlVisitor::errorMessages() const
74 {
75     return m_errorMessages;
76 }
77 
visit(KisExternalLayer * layer)78 bool KisSaveXmlVisitor::visit(KisExternalLayer * layer)
79 {
80     if (layer->inherits("KisReferenceImagesLayer")) {
81         return saveReferenceImagesLayer(layer);
82     } else if (layer->inherits("KisShapeLayer")) {
83         QDomElement layerElement = m_doc.createElement(LAYER);
84         saveLayer(layerElement, SHAPE_LAYER, layer);
85         m_elem.appendChild(layerElement);
86         m_count++;
87         return saveMasks(layer, layerElement);
88     }
89     else if (layer->inherits("KisFileLayer")) {
90         QDomElement layerElement = m_doc.createElement(LAYER);
91         saveLayer(layerElement, FILE_LAYER, layer);
92 
93         KisFileLayer *fileLayer = dynamic_cast<KisFileLayer*>(layer);
94 
95         QString path = fileLayer->path();
96 
97         QDir d(QFileInfo(m_url).absolutePath());
98 
99 #ifndef Q_OS_ANDROID
100         layerElement.setAttribute("source", d.relativeFilePath(path));
101 #else
102         layerElement.setAttribute("source", path);
103 #endif
104 
105         if (fileLayer->scalingMethod() == KisFileLayer::ToImagePPI) {
106             layerElement.setAttribute("scale", "true");
107         }
108         else {
109             layerElement.setAttribute("scale", "false");
110         }
111         layerElement.setAttribute("scalingmethod", (int)fileLayer->scalingMethod());
112         layerElement.setAttribute(COLORSPACE_NAME, layer->original()->colorSpace()->id());
113 
114         m_elem.appendChild(layerElement);
115         m_count++;
116         return saveMasks(layer, layerElement);
117     }
118     return false;
119 }
120 
savePaintLayerAttributes(KisPaintLayer * layer,QDomDocument & doc)121 QDomElement KisSaveXmlVisitor::savePaintLayerAttributes(KisPaintLayer *layer, QDomDocument &doc)
122 {
123     QDomElement element = doc.createElement(LAYER);
124     saveLayer(element, PAINT_LAYER, layer);
125     element.setAttribute(CHANNEL_LOCK_FLAGS, flagsToString(layer->channelLockFlags()));
126     element.setAttribute(COLORSPACE_NAME, layer->paintDevice()->colorSpace()->id());
127 
128     element.setAttribute(ONION_SKIN_ENABLED, layer->onionSkinEnabled());
129     element.setAttribute(VISIBLE_IN_TIMELINE, layer->useInTimeline());
130 
131     return element;
132 }
133 
loadPaintLayerAttributes(const QDomElement & el,KisPaintLayer * layer)134 void KisSaveXmlVisitor::loadPaintLayerAttributes(const QDomElement &el, KisPaintLayer *layer)
135 {
136     loadLayerAttributes(el, layer);
137 
138     if (el.hasAttribute(CHANNEL_LOCK_FLAGS)) {
139         layer->setChannelLockFlags(stringToFlags(el.attribute(CHANNEL_LOCK_FLAGS)));
140     }
141 }
142 
visit(KisPaintLayer * layer)143 bool KisSaveXmlVisitor::visit(KisPaintLayer *layer)
144 {
145     QDomElement layerElement = savePaintLayerAttributes(layer, m_doc);
146     m_elem.appendChild(layerElement);
147     m_count++;
148     return saveMasks(layer, layerElement);
149 }
150 
visit(KisGroupLayer * layer)151 bool KisSaveXmlVisitor::visit(KisGroupLayer *layer)
152 {
153     QDomElement layerElement;
154 
155     if (m_root) // if this is the root we fake so not to save it
156         layerElement = m_elem;
157     else {
158         layerElement = m_doc.createElement(LAYER);
159         saveLayer(layerElement, GROUP_LAYER, layer);
160         layerElement.setAttribute(PASS_THROUGH_MODE, layer->passThroughMode());
161         m_elem.appendChild(layerElement);
162     }
163     QDomElement elem = m_doc.createElement(LAYERS);
164     Q_ASSERT(!layerElement.isNull());
165     layerElement.appendChild(elem);
166     KisSaveXmlVisitor visitor(m_doc, elem, m_count, m_url, false);
167     visitor.setSelectedNodes(m_selectedNodes);
168     m_count++;
169     bool success = visitor.visitAllInverse(layer);
170 
171     m_errorMessages.append(visitor.errorMessages());
172     if (!m_errorMessages.isEmpty()) {
173         return false;
174     }
175 
176     QMapIterator<const KisNode*, QString> i(visitor.nodeFileNames());
177     while (i.hasNext()) {
178         i.next();
179         m_nodeFileNames[i.key()] = i.value();
180     }
181 
182     i = QMapIterator<const KisNode*, QString>(visitor.keyframeFileNames());
183     while (i.hasNext()) {
184         i.next();
185         m_keyframeFileNames[i.key()] = i.value();
186     }
187 
188     return success;
189 }
190 
visit(KisAdjustmentLayer * layer)191 bool KisSaveXmlVisitor::visit(KisAdjustmentLayer* layer)
192 {
193     if (!layer->filter()) {
194         return false;
195     }
196     QDomElement layerElement = m_doc.createElement(LAYER);
197     saveLayer(layerElement, ADJUSTMENT_LAYER, layer);
198     layerElement.setAttribute(FILTER_NAME, layer->filter()->name());
199     layerElement.setAttribute(FILTER_VERSION, layer->filter()->version());
200     m_elem.appendChild(layerElement);
201 
202     m_count++;
203     return saveMasks(layer, layerElement);
204 }
205 
visit(KisGeneratorLayer * layer)206 bool KisSaveXmlVisitor::visit(KisGeneratorLayer *layer)
207 {
208     QDomElement layerElement = m_doc.createElement(LAYER);
209     saveLayer(layerElement, GENERATOR_LAYER, layer);
210     layerElement.setAttribute(GENERATOR_NAME, layer->filter()->name());
211     layerElement.setAttribute(GENERATOR_VERSION, layer->filter()->version());
212     m_elem.appendChild(layerElement);
213 
214     m_count++;
215     return saveMasks(layer, layerElement);
216 }
217 
visit(KisCloneLayer * layer)218 bool KisSaveXmlVisitor::visit(KisCloneLayer *layer)
219 {
220     QDomElement layerElement = m_doc.createElement(LAYER);
221     saveLayer(layerElement, CLONE_LAYER, layer);
222     layerElement.setAttribute(CLONE_FROM, layer->copyFromInfo().name());
223     layerElement.setAttribute(CLONE_FROM_UUID, layer->copyFromInfo().uuid().toString());
224     layerElement.setAttribute(CLONE_TYPE, layer->copyType());
225     m_elem.appendChild(layerElement);
226 
227     m_count++;
228     return saveMasks(layer, layerElement);
229 }
230 
visit(KisFilterMask * mask)231 bool KisSaveXmlVisitor::visit(KisFilterMask *mask)
232 {
233     Q_ASSERT(mask);
234     if (!mask->filter()) {
235         return false;
236     }
237     QDomElement el = m_doc.createElement(MASK);
238     saveMask(el, FILTER_MASK, mask);
239     el.setAttribute(FILTER_NAME, mask->filter()->name());
240     el.setAttribute(FILTER_VERSION, mask->filter()->version());
241 
242     m_elem.appendChild(el);
243 
244     m_count++;
245     return true;
246 }
247 
visit(KisTransformMask * mask)248 bool KisSaveXmlVisitor::visit(KisTransformMask *mask)
249 {
250     Q_ASSERT(mask);
251 
252     QDomElement el = m_doc.createElement(MASK);
253     saveMask(el, TRANSFORM_MASK, mask);
254 
255     m_elem.appendChild(el);
256 
257     m_count++;
258     return true;
259 }
260 
visit(KisTransparencyMask * mask)261 bool KisSaveXmlVisitor::visit(KisTransparencyMask *mask)
262 {
263     Q_ASSERT(mask);
264     QDomElement el = m_doc.createElement(MASK);
265     saveMask(el, TRANSPARENCY_MASK, mask);
266     m_elem.appendChild(el);
267     m_count++;
268     return true;
269 }
270 
visit(KisColorizeMask * mask)271 bool KisSaveXmlVisitor::visit(KisColorizeMask *mask)
272 {
273     Q_ASSERT(mask);
274     QDomElement el = m_doc.createElement(MASK);
275     saveMask(el, COLORIZE_MASK, mask);
276     m_elem.appendChild(el);
277     m_count++;
278     return true;
279 }
280 
visit(KisSelectionMask * mask)281 bool KisSaveXmlVisitor::visit(KisSelectionMask *mask)
282 {
283     Q_ASSERT(mask);
284 
285     QDomElement el = m_doc.createElement(MASK);
286     saveMask(el, SELECTION_MASK, mask);
287     m_elem.appendChild(el);
288     m_count++;
289     return true;
290 }
291 
292 
loadLayerAttributes(const QDomElement & el,KisLayer * layer)293 void KisSaveXmlVisitor::loadLayerAttributes(const QDomElement &el, KisLayer *layer)
294 {
295     if (el.hasAttribute(NAME)) {
296         QString layerName = el.attribute(NAME);
297         if (layerName != layer->name()) {
298             // Make the EXR layername leading in case of conflicts
299             layer->setName(layerName);
300         }
301     }
302 
303     if (el.hasAttribute(CHANNEL_FLAGS)) {
304         layer->setChannelFlags(stringToFlags(el.attribute(CHANNEL_FLAGS)));
305     }
306 
307     if (el.hasAttribute(OPACITY)) {
308         layer->setOpacity(el.attribute(OPACITY).toInt());
309     }
310 
311     if (el.hasAttribute(COMPOSITE_OP)) {
312         layer->setCompositeOpId(el.attribute(COMPOSITE_OP));
313     }
314 
315     if (el.hasAttribute(VISIBLE)) {
316         layer->setVisible(el.attribute(VISIBLE).toInt());
317     }
318 
319     if (el.hasAttribute(LOCKED)) {
320         layer->setUserLocked(el.attribute(LOCKED).toInt());
321     }
322 
323     if (el.hasAttribute(X)) {
324         layer->setX(el.attribute(X).toInt());
325     }
326 
327     if (el.hasAttribute(Y)) {
328         layer->setY(el.attribute(Y).toInt());
329     }
330 
331     if (el.hasAttribute(UUID)) {
332         layer->setUuid(el.attribute(UUID));
333     }
334 
335     if (el.hasAttribute(COLLAPSED)) {
336         layer->setCollapsed(el.attribute(COLLAPSED).toInt());
337     }
338 
339     if (el.hasAttribute(COLOR_LABEL)) {
340         layer->setColorLabelIndex(el.attribute(COLOR_LABEL).toInt());
341     }
342 
343     if (el.hasAttribute(VISIBLE_IN_TIMELINE)) {
344         layer->setUseInTimeline(el.attribute(VISIBLE_IN_TIMELINE).toInt());
345     }
346 
347     if (el.hasAttribute(LAYER_STYLE_UUID)) {
348         QString uuidString = el.attribute(LAYER_STYLE_UUID);
349         QUuid uuid(uuidString);
350         if (!uuid.isNull()) {
351             KisPSDLayerStyleSP dumbLayerStyle(new KisPSDLayerStyle());
352             dumbLayerStyle->setUuid(uuid);
353             layer->setLayerStyle(dumbLayerStyle);
354         } else {
355             warnKrita << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString;
356         }
357     }
358 }
359 
saveNodeKeyframes(const KisNode * node,QString nodeFilename,QDomElement & nodeElement)360 void KisSaveXmlVisitor::saveNodeKeyframes(const KisNode* node, QString nodeFilename, QDomElement& nodeElement)
361 {
362     if (node->isAnimated()) {
363         QString keyframeFile = nodeFilename + ".keyframes.xml";
364         m_keyframeFileNames[node] = keyframeFile;
365         nodeElement.setAttribute(KEYFRAME_FILE, keyframeFile);
366     }
367 }
368 
saveLayer(QDomElement & el,const QString & layerType,const KisLayer * layer)369 void KisSaveXmlVisitor::saveLayer(QDomElement & el, const QString & layerType, const KisLayer * layer)
370 {
371     QString filename = LAYER + QString::number(m_count);
372 
373     el.setAttribute(CHANNEL_FLAGS, flagsToString(layer->channelFlags()));
374     el.setAttribute(NAME, layer->name());
375     el.setAttribute(OPACITY, layer->opacity());
376     el.setAttribute(COMPOSITE_OP, layer->compositeOp()->id());
377     el.setAttribute(VISIBLE, layer->visible());
378     el.setAttribute(LOCKED, layer->userLocked());
379     el.setAttribute(NODE_TYPE, layerType);
380     el.setAttribute(FILE_NAME, filename);
381     el.setAttribute(X, layer->x());
382     el.setAttribute(Y, layer->y());
383     el.setAttribute(UUID, layer->uuid().toString());
384     el.setAttribute(COLLAPSED, layer->collapsed());
385     el.setAttribute(COLOR_LABEL, layer->colorLabelIndex());
386     el.setAttribute(VISIBLE_IN_TIMELINE, layer->useInTimeline());
387 
388     if (layer->layerStyle()) {
389         el.setAttribute(LAYER_STYLE_UUID, layer->layerStyle()->uuid().toString());
390     }
391 
392     Q_FOREACH (KisNodeSP node, m_selectedNodes) {
393         if (node.data() == layer) {
394             el.setAttribute("selected", "true");
395             break;
396         }
397     }
398 
399     saveNodeKeyframes(layer, filename, el);
400 
401     m_nodeFileNames[layer] = filename;
402 
403     dbgFile << "Saved layer "
404             << layer->name()
405             << " of type " << layerType
406             << " with filename " << LAYER + QString::number(m_count);
407 }
408 
saveMask(QDomElement & el,const QString & maskType,const KisMaskSP mask)409 void KisSaveXmlVisitor::saveMask(QDomElement & el, const QString & maskType, const KisMaskSP mask)
410 {
411     QString filename = MASK + QString::number(m_count);
412 
413     el.setAttribute(NAME, mask->name());
414     el.setAttribute(VISIBLE, mask->visible());
415     el.setAttribute(LOCKED, mask->userLocked());
416     el.setAttribute(NODE_TYPE, maskType);
417     el.setAttribute(FILE_NAME, filename);
418     el.setAttribute(X, mask->x());
419     el.setAttribute(Y, mask->y());
420     el.setAttribute(UUID, mask->uuid().toString());
421 
422     if (maskType == SELECTION_MASK) {
423         el.setAttribute(ACTIVE, mask->nodeProperties().boolProperty("active"));
424     } else if (maskType == COLORIZE_MASK) {
425         el.setAttribute(COLORSPACE_NAME, mask->colorSpace()->id());
426         el.setAttribute(COMPOSITE_OP, mask->compositeOpId());
427         el.setAttribute(COLORIZE_EDIT_KEYSTROKES, KisLayerPropertiesIcons::nodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, true).toBool());
428         el.setAttribute(COLORIZE_SHOW_COLORING, KisLayerPropertiesIcons::nodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, true).toBool());
429 
430         const KisColorizeMask *colorizeMask = dynamic_cast<const KisColorizeMask*>(mask.data());
431         KIS_SAFE_ASSERT_RECOVER_NOOP(colorizeMask);
432 
433         if (colorizeMask) {
434             el.setAttribute(COLORIZE_USE_EDGE_DETECTION, colorizeMask->useEdgeDetection());
435             el.setAttribute(COLORIZE_EDGE_DETECTION_SIZE, KisDomUtils::toString(colorizeMask->edgeDetectionSize()));
436             el.setAttribute(COLORIZE_FUZZY_RADIUS, KisDomUtils::toString(colorizeMask->fuzzyRadius()));
437             el.setAttribute(COLORIZE_CLEANUP, int(100 * colorizeMask->cleanUpAmount()));
438             el.setAttribute(COLORIZE_LIMIT_TO_DEVICE, colorizeMask->limitToDeviceBounds());
439         }
440     }
441 
442     saveNodeKeyframes(mask, filename, el);
443 
444     m_nodeFileNames[mask] = filename;
445 
446     dbgFile << "Saved mask "
447             << mask->name()
448             << " of type " << maskType
449             << " with filename " << filename;
450 }
451 
saveMasks(KisNode * node,QDomElement & layerElement)452 bool KisSaveXmlVisitor::saveMasks(KisNode * node, QDomElement & layerElement)
453 {
454     if (node->childCount() > 0) {
455         QDomElement elem = m_doc.createElement(MASKS);
456         Q_ASSERT(!layerElement.isNull());
457         layerElement.appendChild(elem);
458         KisSaveXmlVisitor visitor(m_doc, elem, m_count, m_url, false);
459         visitor.setSelectedNodes(m_selectedNodes);
460         bool success = visitor.visitAllInverse(node);
461         m_errorMessages.append(visitor.errorMessages());
462         if (!m_errorMessages.isEmpty()) {
463             return false;
464         }
465 
466         QMapIterator<const KisNode*, QString> i(visitor.nodeFileNames());
467         while (i.hasNext()) {
468             i.next();
469             m_nodeFileNames[i.key()] = i.value();
470         }
471 
472         i = QMapIterator<const KisNode*, QString>(visitor.keyframeFileNames());
473         while (i.hasNext()) {
474             i.next();
475             m_keyframeFileNames[i.key()] = i.value();
476         }
477 
478         return success;
479     }
480     return true;
481 }
482 
saveReferenceImagesLayer(KisExternalLayer * layer)483 bool KisSaveXmlVisitor::saveReferenceImagesLayer(KisExternalLayer *layer)
484 {
485     auto *referencesLayer = dynamic_cast<KisReferenceImagesLayer*>(layer);
486     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(referencesLayer, false);
487 
488     QDomElement layerElement = m_doc.createElement(LAYER);
489     layerElement.setAttribute(NODE_TYPE, REFERENCE_IMAGES_LAYER);
490 
491     int nextId = 0;
492     Q_FOREACH(KoShape *shape, referencesLayer->shapes()) {
493         auto *reference = dynamic_cast<KisReferenceImage*>(shape);
494         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(reference, false);
495         reference->saveXml(m_doc, layerElement, nextId);
496         nextId++;
497     }
498 
499     m_elem.appendChild(layerElement);
500     m_count++;
501     return true;
502 }
503 
504 
505