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