Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_open_raster_stack_save_visitor.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2006-2007, 2009 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2023 Carsten Hartenfels <carsten.hartenfels@pm.me>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
9
10#include <math.h>
11
12#include <QDomElement>
13
15
17#include "filter/kis_filter.h"
19#include "kis_group_layer.h"
20#include "kis_paint_layer.h"
23#include <kis_clone_layer.h>
25
33
35 : d(new Private)
36{
37 d->saveContext = saveContext;
38 d->activeNodes = activeNodes;
39}
40
45
47{
48 elt.setAttribute("name", layer->name());
49 elt.setAttribute("opacity", QString().setNum(layer->opacity() / 255.0));
50 elt.setAttribute("visibility", layer->visible() ? "visible" : "hidden");
51
52 if (layer->inherits("KisGroupLayer")) {
53 // Workaround for the issue regarding ora specification.
54 // MyPaint treats layer's x and y relative to the group's x and y
55 // while Gimp and Krita think those are absolute values.
56 // Hence we set x and y on group layers to always be 0.
57 elt.setAttribute("x", QString().setNum(0));
58 elt.setAttribute("y", QString().setNum(0));
59
60 } else {
61 elt.setAttribute("x", QString().setNum(layer->exactBounds().x()));
62 elt.setAttribute("y", QString().setNum(layer->exactBounds().y()));
63 }
64
65 if (layer->userLocked()) {
66 elt.setAttribute("edit-locked", "true");
67 }
68 if (d->activeNodes.contains(layer)) {
69 elt.setAttribute("selected", "true");
70 }
71 QString compop = layer->compositeOpId();
72 if (layer->compositeOpId() == COMPOSITE_CLEAR) compop = "svg:clear";
73 else if (layer->compositeOpId() == COMPOSITE_ERASE) compop = "svg:dst-out";
74 else if (layer->compositeOpId() == COMPOSITE_DESTINATION_ATOP) compop = "svg:dst-atop";
75 else if (layer->compositeOpId() == COMPOSITE_DESTINATION_IN) compop = "svg:dst-in";
76 else if (layer->compositeOpId() == COMPOSITE_ADD) compop = "svg:plus";
77 else if (layer->compositeOpId() == COMPOSITE_MULT) compop = "svg:multiply";
78 else if (layer->compositeOpId() == COMPOSITE_SCREEN) compop = "svg:screen";
79 else if (layer->compositeOpId() == COMPOSITE_OVERLAY) compop = "svg:overlay";
80 else if (layer->compositeOpId() == COMPOSITE_DARKEN) compop = "svg:darken";
81 else if (layer->compositeOpId() == COMPOSITE_LIGHTEN) compop = "svg:lighten";
82 else if (layer->compositeOpId() == COMPOSITE_DODGE) compop = "svg:color-dodge";
83 else if (layer->compositeOpId() == COMPOSITE_BURN) compop = "svg:color-burn";
84 else if (layer->compositeOpId() == COMPOSITE_HARD_LIGHT) compop = "svg:hard-light";
85 else if (layer->compositeOpId() == COMPOSITE_SOFT_LIGHT_SVG) compop = "svg:soft-light";
86 else if (layer->compositeOpId() == COMPOSITE_DIFF) compop = "svg:difference";
87 else if (layer->compositeOpId() == COMPOSITE_COLOR) compop = "svg:color";
88 else if (layer->compositeOpId() == COMPOSITE_LUMINIZE) compop = "svg:luminosity";
89 else if (layer->compositeOpId() == COMPOSITE_HUE) compop = "svg:hue";
90 else if (layer->compositeOpId() == COMPOSITE_SATURATION) compop = "svg:saturation";
91 else if (layer->compositeOpId() == COMPOSITE_OVER) compop = "svg:src-over";
92
93 //else if (layer->compositeOpId() == COMPOSITE_EXCLUSION) compop = "svg:exclusion";
94 else compop = "krita:" + layer->compositeOpId();
95
96 // Alpha preserve has a special case with the Normal blend mode, which is
97 // stored as src-atop for compatibility with previous Krita versions that
98 // don't understand the alpha-preserve property, as well as other programs
99 // like MyPaint that don't support alpha preserve on layers in general.
100 if(layer->alphaChannelDisabled()) {
101 if (layer->compositeOpId() == COMPOSITE_OVER) {
102 compop = "svg:src-atop";
103 } else {
104 elt.setAttribute("alpha-preserve", "true");
105 }
106 }
107
108 elt.setAttribute("composite-op", compop);
109}
110
112{
113 return saveLayer(layer);
114}
115
117{
118 return saveLayer(layer);
119}
120
122{
123 QDomElement previousElt = d->currentElement;
124
125 QDomElement elt = d->layerStack.createElement("stack");
126 d->currentElement = elt;
127 saveLayerInfo(elt, layer);
128 QString isolate = "isolate";
129 if (layer->passThroughMode()) {
130 isolate = "auto";
131 }
132 elt.setAttribute("isolation", isolate);
133 visitAll(layer);
134
135 if (!previousElt.isNull()) {
136 previousElt.insertBefore(elt, QDomNode());
137 d->currentElement = previousElt;
138 } else {
139 QDomElement imageElt = d->layerStack.createElement("image");
140 int width = layer->image()->width();
141 int height = layer->image()->height();
142 int xRes = (int)(qRound(layer->image()->xRes() * 72));
143 int yRes = (int)(qRound(layer->image()->yRes() * 72));
144
145 imageElt.setAttribute("version", "0.0.1");
146 imageElt.setAttribute("w", width);
147 imageElt.setAttribute("h", height);
148 imageElt.setAttribute("xres", xRes);
149 imageElt.setAttribute("yres", yRes);
150 imageElt.appendChild(elt);
151 d->layerStack.insertBefore(imageElt, QDomNode());
152 d->currentElement = QDomElement();
154 }
155
156 return true;
157}
158
160{
161 QDomElement elt = d->layerStack.createElement("filter");
162 saveLayerInfo(elt, layer);
163 elt.setAttribute("type", "applications:krita:" + layer->filter()->name());
164 return true;
165}
166
168{
169 return saveLayer(layer);
170}
171
173{
174 return saveLayer(layer);
175}
176
178{
179 if (layer->isFakeNode()) {
180 // don't save grids, reference images layers etc.
181 return true;
182 }
183
184 // here we adjust the bounds to encompass the entire area of the layer, including transforms
185 QRect adjustedBounds = layer->exactBounds();
186
187 if (adjustedBounds.isEmpty()) {
188 // in case of an empty layer, artificially increase the size of the saved rectangle
189 // to just save an empty layer file
190 adjustedBounds.adjust(0, 0, 1, 1);
191 }
192
193 QString filename = d->saveContext->saveDeviceData(layer->projection(), layer->metaData(), adjustedBounds, layer->image()->xRes(), layer->image()->yRes());
194
195 QDomElement elt = d->layerStack.createElement("layer");
196 saveLayerInfo(elt, layer);
197 elt.setAttribute("src", filename);
198 d->currentElement.insertBefore(elt, QDomNode());
199
200 return true;
201}
const QString COMPOSITE_OVER
const QString COMPOSITE_DARKEN
const QString COMPOSITE_OVERLAY
const QString COMPOSITE_DODGE
const QString COMPOSITE_SOFT_LIGHT_SVG
const QString COMPOSITE_ADD
const QString COMPOSITE_LIGHTEN
const QString COMPOSITE_MULT
const QString COMPOSITE_SATURATION
const QString COMPOSITE_DESTINATION_ATOP
const QString COMPOSITE_CLEAR
const QString COMPOSITE_HARD_LIGHT
const QString COMPOSITE_SCREEN
const QString COMPOSITE_DIFF
const QString COMPOSITE_ERASE
const QString COMPOSITE_HUE
const QString COMPOSITE_BURN
const QString COMPOSITE_COLOR
const QString COMPOSITE_DESTINATION_IN
const QString COMPOSITE_LUMINIZE
qint32 width() const
double xRes() const
double yRes() const
qint32 height() const
virtual KisFilterConfigurationSP filter() const
bool visitAll(KisNode *node, bool breakOnFail=false)
void saveStack(const QDomDocument &doc)
QString saveDeviceData(KisPaintDeviceSP dev, KisMetaData::Store *metaData, const QRect &imageRect, qreal xRes, qreal yRes)
bool visit(KisPaintLayer *layer) override
KisOpenRasterStackSaveVisitor(KisOpenRasterSaveContext *, vKisNodeSP activeNodes)
void saveLayerInfo(QDomElement &elt, KisLayer *layer)
const QString & compositeOpId() const
KisImageWSP image
bool userLocked() const
QString name() const
virtual bool isFakeNode() const
quint8 opacity() const
virtual bool visible(bool recursive=false) const
KisPaintDeviceSP projection() const override
Definition kis_layer.cc:820
KisMetaData::Store * metaData()
QRect exactBounds() const override
bool alphaChannelDisabled() const
Definition kis_layer.cc:334