1 /*
2 * Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "kis_asl_xml_writer.h"
20
21 #include <QDomDocument>
22 #include <QColor>
23 #include <QPointF>
24 #include <QUuid>
25 #include <QBuffer>
26
27 #include <resources/KoPattern.h>
28 #include <resources/KoSegmentGradient.h>
29 #include <resources/KoStopGradient.h>
30
31 #include <cfloat>
32
33 #include "kis_dom_utils.h"
34 #include "kis_asl_writer_utils.h"
35
36 struct KisAslXmlWriter::Private
37 {
38 QDomDocument document;
39 QDomElement currentElement;
40 };
41
42
KisAslXmlWriter()43 KisAslXmlWriter::KisAslXmlWriter()
44 : m_d(new Private)
45 {
46 QDomElement el = m_d->document.createElement("asl");
47 m_d->document.appendChild(el);
48 m_d->currentElement = el;
49 }
50
~KisAslXmlWriter()51 KisAslXmlWriter::~KisAslXmlWriter()
52 {
53 }
54
document() const55 QDomDocument KisAslXmlWriter::document() const
56 {
57 if (m_d->document.documentElement() != m_d->currentElement) {
58 warnKrita << "KisAslXmlWriter::document(): unbalanced enter/leave descriptor/array";
59 }
60
61 return m_d->document;
62 }
63
enterDescriptor(const QString & key,const QString & name,const QString & classId)64 void KisAslXmlWriter::enterDescriptor(const QString &key, const QString &name, const QString &classId)
65 {
66 QDomElement el = m_d->document.createElement("node");
67
68 if (!key.isEmpty()) {
69 el.setAttribute("key", key);
70 }
71
72 el.setAttribute("type", "Descriptor");
73 el.setAttribute("name", name);
74 el.setAttribute("classId", classId);
75
76 m_d->currentElement.appendChild(el);
77 m_d->currentElement = el;
78 }
79
leaveDescriptor()80 void KisAslXmlWriter::leaveDescriptor()
81 {
82 if (!m_d->currentElement.parentNode().toElement().isNull()) {
83 m_d->currentElement = m_d->currentElement.parentNode().toElement();
84 } else {
85 warnKrita << "KisAslXmlWriter::leaveDescriptor(): unbalanced enter/leave descriptor";
86 }
87 }
88
enterList(const QString & key)89 void KisAslXmlWriter::enterList(const QString &key)
90 {
91 QDomElement el = m_d->document.createElement("node");
92
93 if (!key.isEmpty()) {
94 el.setAttribute("key", key);
95 }
96
97 el.setAttribute("type", "List");
98
99 m_d->currentElement.appendChild(el);
100 m_d->currentElement = el;
101 }
102
leaveList()103 void KisAslXmlWriter::leaveList()
104 {
105 if (!m_d->currentElement.parentNode().toElement().isNull()) {
106 m_d->currentElement = m_d->currentElement.parentNode().toElement();
107 } else {
108 warnKrita << "KisAslXmlWriter::leaveList(): unbalanced enter/leave list";
109 }
110 }
111
writeDouble(const QString & key,double value)112 void KisAslXmlWriter::writeDouble(const QString &key, double 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", "Double");
121 el.setAttribute("value", KisDomUtils::toString(value));
122
123 m_d->currentElement.appendChild(el);
124 }
125
writeInteger(const QString & key,int value)126 void KisAslXmlWriter::writeInteger(const QString &key, int 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", "Integer");
135 el.setAttribute("value", KisDomUtils::toString(value));
136
137 m_d->currentElement.appendChild(el);
138 }
139
writeEnum(const QString & key,const QString & typeId,const QString & value)140 void KisAslXmlWriter::writeEnum(const QString &key, const QString &typeId, const QString &value)
141 {
142 QDomElement el = m_d->document.createElement("node");
143
144 if (!key.isEmpty()) {
145 el.setAttribute("key", key);
146 }
147
148 el.setAttribute("type", "Enum");
149 el.setAttribute("typeId", typeId);
150 el.setAttribute("value", value);
151
152 m_d->currentElement.appendChild(el);
153 }
154
writeUnitFloat(const QString & key,const QString & unit,double value)155 void KisAslXmlWriter::writeUnitFloat(const QString &key, const QString &unit, double value)
156 {
157 QDomElement el = m_d->document.createElement("node");
158
159 if (!key.isEmpty()) {
160 el.setAttribute("key", key);
161 }
162
163 el.setAttribute("type", "UnitFloat");
164 el.setAttribute("unit", unit);
165 el.setAttribute("value", KisDomUtils::toString(value));
166
167 m_d->currentElement.appendChild(el);
168 }
169
writeText(const QString & key,const QString & value)170 void KisAslXmlWriter::writeText(const QString &key, const QString &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", "Text");
179 el.setAttribute("value", value);
180
181 m_d->currentElement.appendChild(el);
182 }
183
writeBoolean(const QString & key,bool value)184 void KisAslXmlWriter::writeBoolean(const QString &key, bool value)
185 {
186 QDomElement el = m_d->document.createElement("node");
187
188 if (!key.isEmpty()) {
189 el.setAttribute("key", key);
190 }
191
192 el.setAttribute("type", "Boolean");
193 el.setAttribute("value", KisDomUtils::toString(value));
194
195 m_d->currentElement.appendChild(el);
196 }
197
writeColor(const QString & key,const QColor & value)198 void KisAslXmlWriter::writeColor(const QString &key, const QColor &value)
199 {
200 enterDescriptor(key, "", "RGBC");
201
202 writeDouble("Rd ", value.red());
203 writeDouble("Grn ", value.green());
204 writeDouble("Bl ", value.blue());
205
206 leaveDescriptor();
207 }
208
writePoint(const QString & key,const QPointF & value)209 void KisAslXmlWriter::writePoint(const QString &key, const QPointF &value)
210 {
211 enterDescriptor(key, "", "CrPt");
212
213 writeDouble("Hrzn", value.x());
214 writeDouble("Vrtc", value.y());
215
216 leaveDescriptor();
217 }
218
writePhasePoint(const QString & key,const QPointF & value)219 void KisAslXmlWriter::writePhasePoint(const QString &key, const QPointF &value)
220 {
221 enterDescriptor(key, "", "Pnt ");
222
223 writeDouble("Hrzn", value.x());
224 writeDouble("Vrtc", value.y());
225
226 leaveDescriptor();
227 }
228
writeOffsetPoint(const QString & key,const QPointF & value)229 void KisAslXmlWriter::writeOffsetPoint(const QString &key, const QPointF &value)
230 {
231 enterDescriptor(key, "", "Pnt ");
232
233 writeUnitFloat("Hrzn", "#Prc", value.x());
234 writeUnitFloat("Vrtc", "#Prc", value.y());
235
236 leaveDescriptor();
237 }
238
writeCurve(const QString & key,const QString & name,const QVector<QPointF> & points)239 void KisAslXmlWriter::writeCurve(const QString &key, const QString &name, const QVector<QPointF> &points)
240 {
241 enterDescriptor(key, "", "ShpC");
242
243 writeText("Nm ", name);
244
245 enterList("Crv ");
246
247 Q_FOREACH (const QPointF &pt, points) {
248 writePoint("", pt);
249 }
250
251 leaveList();
252 leaveDescriptor();
253 }
254
writePattern(const QString & key,const KoPattern * pattern)255 QString KisAslXmlWriter::writePattern(const QString &key, const KoPattern *pattern)
256 {
257 enterDescriptor(key, "", "KisPattern");
258
259 writeText("Nm ", pattern->name());
260
261 QString uuid = KisAslWriterUtils::getPatternUuidLazy(pattern);
262 writeText("Idnt", uuid);
263
264 // Write pattern data
265
266 QBuffer buffer;
267 buffer.open(QIODevice::WriteOnly);
268 pattern->savePatToDevice(&buffer);
269
270 QDomCDATASection dataSection = m_d->document.createCDATASection(qCompress(buffer.buffer()).toBase64());
271
272 QDomElement dataElement = m_d->document.createElement("node");
273 dataElement.setAttribute("type", "KisPatternData");
274 dataElement.setAttribute("key", "Data");
275 dataElement.appendChild(dataSection);
276
277 m_d->currentElement.appendChild(dataElement);
278
279 leaveDescriptor();
280
281 return uuid;
282 }
283
writePatternRef(const QString & key,const KoPattern * pattern,const QString & uuid)284 void KisAslXmlWriter::writePatternRef(const QString &key, const KoPattern *pattern, const QString &uuid)
285 {
286 enterDescriptor(key, "", "Ptrn");
287
288 writeText("Nm ", pattern->name());
289 writeText("Idnt", uuid);
290
291 leaveDescriptor();
292 }
293
writeGradientImpl(const QString & key,const QString & name,QVector<QColor> colors,QVector<qreal> transparencies,QVector<qreal> positions,QVector<QString> types,QVector<qreal> middleOffsets)294 void KisAslXmlWriter::writeGradientImpl(const QString &key,
295 const QString &name,
296 QVector<QColor> colors,
297 QVector<qreal> transparencies,
298 QVector<qreal> positions,
299 QVector<QString> types,
300 QVector<qreal> middleOffsets)
301 {
302 enterDescriptor(key, "Gradient", "Grdn");
303
304 writeText("Nm ", name);
305 writeEnum("GrdF", "GrdF", "CstS");
306 writeDouble("Intr", 4096);
307
308 enterList("Clrs");
309
310 for (int i = 0; i < colors.size(); i++) {
311 enterDescriptor("", "", "Clrt");
312
313 writeColor("Clr ", colors[i]);
314 writeEnum("Type", "Clry", types[i]);
315 writeInteger("Lctn", positions[i] * 4096.0);
316 writeInteger("Mdpn", middleOffsets[i] * 100.0);
317
318 leaveDescriptor();
319 };
320
321 leaveList();
322
323 enterList("Trns");
324
325 for (int i = 0; i < colors.size(); i++) {
326 enterDescriptor("", "", "TrnS");
327 writeUnitFloat("Opct", "#Prc", transparencies[i] * 100.0);
328 writeInteger("Lctn", positions[i] * 4096.0);
329 writeInteger("Mdpn", middleOffsets[i] * 100.0);
330 leaveDescriptor();
331 };
332
333 leaveList();
334
335 leaveDescriptor();
336 }
337
getSegmentEndpointTypeString(KoGradientSegmentEndpointType segtype)338 QString KisAslXmlWriter::getSegmentEndpointTypeString(KoGradientSegmentEndpointType segtype) {
339 switch (segtype) {
340 case COLOR_ENDPOINT:
341 return "UsrS";
342 break;
343 case FOREGROUND_ENDPOINT:
344 case FOREGROUND_TRANSPARENT_ENDPOINT:
345 return "FrgC";
346 break;
347 case BACKGROUND_ENDPOINT:
348 case BACKGROUND_TRANSPARENT_ENDPOINT:
349 return "BckC";
350 break;
351 default:
352 return "UsrS";
353 }
354 }
355
writeSegmentGradient(const QString & key,const KoSegmentGradient * gradient)356 void KisAslXmlWriter::writeSegmentGradient(const QString &key, const KoSegmentGradient *gradient)
357 {
358 const QList<KoGradientSegment *>&segments = gradient->segments();
359 KIS_SAFE_ASSERT_RECOVER_RETURN(!segments.isEmpty());
360
361 QVector<QColor> colors;
362 QVector<qreal> transparencies;
363 QVector<qreal> positions;
364 QVector<QString> types;
365 QVector<qreal> middleOffsets;
366
367 Q_FOREACH (const KoGradientSegment *seg, segments) {
368 const qreal start = seg->startOffset();
369 const qreal end = seg->endOffset();
370 const qreal mid = (end - start) > DBL_EPSILON ? (seg->middleOffset() - start) / (end - start) : 0.5;
371
372 QColor color = seg->startColor().toQColor();
373 qreal transparency = color.alphaF();
374 color.setAlphaF(1.0);
375
376 QString type = getSegmentEndpointTypeString(seg->startType());
377
378 colors << color;
379 transparencies << transparency;
380 positions << start;
381 types << type;
382 middleOffsets << mid;
383 }
384
385 // last segment
386
387 if (!segments.isEmpty()) {
388 const KoGradientSegment *lastSeg = segments.last();
389
390 QColor color = lastSeg->endColor().toQColor();
391 qreal transparency = color.alphaF();
392 color.setAlphaF(1.0);
393 QString type = getSegmentEndpointTypeString(lastSeg->endType());
394
395 colors << color;
396 transparencies << transparency;
397 positions << lastSeg->endOffset();
398 types << type;
399 middleOffsets << 0.5;
400 }
401
402 writeGradientImpl(key, gradient->name(), colors, transparencies, positions, types, middleOffsets);
403 }
404
writeStopGradient(const QString & key,const KoStopGradient * gradient)405 void KisAslXmlWriter::writeStopGradient(const QString &key, const KoStopGradient *gradient)
406 {
407 QVector<QColor> colors;
408 QVector<qreal> transparencies;
409 QVector<qreal> positions;
410 QVector<QString> types;
411 QVector<qreal> middleOffsets;
412
413 Q_FOREACH (const KoGradientStop &stop, gradient->stops()) {
414 QColor color = stop.color.toQColor();
415 qreal transparency = color.alphaF();
416 color.setAlphaF(1.0);
417
418 QString type;
419 switch (stop.type) {
420 case COLORSTOP:
421 type = "UsrS";
422 break;
423 case FOREGROUNDSTOP:
424 type = "FrgC";
425 break;
426 case BACKGROUNDSTOP:
427 type = "BckC";
428 break;
429 }
430
431 colors << color;
432 transparencies << transparency;
433 positions << stop.position;
434 types << type;
435 middleOffsets << 0.5;
436 }
437
438 writeGradientImpl(key, gradient->name(), colors, transparencies, positions, types, middleOffsets);
439 }
440