Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_kra_saver.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * Copyright 2008 (C) Boudewijn Rempt <boud@valdyas.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6#include "kis_kra_saver.h"
7
8#include "kis_hdr_metadata.h"
9#include "kis_kra_tags.h"
12
13#include <QApplication>
14#include <QMessageBox>
15#include <QDomDocument>
16#include <QDomElement>
17#include <QString>
18#include <QStringList>
19
20#include <QUrl>
21#include <QBuffer>
22
23#include <KoDocumentInfo.h>
25#include <KoColorSpace.h>
26#include <KoColorProfile.h>
27#include <KoColor.h>
28#include <KoColorSet.h>
29#include <KoStore.h>
30#include <KoStoreDevice.h>
31#include <KisResourceTypes.h>
32#include <KisResourceModel.h>
33#include <kis_annotation.h>
34#include <kis_image.h>
37#include <kis_group_layer.h>
38#include <kis_layer.h>
42#include "kis_png_converter.h"
44#include <kis_time_span.h>
45#include "KisDocument.h"
46#include <string>
47#include "kis_dom_utils.h"
48#include "kis_grid_config.h"
49#include "kis_guides_config.h"
52
53#include <KisMirrorAxisConfig.h>
54
55#include <QFileInfo>
56#include <QDir>
57
58
59using namespace KRA;
60
79
80KisKraSaver::KisKraSaver(KisDocument* document, const QString &filename, bool addMergedImage)
81 : m_d(new Private)
82{
83 m_d->doc = document;
84 m_d->filename = filename;
85 m_d->addMergedImage = addMergedImage;
86 m_d->linkedDocumentResources = document->linkedDocumentResources();
87
88 m_d->imageName = m_d->doc->documentInfo()->aboutInfo("title");
89 if (m_d->imageName.isEmpty()) {
90 m_d->imageName = i18n("Unnamed");
91 }
92}
93
95{
96 delete m_d;
97}
98
99QDomElement KisKraSaver::saveXML(QDomDocument& doc, KisImageSP image)
100{
101 QDomElement imageElement = doc.createElement("IMAGE");
102
103 Q_ASSERT(image);
104 imageElement.setAttribute(NAME, m_d->imageName);
105 imageElement.setAttribute(MIME, NATIVE_MIMETYPE);
106 imageElement.setAttribute(WIDTH, KisDomUtils::toString(image->width()));
107 imageElement.setAttribute(HEIGHT, KisDomUtils::toString(image->height()));
108 imageElement.setAttribute(COLORSPACE_NAME, image->colorSpace()->id());
109 imageElement.setAttribute(DESCRIPTION, m_d->doc->documentInfo()->aboutInfo("comment"));
110 // XXX: Save profile as blob inside the image, instead of the product name.
111 if (image->profile() && image->profile()-> valid()) {
112 imageElement.setAttribute(PROFILE, image->profile()->name());
113 }
114 imageElement.setAttribute(X_RESOLUTION, KisDomUtils::toString(image->xRes()*72.0));
115 imageElement.setAttribute(Y_RESOLUTION, KisDomUtils::toString(image->yRes()*72.0));
116
117 //now the proofing options:
118 if (image->proofingConfiguration()) {
119 imageElement.setAttribute(PROOFINGPROFILENAME,
120 KisDomUtils::toString(image->proofingConfiguration()->proofingProfile));
121 imageElement.setAttribute(PROOFINGMODEL, KisDomUtils::toString(image->proofingConfiguration()->proofingModel));
122 imageElement.setAttribute(PROOFINGDEPTH, KisDomUtils::toString(image->proofingConfiguration()->proofingDepth));
123 imageElement.setAttribute(PROOFINGINTENT,
124 KisDomUtils::toString(image->proofingConfiguration()->conversionIntent));
125 imageElement.setAttribute(PROOFINGDISPLAYINTENT,
126 KisDomUtils::toString(image->proofingConfiguration()->displayIntent));
127 bool bcp = image->proofingConfiguration()->useBlackPointCompensationFirstTransform;
128 imageElement.setAttribute(PROOFINGBLACKPOINTCOMPENSATION, bcp ? "true" : "false");
129 bcp = image->proofingConfiguration()->displayFlags.testFlag(
131 imageElement.setAttribute(PROOFINGDISPLAYBLACKPOINTCOMPENSATION, bcp ? "true" : "false");
132 const QString mode = [&]() {
133 switch (image->proofingConfiguration()->displayMode) {
135 return "monitor";
137 return "paper";
139 return "custom";
140 }
141 Q_UNREACHABLE_RETURN("custom");
142 }();
143 imageElement.setAttribute(PROOFINGDISPLAYMODE, mode);
144 imageElement.setAttribute(PROOFINGADAPTATIONSTATE,
145 KisDomUtils::toString(image->proofingConfiguration()->legacyAdaptationState()));
146 }
147
148 quint32 count = 1; // We don't save the root layer, but it does count
149 KisSaveXmlVisitor visitor(doc, imageElement, count, m_d->filename, true);
151
152 image->rootLayer()->accept(visitor);
153 m_d->errorMessages.append(visitor.errorMessages());
154
155 m_d->nodeFileNames = visitor.nodeFileNames();
157
158 saveHDRMetadata(doc, imageElement, image);
159 saveBackgroundColor(doc, imageElement, image);
160 saveAssistantsGlobalColor(doc, imageElement);
161 saveWarningColor(doc, imageElement, image);
162 saveCompositions(doc, imageElement, image);
163 saveAssistantsList(doc, imageElement);
164 saveGrid(doc, imageElement);
165 saveGuides(doc, imageElement);
166 saveMirrorAxis(doc, imageElement);
167 saveColorHistory(doc, imageElement);
168 saveResourcesToXML(doc, imageElement);
169
170 // Redundancy -- Save animation metadata in XML to prevent data loss for the time being...
171 QDomElement animationElement = doc.createElement("animation");
172 KisDomUtils::saveValue(&animationElement, "framerate", image->animationInterface()->framerate());
173 KisDomUtils::saveValue(&animationElement, "range", image->animationInterface()->documentPlaybackRange());
174 KisDomUtils::saveValue(&animationElement, "currentTime", image->animationInterface()->currentUITime());
175 imageElement.appendChild(animationElement);
176
177 vKisAnnotationSP_it beginIt = image->beginAnnotations();
178 vKisAnnotationSP_it endIt = image->endAnnotations();
179
180 if (beginIt != endIt) {
181 QDomElement annotationsElement = doc.createElement(ANNOTATIONS);
182 vKisAnnotationSP_it it = beginIt;
183 while (it != endIt) {
184 if (!(*it) || (*it)->type().isEmpty()) {
185 it++;
186 continue;
187 }
188 QString type = (*it)->type();
189
190 if (!m_d->specialAnnotations.contains(type)) {
191
192 QString description = (*it)->description();
193 QDomElement annotationElement = doc.createElement(ANNOTATION);
194 annotationsElement.appendChild(annotationElement);
195 annotationElement.setAttribute("type", type);
196 annotationElement.setAttribute("description", description);
197 }
198 it++;
199 }
200 imageElement.appendChild(annotationsElement);
201 }
202
203
204 return imageElement;
205}
206
207bool KisKraSaver::saveResources(KoStore *store, KisImageSP image, const QString &uri)
208{
209 Q_UNUSED(image);
210 Q_UNUSED(uri);
211
213
214 Q_FOREACH (const KoResourceLoadResult &result, embeddedResources) {
216
217 if (result.type() == KoResourceLoadResult::FailedLink) {
218 m_d->warningMessages << i18nc("Error message when saving a .kra file", "Could not export resource for embedding: %1", result.signature().filename);
219 continue;
220 }
221
222 KoEmbeddedResource resource = result.embeddedResource();
223
224 QString path = RESOURCE_PATH + "/" + resource.signature().type;
225
226 if (resource.signature().type == ResourceType::Palettes) {
227 path = m_d->imageName + PALETTE_PATH;
228 }
229
230 const QString fileName = resource.signature().filename;
231
232 if (!store->open(path + '/' + fileName)) {
233 m_d->warningMessages << i18nc("Error message when saving a .kra file", "Could not write resource: %1", result.signature().filename);
234 continue;
235 }
236
237 // we first read into a buffer to make sure the save operation is transactional,
238 // that is, either resource is saves correctly, or the file is left empty.
239 QByteArray ba = resource.data();
240
241 qint64 nwritten = 0;
242 if (!ba.isEmpty()) {
243 nwritten = store->write(ba);
244 } else {
245 m_d->warningMessages << i18nc("Error message when saving a .kra file", "Written resource is empty: %1", result.signature().filename);
246 }
247
248 store->close();
249
250 if (nwritten != ba.size()) {
251 m_d->warningMessages << i18nc("Error message when saving a .kra file", "Written resource is incomplete: %1", result.signature().filename);
252 }
253 }
254
255 return true;
256}
257
258bool KisKraSaver::saveStoryboard(KoStore *store, KisImageSP image, const QString &uri)
259{
260 Q_UNUSED(image);
261 Q_UNUSED(uri);
262
263 bool success = true;
264 if (m_d->doc->getStoryboardItemList().count() == 0) {
265 return true;
266 } else {
267 if (!store->open(m_d->imageName + STORYBOARD_PATH + "index.xml")) {
268 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save storyboards.");
269 return false;
270 }
271
272 QDomDocument storyboardDocument = m_d->doc->createDomDocument("storyboard-info", "1.1");
273 QDomElement root = storyboardDocument.documentElement();
274 saveStoryboardToXML(storyboardDocument, root);
275
276 QByteArray ba = storyboardDocument.toByteArray();
277 qint64 nwritten = 0;
278 if (!ba.isEmpty()) {
279 nwritten = store->write(ba);
280 } else {
281 success = false;
282 qWarning() << "Could not save storyboard data to a byte array!";
283 }
284
285 bool r = store->close();
286 success = success && r && (nwritten == ba.size());
287 }
288
289 if (!success) {
290 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save storyboards.");
291 return false;
292 }
293
294 return success;
295}
296
297bool KisKraSaver::saveAnimationMetadata(KoStore *store, KisImageSP image, const QString &uri)
298{
299 Q_UNUSED(uri);
300
301 if (!store->open(m_d->imageName + ANIMATION_METADATA_PATH + "index.xml")) {
302 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save animation meta data.");
303 return false;
304 }
305
306 QDomDocument animationDocument = m_d->doc->createDomDocument("animation-metadata", "1.1");
307 QDomElement root = animationDocument.documentElement();
308 saveAnimationMetadataToXML(animationDocument, root, image);
309
310 bool success = true;
311
312 QByteArray ba = animationDocument.toByteArray();
313 qint64 nwritten = 0;
314 if (!ba.isEmpty()) {
315 nwritten = store->write(ba);
316 } else {
317 qWarning() << "Could not save animation meta data to a byte array!";
318 success = false;
319 }
320
321 bool r = store->close();
322
323 success = success && r && (nwritten == ba.size());
324
325 if (!success) {
326 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save animation meta data.");
327 return false;
328 }
329
330 return true;
331}
332
334{
335 if (m_d->doc->getAudioTracks().isEmpty())
336 return true;
337
338 if (!store->open(m_d->imageName + AUDIO_PATH + "index.xml")) {
339 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save audio meta data.");
340 return false;
341 }
342
343 QDomDocument audioDocument = m_d->doc->createDomDocument("audio-info", "1.1");
344 QDomElement root = audioDocument.documentElement();
345 saveAudioXML(audioDocument, root);
346
347 bool success = true;
348 QByteArray byteArray = audioDocument.toByteArray();
349 qint64 bytesWriteCount = 0;
350 if (!byteArray.isEmpty()) {
351 bytesWriteCount = store->write(byteArray);
352 } else {
353 qWarning() << "Could not save audio data to a byte array!";
354 success = false;
355 }
356
357 bool closeOK = store->close();
358
359 success = success && closeOK && (bytesWriteCount == byteArray.size());
360
361 if (!success) {
362 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save audio meta data.");
363 return false;
364 }
365
366 return true;
367}
368
369void KisKraSaver::saveResourcesToXML(QDomDocument &doc, QDomElement &element)
370{
371 QDomElement ePalette = doc.createElement(PALETTES);
372 QDomElement eResources = doc.createElement(RESOURCES);
373
374 Q_FOREACH (const KoResourceLoadResult resource, m_d->linkedDocumentResources) {
375 // all warnings will be issued in KisKraSaver::saveResources()
376 if (resource.type() != KoResourceLoadResult::EmbeddedResource) continue;
377
378 KoResourceSignature sig = resource.signature();
379
380 QDomElement eResource = doc.createElement("resource");
381 eResource.setAttribute("type", sig.type);
382 eResource.setAttribute("name", sig.name);
383 eResource.setAttribute("filename", sig.filename);
384 eResource.setAttribute("md5sum", sig.md5sum);
385
386 if (sig.type == ResourceType::Palettes) {
387 ePalette.appendChild(eResource);
388 }
389 else {
390 eResources.appendChild(eResource);
391
392 }
393 }
394 element.appendChild(ePalette);
395 element.appendChild(eResources);
396}
397
398void KisKraSaver::saveStoryboardToXML(QDomDocument& doc, QDomElement &element)
399{
400 //saving storyboard comments
401 QDomElement eCommentList = doc.createElement("StoryboardCommentList");
403 QDomElement commentElement = doc.createElement("storyboardcomment");
404 commentElement.setAttribute("name", comment.name);
405 commentElement.setAttribute("visibility", comment.visibility);
406 eCommentList.appendChild(commentElement);
407 }
408 element.appendChild(eCommentList);
409
410 //saving storyboard items
411 QDomElement eItemList = doc.createElement("StoryboardItemList");
413 QDomElement eItem = item->toXML(doc);
414 eItemList.appendChild(eItem);
415 }
416 element.appendChild(eItemList);
417}
418
419void KisKraSaver::saveAnimationMetadataToXML(QDomDocument &doc, QDomElement &element, KisImageSP image)
420{
421 KisDomUtils::saveValue(&element, "framerate", image->animationInterface()->framerate());
423 KisDomUtils::saveValue(&element, "currentTime", image->animationInterface()->currentUITime());
424
425 {
426 QDomElement exportItemElem = doc.createElement("export-settings");
427 KisDomUtils::saveValue(&exportItemElem, "sequenceFilePath", image->animationInterface()->exportSequenceFilePath());
428 KisDomUtils::saveValue(&exportItemElem, "sequenceBaseName", image->animationInterface()->exportSequenceBaseName());
429 KisDomUtils::saveValue(&exportItemElem, "sequenceInitialFrameNumber", image->animationInterface()->exportInitialFrameNumber());
430 element.appendChild(exportItemElem);
431 }
432}
433
434bool KisKraSaver::saveKeyframes(KoStore *store, const QString &uri, bool external)
435{
436 QMap<const KisNode*, QString>::iterator it;
437
438 for (it = m_d->keyframeFilenames.begin(); it != m_d->keyframeFilenames.end(); it++) {
439 const KisNode *node = it.key();
440 QString filename = it.value();
441
442 QString location =
443 (external ? QString() : uri)
444 + m_d->imageName + LAYER_PATH + filename;
445
446 if (!saveNodeKeyframes(store, location, node)) {
447 return false;
448 }
449 }
450
451 return true;
452}
453
454bool KisKraSaver::saveNodeKeyframes(KoStore *store, QString location, const KisNode *node)
455{
456 QDomDocument doc = KisDocument::createDomDocument("krita-keyframes", "keyframes", "1.0");
457 QDomElement root = doc.documentElement();
458
459 KisKeyframeChannel *channel;
460 Q_FOREACH (channel, node->keyframeChannels()) {
461 QDomElement element = channel->toXML(doc, m_d->nodeFileNames[node]);
462 root.appendChild(element);
463 }
464
465 bool success = true;
466 if (store->open(location)) {
467 QByteArray xml = doc.toByteArray();
468 qint64 nwritten = store->write(xml);
469 bool r = store->close();
470 success = r && (nwritten == xml.size());
471 } else {
472 success = false;
473 }
474 if (!success) {
475 m_d->errorMessages << i18nc("Error message on saving a .kra file", "Could not save keyframes.");
476 return false;
477 }
478
479 return true;
480}
481
482bool KisKraSaver::saveBinaryData(KoStore* store, KisImageSP image, const QString &uri, bool external, bool addMergedImage)
483{
484 QString location;
485
486 // Save the layers data
488
489 if (external)
490 visitor.setExternalUri(uri);
491
492 image->rootLayer()->accept(visitor);
493
494 m_d->errorMessages.append(visitor.errorMessages());
495 if (!m_d->errorMessages.isEmpty()) {
496 return false;
497 }
498
499 bool success = true;
500 bool r = true;
501 qint64 nwritten = 0;
502
503 // saving annotations
504 bool savingAnnotationsSuccess = true;
505 KisAnnotationSP annotation = image->annotation("exif");
506 if (annotation) {
507 location = external ? QString() : uri;
508 location += m_d->imageName + EXIF_PATH;
509 if (store->open(location)) {
510 nwritten = store->write(annotation->annotation());
511 r = store->close();
512 savingAnnotationsSuccess = savingAnnotationsSuccess && (nwritten == annotation->annotation().size()) && r;
513 } else {
514 savingAnnotationsSuccess = false;
515 }
516 }
517
518 if (!savingAnnotationsSuccess) {
519 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save annotations."));
520 }
521
522 success = success && savingAnnotationsSuccess;
523
524 bool savingImageProfileSuccess = true;
525 if (image->profile()) {
526 const KoColorProfile *profile = image->profile();
527 KisAnnotationSP annotation;
528 if (profile) {
529 QByteArray profileRawData = profile->rawData();
530 if (!profileRawData.isEmpty()) {
531 if (profile->type() == "icc") {
532 annotation = new KisAnnotation(ICC, profile->name(), profile->rawData());
533 } else {
534 annotation = new KisAnnotation(PROFILE, profile->name(), profile->rawData());
535 }
536 }
537 }
538
539 if (annotation) {
540 location = external ? QString() : uri;
541 location += m_d->imageName + ICC_PATH;
542 if (store->open(location)) {
543 nwritten = store->write(annotation->annotation());
544 r = store->close();
545 savingImageProfileSuccess = savingImageProfileSuccess && (nwritten == annotation->annotation().size()) && r;
546 } else {
547 savingImageProfileSuccess = false;
548 }
549 }
550 }
551
552 if (!savingImageProfileSuccess) {
553 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save image profile."));
554 }
555 success = success && savingImageProfileSuccess;
556
557 //This'll embed the profile used for proofing into the kra file.
558 bool savingSoftproofingProfileSuccess = true;
559 if (image->proofingConfiguration()) {
560 const KoColorProfile *proofingProfile =
562 if (proofingProfile && proofingProfile->valid()) {
563 QByteArray proofingProfileRaw = proofingProfile->rawData();
564 if (!proofingProfileRaw.isEmpty()) {
565 annotation = new KisAnnotation(ICCPROOFINGPROFILE, proofingProfile->name(), proofingProfile->rawData());
566 }
567 }
568 if (annotation) {
569 location = external ? QString() : uri;
570 location += m_d->imageName + ICC_PROOFING_PATH;
571 if (store->open(location)) {
572 nwritten = store->write(annotation->annotation());
573 r = store->close();
574 savingSoftproofingProfileSuccess =
575 savingSoftproofingProfileSuccess && (nwritten == annotation->annotation().size()) && r;
576 } else {
577 savingSoftproofingProfileSuccess = false;
578 }
579 }
580 }
581
582 if (!savingSoftproofingProfileSuccess) {
583 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save softproofing color profile."));
584 }
585
586 success = success && savingSoftproofingProfileSuccess;
587
588 // Save the remaining annotations
589 vKisAnnotationSP_it beginIt = image->beginAnnotations();
590 vKisAnnotationSP_it endIt = image->endAnnotations();
591
592 bool savingRemainingAnnotationsSuccess = true;
593 if (beginIt != endIt) {
594 vKisAnnotationSP_it it = beginIt;
595 while (it != endIt) {
596 if (!(*it) || (*it)->type().isEmpty()) {
597 it++;
598 continue;
599 }
600 QString type = (*it)->type();
601
602 if (!m_d->specialAnnotations.contains(type)) {
603 location = external ? QString() : uri;
604 location += m_d->imageName + ANNOTATIONS_PATH + type;
605 if (store->open(location)) {
606 nwritten = store->write((*it)->annotation());
607 r = store->close();
608 savingRemainingAnnotationsSuccess = savingRemainingAnnotationsSuccess && (nwritten == (*it)->annotation().size()) && r;
609 } else {
610 savingRemainingAnnotationsSuccess = false;
611 }
612 }
613 it++;
614 }
615 }
616
617 if (!savingRemainingAnnotationsSuccess) {
618 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save additional annotations."));
619 }
620
621 success = success && savingRemainingAnnotationsSuccess;
622
623 bool savingLayerStylesSuccess = true;
624 {
626 QVector<KisPSDLayerStyleSP> stylesClones = serializer.collectAllLayerStyles(image->root());
627 if (stylesClones.size() > 0) {
628 location = external ? QString() : uri;
629 location += m_d->imageName + LAYER_STYLES_PATH;
630
631 if (store->open(location)) {
632 QBuffer aslBuffer;
633 if (aslBuffer.open(QIODevice::WriteOnly)) {
634 serializer.setStyles(stylesClones);
635 serializer.saveToDevice(aslBuffer);
636 aslBuffer.close();
637 nwritten = store->write(aslBuffer.buffer());
638 savingLayerStylesSuccess = savingLayerStylesSuccess && (nwritten == aslBuffer.buffer().size());
639 } else {
640 savingLayerStylesSuccess = false;
641 }
642 r = store->close();
643 savingLayerStylesSuccess = savingLayerStylesSuccess && r;
644 } else {
645 savingLayerStylesSuccess = false;
646 }
647 }
648 }
649
650 if (!savingLayerStylesSuccess) {
651 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save layer styles."));
652 }
653
654 success = success && savingLayerStylesSuccess;
655
656 bool savingMergedImageSuccess = true;
657 if (addMergedImage) {
658 KisPaintDeviceSP dev = image->projection();
659 store->setCompressionEnabled(false);
660 r = KisPNGConverter::saveDeviceToStore("mergedimage.png", image->bounds(), image->xRes(), image->yRes(), dev, store);
661 savingMergedImageSuccess = savingMergedImageSuccess && r;
662 store->setCompressionEnabled(KisConfig(true).compressKra());
663 }
664
665 if (!savingMergedImageSuccess) {
666 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save merged image."));
667 }
668
669 success = success && savingMergedImageSuccess;
670
671 r = saveAssistants(store, uri,external);
672 success = success && r;
673
674 return success;
675}
676
677
678
683
688
689void KisKraSaver::saveBackgroundColor(QDomDocument& doc, QDomElement& element, KisImageSP image)
690{
691 QDomElement e = doc.createElement(CANVASPROJECTIONCOLOR);
692 KoColor color = image->defaultProjectionColor();
693 QByteArray colorData = QByteArray::fromRawData((const char*)color.data(), color.colorSpace()->pixelSize());
694 e.setAttribute(COLORBYTEDATA, QString(colorData.toBase64()));
695 element.appendChild(e);
696}
697
698void KisKraSaver::saveColorHistory(QDomDocument &doc, QDomElement &element)
699{
700 QDomElement colorsElement = doc.createElement(COLORHISTORY);
701 saveKoColors(doc, colorsElement, m_d->doc->colorHistoryColors());
702
703 element.appendChild(colorsElement);
704}
705
706void KisKraSaver::saveHDRMetadata(QDomDocument &doc, QDomElement &element, KisImageSP image)
707{
709 return;
710 }
711 QDomElement hdr = doc.createElement(HDRMETADATA);
712 if (image->hdrReferenceWhiteLightLevel()) {
714 }
717 QDomElement clli = doc.createElement(CONTENTLIGHTLEVEL);
721 clli.setAttribute(CLLI_CALCTYPE, CLLI_CALC_XYZY);
723 clli.setAttribute(CLLI_CALCTYPE, CLLI_CALC_Rec2020);
725 clli.setAttribute(CLLI_CALCTYPE, CLLI_CALC_RGB_XYZ_FALLBACK);
726 }
727 hdr.appendChild(clli);
728 }
729 if (image->colorVolumeInformation()) {
731 QDomElement cvi = doc.createElement(COLORVOLUME);
734
735 KisDomUtils::saveValue(&cvi, COLORVOLUMERED, cv.red.asVector().toPointF());
737 KisDomUtils::saveValue(&cvi, COLORVOLUMEBLUE, cv.blue.asVector().toPointF());
739
740 hdr.appendChild(cvi);
741 }
742 element.appendChild(hdr);
743}
744
745void KisKraSaver::saveAssistantsGlobalColor(QDomDocument& doc, QDomElement& element)
746{
747 QDomElement e = doc.createElement(GLOBALASSISTANTSCOLOR);
749 e.setAttribute(SIMPLECOLORDATA, QString(colorString));
750 element.appendChild(e);
751}
752
753void KisKraSaver::saveWarningColor(QDomDocument& doc, QDomElement& element, KisImageSP image)
754{
755 if (image->proofingConfiguration()) {
756 QDomElement e = doc.createElement(PROOFINGWARNINGCOLOR);
757 KoColor color = image->proofingConfiguration()->warningColor;
758 color.toXML(doc, e);
759 element.appendChild(e);
760 }
761}
762
763void KisKraSaver::saveCompositions(QDomDocument& doc, QDomElement& element, KisImageSP image)
764{
765 if (!image->compositions().isEmpty()) {
766 QDomElement e = doc.createElement("compositions");
767 Q_FOREACH (KisLayerCompositionSP composition, image->compositions()) {
768 composition->save(doc, e);
769 }
770 element.appendChild(e);
771 }
772}
773
774bool KisKraSaver::saveAssistants(KoStore* store, QString uri, bool external)
775{
776 QString location;
777 QMap<QString, int> assistantcounters;
778 QByteArray data;
779
781 QMap<KisPaintingAssistantHandleSP, int> handlemap;
782
783 bool success = true;
784 if (!assistants.isEmpty()) {
785
786 Q_FOREACH (KisPaintingAssistantSP assist, assistants){
787 if (!assistantcounters.contains(assist->id())){
788 assistantcounters.insert(assist->id(),0);
789 }
790 location = external ? QString() : uri;
791 location += m_d->imageName + ASSISTANTS_PATH;
792 location += QString(assist->id()+"%1.assistant").arg(assistantcounters[assist->id()]);
793
794 data = assist->saveXml(handlemap);
795 if (store->open(location)) {
796 qint64 nwritten = store->write(data);
797 bool r = store->close();
798 success = success && r && (nwritten == data.size());
799 } else {
800 success = false;
801 }
802 assistantcounters[assist->id()]++;
803 }
804 }
805 if (!success) {
806 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save assistants."));
807 }
808 return true;
809}
810
811bool KisKraSaver::saveAssistantsList(QDomDocument& doc, QDomElement& element)
812{
813 int count_ellipse = 0,
814 count_twopoint = 0,
815 count_perspective = 0,
816 count_ruler = 0,
817 count_vanishingpoint = 0,
818 count_infiniteruler = 0,
819 count_parallelruler = 0,
820 count_concentricellipse = 0,
821 count_fisheyepoint = 0,
822 count_spline = 0,
823 count_perspectiveellipse = 0,
824 count_curvilinearperspective = 0;
826 if (!assistants.isEmpty()) {
827 QDomElement assistantsElement = doc.createElement("assistants");
828 Q_FOREACH (KisPaintingAssistantSP assist, assistants){
829 if (assist->id() == "ellipse"){
830 assist->saveXmlList(doc, assistantsElement, count_ellipse);
831 count_ellipse++;
832 }
833 else if (assist->id() == "spline"){
834 assist->saveXmlList(doc, assistantsElement, count_spline);
835 count_spline++;
836 }
837 else if (assist->id() == "perspective"){
838 assist->saveXmlList(doc, assistantsElement, count_perspective);
839 count_perspective++;
840 }
841 else if (assist->id() == "vanishing point"){
842 assist->saveXmlList(doc, assistantsElement, count_vanishingpoint);
843 count_vanishingpoint++;
844 }
845 else if (assist->id() == "infinite ruler"){
846 assist->saveXmlList(doc, assistantsElement, count_infiniteruler);
847 count_infiniteruler++;
848 }
849 else if (assist->id() == "parallel ruler"){
850 assist->saveXmlList(doc, assistantsElement, count_parallelruler);
851 count_parallelruler++;
852 }
853 else if (assist->id() == "concentric ellipse"){
854 assist->saveXmlList(doc, assistantsElement, count_concentricellipse);
855 count_concentricellipse++;
856 }
857 else if (assist->id() == "fisheye-point"){
858 assist->saveXmlList(doc, assistantsElement, count_fisheyepoint);
859 count_fisheyepoint++;
860 }
861 else if (assist->id() == "two point"){
862 assist->saveXmlList(doc, assistantsElement, count_twopoint);
863 count_twopoint++;
864 }
865 else if (assist->id() == "ruler"){
866 assist->saveXmlList(doc, assistantsElement, count_ruler);
867 count_ruler++;
868 }
869 else if (assist->id() == "perspective ellipse"){
870 assist->saveXmlList(doc, assistantsElement, count_perspectiveellipse);
871 count_perspectiveellipse++;
872 }
873 else if (assist->id() == "curvilinear-perspective"){
874 assist->saveXmlList(doc, assistantsElement, count_curvilinearperspective);
875 count_curvilinearperspective++;
876 }
877 }
878 element.appendChild(assistantsElement);
879 }
880 return true;
881}
882
883bool KisKraSaver::saveGrid(QDomDocument& doc, QDomElement& element)
884{
885 KisGridConfig config = m_d->doc->gridConfig();
886
887 if (!config.isDefault()) {
888 QDomElement gridElement = config.saveDynamicDataToXml(doc, "grid");
889 element.appendChild(gridElement);
890 }
891
892 return true;
893}
894
895bool KisKraSaver::saveGuides(QDomDocument& doc, QDomElement& element)
896{
897 KisGuidesConfig guides = m_d->doc->guidesConfig();
898
899 if (!guides.isDefault()) {
900 QDomElement guidesElement = guides.saveToXml(doc, "guides");
901 element.appendChild(guidesElement);
902 }
903
904 return true;
905}
906
907bool KisKraSaver::saveMirrorAxis(QDomDocument &doc, QDomElement &element)
908{
909 KisMirrorAxisConfig mirrorAxisConfig = m_d->doc->mirrorAxisConfig();
910
911 if (!mirrorAxisConfig.isDefault()) {
912 QDomElement mirrorAxisElement = mirrorAxisConfig.saveToXml(doc, MIRROR_AXIS);
913 element.appendChild(mirrorAxisElement);
914 }
915
916 return true;
917}
918
919bool KisKraSaver::saveAudioXML(QDomDocument& doc, QDomElement& element)
920{
922 const qreal volume = m_d->doc->getAudioLevel();
923
924 if (!clips.isEmpty()) {
925 QDomElement audioClips = doc.createElement("audioClips");
926 Q_FOREACH(const QFileInfo& file, clips) {
927 QDomElement clip = doc.createElement(QString("Clip"));
928 clip.setAttribute("filePath", file.absoluteFilePath());
929 clip.setAttribute("volume", volume);
930 audioClips.appendChild(clip);
931 }
932 element.appendChild(audioClips);
933 }
934
935 return true;
936}
937
938bool KisKraSaver::saveKoColors(QDomDocument &doc, QDomElement &colorsElement,
939 const QList<KoColor> &colors) const
940{
941 // Writes like <colors><RGB ../><RGB .. /> ... </colors>
942 Q_FOREACH(const KoColor & color, colors) {
943 color.toXML(doc, colorsElement);
944 }
945 return true;
946}
947
const QString DESCRIPTION
A data extension mechanism for Krita.
static QVector< KisPSDLayerStyleSP > collectAllLayerStyles(KisNodeSP root)
void setStyles(const QVector< KisPSDLayerStyleSP > &styles)
KisMirrorAxisConfig mirrorAxisConfig
KoDocumentInfo * documentInfo() const
QList< KoColor > colorHistoryColors() const
QDomDocument createDomDocument(const QString &tagName, const QString &version) const
qreal getAudioLevel()
KisGridConfig gridConfig
QColor assistantsGlobalColor()
StoryboardItemList getStoryboardItemList()
returns the list of pointers to storyboard Items for the document
KisNodeWSP preActivatedNode
QList< KisPaintingAssistantSP > assistants
QVector< StoryboardComment > getStoryboardCommentsList()
returns the list of comments for the storyboard docker in the document
QVector< QFileInfo > getAudioTracks() const
KisGuidesConfig guidesConfig
QDomElement saveDynamicDataToXml(QDomDocument &doc, const QString &tag) const
bool isDefault() const
QDomElement saveToXml(QDomDocument &doc, const QString &tag) const
const KisTimeSpan & documentPlaybackRange() const
documentPlaybackRange
std::optional< KisRelativeContentLightLevelInformation > relativeContentLightLevelInformation() const
relativeContentLightLevelInformation This returns (optionally) a KisRelativeContentLightLevelInformat...
vKisAnnotationSP_it endAnnotations()
KisGroupLayerSP rootLayer() const
const KoColorSpace * colorSpace() const
KisImageAnimationInterface * animationInterface() const
KisAnnotationSP annotation(const QString &type)
KisPaintDeviceSP projection() const
qint32 width() const
std::optional< double > hdrReferenceWhiteLightLevel() const
HDR Reference White livel.
double xRes() const
double yRes() const
qint32 height() const
QList< KisLayerCompositionSP > compositions()
QRect bounds() const override
KoColor defaultProjectionColor() const
std::optional< KisColorVolumeInformation > colorVolumeInformation() const
colorVolumeInformation
const KoColorProfile * profile() const
vKisAnnotationSP_it beginAnnotations()
KisProofingConfigurationSP proofingConfiguration() const
proofingConfiguration
KisKeyframeChannel stores and manages KisKeyframes. Maps units of time to virtual keyframe values....
virtual QDomElement toXML(QDomDocument doc, const QString &layerFilename)
QStringList errorMessages() const
void setExternalUri(const QString &uri)
bool saveKeyframes(KoStore *store, const QString &uri, bool external)
void saveStoryboardToXML(QDomDocument &doc, QDomElement &element)
Private *const m_d
bool saveNodeKeyframes(KoStore *store, QString location, const KisNode *node)
bool saveMirrorAxis(QDomDocument &doc, QDomElement &element)
bool saveAssistants(KoStore *store, QString uri, bool external)
QStringList errorMessages() const
QDomElement saveXML(QDomDocument &doc, KisImageSP image)
bool saveAnimationMetadata(KoStore *store, KisImageSP image, const QString &uri)
void saveResourcesToXML(QDomDocument &doc, QDomElement &element)
bool saveAudioXML(QDomDocument &doc, QDomElement &element)
bool saveResources(KoStore *store, KisImageSP image, const QString &uri)
bool saveBinaryData(KoStore *store, KisImageSP image, const QString &uri, bool external, bool addMergedImage)
bool saveAudio(KoStore *store)
void saveHDRMetadata(QDomDocument &doc, QDomElement &element, KisImageSP image)
void saveWarningColor(QDomDocument &doc, QDomElement &element, KisImageSP image)
bool saveKoColors(QDomDocument &doc, QDomElement &element, const QList< KoColor > &colors) const
void saveColorHistory(QDomDocument &doc, QDomElement &element)
bool saveGrid(QDomDocument &doc, QDomElement &element)
bool saveGuides(QDomDocument &doc, QDomElement &element)
KisKraSaver(KisDocument *document, const QString &filename, bool addMergedImage=true)
void saveBackgroundColor(QDomDocument &doc, QDomElement &element, KisImageSP image)
bool saveStoryboard(KoStore *store, KisImageSP image, const QString &uri)
QStringList warningMessages() const
void saveCompositions(QDomDocument &doc, QDomElement &element, KisImageSP image)
bool saveAssistantsList(QDomDocument &doc, QDomElement &element)
void saveAssistantsGlobalColor(QDomDocument &doc, QDomElement &element)
void saveAnimationMetadataToXML(QDomDocument &doc, QDomElement &element, KisImageSP image)
The KisMirrorAxisConfig class stores configuration for the KisMirrorAxis canvas decoration....
QDomElement saveToXml(QDomDocument &doc, const QString &tag) const
saveToXml() function for KisKraSaver
bool isDefault() const
Check whether the config object was changed, or is the class default.
static bool saveDeviceToStore(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP dev, KoStore *store, KisMetaData::Store *metaData=0)
saveDeviceToStore saves the given paint device to the KoStore. If the device is not 8 bits sRGB,...
@ Custom
Let artists configure their own.
@ Paper
Whether to use Paper settings (absolute colorimetric, 0% adaptation.)
@ Monitor
Whether to use monitor rendering intent and flags for the second transform.
QMap< const KisNode *, QString > keyframeFileNames()
QMap< const KisNode *, QString > nodeFileNames()
QStringList errorMessages() const
void setSelectedNodes(vKisNodeSP selectedNodes)
virtual quint32 pixelSize() const =0
void toXML(QDomDocument &doc, QDomElement &colorElt) const
Definition KoColor.cpp:304
quint8 * data()
Definition KoColor.h:144
const KoColorSpace * colorSpace() const
return the current colorSpace
Definition KoColor.h:82
QString aboutInfo(const QString &info) const
const KoResourceSignature & signature() const
QByteArray data() const
KoEmbeddedResource embeddedResource() const noexcept
KoResourceSignature signature() const
A simple wrapper object for the main information about the resource.
qint64 write(const QByteArray &data)
Definition KoStore.cpp:198
bool close()
Definition KoStore.cpp:156
virtual void setCompressionEnabled(bool e)
Definition KoStore.cpp:403
bool open(const QString &name)
Definition KoStore.cpp:109
This file is part of the Krita application in calligra.
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
vKisAnnotationSP::iterator vKisAnnotationSP_it
Definition kis_types.h:181
const QString LAYER_PATH
const QString COLORVOLUMEBLUE
const QString CLLI_CALC_RGB_XYZ_FALLBACK
const QString CLLI_CALC_Rec2020
const QString PALETTES
const QString PROOFINGPROFILENAME
const QString COLORHISTORY
const QString CANVASPROJECTIONCOLOR
const QString COLORVOLUMERED
const QString COLORBYTEDATA
const QString PROOFINGBLACKPOINTCOMPENSATION
const QString NAME
const QString COLORVOLUMEMAXLUMINANCE
const QString ICC_PROOFING_PATH
const QString Y_RESOLUTION
const QString HEIGHT
const QString ASSISTANTS_PATH
const QString PROOFINGDEPTH
const QString ANIMATION_METADATA_PATH
const QString NATIVE_MIMETYPE
const QString LAYER_STYLES_PATH
const QString X_RESOLUTION
const QString STORYBOARD_PATH
const QString RESOURCES
const QString ICC
const QString ANNOTATIONS_PATH
const QString PROOFINGDISPLAYBLACKPOINTCOMPENSATION
const QString PROOFINGDISPLAYMODE
const QString MAXCLL
const QString HDRREFERENCEWHITE
const QString CONTENTLIGHTLEVEL
const QString ICC_PATH
const QString PROOFINGWARNINGCOLOR
const QString COLORVOLUMEGREEN
const QString COLORVOLUME
const QString PROOFINGDISPLAYINTENT
const QString CLLI_CALC_XYZY
const QString WIDTH
const QString EXIF_PATH
const QString MIME
const QString GLOBALASSISTANTSCOLOR
const QString ANNOTATIONS
const QString COLORVOLUMEWHITE
const QString RESOURCE_PATH
const QString PROOFINGMODEL
const QString AUDIO_PATH
const QString PALETTE_PATH
const QString COLORVOLUMEMINLUMINANCE
const QString PROOFINGINTENT
const QString HDRMETADATA
const QString ICCPROOFINGPROFILE
const QString ANNOTATION
const QString MIRROR_AXIS
const QString MAXFALL
const QString SIMPLECOLORDATA
const QString PROOFINGADAPTATIONSTATE
const QString PROFILE
const QString COLORSPACE_NAME
const QString CLLI_CALCTYPE
void saveValue(QDomElement *parent, const QString &tag, const QSize &size)
QString qColorToQString(QColor color)
QString toString(const QString &value)
const QString Palettes
QMap< QString, KisKeyframeChannel * > keyframeChannels
The KisColorVolumeInformation class is a struct that represents the 'mastering' display....
double maxLuminance
Maximum screen brightness in cd/m²
KoColorimetryUtils::xy blue
xyY location of the blue colorant.
double minLuminance
Minimum screen brightness in cd/m²
KoColorimetryUtils::xy white
xyY location of the whitepoint.
KoColorimetryUtils::xy red
xyY location of the red colorant.
KoColorimetryUtils::xy green
xyY location of the green colorant.
bool accept(KisNodeVisitor &v) override
QMap< const KisNode *, QString > keyframeFilenames
QStringList warningMessages
QList< KoResourceLoadResult > linkedDocumentResources
QMap< const KisNode *, QString > nodeFileNames
QStringList specialAnnotations
@ XYZLuminance
Calculate luminance by converting to xyz.
@ Rec2020Component
Calculate luminance by converting to rec2020.
@ RGBComponent
Calculate luminance on the RGB components if possible.
double maxFrameAverageLightLevel
maxFrameAverage MaxFrameAverageLightLevel or MaxFALL is the average of average pixel brightnesses in ...
virtual QByteArray rawData() const
virtual QString type() const
virtual bool valid() const =0
const KoColorProfile * profileByName(const QString &name) const
static KoColorSpaceRegistry * instance()