Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_asl_reader.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
7#include "kis_asl_reader.h"
8
9#include "kis_dom_utils.h"
10
11#include <stdexcept>
12#include <string>
13
14#include <QBuffer>
15#include <QDomDocument>
16#include <QIODevice>
17
18#include "compression.h"
20#include "psd.h"
21#include "psd_utils.h"
22
25
26namespace Private
27{
35template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
36QString readDoubleAsString(QIODevice &device)
37{
38 double value = 0.0;
39 SAFE_READ_EX(byteOrder, device, value);
40
42}
43
44template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
45QString readIntAsString(QIODevice &device)
46{
47 quint32 value = 0.0;
48 SAFE_READ_EX(byteOrder, device, value);
49
51}
52
53template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
54QString readBoolAsString(QIODevice &device)
55{
56 quint8 value = 0.0;
57 SAFE_READ_EX(byteOrder, device, value);
58
60}
61
68QDomElement appendXMLNodeCommon(const QString &key, const QString &value, const QString &type, QDomElement *parent, QDomDocument *doc)
69{
70 QDomElement el = doc->createElement("node");
71 if (!key.isEmpty()) {
72 el.setAttribute("key", key);
73 }
74 el.setAttribute("type", type);
75 el.setAttribute("value", value);
76 parent->appendChild(el);
77
78 return el;
79}
80
81QDomElement appendXMLNodeCommonNoValue(const QString &key, const QString &type, QDomElement *parent, QDomDocument *doc)
82{
83 QDomElement el = doc->createElement("node");
84 if (!key.isEmpty()) {
85 el.setAttribute("key", key);
86 }
87 el.setAttribute("type", type);
88 parent->appendChild(el);
89
90 return el;
91}
92
93void appendIntegerXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
94{
95 appendXMLNodeCommon(key, value, "Integer", parent, doc);
96}
97
98void appendDoubleXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
99{
100 appendXMLNodeCommon(key, value, "Double", parent, doc);
101}
102
103void appendTextXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
104{
105 appendXMLNodeCommon(key, value, "Text", parent, doc);
106}
107
108void appendPointXMLNode(const QString &key, const QPointF &pt, QDomElement *parent, QDomDocument *doc)
109{
110 QDomElement el = appendXMLNodeCommonNoValue(key, "Descriptor", parent, doc);
111 el.setAttribute("classId", "CrPt");
112 el.setAttribute("name", "");
113
114 appendDoubleXMLNode("Hrzn", KisDomUtils::toString(pt.x()), &el, doc);
115 appendDoubleXMLNode("Vrtc", KisDomUtils::toString(pt.x()), &el, doc);
116}
117
122template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
123void readDescriptor(QIODevice &device, const QString &key, QDomElement *parent, QDomDocument *doc);
124
125template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
126void readChildObject(QIODevice &device, QDomElement *parent, QDomDocument *doc, bool skipKey = false)
127{
128 using namespace KisAslReaderUtils;
129
130 QString key;
131
132 if (!skipKey) {
133 key = readVarString<byteOrder>(device);
134 }
135
136 QString OSType = readFixedString<byteOrder>(device);
137
138 // dbgKrita << "Child" << ppVar(key) << ppVar(OSType);
139
140 if (OSType == "obj ") {
141 throw KisAslReaderUtils::ASLParseException("OSType 'obj' not implemented");
142
143 } else if (OSType == "Objc" || OSType == "GlbO") {
144 readDescriptor<byteOrder>(device, key, parent, doc);
145
146 } else if (OSType == "VlLs") {
147 quint32 numItems = GARBAGE_VALUE_MARK;
148 SAFE_READ_EX(byteOrder, device, numItems);
149
150 QDomElement el = appendXMLNodeCommonNoValue(key, "List", parent, doc);
151 for (quint32 i = 0; i < numItems; i++) {
152 readChildObject<byteOrder>(device, &el, doc, true);
153 }
154
155 } else if (OSType == "doub") {
156 appendDoubleXMLNode(key, readDoubleAsString<byteOrder>(device), parent, doc);
157
158 } else if (OSType == "UntF") {
159 const QString unit = readFixedString<byteOrder>(device);
160 const QString value = readDoubleAsString<byteOrder>(device);
161
162 QDomElement el = appendXMLNodeCommon(key, value, "UnitFloat", parent, doc);
163 el.setAttribute("unit", unit);
164
165 } else if (OSType == "TEXT") {
166 QString unicodeString = readUnicodeString<byteOrder>(device);
167 appendTextXMLNode(key, unicodeString, parent, doc);
168
169 } else if (OSType == "enum") {
170 const QString typeId = readVarString<byteOrder>(device);
171 const QString value = readVarString<byteOrder>(device);
172
173 QDomElement el = appendXMLNodeCommon(key, value, "Enum", parent, doc);
174 el.setAttribute("typeId", typeId);
175
176 } else if (OSType == "long") {
177 appendIntegerXMLNode(key, readIntAsString<byteOrder>(device), parent, doc);
178
179 } else if (OSType == "bool") {
180 const QString value = readBoolAsString<byteOrder>(device);
181 appendXMLNodeCommon(key, value, "Boolean", parent, doc);
182
183 } else if (OSType == "type") {
184 throw KisAslReaderUtils::ASLParseException("OSType 'type' not implemented");
185 } else if (OSType == "GlbC") {
186 throw KisAslReaderUtils::ASLParseException("OSType 'GlbC' not implemented");
187 } else if (OSType == "alis") {
188 throw KisAslReaderUtils::ASLParseException("OSType 'alis' not implemented");
189 } else if (OSType == "tdta") {
190
191 if (key == "EngineData") {
192 // This is PSD text engine data, which outputs
193 // Carousel Object Structure (PDF) data, much like the "Txt2" additional info block.
194 quint32 len = 0.0;
195 SAFE_READ_EX(byteOrder, device, len);
196 QByteArray ba = device.read(len);
197
198 QDomCDATASection dataSection;
199 dataSection = doc->createCDATASection(ba.toBase64());
200 QDomElement dataElement = doc->createElement("node");
201 dataElement.setAttribute("type", "RawData");
202 dataElement.setAttribute("key", key);
203 dataElement.appendChild(dataSection);
204 parent->appendChild(dataElement);
205 } else {
206 throw KisAslReaderUtils::ASLParseException("OSType 'tdta' not implemented");
207 }
208 }
209}
210
211
212
213template<psd_byte_order byteOrder>
214void readDescriptor(QIODevice &device, const QString &key, QDomElement *parent, QDomDocument *doc)
215{
216 using namespace KisAslReaderUtils;
217
218 QString name = readUnicodeString(device);
219 QString classId = readVarString(device);
220
221 quint32 numChildren = GARBAGE_VALUE_MARK;
222 SAFE_READ_EX(byteOrder, device, numChildren);
223
224 QDomElement el = appendXMLNodeCommonNoValue(key, "Descriptor", parent, doc);
225 el.setAttribute("classId", classId);
226 el.setAttribute("name", name);
227
228 // dbgKrita << "Descriptor" << ppVar(key) << ppVar(classId) << ppVar(numChildren);
229
230 for (quint32 i = 0; i < numChildren; i++) {
231 readChildObject<byteOrder>(device, &el, doc);
232 }
233}
234
235template<psd_byte_order byteOrder>
236QImage readVirtualArrayList(QIODevice &device, int numPlanes, const QVector<QRgb> &palette)
237{
238 using namespace KisAslReaderUtils;
239
240 quint32 arrayVersion = GARBAGE_VALUE_MARK;
241 SAFE_READ_EX(byteOrder, device, arrayVersion);
242
243 if (arrayVersion != 3) {
244 throw ASLParseException("VAList version is not '3'!");
245 }
246
247 quint32 arrayLength = GARBAGE_VALUE_MARK;
248 SAFE_READ_EX(byteOrder, device, arrayLength);
249
250 SETUP_OFFSET_VERIFIER(vaEndVerifier, device, arrayLength, 100);
251
252 quint32 x0 = 0;
253 quint32 y0 = 0;
254 quint32 x1 = 0;
255 quint32 y1 = 0;
256 SAFE_READ_EX(byteOrder, device, y0);
257 SAFE_READ_EX(byteOrder, device, x0);
258 SAFE_READ_EX(byteOrder, device, y1);
259 SAFE_READ_EX(byteOrder, device, x1);
260 QRect arrayRect(x0, y0, x1 - x0, y1 - y0);
261
262 quint32 numberOfChannels = GARBAGE_VALUE_MARK;
263 SAFE_READ_EX(byteOrder, device, numberOfChannels);
264
265 if (numberOfChannels != 24) {
266 throw ASLParseException("VAList: Krita doesn't support ASL files with 'numberOfChannels' flag not equal to 24 (it is not documented)!");
267 }
268
269 dbgKrita << ppVar(arrayVersion);
270 dbgKrita << ppVar(arrayLength);
271 dbgKrita << ppVar(arrayRect);
272 dbgKrita << ppVar(numberOfChannels);
273
274 if (numPlanes != 1 && numPlanes != 3) {
275 throw ASLParseException("VAList: unsupported number of planes!");
276 }
277
278 QVector<QByteArray> dataPlanes;
279 dataPlanes.resize(3);
280
281 quint32 pixelDepth1 = GARBAGE_VALUE_MARK;
282
283 for (int i = 0; i < numPlanes; i++) {
284 quint32 arrayWritten = GARBAGE_VALUE_MARK;
285 if (!psdread<byteOrder>(device, arrayWritten) || !arrayWritten) {
286 throw ASLParseException("VAList plane has not-written flag set!");
287 }
288
289 quint32 arrayPlaneLength = GARBAGE_VALUE_MARK;
290 if (!psdread<byteOrder>(device, arrayPlaneLength) || !arrayPlaneLength) {
291 throw ASLParseException("VAList has plane length set to zero!");
292 }
293
294 SETUP_OFFSET_VERIFIER(planeEndVerifier, device, arrayPlaneLength, 0);
295 qint64 nextPos = device.pos() + arrayPlaneLength;
296
297 SAFE_READ_EX(byteOrder, device, pixelDepth1);
298
299 quint32 x0 = 0;
300 quint32 y0 = 0;
301 quint32 x1 = 0;
302 quint32 y1 = 0;
303 SAFE_READ_EX(byteOrder, device, y0);
304 SAFE_READ_EX(byteOrder, device, x0);
305 SAFE_READ_EX(byteOrder, device, y1);
306 SAFE_READ_EX(byteOrder, device, x1);
307 QRect planeRect(x0, y0, x1 - x0, y1 - y0);
308
309 if (planeRect != arrayRect) {
310 throw ASLParseException("VAList: planes are not uniform. Not supported yet!");
311 }
312
313 quint16 pixelDepth2 = GARBAGE_VALUE_MARK;
314 SAFE_READ_EX(byteOrder, device, pixelDepth2);
315
316 quint8 useCompression = 9;
317 SAFE_READ_EX(byteOrder, device, useCompression);
318
319 // dbgKrita << "plane index:" << ppVar(i);
320 // dbgKrita << ppVar(arrayWritten);
321 // dbgKrita << ppVar(arrayPlaneLength);
322 // dbgKrita << ppVar(pixelDepth1);
323 // dbgKrita << ppVar(planeRect);
324 // dbgKrita << ppVar(pixelDepth2);
325 // dbgKrita << ppVar(useCompression);
326
327 if (pixelDepth1 != pixelDepth2) {
328 throw ASLParseException("VAList: two pixel depths of the plane are not equal (it is not documented)!");
329 }
330
331 if (pixelDepth1 != 1 && pixelDepth1 != 8 && pixelDepth1 != 16) {
332 throw ASLParseException(QString("VAList: unsupported pixel depth: %1!").arg(pixelDepth1));
333 }
334
335 const int channelSize = (pixelDepth1 == 1 || pixelDepth1 == 8) ? 1 : 2;
336
337 const int dataLength = planeRect.width() * planeRect.height() * channelSize;
338
339 if (useCompression == psd_compression_type::Uncompressed) {
340 dataPlanes[i] = device.read(arrayPlaneLength - 23);
341 } else if (useCompression == psd_compression_type::RLE) {
342 const int numRows = planeRect.height();
343
344 QVector<quint16> rowSizes;
345 rowSizes.resize(numRows);
346
347 for (int row = 0; row < numRows; row++) {
348 quint16 rowSize = GARBAGE_VALUE_MARK;
349 SAFE_READ_EX(byteOrder, device, rowSize);
350 rowSizes[row] = rowSize;
351 }
352
353 for (int row = 0; row < numRows; row++) {
354 const quint16 rowSize = rowSizes[row];
355
356 QByteArray compressedData = device.read(rowSize);
357
358 if (compressedData.size() != rowSize) {
359 throw ASLParseException("VAList: failed to read compressed data!");
360 }
361
362 dbgFile << "Going to decompress the pattern";
363
364 QByteArray uncompressedData =
365 Compression::uncompress(planeRect.width() * channelSize, compressedData, psd_compression_type::RLE);
366
367 if (uncompressedData.size() != planeRect.width()) {
368 throw ASLParseException("VAList: failed to decompress data!");
369 }
370
371 dataPlanes[i].append(uncompressedData);
372 }
373 } else if (useCompression == psd_compression_type::ZIP) {
374 QByteArray compressedBytes = device.read(arrayPlaneLength - 23);
375 dataPlanes[i] = Compression::uncompress(dataLength, compressedBytes, psd_compression_type::ZIP);
376 } else {
377 throw ASLParseException("VAList: ZIP compression is not implemented yet!");
378 }
379
380 if (dataPlanes[i].size() != dataLength) {
381 throw ASLParseException("VAList: failed to read/uncompress data plane!");
382 }
383
384 if (device.pos() != nextPos) {
385 warnFile << "VAList: Data is left out from reading"
386 << "(" << device.pos() << ")";
387 }
388 device.seek(nextPos);
389 }
390
391 QImage::Format format{};
392
393 if (pixelDepth1 == 1 || !palette.isEmpty()) {
394 if (palette.isEmpty()) {
395 format = QImage::Format_Grayscale8;
396 } else {
397 format = QImage::Format_Indexed8;
398 }
399 } else if (pixelDepth1 == 8) {
400 format = QImage::Format_ARGB32;
401 } else {
402 format = QImage::Format_RGBA64;
403 }
404
405 QImage image(arrayRect.size(), format);
406
407 if (format == QImage::Format_Indexed8) {
408 image.setColorTable(palette);
409 }
410 dbgFile << "Loading the data into an image of format" << format << arrayRect << "(" << device.pos() << ")";
411
412 const int dataLength = arrayRect.width() * arrayRect.height();
413
414 if (format == QImage::Format_ARGB32) {
415 quint8 *dstPtr = image.bits();
416
417 // This copies the single channel data into all three rgb channels, creating a grayscale picture
418 for (int i = 0; i < dataLength; i++) {
419 for (int j = 2; j >= 0; j--) {
420 int plane;
421 if (numPlanes == 1) {
422 plane = 0;
423 }
424 else {
425 plane = j;
426 }
427 *dstPtr++ = dataPlanes[plane][i];
428 }
429 *dstPtr++ = 0xFF;
430 }
431 } else if (format == QImage::Format_Indexed8 || format == QImage::Format_Grayscale8) {
432 const auto *dataPlane = reinterpret_cast<const quint8 *>(dataPlanes[0].constData());
433
434 for (int x = 0; x < arrayRect.height(); x++) {
435 quint8 *dstPtr = image.scanLine(x);
436
437 for (int y = 0; y < arrayRect.width(); y++) {
438 *dstPtr++ = dataPlane[x * arrayRect.width() + y];
439 }
440 }
441 } else {
442 quint16 *dstPtr = reinterpret_cast<quint16 *>(image.bits());
443
444 for (int i = 0; i < dataLength; i++) {
445 for (int j = 0; j <= 2; j++) {
446 const int plane = qMin(numPlanes, j);
447 const quint16 *dataPlane = reinterpret_cast<const quint16 *>(dataPlanes[plane].constData());
448 *dstPtr++ = qFromBigEndian(dataPlane[i]);
449 }
450 *dstPtr++ = 0xFFFF;
451 }
452 }
453
454 // static int i = -1; i++;
455 // QString filename = QString("pattern_image_%1.png").arg(i);
456 // dbgKrita << "### dumping pattern image" << ppVar(filename);
457 // image.save(filename);
458
459 return image.convertToFormat(QImage::Format_ARGB32, Qt::AutoColor | Qt::PreferDither);
460}
461
462template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
463qint64 readPattern(QIODevice &device, QDomElement *parent, QDomDocument *doc)
464{
465 using namespace KisAslReaderUtils;
466
467 quint32 patternSize = GARBAGE_VALUE_MARK;
468 SAFE_READ_EX(byteOrder, device, patternSize);
469
470 // patterns are always aligned by 4 bytes
471 patternSize = KisAslWriterUtils::alignOffsetCeil(patternSize, 4);
472
473 SETUP_OFFSET_VERIFIER(patternEndVerifier, device, patternSize, 0);
474
475 quint32 patternVersion = GARBAGE_VALUE_MARK;
476 SAFE_READ_EX(byteOrder, device, patternVersion);
477
478 if (patternVersion != 1) {
479 throw ASLParseException("Pattern version is not \'1\'");
480 }
481
482 quint32 patternImageMode = GARBAGE_VALUE_MARK;
483 SAFE_READ_EX(byteOrder, device, patternImageMode);
484
485 dbgFile << "Pattern format:" << patternImageMode << "(" << device.pos() << ")";
486
487 quint16 patternHeight = GARBAGE_VALUE_MARK;
488 SAFE_READ_EX(byteOrder, device, patternHeight);
489
490 dbgFile << "Pattern height:" << patternHeight << "(" << device.pos() << ")";
491
492 quint16 patternWidth = GARBAGE_VALUE_MARK;
493 SAFE_READ_EX(byteOrder, device, patternWidth);
494
495 dbgFile << "Pattern width:" << patternHeight << "(" << device.pos() << ")";
496
497 QString patternName;
498 psdread_unicodestring<byteOrder>(device, patternName);
499
500 dbgFile << "Pattern name:" << patternName << "(" << device.pos() << ")";
501
502 QString patternUuid = readPascalString<byteOrder>(device);
503
504 dbgFile << "Pattern UUID:" << patternUuid << "(" << device.pos() << ")";
505
506 // dbgKrita << "--";
507 // dbgKrita << ppVar(patternSize);
508 // dbgKrita << ppVar(patternImageMode);
509 // dbgKrita << ppVar(patternHeight);
510 // dbgKrita << ppVar(patternWidth);
511 // dbgKrita << ppVar(patternName);
512 // dbgKrita << ppVar(patternUuid);
513
514 int numPlanes = 0;
515 psd_color_mode mode = static_cast<psd_color_mode>(patternImageMode);
516
517 switch (mode) {
518 case MultiChannel:
519 case Grayscale:
520 case Indexed:
521 numPlanes = 1;
522 break;
523 case RGB:
524 numPlanes = 3;
525 break;
526 default: {
527 QString msg = QString("Unsupported image mode: %1!").arg(mode);
528 throw ASLParseException(msg);
529 }
530 }
531
533
534 if (mode == Indexed) {
535
536 palette.resize(256);
537
538 for(auto i = 0; i < 256; i++) {
539 quint8 r = 0;
540 quint8 g = 0;
541 quint8 b = 0;
542 psdread<byteOrder>(device, r);
543 psdread<byteOrder>(device, g);
544 psdread<byteOrder>(device, b);
545 palette[i] = qRgb(r, g, b);
546 }
547
548 dbgFile << "Palette: " << palette << "(" << device.pos() << ")";
549
550 // XXX: there's no way to detect this. Assume the 772 length
551 quint16 validColours = GARBAGE_VALUE_MARK;
552 psdread<byteOrder>(device, validColours);
553 palette.resize(validColours);
554 dbgFile << "Palette real size:" << validColours << "(" << device.pos() << ")";
555
556 // Set transparent colour
557 quint16 transparentIdx = GARBAGE_VALUE_MARK;
558 psdread<byteOrder>(device, transparentIdx);
559 dbgFile << "Transparent index:" << transparentIdx << "(" << device.pos() << ")";
560
561 palette[transparentIdx] = qRgba(qRed(palette[transparentIdx]),
562 qGreen(palette[transparentIdx]),
563 qBlue(palette[transparentIdx]), 0x00);
564 }
565
570 QDomElement pat = doc->createElement("node");
571
572 pat.setAttribute("classId", "KisPattern");
573 pat.setAttribute("type", "Descriptor");
574 pat.setAttribute("name", "");
575
576 QBuffer patternBuf;
577 patternBuf.open(QIODevice::WriteOnly);
578
579 { // ensure we don't keep resources for too long
580 // XXX: this QImage should tolerate 16-bit and higher
581 QString fileName = QString("%1.pat").arg(patternUuid);
582 QImage patternImage = readVirtualArrayList<byteOrder>(device, numPlanes, palette);
583 KoPattern realPattern(patternImage, patternName, fileName);
584 realPattern.savePatToDevice(&patternBuf);
585 }
586
592 appendTextXMLNode("Nm ", patternName, &pat, doc);
593 appendTextXMLNode("Idnt", patternUuid, &pat, doc);
594
595 QDomCDATASection dataSection = doc->createCDATASection(qCompress(patternBuf.buffer()).toBase64());
596
597 QDomElement dataElement = doc->createElement("node");
598 dataElement.setAttribute("type", "KisPatternData");
599 dataElement.setAttribute("key", "Data");
600
601 dataElement.appendChild(dataSection);
602 pat.appendChild(dataElement);
603 parent->appendChild(pat);
604
605 return sizeof(patternSize) + patternSize;
606}
607
608QDomDocument readFileImpl(QIODevice &device)
609{
610 using namespace KisAslReaderUtils;
611
612 QDomDocument doc;
613 QDomElement root = doc.createElement("asl");
614 doc.appendChild(root);
615
616 {
617 quint16 stylesVersion = GARBAGE_VALUE_MARK;
618 SAFE_READ_SIGNATURE_EX(psd_byte_order::psdBigEndian, device, stylesVersion, 2);
619 }
620
621 {
622 quint32 aslSignature = GARBAGE_VALUE_MARK;
623 const quint32 refSignature = 0x3842534c; // '8BSL' in little-endian
624 SAFE_READ_SIGNATURE_EX(psd_byte_order::psdBigEndian, device, aslSignature, refSignature);
625 }
626
627 {
628 quint16 patternsVersion = GARBAGE_VALUE_MARK;
629 SAFE_READ_SIGNATURE_EX(psd_byte_order::psdBigEndian, device, patternsVersion, 3);
630 }
631
632 // Patterns
633
634 {
635 quint32 patternsSize = GARBAGE_VALUE_MARK;
636 SAFE_READ_EX(psd_byte_order::psdBigEndian, device, patternsSize);
637
638 if (patternsSize > 0) {
639 SETUP_OFFSET_VERIFIER(patternsSectionVerifier, device, patternsSize, 0);
640
641 QDomElement patternsRoot = doc.createElement("node");
642 patternsRoot.setAttribute("type", "List");
643 patternsRoot.setAttribute("key", ResourceType::Patterns);
644 root.appendChild(patternsRoot);
645
646 try {
647 qint64 bytesRead = 0;
648 while (bytesRead < patternsSize) {
649 qint64 chunk = readPattern(device, &patternsRoot, &doc);
650 bytesRead += chunk;
651 }
652 } catch (ASLParseException &e) {
653 warnKrita << "WARNING: ASL (emb. pattern):" << e.what();
654 }
655 }
656 }
657
658 // Styles
659
660 quint32 numStyles = GARBAGE_VALUE_MARK;
661 SAFE_READ_EX(psd_byte_order::psdBigEndian, device, numStyles);
662
663 for (int i = 0; i < (int)numStyles; i++) {
664 quint32 bytesToRead = GARBAGE_VALUE_MARK;
665 SAFE_READ_EX(psd_byte_order::psdBigEndian, device, bytesToRead);
666
667 SETUP_OFFSET_VERIFIER(singleStyleSectionVerifier, device, bytesToRead, 0);
668
669 {
670 quint32 stylesFormatVersion = GARBAGE_VALUE_MARK;
671 SAFE_READ_SIGNATURE_EX(psd_byte_order::psdBigEndian, device, stylesFormatVersion, 16);
672 }
673
674 readDescriptor(device, "", &root, &doc);
675
676 {
677 quint32 stylesFormatVersion = GARBAGE_VALUE_MARK;
678 SAFE_READ_SIGNATURE_EX(psd_byte_order::psdBigEndian, device, stylesFormatVersion, 16);
679 }
680
681 readDescriptor(device, "", &root, &doc);
682 }
683
684 return doc;
685}
686
687} // namespace Private
688
689QDomDocument KisAslReader::readFile(QIODevice &device)
690{
691 QDomDocument doc;
692
693 if (device.isSequential()) {
694 warnKrita << "WARNING: *** KisAslReader::readFile: the supplied"
695 << "IO device is sequential. Chances are that"
696 << "the layer style will *not* be loaded correctly!";
697 }
698
699 try {
700 doc = Private::readFileImpl(device);
702 warnKrita << "WARNING: ASL:" << e.what();
703 }
704
705 return doc;
706}
707
708template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
709QDomDocument readLfx2PsdSectionImpl(QIODevice &device);
710
711QDomDocument KisAslReader::readLfx2PsdSection(QIODevice &device, psd_byte_order byteOrder)
712{
713 switch (byteOrder) {
715 return readLfx2PsdSectionImpl<psd_byte_order::psdLittleEndian>(device);
716 default:
717 return readLfx2PsdSectionImpl(device);
718 }
719}
720
721template<psd_byte_order byteOrder>
722QDomDocument readLfx2PsdSectionImpl(QIODevice &device)
723{
724 QDomDocument doc;
725
726 if (device.isSequential()) {
727 warnKrita << "WARNING: *** KisAslReader::readLfx2PsdSection: the supplied"
728 << "IO device is sequential. Chances are that"
729 << "the layer style will *not* be loaded correctly!";
730 }
731
732 try {
733 {
734 quint32 objectEffectsVersion = GARBAGE_VALUE_MARK;
735 const quint32 ref = 0x00;
736 SAFE_READ_SIGNATURE_EX(byteOrder, device, objectEffectsVersion, ref);
737 }
738
739 {
740 quint32 descriptorVersion = GARBAGE_VALUE_MARK;
741 const quint32 ref = 0x10;
742 SAFE_READ_SIGNATURE_EX(byteOrder, device, descriptorVersion, ref);
743 }
744
745 QDomElement root = doc.createElement("asl");
746 doc.appendChild(root);
747
748 Private::readDescriptor<byteOrder>(device, "", &root, &doc);
749
751 warnKrita << "WARNING: PSD: lfx2 section:" << e.what();
752 }
753
754 return doc;
755}
756
757template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
758QDomDocument readFillLayerImpl(QIODevice &device);
759
760QDomDocument KisAslReader::readFillLayer(QIODevice &device, psd_byte_order byteOrder)
761{
762 switch (byteOrder) {
764 return readFillLayerImpl<psd_byte_order::psdLittleEndian>(device);
765 default:
766 return readFillLayerImpl(device);
767 }
768}
769
770template<psd_byte_order byteOrder>
771QDomDocument readFillLayerImpl(QIODevice &device)
772{
773 QDomDocument doc;
774
775 if (device.isSequential()) {
776 warnKrita << "WARNING: *** KisAslReader::readFillLayerPsdSection: the supplied"
777 << "IO device is sequential. Chances are that"
778 << "the fill config will *not* be loaded correctly!";
779 }
780 try {
781
782 {
783 quint32 descriptorVersion = GARBAGE_VALUE_MARK;
784 SAFE_READ_SIGNATURE_EX(byteOrder, device, descriptorVersion, 16);
785 }
786
787 QDomElement root = doc.createElement("asl");
788 doc.appendChild(root);
789
790 Private::readDescriptor<byteOrder>(device, "", &root, &doc);
791
793 warnKrita << "WARNING: PSD: SoCo section:" << e.what();
794 }
795
796 return doc;
797}
798
799template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
800QDomDocument readTypeToolObjectSettingsImpl(QIODevice &device, QTransform &transform);
801
802QDomDocument KisAslReader::readTypeToolObjectSettings(QIODevice &device, QTransform &transform, psd_byte_order byteOrder)
803{
804 switch (byteOrder) {
806 return readTypeToolObjectSettingsImpl<psd_byte_order::psdLittleEndian>(device, transform);
807 default:
808 return readTypeToolObjectSettingsImpl(device, transform);
809 }
810}
811
812template<psd_byte_order byteOrder>
813QDomDocument readTypeToolObjectSettingsImpl(QIODevice &device, QTransform &transform)
814{
815 QDomDocument doc;
816
817 if (device.isSequential()) {
818 warnKrita << "WARNING: *** KisAslReader::readTypeToolObjectSettings: the supplied"
819 << "IO device is sequential. Chances are that"
820 << "the fill config will *not* be loaded correctly!";
821 }
822 try {
823
824 {
825 quint16 descriptorVersion = GARBAGE_VALUE_MARK;
826 SAFE_READ_SIGNATURE_EX(byteOrder, device, descriptorVersion, 1);
827
828 }
829
830 // transform matrix.
831 double xx = GARBAGE_VALUE_MARK;
832 SAFE_READ_EX(byteOrder, device, xx);
833 double xy = GARBAGE_VALUE_MARK;
834 SAFE_READ_EX(byteOrder, device, xy);
835 double yx = GARBAGE_VALUE_MARK;
836 SAFE_READ_EX(byteOrder, device, yx);
837 double yy = GARBAGE_VALUE_MARK;
838 SAFE_READ_EX(byteOrder, device, yy);
839 double tx = GARBAGE_VALUE_MARK;
840 SAFE_READ_EX(byteOrder, device, tx);
841 double ty = GARBAGE_VALUE_MARK;
842 SAFE_READ_EX(byteOrder, device, ty);
843
844 {
845 quint16 textVersion = GARBAGE_VALUE_MARK;
846 SAFE_READ_SIGNATURE_EX(byteOrder, device, textVersion, 50);
847 quint32 descriptorVersion = GARBAGE_VALUE_MARK;
848 SAFE_READ_SIGNATURE_EX(byteOrder, device, descriptorVersion, 16);
849 }
850
851 transform = QTransform(xx, xy, yx, yy, tx, ty);
852
853 QDomElement root = doc.createElement("asl");
854 doc.appendChild(root);
855 // text layer data
856 Private::readDescriptor<byteOrder>(device, "", &root, &doc);
857
858 // warp data
859 {
860 quint16 textVersion = GARBAGE_VALUE_MARK;
861 SAFE_READ_SIGNATURE_EX(byteOrder, device, textVersion, 1);
862 quint32 descriptorVersion = GARBAGE_VALUE_MARK;
863 SAFE_READ_SIGNATURE_EX(byteOrder, device, descriptorVersion, 16);
864 }
865
866 Private::readDescriptor<byteOrder>(device, "", &root, &doc);
867
868 /*// bounding box
869 quint64 left = GARBAGE_VALUE_MARK;
870 SAFE_READ_EX(byteOrder, device, left);
871 quint64 top = GARBAGE_VALUE_MARK;
872 SAFE_READ_EX(byteOrder, device, top);
873 quint64 right = GARBAGE_VALUE_MARK;
874 SAFE_READ_EX(byteOrder, device, right);
875 quint64 bottom = GARBAGE_VALUE_MARK;
876 SAFE_READ_EX(byteOrder, device, bottom);
877 */
878
879
881 warnKrita << "WARNING: PSD: TySh section:" << e.what();
882 }
883
884 return doc;
885}
886
887template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
888QDomDocument readVectorStrokeImpl(QIODevice &device);
889
890QDomDocument KisAslReader::readVectorStroke(QIODevice &device, psd_byte_order byteOrder)
891{
892 switch (byteOrder) {
894 return readVectorStrokeImpl<psd_byte_order::psdLittleEndian>(device);
895 default:
896 return readVectorStrokeImpl(device);
897 }
898}
899
900template<psd_byte_order byteOrder>
901QDomDocument readVectorStrokeImpl(QIODevice &device)
902{
903 QDomDocument doc;
904
905 if (device.isSequential()) {
906 warnKrita << "WARNING: *** KisAslReader::readVectorStroke: the supplied"
907 << "IO device is sequential. Chances are that"
908 << "the fill config will *not* be loaded correctly!";
909 }
910 try {
911
912 {
913 quint32 descriptorVersion = GARBAGE_VALUE_MARK;
914 SAFE_READ_SIGNATURE_EX(byteOrder, device, descriptorVersion, 16);
915 }
916
917 QDomElement root = doc.createElement("asl");
918 doc.appendChild(root);
919
920 Private::readDescriptor<byteOrder>(device, "", &root, &doc);
921
923 warnKrita << "WARNING: PSD: vmsk section:" << e.what();
924 }
925
926 return doc;
927}
928
929template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
930QDomDocument readVectorOriginationDataImpl(QIODevice &device);
931QDomDocument KisAslReader::readVectorOriginationData(QIODevice &device, psd_byte_order byteOrder)
932{
933 switch (byteOrder) {
935 return readVectorOriginationDataImpl<psd_byte_order::psdLittleEndian>(device);
936 default:
937 return readVectorOriginationDataImpl(device);
938 }
939}
940
941template<psd_byte_order byteOrder>
942QDomDocument readVectorOriginationDataImpl(QIODevice &device)
943{
944 QDomDocument doc;
945
946 if (device.isSequential()) {
947 warnKrita << "WARNING: *** KisAslReader::readVectorStroke: the supplied"
948 << "IO device is sequential. Chances are that"
949 << "the fill config will *not* be loaded correctly!";
950 }
951 try {
952
953 {
954 quint32 version = GARBAGE_VALUE_MARK;
955 SAFE_READ_SIGNATURE_EX(byteOrder, device, version, 1);
956 }
957 {
958 quint32 descriptorVersion = GARBAGE_VALUE_MARK;
959 SAFE_READ_SIGNATURE_EX(byteOrder, device, descriptorVersion, 16);
960 }
961
962 QDomElement root = doc.createElement("asl");
963 doc.appendChild(root);
964
965 Private::readDescriptor<byteOrder>(device, "", &root, &doc);
966
968 warnKrita << "WARNING: PSD: vogk section:" << e.what();
969 }
970
971 return doc;
972}
973
974template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
975QDomDocument readPsdSectionPatternImpl(QIODevice &device, qint64 bytesLeft);
976
977QDomDocument KisAslReader::readPsdSectionPattern(QIODevice &device, qint64 bytesLeft, psd_byte_order byteOrder)
978{
979 switch (byteOrder) {
981 return readPsdSectionPatternImpl<psd_byte_order::psdLittleEndian>(device, bytesLeft);
982 default:
983 return readPsdSectionPatternImpl(device, bytesLeft);
984 }
985}
986
987template<psd_byte_order byteOrder>
988QDomDocument readPsdSectionPatternImpl(QIODevice &device, qint64 bytesLeft)
989{
990 QDomDocument doc;
991
992 QDomElement root = doc.createElement("asl");
993 doc.appendChild(root);
994
995 QDomElement pat = doc.createElement("node");
996 root.appendChild(pat);
997
998 pat.setAttribute("classId", ResourceType::Patterns);
999 pat.setAttribute("type", "Descriptor");
1000 pat.setAttribute("name", "");
1001
1002 try {
1003 qint64 bytesRead = 0;
1004 while (bytesRead < bytesLeft) {
1005 qint64 chunk = Private::readPattern<byteOrder>(device, &pat, &doc);
1006 bytesRead += chunk;
1007 }
1009 warnKrita << "WARNING: PSD (emb. pattern):" << e.what();
1010 }
1011
1012 return doc;
1013}
float value(const T *src, size_t ch)
static QByteArray uncompress(int unpacked_len, QByteArray bytes, psd_compression_type compressionType, int row_size=0, int color_depth=0)
static QDomDocument readLfx2PsdSection(QIODevice &device, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readPsdSectionPattern(QIODevice &device, qint64 bytesLeft, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
QDomDocument readFile(QIODevice &device)
static QDomDocument readTypeToolObjectSettings(QIODevice &device, QTransform &transform, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readVectorOriginationData(QIODevice &device, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readVectorStroke(QIODevice &device, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readFillLayer(QIODevice &device, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
Write API docs here.
Definition KoPattern.h:21
bool savePatToDevice(QIODevice *dev) const
QDomDocument readLfx2PsdSectionImpl(QIODevice &device)
QDomDocument readPsdSectionPatternImpl(QIODevice &device, qint64 bytesLeft)
QDomDocument readVectorOriginationDataImpl(QIODevice &device)
QDomDocument readTypeToolObjectSettingsImpl(QIODevice &device, QTransform &transform)
QDomDocument readFillLayerImpl(QIODevice &device)
QDomDocument readVectorStrokeImpl(QIODevice &device)
#define GARBAGE_VALUE_MARK
#define SAFE_READ_EX(byteOrder, device, varname)
#define SAFE_READ_SIGNATURE_EX(byteOrder, device, varname, expected)
#define dbgKrita
Definition kis_debug.h:45
#define warnFile
Definition kis_debug.h:95
#define warnKrita
Definition kis_debug.h:87
#define ppVar(var)
Definition kis_debug.h:155
#define dbgFile
Definition kis_debug.h:53
#define SETUP_OFFSET_VERIFIER(name, device, expectedOffset, maxPadding)
qint64 alignOffsetCeil(qint64 pos, qint64 alignment)
QString toString(const QString &value)
void appendIntegerXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
void appendDoubleXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
void appendPointXMLNode(const QString &key, const QPointF &pt, QDomElement *parent, QDomDocument *doc)
void appendTextXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
void readDescriptor(QIODevice &device, const QString &key, QDomElement *parent, QDomDocument *doc)
QDomDocument readFileImpl(QIODevice &device)
QString readBoolAsString(QIODevice &device)
QImage readVirtualArrayList(QIODevice &device, int numPlanes, const QVector< QRgb > &palette)
QString readDoubleAsString(QIODevice &device)
QDomElement appendXMLNodeCommonNoValue(const QString &key, const QString &type, QDomElement *parent, QDomDocument *doc)
void readChildObject(QIODevice &device, QDomElement *parent, QDomDocument *doc, bool skipKey=false)
qint64 readPattern(QIODevice &device, QDomElement *parent, QDomDocument *doc)
QDomElement appendXMLNodeCommon(const QString &key, const QString &value, const QString &type, QDomElement *parent, QDomDocument *doc)
QString readIntAsString(QIODevice &device)
const QString Patterns
rgba palette[MAX_PALETTE]
Definition palette.c:35
psd_byte_order
Definition psd.h:33
@ RLE
Definition psd.h:41
@ Uncompressed
Definition psd.h:40
@ ZIP
Definition psd.h:42
psd_color_mode
Definition psd.h:50
@ RGB
Definition psd.h:54
@ Indexed
Definition psd.h:53
@ MultiChannel
Definition psd.h:56
@ Grayscale
Definition psd.h:52