File indexing completed on 2024-06-16 04:13:49

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_asl_reader.h"
0008 
0009 #include "kis_dom_utils.h"
0010 
0011 #include <stdexcept>
0012 #include <string>
0013 
0014 #include <QBuffer>
0015 #include <QDomDocument>
0016 #include <QIODevice>
0017 
0018 #include "compression.h"
0019 #include "kis_offset_on_exit_verifier.h"
0020 #include "psd.h"
0021 #include "psd_utils.h"
0022 
0023 #include "kis_asl_reader_utils.h"
0024 #include "kis_asl_writer_utils.h"
0025 
0026 namespace Private
0027 {
0028 /**
0029  * Numerical fetch functions
0030  *
0031  * We read numbers and convert them to strings to be able to store
0032  * them in XML.
0033  */
0034 
0035 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0036 QString readDoubleAsString(QIODevice &device)
0037 {
0038     double value = 0.0;
0039     SAFE_READ_EX(byteOrder, device, value);
0040 
0041     return KisDomUtils::toString(value);
0042 }
0043 
0044 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0045 QString readIntAsString(QIODevice &device)
0046 {
0047     quint32 value = 0.0;
0048     SAFE_READ_EX(byteOrder, device, value);
0049 
0050     return KisDomUtils::toString(value);
0051 }
0052 
0053 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0054 QString readBoolAsString(QIODevice &device)
0055 {
0056     quint8 value = 0.0;
0057     SAFE_READ_EX(byteOrder, device, value);
0058 
0059     return KisDomUtils::toString(value);
0060 }
0061 
0062 /**
0063  * XML generation functions
0064  *
0065  * Add a node and fill the corresponding attributes
0066  */
0067 
0068 QDomElement appendXMLNodeCommon(const QString &key, const QString &value, const QString &type, QDomElement *parent, QDomDocument *doc)
0069 {
0070     QDomElement el = doc->createElement("node");
0071     if (!key.isEmpty()) {
0072         el.setAttribute("key", key);
0073     }
0074     el.setAttribute("type", type);
0075     el.setAttribute("value", value);
0076     parent->appendChild(el);
0077 
0078     return el;
0079 }
0080 
0081 QDomElement appendXMLNodeCommonNoValue(const QString &key, const QString &type, QDomElement *parent, QDomDocument *doc)
0082 {
0083     QDomElement el = doc->createElement("node");
0084     if (!key.isEmpty()) {
0085         el.setAttribute("key", key);
0086     }
0087     el.setAttribute("type", type);
0088     parent->appendChild(el);
0089 
0090     return el;
0091 }
0092 
0093 void appendIntegerXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
0094 {
0095     appendXMLNodeCommon(key, value, "Integer", parent, doc);
0096 }
0097 
0098 void appendDoubleXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
0099 {
0100     appendXMLNodeCommon(key, value, "Double", parent, doc);
0101 }
0102 
0103 void appendTextXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
0104 {
0105     appendXMLNodeCommon(key, value, "Text", parent, doc);
0106 }
0107 
0108 void appendPointXMLNode(const QString &key, const QPointF &pt, QDomElement *parent, QDomDocument *doc)
0109 {
0110     QDomElement el = appendXMLNodeCommonNoValue(key, "Descriptor", parent, doc);
0111     el.setAttribute("classId", "CrPt");
0112     el.setAttribute("name", "");
0113 
0114     appendDoubleXMLNode("Hrzn", KisDomUtils::toString(pt.x()), &el, doc);
0115     appendDoubleXMLNode("Vrtc", KisDomUtils::toString(pt.x()), &el, doc);
0116 }
0117 
0118 /**
0119  * ASL -> XML parsing functions
0120  */
0121 
0122 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0123 void readDescriptor(QIODevice &device, const QString &key, QDomElement *parent, QDomDocument *doc);
0124 
0125 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0126 void readChildObject(QIODevice &device, QDomElement *parent, QDomDocument *doc, bool skipKey = false)
0127 {
0128     using namespace KisAslReaderUtils;
0129 
0130     QString key;
0131 
0132     if (!skipKey) {
0133         key = readVarString<byteOrder>(device);
0134     }
0135 
0136     QString OSType = readFixedString<byteOrder>(device);
0137 
0138     // dbgKrita << "Child" << ppVar(key) << ppVar(OSType);
0139 
0140     if (OSType == "obj ") {
0141         throw KisAslReaderUtils::ASLParseException("OSType 'obj' not implemented");
0142 
0143     } else if (OSType == "Objc" || OSType == "GlbO") {
0144         readDescriptor<byteOrder>(device, key, parent, doc);
0145 
0146     } else if (OSType == "VlLs") {
0147         quint32 numItems = GARBAGE_VALUE_MARK;
0148         SAFE_READ_EX(byteOrder, device, numItems);
0149 
0150         QDomElement el = appendXMLNodeCommonNoValue(key, "List", parent, doc);
0151         for (quint32 i = 0; i < numItems; i++) {
0152             readChildObject<byteOrder>(device, &el, doc, true);
0153         }
0154 
0155     } else if (OSType == "doub") {
0156         appendDoubleXMLNode(key, readDoubleAsString<byteOrder>(device), parent, doc);
0157 
0158     } else if (OSType == "UntF") {
0159         const QString unit = readFixedString<byteOrder>(device);
0160         const QString value = readDoubleAsString<byteOrder>(device);
0161 
0162         QDomElement el = appendXMLNodeCommon(key, value, "UnitFloat", parent, doc);
0163         el.setAttribute("unit", unit);
0164 
0165     } else if (OSType == "TEXT") {
0166         QString unicodeString = readUnicodeString<byteOrder>(device);
0167         appendTextXMLNode(key, unicodeString, parent, doc);
0168 
0169     } else if (OSType == "enum") {
0170         const QString typeId = readVarString<byteOrder>(device);
0171         const QString value = readVarString<byteOrder>(device);
0172 
0173         QDomElement el = appendXMLNodeCommon(key, value, "Enum", parent, doc);
0174         el.setAttribute("typeId", typeId);
0175 
0176     } else if (OSType == "long") {
0177         appendIntegerXMLNode(key, readIntAsString<byteOrder>(device), parent, doc);
0178 
0179     } else if (OSType == "bool") {
0180         const QString value = readBoolAsString<byteOrder>(device);
0181         appendXMLNodeCommon(key, value, "Boolean", parent, doc);
0182 
0183     } else if (OSType == "type") {
0184         throw KisAslReaderUtils::ASLParseException("OSType 'type' not implemented");
0185     } else if (OSType == "GlbC") {
0186         throw KisAslReaderUtils::ASLParseException("OSType 'GlbC' not implemented");
0187     } else if (OSType == "alis") {
0188         throw KisAslReaderUtils::ASLParseException("OSType 'alis' not implemented");
0189     } else if (OSType == "tdta") {
0190         throw KisAslReaderUtils::ASLParseException("OSType 'tdta' not implemented");
0191     }
0192 }
0193 
0194 template<psd_byte_order byteOrder>
0195 void readDescriptor(QIODevice &device, const QString &key, QDomElement *parent, QDomDocument *doc)
0196 {
0197     using namespace KisAslReaderUtils;
0198 
0199     QString name = readUnicodeString(device);
0200     QString classId = readVarString(device);
0201 
0202     quint32 numChildren = GARBAGE_VALUE_MARK;
0203     SAFE_READ_EX(byteOrder, device, numChildren);
0204 
0205     QDomElement el = appendXMLNodeCommonNoValue(key, "Descriptor", parent, doc);
0206     el.setAttribute("classId", classId);
0207     el.setAttribute("name", name);
0208 
0209     // dbgKrita << "Descriptor" << ppVar(key) << ppVar(classId) << ppVar(numChildren);
0210 
0211     for (quint32 i = 0; i < numChildren; i++) {
0212         readChildObject<byteOrder>(device, &el, doc);
0213     }
0214 }
0215 
0216 template<psd_byte_order byteOrder>
0217 QImage readVirtualArrayList(QIODevice &device, int numPlanes, const QVector<QRgb> &palette)
0218 {
0219     using namespace KisAslReaderUtils;
0220 
0221     quint32 arrayVersion = GARBAGE_VALUE_MARK;
0222     SAFE_READ_EX(byteOrder, device, arrayVersion);
0223 
0224     if (arrayVersion != 3) {
0225         throw ASLParseException("VAList version is not '3'!");
0226     }
0227 
0228     quint32 arrayLength = GARBAGE_VALUE_MARK;
0229     SAFE_READ_EX(byteOrder, device, arrayLength);
0230 
0231     SETUP_OFFSET_VERIFIER(vaEndVerifier, device, arrayLength, 100);
0232 
0233     quint32 x0 = 0;
0234     quint32 y0 = 0;
0235     quint32 x1 = 0;
0236     quint32 y1 = 0;
0237     SAFE_READ_EX(byteOrder, device, y0);
0238     SAFE_READ_EX(byteOrder, device, x0);
0239     SAFE_READ_EX(byteOrder, device, y1);
0240     SAFE_READ_EX(byteOrder, device, x1);
0241     QRect arrayRect(x0, y0, x1 - x0, y1 - y0);
0242 
0243     quint32 numberOfChannels = GARBAGE_VALUE_MARK;
0244     SAFE_READ_EX(byteOrder, device, numberOfChannels);
0245 
0246     if (numberOfChannels != 24) {
0247         throw ASLParseException("VAList: Krita doesn't support ASL files with 'numberOfChannels' flag not equal to 24 (it is not documented)!");
0248     }
0249 
0250     // dbgKrita << ppVar(arrayVersion);
0251     // dbgKrita << ppVar(arrayLength);
0252     // dbgKrita << ppVar(arrayRect);
0253     // dbgKrita << ppVar(numberOfChannels);
0254 
0255     if (numPlanes != 1 && numPlanes != 3) {
0256         throw ASLParseException("VAList: unsupported number of planes!");
0257     }
0258 
0259     QVector<QByteArray> dataPlanes;
0260     dataPlanes.resize(3);
0261 
0262     quint32 pixelDepth1 = GARBAGE_VALUE_MARK;
0263 
0264     for (int i = 0; i < numPlanes; i++) {
0265         quint32 arrayWritten = GARBAGE_VALUE_MARK;
0266         if (!psdread<byteOrder>(device, arrayWritten) || !arrayWritten) {
0267             throw ASLParseException("VAList plane has not-written flag set!");
0268         }
0269 
0270         quint32 arrayPlaneLength = GARBAGE_VALUE_MARK;
0271         if (!psdread<byteOrder>(device, arrayPlaneLength) || !arrayPlaneLength) {
0272             throw ASLParseException("VAList has plane length set to zero!");
0273         }
0274 
0275         SETUP_OFFSET_VERIFIER(planeEndVerifier, device, arrayPlaneLength, 0);
0276         qint64 nextPos = device.pos() + arrayPlaneLength;
0277 
0278         SAFE_READ_EX(byteOrder, device, pixelDepth1);
0279 
0280         quint32 x0 = 0;
0281         quint32 y0 = 0;
0282         quint32 x1 = 0;
0283         quint32 y1 = 0;
0284         SAFE_READ_EX(byteOrder, device, y0);
0285         SAFE_READ_EX(byteOrder, device, x0);
0286         SAFE_READ_EX(byteOrder, device, y1);
0287         SAFE_READ_EX(byteOrder, device, x1);
0288         QRect planeRect(x0, y0, x1 - x0, y1 - y0);
0289 
0290         if (planeRect != arrayRect) {
0291             throw ASLParseException("VAList: planes are not uniform. Not supported yet!");
0292         }
0293 
0294         quint16 pixelDepth2 = GARBAGE_VALUE_MARK;
0295         SAFE_READ_EX(byteOrder, device, pixelDepth2);
0296 
0297         quint8 useCompression = 9;
0298         SAFE_READ_EX(byteOrder, device, useCompression);
0299 
0300         // dbgKrita << "plane index:" << ppVar(i);
0301         // dbgKrita << ppVar(arrayWritten);
0302         // dbgKrita << ppVar(arrayPlaneLength);
0303         // dbgKrita << ppVar(pixelDepth1);
0304         // dbgKrita << ppVar(planeRect);
0305         // dbgKrita << ppVar(pixelDepth2);
0306         // dbgKrita << ppVar(useCompression);
0307 
0308         if (pixelDepth1 != pixelDepth2) {
0309             throw ASLParseException("VAList: two pixel depths of the plane are not equal (it is not documented)!");
0310         }
0311 
0312         if (pixelDepth1 != 1 && pixelDepth1 != 8 && pixelDepth1 != 16) {
0313             throw ASLParseException(QString("VAList: unsupported pixel depth: %1!").arg(pixelDepth1));
0314         }
0315 
0316         const int channelSize = (pixelDepth1 == 1 || pixelDepth1 == 8) ? 1 : 2;
0317 
0318         const int dataLength = planeRect.width() * planeRect.height() * channelSize;
0319 
0320         if (useCompression == psd_compression_type::Uncompressed) {
0321             dataPlanes[i] = device.read(arrayPlaneLength - 23);
0322         } else if (useCompression == psd_compression_type::RLE) {
0323             const int numRows = planeRect.height();
0324 
0325             QVector<quint16> rowSizes;
0326             rowSizes.resize(numRows);
0327 
0328             for (int row = 0; row < numRows; row++) {
0329                 quint16 rowSize = GARBAGE_VALUE_MARK;
0330                 SAFE_READ_EX(byteOrder, device, rowSize);
0331                 rowSizes[row] = rowSize;
0332             }
0333 
0334             for (int row = 0; row < numRows; row++) {
0335                 const quint16 rowSize = rowSizes[row];
0336 
0337                 QByteArray compressedData = device.read(rowSize);
0338 
0339                 if (compressedData.size() != rowSize) {
0340                     throw ASLParseException("VAList: failed to read compressed data!");
0341                 }
0342 
0343                 dbgFile << "Going to decompress the pattern";
0344 
0345                 QByteArray uncompressedData =
0346                     Compression::uncompress(planeRect.width() * channelSize, compressedData, psd_compression_type::RLE);
0347 
0348                 if (uncompressedData.size() != planeRect.width()) {
0349                     throw ASLParseException("VAList: failed to decompress data!");
0350                 }
0351 
0352                 dataPlanes[i].append(uncompressedData);
0353             }
0354         } else if (useCompression == psd_compression_type::ZIP) {
0355             QByteArray compressedBytes = device.read(arrayPlaneLength - 23);
0356             dataPlanes[i] = Compression::uncompress(dataLength, compressedBytes, psd_compression_type::ZIP);
0357         } else {
0358             throw ASLParseException("VAList: ZIP compression is not implemented yet!");
0359         }
0360 
0361         if (dataPlanes[i].size() != dataLength) {
0362             throw ASLParseException("VAList: failed to read/uncompress data plane!");
0363         }
0364 
0365         if (device.pos() != nextPos) {
0366             warnFile << "VAList: Data is left out from reading"
0367                      << "(" << device.pos() << ")";
0368         }
0369         device.seek(nextPos);
0370     }
0371 
0372     QImage::Format format{};
0373     
0374     if (pixelDepth1 == 1 || !palette.isEmpty()) {
0375         if (palette.isEmpty()) {
0376             format = QImage::Format_Grayscale8;
0377         } else {
0378             format = QImage::Format_Indexed8;
0379         }
0380     } else if (pixelDepth1 == 8) {
0381         format = QImage::Format_ARGB32;
0382     } else {
0383 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
0384         format = QImage::Format_RGBA64;
0385 #else
0386         throw ASLParseException("Qt does not support RGBA64!");
0387 #endif
0388     }
0389 
0390     QImage image(arrayRect.size(), format);
0391 
0392     if (format == QImage::Format_Indexed8) {
0393         image.setColorTable(palette);
0394     }
0395     dbgFile << "Loading the data into an image of format" << format << arrayRect << "(" << device.pos() << ")";
0396 
0397     const int dataLength = arrayRect.width() * arrayRect.height();
0398 
0399     if (format == QImage::Format_ARGB32) {
0400         quint8 *dstPtr = image.bits();
0401 
0402         for (int i = 0; i < dataLength; i++) {
0403             for (int j = 2; j >= 0; j--) {
0404                 const int plane = qMin(numPlanes, j);
0405                 *dstPtr++ = dataPlanes[plane][i];
0406             }
0407             *dstPtr++ = 0xFF;
0408         }
0409     } else if (format == QImage::Format_Indexed8 || format == QImage::Format_Grayscale8) {
0410         const auto *dataPlane = reinterpret_cast<const quint8 *>(dataPlanes[0].constData());
0411 
0412         for (int x = 0; x < arrayRect.height(); x++) {
0413             quint8 *dstPtr = image.scanLine(x);
0414 
0415             for (int y = 0; y < arrayRect.width(); y++) {
0416                 *dstPtr++ = dataPlane[x * arrayRect.width() + y];
0417             }
0418         }
0419     } else {
0420         quint16 *dstPtr = reinterpret_cast<quint16 *>(image.bits());
0421 
0422         for (int i = 0; i < dataLength; i++) {
0423             for (int j = 0; j <= 2; j++) {
0424                 const int plane = qMin(numPlanes, j);
0425                 const quint16 *dataPlane = reinterpret_cast<const quint16 *>(dataPlanes[plane].constData());
0426                 *dstPtr++ = qFromBigEndian(dataPlane[i]);
0427             }
0428             *dstPtr++ = 0xFFFF;
0429         }
0430     }
0431 
0432     // static int i = -1; i++;
0433     // QString filename = QString("pattern_image_%1.png").arg(i);
0434     // dbgKrita << "### dumping pattern image" << ppVar(filename);
0435     // image.save(filename);
0436 
0437     return image.convertToFormat(QImage::Format_ARGB32, Qt::AutoColor | Qt::PreferDither);
0438 }
0439 
0440 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0441 qint64 readPattern(QIODevice &device, QDomElement *parent, QDomDocument *doc)
0442 {
0443     using namespace KisAslReaderUtils;
0444 
0445     quint32 patternSize = GARBAGE_VALUE_MARK;
0446     SAFE_READ_EX(byteOrder, device, patternSize);
0447 
0448     // patterns are always aligned by 4 bytes
0449     patternSize = KisAslWriterUtils::alignOffsetCeil(patternSize, 4);
0450 
0451     SETUP_OFFSET_VERIFIER(patternEndVerifier, device, patternSize, 0);
0452 
0453     quint32 patternVersion = GARBAGE_VALUE_MARK;
0454     SAFE_READ_EX(byteOrder, device, patternVersion);
0455 
0456     if (patternVersion != 1) {
0457         throw ASLParseException("Pattern version is not \'1\'");
0458     }
0459 
0460     quint32 patternImageMode = GARBAGE_VALUE_MARK;
0461     SAFE_READ_EX(byteOrder, device, patternImageMode);
0462 
0463     dbgFile << "Pattern format:" << patternImageMode << "(" << device.pos() << ")";
0464 
0465     quint16 patternHeight = GARBAGE_VALUE_MARK;
0466     SAFE_READ_EX(byteOrder, device, patternHeight);
0467 
0468     dbgFile << "Pattern height:" << patternHeight << "(" << device.pos() << ")";
0469 
0470     quint16 patternWidth = GARBAGE_VALUE_MARK;
0471     SAFE_READ_EX(byteOrder, device, patternWidth);
0472 
0473     dbgFile << "Pattern width:" << patternHeight << "(" << device.pos() << ")";
0474 
0475     QString patternName;
0476     psdread_unicodestring<byteOrder>(device, patternName);
0477 
0478     dbgFile << "Pattern name:" << patternName << "(" << device.pos() << ")";
0479 
0480     QString patternUuid = readPascalString<byteOrder>(device);
0481 
0482     dbgFile << "Pattern UUID:" << patternUuid << "(" << device.pos() << ")";
0483 
0484     // dbgKrita << "--";
0485     // dbgKrita << ppVar(patternSize);
0486     // dbgKrita << ppVar(patternImageMode);
0487     // dbgKrita << ppVar(patternHeight);
0488     // dbgKrita << ppVar(patternWidth);
0489     // dbgKrita << ppVar(patternName);
0490     // dbgKrita << ppVar(patternUuid);
0491 
0492     int numPlanes = 0;
0493     psd_color_mode mode = static_cast<psd_color_mode>(patternImageMode);
0494 
0495     switch (mode) {
0496     case MultiChannel:
0497     case Grayscale:
0498     case Indexed:
0499         numPlanes = 1;
0500         break;
0501     case RGB:
0502         numPlanes = 3;
0503         break;
0504     default: {
0505         QString msg = QString("Unsupported image mode: %1!").arg(mode);
0506         throw ASLParseException(msg);
0507     }
0508     }
0509 
0510     QVector<QRgb> palette;
0511 
0512     if (mode == Indexed) {
0513 
0514         palette.resize(256);
0515 
0516         for(auto i = 0; i < 256; i++) {
0517             quint8 r = 0;
0518             quint8 g = 0;
0519             quint8 b = 0;
0520             psdread<byteOrder>(device, r);
0521             psdread<byteOrder>(device, g);
0522             psdread<byteOrder>(device, b);
0523             palette[i] = qRgb(r, g, b);
0524         }
0525 
0526         dbgFile << "Palette: " << palette << "(" << device.pos() << ")";
0527 
0528         // XXX: there's no way to detect this. Assume the 772 length
0529         quint16 validColours = GARBAGE_VALUE_MARK;
0530         psdread<byteOrder>(device, validColours);
0531         palette.resize(validColours);
0532         dbgFile << "Palette real size:" << validColours << "(" << device.pos() << ")";
0533 
0534         // Set transparent colour
0535         quint16 transparentIdx = GARBAGE_VALUE_MARK;
0536         psdread<byteOrder>(device, transparentIdx);
0537         dbgFile << "Transparent index:" << transparentIdx << "(" << device.pos() << ")";
0538 
0539         palette[transparentIdx] = qRgba(qRed(palette[transparentIdx]),
0540                                         qGreen(palette[transparentIdx]),
0541                                         qBlue(palette[transparentIdx]), 0x00);
0542     }
0543 
0544     /**
0545      * Create XML data
0546      */
0547 
0548     QDomElement pat = doc->createElement("node");
0549 
0550     pat.setAttribute("classId", "KisPattern");
0551     pat.setAttribute("type", "Descriptor");
0552     pat.setAttribute("name", "");
0553 
0554     QBuffer patternBuf;
0555     patternBuf.open(QIODevice::WriteOnly);
0556 
0557     { // ensure we don't keep resources for too long
0558         // XXX: this QImage should tolerate 16-bit and higher
0559         QString fileName = QString("%1.pat").arg(patternUuid);
0560         QImage patternImage = readVirtualArrayList<byteOrder>(device, numPlanes, palette);
0561         KoPattern realPattern(patternImage, patternName, fileName);
0562         realPattern.savePatToDevice(&patternBuf);
0563     }
0564 
0565     /**
0566      * We are loading the pattern and convert it into ARGB right away,
0567      * so we need not store real image mode and size of the pattern
0568      * externally.
0569      */
0570     appendTextXMLNode("Nm  ", patternName, &pat, doc);
0571     appendTextXMLNode("Idnt", patternUuid, &pat, doc);
0572 
0573     QDomCDATASection dataSection = doc->createCDATASection(qCompress(patternBuf.buffer()).toBase64());
0574 
0575     QDomElement dataElement = doc->createElement("node");
0576     dataElement.setAttribute("type", "KisPatternData");
0577     dataElement.setAttribute("key", "Data");
0578 
0579     dataElement.appendChild(dataSection);
0580     pat.appendChild(dataElement);
0581     parent->appendChild(pat);
0582 
0583     return sizeof(patternSize) + patternSize;
0584 }
0585 
0586 QDomDocument readFileImpl(QIODevice &device)
0587 {
0588     using namespace KisAslReaderUtils;
0589 
0590     QDomDocument doc;
0591     QDomElement root = doc.createElement("asl");
0592     doc.appendChild(root);
0593 
0594     {
0595         quint16 stylesVersion = GARBAGE_VALUE_MARK;
0596         SAFE_READ_SIGNATURE_EX(psd_byte_order::psdBigEndian, device, stylesVersion, 2);
0597     }
0598 
0599     {
0600         quint32 aslSignature = GARBAGE_VALUE_MARK;
0601         const quint32 refSignature = 0x3842534c; // '8BSL' in little-endian
0602         SAFE_READ_SIGNATURE_EX(psd_byte_order::psdBigEndian, device, aslSignature, refSignature);
0603     }
0604 
0605     {
0606         quint16 patternsVersion = GARBAGE_VALUE_MARK;
0607         SAFE_READ_SIGNATURE_EX(psd_byte_order::psdBigEndian, device, patternsVersion, 3);
0608     }
0609 
0610     // Patterns
0611 
0612     {
0613         quint32 patternsSize = GARBAGE_VALUE_MARK;
0614         SAFE_READ_EX(psd_byte_order::psdBigEndian, device, patternsSize);
0615 
0616         if (patternsSize > 0) {
0617             SETUP_OFFSET_VERIFIER(patternsSectionVerifier, device, patternsSize, 0);
0618 
0619             QDomElement patternsRoot = doc.createElement("node");
0620             patternsRoot.setAttribute("type", "List");
0621             patternsRoot.setAttribute("key", ResourceType::Patterns);
0622             root.appendChild(patternsRoot);
0623 
0624             try {
0625                 qint64 bytesRead = 0;
0626                 while (bytesRead < patternsSize) {
0627                     qint64 chunk = readPattern(device, &patternsRoot, &doc);
0628                     bytesRead += chunk;
0629                 }
0630             } catch (ASLParseException &e) {
0631                 warnKrita << "WARNING: ASL (emb. pattern):" << e.what();
0632             }
0633         }
0634     }
0635 
0636     // Styles
0637 
0638     quint32 numStyles = GARBAGE_VALUE_MARK;
0639     SAFE_READ_EX(psd_byte_order::psdBigEndian, device, numStyles);
0640 
0641     for (int i = 0; i < (int)numStyles; i++) {
0642         quint32 bytesToRead = GARBAGE_VALUE_MARK;
0643         SAFE_READ_EX(psd_byte_order::psdBigEndian, device, bytesToRead);
0644 
0645         SETUP_OFFSET_VERIFIER(singleStyleSectionVerifier, device, bytesToRead, 0);
0646 
0647         {
0648             quint32 stylesFormatVersion = GARBAGE_VALUE_MARK;
0649             SAFE_READ_SIGNATURE_EX(psd_byte_order::psdBigEndian, device, stylesFormatVersion, 16);
0650         }
0651 
0652         readDescriptor(device, "", &root, &doc);
0653 
0654         {
0655             quint32 stylesFormatVersion = GARBAGE_VALUE_MARK;
0656             SAFE_READ_SIGNATURE_EX(psd_byte_order::psdBigEndian, device, stylesFormatVersion, 16);
0657         }
0658 
0659         readDescriptor(device, "", &root, &doc);
0660     }
0661 
0662     return doc;
0663 }
0664 
0665 } // namespace Private
0666 
0667 QDomDocument KisAslReader::readFile(QIODevice &device)
0668 {
0669     QDomDocument doc;
0670 
0671     if (device.isSequential()) {
0672         warnKrita << "WARNING: *** KisAslReader::readFile: the supplied"
0673                   << "IO device is sequential. Chances are that"
0674                   << "the layer style will *not* be loaded correctly!";
0675     }
0676 
0677     try {
0678         doc = Private::readFileImpl(device);
0679     } catch (KisAslReaderUtils::ASLParseException &e) {
0680         warnKrita << "WARNING: ASL:" << e.what();
0681     }
0682 
0683     return doc;
0684 }
0685 
0686 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0687 QDomDocument readLfx2PsdSectionImpl(QIODevice &device);
0688 
0689 QDomDocument KisAslReader::readLfx2PsdSection(QIODevice &device, psd_byte_order byteOrder)
0690 {
0691     switch (byteOrder) {
0692     case psd_byte_order::psdLittleEndian:
0693         return readLfx2PsdSectionImpl<psd_byte_order::psdLittleEndian>(device);
0694     default:
0695         return readLfx2PsdSectionImpl(device);
0696     }
0697 }
0698 
0699 template<psd_byte_order byteOrder>
0700 QDomDocument readLfx2PsdSectionImpl(QIODevice &device)
0701 {
0702     QDomDocument doc;
0703 
0704     if (device.isSequential()) {
0705         warnKrita << "WARNING: *** KisAslReader::readLfx2PsdSection: the supplied"
0706                   << "IO device is sequential. Chances are that"
0707                   << "the layer style will *not* be loaded correctly!";
0708     }
0709 
0710     try {
0711         {
0712             quint32 objectEffectsVersion = GARBAGE_VALUE_MARK;
0713             const quint32 ref = 0x00;
0714             SAFE_READ_SIGNATURE_EX(byteOrder, device, objectEffectsVersion, ref);
0715         }
0716 
0717         {
0718             quint32 descriptorVersion = GARBAGE_VALUE_MARK;
0719             const quint32 ref = 0x10;
0720             SAFE_READ_SIGNATURE_EX(byteOrder, device, descriptorVersion, ref);
0721         }
0722 
0723         QDomElement root = doc.createElement("asl");
0724         doc.appendChild(root);
0725 
0726         Private::readDescriptor<byteOrder>(device, "", &root, &doc);
0727 
0728     } catch (KisAslReaderUtils::ASLParseException &e) {
0729         warnKrita << "WARNING: PSD: lfx2 section:" << e.what();
0730     }
0731 
0732     return doc;
0733 }
0734 
0735 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0736 QDomDocument readFillLayerImpl(QIODevice &device);
0737 
0738 QDomDocument KisAslReader::readFillLayer(QIODevice &device, psd_byte_order byteOrder)
0739 {
0740     switch (byteOrder) {
0741     case psd_byte_order::psdLittleEndian:
0742         return readFillLayerImpl<psd_byte_order::psdLittleEndian>(device);
0743     default:
0744         return readFillLayerImpl(device);
0745     }
0746 }
0747 
0748 template<psd_byte_order byteOrder>
0749 QDomDocument readFillLayerImpl(QIODevice &device)
0750 {
0751     QDomDocument doc;
0752 
0753     if (device.isSequential()) {
0754         warnKrita << "WARNING: *** KisAslReader::readFillLayerPsdSection: the supplied"
0755                   << "IO device is sequential. Chances are that"
0756                   << "the fill config will *not* be loaded correctly!";
0757     }
0758     try {
0759 
0760         {
0761             quint32 descriptorVersion = GARBAGE_VALUE_MARK;
0762             SAFE_READ_SIGNATURE_EX(byteOrder, device, descriptorVersion, 16);
0763         }
0764 
0765         QDomElement root = doc.createElement("asl");
0766         doc.appendChild(root);
0767 
0768         Private::readDescriptor<byteOrder>(device, "", &root, &doc);
0769 
0770     } catch (KisAslReaderUtils::ASLParseException &e) {
0771         warnKrita << "WARNING: PSD: SoCo section:" << e.what();
0772     }
0773 
0774     return doc;
0775 }
0776 
0777 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0778 QDomDocument readPsdSectionPatternImpl(QIODevice &device, qint64 bytesLeft);
0779 
0780 QDomDocument KisAslReader::readPsdSectionPattern(QIODevice &device, qint64 bytesLeft, psd_byte_order byteOrder)
0781 {
0782     switch (byteOrder) {
0783     case psd_byte_order::psdLittleEndian:
0784         return readPsdSectionPatternImpl<psd_byte_order::psdLittleEndian>(device, bytesLeft);
0785     default:
0786         return readPsdSectionPatternImpl(device, bytesLeft);
0787     }
0788 }
0789 
0790 template<psd_byte_order byteOrder>
0791 QDomDocument readPsdSectionPatternImpl(QIODevice &device, qint64 bytesLeft)
0792 {
0793     QDomDocument doc;
0794 
0795     QDomElement root = doc.createElement("asl");
0796     doc.appendChild(root);
0797 
0798     QDomElement pat = doc.createElement("node");
0799     root.appendChild(pat);
0800 
0801     pat.setAttribute("classId", ResourceType::Patterns);
0802     pat.setAttribute("type", "Descriptor");
0803     pat.setAttribute("name", "");
0804 
0805     try {
0806         qint64 bytesRead = 0;
0807         while (bytesRead < bytesLeft) {
0808             qint64 chunk = Private::readPattern<byteOrder>(device, &pat, &doc);
0809             bytesRead += chunk;
0810         }
0811     } catch (KisAslReaderUtils::ASLParseException &e) {
0812         warnKrita << "WARNING: PSD (emb. pattern):" << e.what();
0813     }
0814 
0815     return doc;
0816 }