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 }