Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_asl_xml_writer.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QBuffer>
10#include <QColor>
11#include <QDomDocument>
12#include <QPointF>
13#include <QUuid>
14
15#include <resources/KoPattern.h>
18
19#include <cfloat>
20
22#include "kis_dom_utils.h"
23
25 QDomDocument document;
26 QDomElement currentElement;
27};
28
30 : m_d(new Private)
31{
32 QDomElement el = m_d->document.createElement("asl");
33 m_d->document.appendChild(el);
34 m_d->currentElement = el;
35}
36
40
41QDomDocument KisAslXmlWriter::document() const
42{
43 if (m_d->document.documentElement() != m_d->currentElement) {
44 warnKrita << "KisAslXmlWriter::document(): unbalanced enter/leave descriptor/array";
45 }
46
47 return m_d->document;
48}
49
50void KisAslXmlWriter::enterDescriptor(const QString &key, const QString &name, const QString &classId)
51{
52 QDomElement el = m_d->document.createElement("node");
53
54 if (!key.isEmpty()) {
55 el.setAttribute("key", key);
56 }
57
58 el.setAttribute("type", "Descriptor");
59 el.setAttribute("name", name);
60 el.setAttribute("classId", classId);
61
62 m_d->currentElement.appendChild(el);
63 m_d->currentElement = el;
64}
65
67{
68 if (!m_d->currentElement.parentNode().toElement().isNull()) {
69 m_d->currentElement = m_d->currentElement.parentNode().toElement();
70 } else {
71 warnKrita << "KisAslXmlWriter::leaveDescriptor(): unbalanced enter/leave descriptor";
72 }
73}
74
75void KisAslXmlWriter::enterList(const QString &key)
76{
77 QDomElement el = m_d->document.createElement("node");
78
79 if (!key.isEmpty()) {
80 el.setAttribute("key", key);
81 }
82
83 el.setAttribute("type", "List");
84
85 m_d->currentElement.appendChild(el);
86 m_d->currentElement = el;
87}
88
90{
91 if (!m_d->currentElement.parentNode().toElement().isNull()) {
92 m_d->currentElement = m_d->currentElement.parentNode().toElement();
93 } else {
94 warnKrita << "KisAslXmlWriter::leaveList(): unbalanced enter/leave list";
95 }
96}
97
98void KisAslXmlWriter::writeDouble(const QString &key, double value)
99{
100 QDomElement el = m_d->document.createElement("node");
101
102 if (!key.isEmpty()) {
103 el.setAttribute("key", key);
104 }
105
106 el.setAttribute("type", "Double");
107 el.setAttribute("value", KisDomUtils::toString(value));
108
109 m_d->currentElement.appendChild(el);
110}
111
112void KisAslXmlWriter::writeInteger(const QString &key, int value)
113{
114 QDomElement el = m_d->document.createElement("node");
115
116 if (!key.isEmpty()) {
117 el.setAttribute("key", key);
118 }
119
120 el.setAttribute("type", "Integer");
121 el.setAttribute("value", KisDomUtils::toString(value));
122
123 m_d->currentElement.appendChild(el);
124}
125
126void KisAslXmlWriter::writeEnum(const QString &key, const QString &typeId, const QString &value)
127{
128 QDomElement el = m_d->document.createElement("node");
129
130 if (!key.isEmpty()) {
131 el.setAttribute("key", key);
132 }
133
134 el.setAttribute("type", "Enum");
135 el.setAttribute("typeId", typeId);
136 el.setAttribute("value", value);
137
138 m_d->currentElement.appendChild(el);
139}
140
141void KisAslXmlWriter::writeUnitFloat(const QString &key, const QString &unit, double value)
142{
143 QDomElement el = m_d->document.createElement("node");
144
145 if (!key.isEmpty()) {
146 el.setAttribute("key", key);
147 }
148
149 el.setAttribute("type", "UnitFloat");
150 el.setAttribute("unit", unit);
151 el.setAttribute("value", KisDomUtils::toString(value));
152
153 m_d->currentElement.appendChild(el);
154}
155
156void KisAslXmlWriter::writeText(const QString &key, const QString &value)
157{
158 QDomElement el = m_d->document.createElement("node");
159
160 if (!key.isEmpty()) {
161 el.setAttribute("key", key);
162 }
163
164 el.setAttribute("type", "Text");
165 el.setAttribute("value", value);
166
167 m_d->currentElement.appendChild(el);
168}
169
170void KisAslXmlWriter::writeBoolean(const QString &key, bool value)
171{
172 QDomElement el = m_d->document.createElement("node");
173
174 if (!key.isEmpty()) {
175 el.setAttribute("key", key);
176 }
177
178 el.setAttribute("type", "Boolean");
179 el.setAttribute("value", KisDomUtils::toString(value));
180
181 m_d->currentElement.appendChild(el);
182}
183
184void KisAslXmlWriter::writeColor(const QString &key, const KoColor &value)
185{
186 QDomDocument doc;
187 QDomElement el = doc.createElement("color");
188 value.toXML(doc, el);
189 QDomElement colorEl = el.firstChildElement();
190 if (value.colorSpace()->colorModelId() == RGBAColorModelID) {
191 enterDescriptor(key, "", "RGBC");
192
193 double v = qBound(0.0, KisDomUtils::toDouble(colorEl.attribute("r", "0.0")) * 255.0, 255.0);
194 writeDouble("Rd ", v);
195 v = qBound(0.0, KisDomUtils::toDouble(colorEl.attribute("g", "0.0")) * 255.0, 255.0);
196 writeDouble("Grn ", v);
197 v = qBound(0.0, KisDomUtils::toDouble(colorEl.attribute("b", "0.0")) * 255.0, 255.0);
198 writeDouble("Bl ", v);
199 } else if (value.colorSpace()->colorModelId() == CMYKAColorModelID) {
200 enterDescriptor(key, "", "CMYC");
201
202 double v = qBound(0.0, KisDomUtils::toDouble(colorEl.attribute("c", "0.0")) * 100.0, 100.0);
203 writeDouble("Cyn ", v);
204 v = qBound(0.0, KisDomUtils::toDouble(colorEl.attribute("m", "0.0")) * 100.0, 100.0);
205 writeDouble("Mgnt", v);
206 v = qBound(0.0, KisDomUtils::toDouble(colorEl.attribute("y", "0.0")) * 100.0, 100.0);
207 writeDouble("Ylw ", v);
208 v = qBound(0.0, KisDomUtils::toDouble(colorEl.attribute("k", "0.0")) * 100.0, 100.0);
209 writeDouble("Blck", v);
210 } else if (value.colorSpace()->colorModelId() == LABAColorModelID) {
211 enterDescriptor(key, "", "LbCl");
212
213 double v = KisDomUtils::toDouble(colorEl.attribute("L", "0.0"));
214 writeDouble("Lmnc", v);
215 v = KisDomUtils::toDouble(colorEl.attribute("a", "0.0"));
216 writeDouble("A ", v);
217 v = KisDomUtils::toDouble(colorEl.attribute("b", "0.0"));
218 writeDouble("B ", v);
219 } else if (value.colorSpace()->colorModelId() == GrayAColorModelID) {
220 enterDescriptor(key, "", "Grsc");
221
222 double v = qBound(0.0, KisDomUtils::toDouble(colorEl.attribute("g", "0.0")) * 100.0, 100.0);
223 writeDouble("Gry ", v);
224 } else { // default to sRGB
225 enterDescriptor(key, "", "RGBC");
226
227 writeDouble("Rd ", value.toQColor().red());
228 writeDouble("Grn ", value.toQColor().green());
229 writeDouble("Bl ", value.toQColor().blue());
230 }
231 if (value.metadata().keys().contains("psdSpotBook")) {
232 QVariant v;
233 v = value.metadata().value("spotName");
234 if (v.isValid()) {
235 writeText("Nm ", v.toString());
236 }
237 v = value.metadata().value("psdSpotBook");
238 if (v.isValid()) {
239 writeText("Bk ", v.toString());
240 }
241 bool ok;
242 v = value.metadata().value("psdSpotBookId");
243 int bookid = v.toInt(&ok);
244 if (ok) {
245 writeInteger("bookID", bookid);
246 }
247 }
248
250}
251
252void KisAslXmlWriter::writePoint(const QString &key, const QPointF &value)
253{
254 enterDescriptor(key, "", "CrPt");
255
256 writeDouble("Hrzn", value.x());
257 writeDouble("Vrtc", value.y());
258
260}
261
262void KisAslXmlWriter::writePhasePoint(const QString &key, const QPointF &value)
263{
264 enterDescriptor(key, "", "Pnt ");
265
266 writeDouble("Hrzn", value.x());
267 writeDouble("Vrtc", value.y());
268
270}
271
272void KisAslXmlWriter::writeOffsetPoint(const QString &key, const QPointF &value)
273{
274 enterDescriptor(key, "", "Pnt ");
275
276 writeUnitFloat("Hrzn", "#Prc", value.x());
277 writeUnitFloat("Vrtc", "#Prc", value.y());
278
280}
281
282void KisAslXmlWriter::writeCurve(const QString &key, const QString &name, const QVector<QPointF> &points)
283{
284 enterDescriptor(key, "", "ShpC");
285
286 writeText("Nm ", name);
287
288 enterList("Crv ");
289
290 Q_FOREACH (const QPointF &pt, points) {
291 writePoint("", pt);
292 }
293
294 leaveList();
296}
297
298QString KisAslXmlWriter::writePattern(const QString &key, const KoPatternSP pattern)
299{
300 enterDescriptor(key, "", "KisPattern");
301
302 writeText("Nm ", pattern->name());
303
304 QString uuid = KisAslWriterUtils::getPatternUuidLazy(pattern);
305 writeText("Idnt", uuid);
306
307 // Write pattern data
308
309 QBuffer buffer;
310 buffer.open(QIODevice::WriteOnly);
311 pattern->savePatToDevice(&buffer);
312
313 QDomCDATASection dataSection = m_d->document.createCDATASection(qCompress(buffer.buffer()).toBase64());
314
315 QDomElement dataElement = m_d->document.createElement("node");
316 dataElement.setAttribute("type", "KisPatternData");
317 dataElement.setAttribute("key", "Data");
318 dataElement.appendChild(dataSection);
319
320 m_d->currentElement.appendChild(dataElement);
321
323
324 return uuid;
325}
326
327void KisAslXmlWriter::writePatternRef(const QString &key, const KoPatternSP pattern, const QString &uuid)
328{
329 enterDescriptor(key, "", "Ptrn");
330
331 writeText("Nm ", pattern->name());
332 writeText("Idnt", uuid);
333
335}
336
338 const QString &name,
339 QVector<KoColor> colors,
340 QVector<qreal> transparencies,
341 QVector<qreal> positions,
342 QVector<QString> types,
343 QVector<qreal> middleOffsets)
344{
345 enterDescriptor(key, "Gradient", "Grdn");
346
347 writeText("Nm ", name);
348 writeEnum("GrdF", "GrdF", "CstS");
349 writeDouble("Intr", 4096);
350
351 enterList("Clrs");
352
353 for (int i = 0; i < colors.size(); i++) {
354 enterDescriptor("", "", "Clrt");
355
356 writeColor("Clr ", colors[i]);
357 writeEnum("Type", "Clry", types[i]);
358 writeInteger("Lctn", positions[i] * 4096.0);
359 writeInteger("Mdpn", middleOffsets[i] * 100.0);
360
362 };
363
364 leaveList();
365
366 enterList("Trns");
367
368 for (int i = 0; i < colors.size(); i++) {
369 enterDescriptor("", "", "TrnS");
370 writeUnitFloat("Opct", "#Prc", transparencies[i] * 100.0);
371 writeInteger("Lctn", positions[i] * 4096.0);
372 writeInteger("Mdpn", middleOffsets[i] * 100.0);
374 };
375
376 leaveList();
377
379}
380
382{
383 switch (segtype) {
384 case COLOR_ENDPOINT:
385 return "UsrS";
386 break;
389 return "FrgC";
390 break;
393 return "BckC";
394 break;
395 default:
396 return "UsrS";
397 }
398}
399
400void KisAslXmlWriter::writeSegmentGradient(const QString &key, const KoSegmentGradient &gradient)
401{
402 const QList<KoGradientSegment *> &segments = gradient.segments();
403 KIS_SAFE_ASSERT_RECOVER_RETURN(!segments.isEmpty());
404
405 QVector<KoColor> colors;
406 QVector<qreal> transparencies;
407 QVector<qreal> positions;
408 QVector<QString> types;
409 QVector<qreal> middleOffsets;
410
411 Q_FOREACH (const KoGradientSegment *seg, segments) {
412 const qreal start = seg->startOffset();
413 const qreal end = seg->endOffset();
414 const qreal mid = (end - start) > DBL_EPSILON ? (seg->middleOffset() - start) / (end - start) : 0.5;
415
416 KoColor color = seg->startColor();
417 qreal transparency = color.opacityF();
418 color.setOpacity(1.0);
419
420 QString type = getSegmentEndpointTypeString(seg->startType());
421
422 colors << color;
423 transparencies << transparency;
424 positions << start;
425 types << type;
426 middleOffsets << mid;
427 }
428
429 // last segment
430
431 if (!segments.isEmpty()) {
432 const KoGradientSegment *lastSeg = segments.last();
433
434 KoColor color = lastSeg->endColor();
435 qreal transparency = color.opacityF();
436 color.setOpacity(1.0);
437 QString type = getSegmentEndpointTypeString(lastSeg->endType());
438
439 colors << color;
440 transparencies << transparency;
441 positions << lastSeg->endOffset();
442 types << type;
443 middleOffsets << 0.5;
444 }
445
446 writeGradientImpl(key, gradient.name(), colors, transparencies, positions, types, middleOffsets);
447}
448
449void KisAslXmlWriter::writeStopGradient(const QString &key, const KoStopGradient &gradient)
450{
451 QVector<KoColor> colors;
452 QVector<qreal> transparencies;
453 QVector<qreal> positions;
454 QVector<QString> types;
455 QVector<qreal> middleOffsets;
456
457 Q_FOREACH (const KoGradientStop &stop, gradient.stops()) {
458 KoColor color = stop.color;
459 qreal transparency = color.opacityF();
460 color.setOpacity(1.0);
461
462 QString type;
463 switch (stop.type) {
464 case COLORSTOP:
465 type = "UsrS";
466 break;
467 case FOREGROUNDSTOP:
468 type = "FrgC";
469 break;
470 case BACKGROUNDSTOP:
471 type = "BckC";
472 break;
473 }
474
475 colors << color;
476 transparencies << transparency;
477 positions << stop.position;
478 types << type;
479 middleOffsets << 0.5;
480 }
481
482 writeGradientImpl(key, gradient.name(), colors, transparencies, positions, types, middleOffsets);
483}
484
485void KisAslXmlWriter::writeRawData(const QString key, const QByteArray *rawData)
486{
487 QDomCDATASection dataSection = m_d->document.createCDATASection(rawData->toBase64());
488 QDomElement dataElement = m_d->document.createElement("node");
489 dataElement.setAttribute("type", "RawData");
490 dataElement.setAttribute("key", key);
491 dataElement.appendChild(dataSection);
492 m_d->currentElement.appendChild(dataElement);
493}
494
495void KisAslXmlWriter::writeTransform(const QString &key, const QTransform &transform)
496{
497 enterDescriptor(key, "Transform", "Trnf");
498
499 writeDouble("xx", transform.m11());
500 writeDouble("xy", transform.m12());
501 writeDouble("yx", transform.m21());
502 writeDouble("yy", transform.m22());
503 writeDouble("tx", transform.dx());
504 writeDouble("ty", transform.dy());
505
507}
508
509void KisAslXmlWriter::writeUnitRect(const QString &key, const QString &unit, const QRectF &rect)
510{
511 enterDescriptor(key, "", "unitRect");
512
513 writeInteger("unitValueQuadVersion", 1);
514 writeUnitFloat("Top ", unit, rect.top());
515 writeUnitFloat("Left", unit, rect.left());
516 writeUnitFloat("Btom", unit, rect.bottom());
517 writeUnitFloat("Rght", unit, rect.right());
518
520}
521
522void KisAslXmlWriter::writeFloatRect(const QString &key, const QRectF &rect)
523{
524 enterDescriptor(key, "", "classFloatRect");
525
526 writeDouble("Top ", rect.top());
527 writeDouble("Left", rect.left());
528 writeDouble("Btom", rect.bottom());
529 writeDouble("Rght", rect.right());
530
532}
533
534void KisAslXmlWriter::writePointRect(const QString &key, const QPolygonF &transformedRect)
535{
536 if (transformedRect.size() < 4) {
537 warnKrita << "KisAslXmlWriter::writePointRect(): too few points to write descriptor.";
538 return;
539 }
540 enterDescriptor(key, "", "null");
541
542 writePoint("rectangleCornerA", transformedRect.at(0));
543 writePoint("rectangleCornerB", transformedRect.at(1));
544 writePoint("rectangleCornerC", transformedRect.at(2));
545 writePoint("rectangleCornerD", transformedRect.at(3));
546
548}
float value(const T *src, size_t ch)
qreal v
const KoID GrayAColorModelID("GRAYA", ki18n("Grayscale/Alpha"))
const KoID CMYKAColorModelID("CMYKA", ki18n("CMYK/Alpha"))
const KoID LABAColorModelID("LABA", ki18n("L*a*b*/Alpha"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
KoGradientSegmentEndpointType
@ COLOR_ENDPOINT
@ BACKGROUND_TRANSPARENT_ENDPOINT
@ FOREGROUND_ENDPOINT
@ BACKGROUND_ENDPOINT
@ FOREGROUND_TRANSPARENT_ENDPOINT
@ FOREGROUNDSTOP
@ BACKGROUNDSTOP
@ COLORSTOP
QString getSegmentEndpointTypeString(KoGradientSegmentEndpointType segtype)
void writeTransform(const QString &key, const QTransform &transform)
void writeColor(const QString &key, const KoColor &value)
void writeBoolean(const QString &key, bool value)
QString writePattern(const QString &key, const KoPatternSP pattern)
void writeEnum(const QString &key, const QString &typeId, const QString &value)
void writeStopGradient(const QString &key, const KoStopGradient &gradient)
void enterList(const QString &key)
void enterDescriptor(const QString &key, const QString &name, const QString &classId)
void writeUnitFloat(const QString &key, const QString &unit, double value)
void writeDouble(const QString &key, double value)
QDomDocument document() const
void writeText(const QString &key, const QString &value)
void writeSegmentGradient(const QString &key, const KoSegmentGradient &gradient)
void writePoint(const QString &key, const QPointF &value)
void writePointRect(const QString &key, const QPolygonF &transformedRect)
void writeRawData(const QString key, const QByteArray *rawData)
void writeGradientImpl(const QString &key, const QString &name, QVector< KoColor > colors, QVector< qreal > transparencies, QVector< qreal > positions, QVector< QString > types, QVector< qreal > middleOffsets)
void writeUnitRect(const QString &key, const QString &unit, const QRectF &rect)
void writePatternRef(const QString &key, const KoPatternSP pattern, const QString &uuid)
void writeOffsetPoint(const QString &key, const QPointF &value)
void writeFloatRect(const QString &key, const QRectF &rect)
void writeCurve(const QString &key, const QString &name, const QVector< QPointF > &points)
void writeInteger(const QString &key, int value)
void writePhasePoint(const QString &key, const QPointF &value)
const QScopedPointer< Private > m_d
qreal opacityF() const
Definition KoColor.cpp:345
void setOpacity(quint8 alpha)
Definition KoColor.cpp:333
Write API docs here.
KoGradientSegmentEndpointType endType() const
KoGradientSegmentEndpointType startType() const
const KoColor & startColor() const
const KoColor & endColor() const
const QList< KoGradientSegment * > & segments() const
QList< KoGradientStop > stops() const
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define warnKrita
Definition kis_debug.h:87
QString getPatternUuidLazy(const KoPatternSP pattern)
double toDouble(const QString &str, bool *ok=nullptr)
QString toString(const QString &value)
KoGradientStopType type
QString name