Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_kra_save_visitor.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
3 * SPDX-FileCopyrightText: 2005 C. Boemann <cbo@boemann.dk>
4 * SPDX-FileCopyrightText: 2007-2008 Boudewijn Rempt <boud@valdyas.org>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
10#include "kis_kra_tags.h"
11
12#include <QBuffer>
13#include <QByteArray>
14
15#include <KoColorProfile.h>
16#include <KoStore.h>
17#include <KoColorSpace.h>
18
20#include <KisReferenceImage.h>
25#include <kis_annotation.h>
26#include <kis_clone_layer.h>
27#include <kis_file_layer.h>
28#include <kis_filter_mask.h>
29#include <kis_group_layer.h>
30#include <kis_image.h>
31#include <kis_layer.h>
32#include <kis_mask.h>
34#include <kis_meta_data_store.h>
35#include <kis_paint_layer.h>
36#include <kis_pixel_selection.h>
37#include <kis_selection.h>
39#include <kis_selection_mask.h>
40#include <kis_shape_layer.h>
41#include <kis_transform_mask.h>
44
45#include "kis_config.h"
48
51
53#include <KoStoreDevice.h>
55#include "kis_dom_utils.h"
56
57
58using namespace KRA;
59
60KisKraSaveVisitor::KisKraSaveVisitor(KoStore *store, const QString & name, QMap<const KisNode*, QString> nodeFileNames)
62 , m_store(store)
63 , m_external(false)
64 , m_name(name)
65 , m_nodeFileNames(nodeFileNames)
66 , m_writer(new KisStorePaintDeviceWriter(store))
67{
68}
69
74
75void KisKraSaveVisitor::setExternalUri(const QString &uri)
76{
77 m_external = true;
78 m_uri = uri;
79}
80
82{
83 bool result = false;
84 if (auto* referencesLayer = dynamic_cast<KisReferenceImagesLayer*>(layer)) {
85 result = true;
86 QList <KoShape *> shapes = referencesLayer->shapes();
87 std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
88 Q_FOREACH(KoShape *shape, shapes) {
89 auto *reference = dynamic_cast<KisReferenceImage*>(shape);
90 KIS_ASSERT_RECOVER_RETURN_VALUE(reference, false);
91 bool saved = reference->saveImage(m_store);
92 if (!saved) {
93 m_errorMessages << i18n("Failed to save reference image %1.", reference->internalFile());
94 result = false;
95 }
96 }
97 }
98 else if (KisShapeLayer *shapeLayer = dynamic_cast<KisShapeLayer*>(layer)) {
99 if (!saveMetaData(layer)) {
100 m_errorMessages << i18n("Failed to save the metadata for layer %1.", layer->name());
101 return false;
102 }
104 QString location = getLocation(layer, DOT_SHAPE_LAYER);
105 result = m_store->enterDirectory(location);
106 if (!result) {
107 m_errorMessages << i18n("Failed to open %1.", location);
108 }
109 else {
110 result = shapeLayer->saveLayer(m_store);
112 }
113 }
114 else if (KisFileLayer *fileLayer = dynamic_cast<KisFileLayer*>(layer)) {
115 Q_UNUSED(fileLayer); // We don't save data for file layers, but we still want to save the masks.
116 result = true;
117 }
118 return result && visitAllInverse(layer);
119}
120
122{
123 if (!savePaintDevice(layer->paintDevice(), getLocation(layer))) {
124 m_errorMessages << i18n("Failed to save the pixel data for layer %1.", layer->name());
125 return false;
126 }
127 if (!saveAnnotations(layer)) {
128 m_errorMessages << i18n("Failed to save the annotations for layer %1.", layer->name());
129 return false;
130 }
131 if (!saveMetaData(layer)) {
132 m_errorMessages << i18n("Failed to save the metadata for layer %1.", layer->name());
133 return false;
134 }
135 return visitAllInverse(layer);
136}
137
139{
140 if (!saveMetaData(layer)) {
141 m_errorMessages << i18n("Failed to save the metadata for layer %1.", layer->name());
142 return false;
143 }
144 return visitAllInverse(layer);
145}
146
148{
149 if (!layer->filter()) {
150 m_errorMessages << i18n("Failed to save the filter layer %1: it has no filter.", layer->name());
151 return false;
152 }
153 if (!saveSelection(layer)) {
154 m_errorMessages << i18n("Failed to save the selection for filter layer %1.", layer->name());
155 return false;
156 }
157 if (!saveFilterConfiguration(layer)) {
158 m_errorMessages << i18n("Failed to save the filter configuration for filter layer %1.", layer->name());
159 return false;
160 }
161 if (!saveMetaData(layer)) {
162 m_errorMessages << i18n("Failed to save the metadata for layer %1.", layer->name());
163 return false;
164 }
165 return visitAllInverse(layer);
166}
167
169{
170 if (!saveSelection(layer)) {
171 m_errorMessages << i18n("Failed to save the selection for layer %1.", layer->name());
172 return false;
173 }
174 if (!saveFilterConfiguration(layer)) {
175 m_errorMessages << i18n("Failed to save the generator configuration for layer %1.", layer->name());
176 return false;
177 }
178 if (!saveMetaData(layer)) {
179 m_errorMessages << i18n("Failed to save the metadata for layer %1.", layer->name());
180 return false;
181 }
182 return visitAllInverse(layer);
183}
184
186{
187 // Clone layers do not have a profile
188 if (!saveMetaData(layer)) {
189 m_errorMessages << i18n("Failed to save the metadata for layer %1.", layer->name());
190 return false;
191 }
192 return visitAllInverse(layer);
193}
194
196{
197 if (!mask->filter()) {
198 m_errorMessages << i18n("Failed to save filter mask %1. It has no filter.", mask->name());
199 return false;
200 }
201 if (!saveSelection(mask)) {
202 m_errorMessages << i18n("Failed to save the selection for filter mask %1.", mask->name());
203 return false;
204 }
205 if (!saveFilterConfiguration(mask)) {
206 m_errorMessages << i18n("Failed to save the filter configuration for filter mask %1.", mask->name());
207 return false;
208 }
209 return true;
210}
211
213{
214 QDomDocument doc("transform_params");
215
216 QDomElement root = doc.createElement("transform_params");
217
218 QDomElement main = doc.createElement("main");
219 main.setAttribute("id", mask->transformParams()->id());
220
221 QDomElement data = doc.createElement("data");
222 mask->transformParams()->toXML(&data);
223
224 doc.appendChild(root);
225 root.appendChild(main);
226 root.appendChild(data);
227
228 QString location = getLocation(mask, DOT_TRANSFORMCONFIG);
229 if (m_store->open(location)) {
230 QByteArray a = doc.toByteArray();
231 bool retval = m_store->write(a) == a.size();
232
233 if (!retval) {
234 warnFile << "Could not write transform mask configuration";
235 }
236 if (!m_store->close()) {
237 warnFile << "Could not close store after writing transform mask configuration";
238 retval = false;
239 }
240 return retval;
241 }
242 return false;
243}
244
246{
247 if (!saveSelection(mask)) {
248 m_errorMessages << i18n("Failed to save the selection for transparency mask %1.", mask->name());
249 return false;
250 }
251 return true;
252}
253
255{
256 if (!saveSelection(mask)) {
257 m_errorMessages << i18n("Failed to save the selection for local selection %1.", mask->name());
258 return false;
259 }
260 return true;
261}
262
264{
266 QString location = getLocation(mask, DOT_COLORIZE_MASK);
267 bool result = m_store->enterDirectory(location);
268
269 if (!result) {
270 m_errorMessages << i18n("Failed to open %1.", location);
271 return false;
272 }
273
274 if (!m_store->open("content.xml"))
275 return false;
276
277 KoStoreDevice storeDev(m_store);
278
279 QDomDocument doc("doc");
280 QDomElement root = doc.createElement("colorize");
281 doc.appendChild(root);
283
284 QTextStream stream(&storeDev);
286 stream << doc;
287
288 if (!m_store->close())
289 return false;
290
291 int i = 0;
292 Q_FOREACH (const KisLazyFillTools::KeyStroke &stroke, mask->fetchKeyStrokesDirect()) {
293 const QString fileName = QString("%1_%2").arg(COLORIZE_KEYSTROKE).arg(i++);
294 savePaintDevice(stroke.dev, fileName);
295 }
296
298 saveIccProfile(mask, mask->colorSpace()->profile());
299
301
302 return true;
303}
304
309
311{
313 return dev->write(store);
314 }
315
317 return dev->defaultPixel();
318 }
319};
320
322{
324 : m_frameId(frameId) {}
325
327 return dev->framesInterface()->writeFrame(store, m_frameId);
328 }
329
333
334 int m_frameId;
335};
336
338 QString location)
339{
340 // Layer data
341 KisConfig cfg(true);
343
344 KisPaintDeviceFramesInterface *frameInterface = device->framesInterface();
345 QList<int> frames;
346
347 if (frameInterface) {
348 frames = frameInterface->frames();
349 }
350
351 if (!frameInterface || frames.count() <= 1) {
352 savePaintDeviceFrame(device, location, SimpleDevicePolicy());
353 } else {
354 KisRasterKeyframeChannel *keyframeChannel = device->keyframeChannel();
355
356 for (int i = 0; i < frames.count(); i++) {
357 int id = frames[i];
358
359 QString frameFilename = getLocation(keyframeChannel->frameFilename(id));
360 Q_ASSERT(!frameFilename.isEmpty());
361
362 if (!savePaintDeviceFrame(device, frameFilename, FramedDevicePolicy(id))) {
363 return false;
364 }
365 }
366 }
367
369 return true;
370}
371
372
373template<class DevicePolicy>
375{
376 if (m_store->open(location)) {
377 if (!policy.write(device, *m_writer)) {
378 device->disconnect();
379 m_store->close();
380 return false;
381 }
382
383 m_store->close();
384 }
385 if (m_store->open(location + ".defaultpixel")) {
386 m_store->write((char*)policy.defaultPixel(device).data(), device->colorSpace()->pixelSize());
387 m_store->close();
388 }
389
390 return true;
391}
392
394{
395 if (!layer) return false;
396 if (!layer->paintDevice()) return false;
397 if (!layer->paintDevice()->colorSpace()) return false;
398
399 if (layer->paintDevice()->colorSpace()->profile()) {
400 return saveIccProfile(layer, layer->paintDevice()->colorSpace()->profile());
401 }
402 return true;
403
404}
405
407{
408 if (profile) {
409 KisAnnotationSP annotation;
410 if (profile) {
411 QByteArray profileRawData = profile->rawData();
412 if (!profileRawData.isEmpty()) {
413 if (profile->type() == "icc") {
414 annotation = new KisAnnotation(ICC, profile->name(), profile->rawData());
415 } else {
416 annotation = new KisAnnotation(PROFILE, profile->name(), profile->rawData());
417 }
418 }
419 }
420
421 if (annotation) {
422 // save layer profile
423 if (m_store->open(getLocation(node, DOT_ICC))) {
424 m_store->write(annotation->annotation());
425 m_store->close();
426 } else {
427 return false;
428 }
429 }
430 }
431 return true;
432}
433
435{
436 KisSelectionSP selection;
437 if (node->inherits("KisMask")) {
438 selection = static_cast<KisMask*>(node)->selection();
439 } else if (node->inherits("KisAdjustmentLayer")) {
440 selection = static_cast<KisAdjustmentLayer*>(node)->internalSelection();
441 } else if (node->inherits("KisGeneratorLayer")) {
442 selection = static_cast<KisGeneratorLayer*>(node)->internalSelection();
443 } else {
444 return false;
445 }
446
447 bool retval = true;
448
449 if (node->isAnimated() || selection->hasNonEmptyPixelSelection()) {
450 KisPaintDeviceSP dev = selection->pixelSelection();
452 m_errorMessages << i18n("Failed to save the pixel selection data for layer %1.", node->name());
453 retval = false;
454 }
455 }
456
457 if (selection->hasNonEmptyShapeSelection()) {
460 if (retval) {
461 KisShapeSelection* shapeSelection = dynamic_cast<KisShapeSelection*>(selection->shapeSelection());
462 if (!shapeSelection) {
463 retval = false;
464 }
465
466 if (retval && !shapeSelection->saveSelection(m_store, node->image()->bounds())) {
467 m_errorMessages << i18n("Failed to save the vector selection data for layer %1.", node->name());
468 retval = false;
469 }
470 }
472 }
473 return retval;
474}
475
477{
478 KisNodeFilterInterface *filterInterface =
479 dynamic_cast<KisNodeFilterInterface*>(node);
480
482
483 if (filterInterface) {
484 filter = filterInterface->filter();
485 }
486
487 bool retval = false;
488
489 if (filter) {
490 QString location = getLocation(node, DOT_FILTERCONFIG);
491 if (m_store->open(location)) {
492 QString s = filter->toXML();
493 retval = (m_store->write(s.toUtf8(), qstrlen(s.toUtf8())) == qstrlen(s.toUtf8())); m_store->close();
494 }
495 }
496 return retval;
497}
498
500{
501 if (!node->inherits("KisLayer")) return true;
502
503 KisMetaData::Store* metadata = (static_cast<KisLayer*>(node))->metaData();
504 if (metadata->isEmpty()) return true;
505
506 // Serialize all the types of metadata there are
508 if (!backend->supportSaving()) {
509 dbgFile << "Backend " << backend->id() << " does not support saving.";
510 return false;
511 }
512
513 QString location = getLocation(node, QString(".") + backend->id() + DOT_METADATA);
514 dbgFile << "going to save " << backend->id() << ", " << backend->name() << " to " << location;
515
516 QBuffer buffer;
517 // not that the metadata backends every return anything but true...
518 bool retval = backend->saveTo(metadata, &buffer);
519
520 if (!retval) {
521 m_errorMessages << i18n("The metadata backend failed to save the metadata for %1", node->name());
522 }
523 else {
524 QByteArray data = buffer.data();
525 dbgFile << "\t information size is" << data.size();
526
527 if (data.size() > 0 && m_store->open(location)) {
528 retval = m_store->write(data, data.size());
529 m_store->close();
530 }
531 if (!retval) {
532 m_errorMessages << i18n("Could not write for %1 metadata to the file.", node->name());
533 }
534 }
535 return retval;
536}
537
538QString KisKraSaveVisitor::getLocation(KisNode* node, const QString& suffix)
539{
540
541 Q_ASSERT(m_nodeFileNames.contains(node));
542 return getLocation(m_nodeFileNames[node], suffix);
543}
544
545QString KisKraSaveVisitor::getLocation(const QString &filename, const QString& suffix)
546{
547 QString location = m_external ? QString() : m_uri;
548 location += m_name + LAYER_PATH + filename + suffix;
549 return location;
550}
A data extension mechanism for Krita.
const KoColorSpace * colorSpace() const override
KisPaintDeviceSP coloringProjection() const
QList< KisLazyFillTools::KeyStroke > fetchKeyStrokesDirect() const
bool compressKra(bool defaultValue=false) const
The KisFileLayer class loads a particular file as a layer into the layer stack.
QRect bounds() const override
bool saveAnnotations(KisLayer *layer)
KisPaintDeviceWriter * m_writer
bool savePaintDeviceFrame(KisPaintDeviceSP device, QString location, DevicePolicy policy)
bool saveMetaData(KisNode *node)
bool saveFilterConfiguration(KisNode *node)
QStringList errorMessages() const
QString getLocation(KisNode *node, const QString &suffix=QString())
bool visit(KisNode *) override
QMap< const KisNode *, QString > m_nodeFileNames
KisKraSaveVisitor(KoStore *store, const QString &name, QMap< const KisNode *, QString > nodeFileNames)
void setExternalUri(const QString &uri)
bool saveIccProfile(KisNode *node, const KoColorProfile *profile)
bool savePaintDevice(KisPaintDeviceSP device, QString location)
bool saveSelection(KisNode *node)
virtual QString name() const =0
virtual bool saveTo(const Store *store, QIODevice *ioDevice, HeaderType headerType=NoHeader) const =0
virtual QString id() const =0
virtual bool supportSaving() const =0
static KisMetadataBackendRegistry * instance()
virtual KisFilterConfigurationSP filter() const
bool visitAllInverse(KisNode *node, bool breakOnFail=false)
KoColor frameDefaultPixel(int frameId) const
bool writeFrame(KisPaintDeviceWriter &store, int frameId)
KisRasterKeyframeChannel * keyframeChannel() const
bool write(KisPaintDeviceWriter &store)
KisPaintDeviceFramesInterface * framesInterface()
const KoColorSpace * colorSpace() const
KoColor defaultPixel() const
The KisRasterKeyframeChannel is a concrete KisKeyframeChannel subclass that stores and manages KisRas...
QString frameFilename(int frameId) const
The KisReferenceImage class represents a single reference image.
bool saveSelection(KoStore *store, const QRect &imageRect) const
virtual quint32 pixelSize() const =0
virtual const KoColorProfile * profile() const =0
T get(const QString &id) const
static bool compareShapeZIndex(KoShape *s1, KoShape *s2)
Definition KoShape.cpp:434
qint64 write(const QByteArray &data)
Definition KoStore.cpp:198
bool close()
Definition KoStore.cpp:156
virtual void setCompressionEnabled(bool e)
Definition KoStore.cpp:403
void pushDirectory()
Definition KoStore.cpp:294
void popDirectory()
Definition KoStore.cpp:300
virtual bool enterDirectory(const QString &directory)
Definition KoStore.cpp:253
bool open(const QString &name)
Definition KoStore.cpp:109
This file is part of the Krita application in calligra.
#define KIS_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:85
#define warnFile
Definition kis_debug.h:95
#define dbgFile
Definition kis_debug.h:53
int main(int argc, char **argv)
Definition main.cpp:26
const QString LAYER_PATH
const QString DOT_SHAPE_SELECTION
const QString DOT_PIXEL_SELECTION
const QString DOT_METADATA
const QString DOT_SHAPE_LAYER
const QString ICC
const QString DOT_ICC
const QString DOT_FILTERCONFIG
const QString DOT_COLORIZE_MASK
const QString COLORIZE_KEYSTROKE
const QString DOT_TRANSFORMCONFIG
const QString COLORIZE_COLORING_DEVICE
const QString PROFILE
const QString COLORIZE_KEYSTROKES_SECTION
void saveValue(QDomElement *parent, const QString &tag, const QSize &size)
void setUtf8OnStream(QTextStream &stream)
bool write(KisPaintDeviceSP dev, KisPaintDeviceWriter &store)
KoColor defaultPixel(KisPaintDeviceSP dev) const
KisImageWSP image
bool isAnimated() const
virtual KisPaintDeviceSP paintDevice() const =0
QString name() const
KisPaintDeviceSP paintDevice
bool hasNonEmptyPixelSelection() const
KisPixelSelectionSP pixelSelection
bool hasNonEmptyShapeSelection() const
KisSelectionComponent * shapeSelection
KisTransformMaskParamsInterfaceSP transformParams() const
virtual QByteArray rawData() const
virtual QString type() const
bool write(KisPaintDeviceSP dev, KisPaintDeviceWriter &store)
KoColor defaultPixel(KisPaintDeviceSP dev) const