File indexing completed on 2024-05-12 15:59:38
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Boudewijn Rempt <boud@valdyas.org> 0003 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "psd_additional_layer_info_block.h" 0009 #include "psd.h" 0010 0011 #include <QDomDocument> 0012 0013 #include <asl/kis_offset_on_exit_verifier.h> 0014 0015 #include <asl/kis_asl_patterns_writer.h> 0016 #include <asl/kis_asl_reader.h> 0017 #include <asl/kis_asl_reader_utils.h> 0018 #include <asl/kis_asl_writer.h> 0019 #include <asl/kis_asl_writer_utils.h> 0020 0021 0022 PsdAdditionalLayerInfoBlock::PsdAdditionalLayerInfoBlock(const PSDHeader &header) 0023 : m_header(header) 0024 , sectionDividerType(psd_other) 0025 { 0026 } 0027 0028 void PsdAdditionalLayerInfoBlock::setExtraLayerInfoBlockHandler(ExtraLayerInfoBlockHandler handler) 0029 { 0030 m_layerInfoBlockHandler = handler; 0031 } 0032 0033 void PsdAdditionalLayerInfoBlock::setUserMaskInfoBlockHandler(UserMaskInfoBlockHandler handler) 0034 { 0035 m_userMaskBlockHandler = handler; 0036 } 0037 0038 bool PsdAdditionalLayerInfoBlock::read(QIODevice &io) 0039 { 0040 bool result = true; 0041 0042 try { 0043 switch (m_header.byteOrder) { 0044 case psd_byte_order::psdLittleEndian: 0045 readImpl<psd_byte_order::psdLittleEndian>(io); 0046 break; 0047 default: 0048 readImpl(io); 0049 break; 0050 } 0051 } catch (KisAslReaderUtils::ASLParseException &e) { 0052 error = e.what(); 0053 result = false; 0054 } 0055 0056 return result; 0057 } 0058 0059 template<psd_byte_order byteOrder> 0060 void PsdAdditionalLayerInfoBlock::readImpl(QIODevice &io) 0061 { 0062 using namespace KisAslReaderUtils; 0063 0064 QStringList longBlocks; 0065 if (m_header.version > 1) { 0066 longBlocks << "LMsk" 0067 << "Lr16" 0068 << "Lr32" 0069 << "Layr" 0070 << "Mt16" 0071 << "Mt32" 0072 << "Mtrn" 0073 << "Alph" 0074 << "FMsk" 0075 << "lnk2" 0076 << "FEid" 0077 << "FXid" 0078 << "PxSD"; 0079 } 0080 0081 while (!io.atEnd()) { 0082 { 0083 const std::array<quint8, 4> refSignature1 = {'8', 'B', 'I', 'M'}; // '8BIM' in big-endian 0084 const std::array<quint8, 4> refSignature2 = {'8', 'B', '6', '4'}; // '8B64' in big-endian 0085 0086 if (!TRY_READ_SIGNATURE_2OPS_EX<byteOrder>(io, refSignature1, refSignature2)) { 0087 break; 0088 } 0089 } 0090 0091 QString key = readFixedString<byteOrder>(io); 0092 dbgFile << "found info block with key" << key << "(" << io.pos() << ")"; 0093 0094 quint64 blockSize = GARBAGE_VALUE_MARK; 0095 if (longBlocks.contains(key)) { 0096 SAFE_READ_EX(byteOrder, io, blockSize); 0097 } else { 0098 quint32 size32; 0099 SAFE_READ_EX(byteOrder, io, size32); 0100 blockSize = size32; 0101 } 0102 0103 // Since TIFF headers are padded to multiples of 4, 0104 // staving them off here is way easier. 0105 if (m_header.tiffStyleLayerBlock) { 0106 if (blockSize % 4U) { 0107 dbgFile << "(TIFF) WARNING: current block size is NOT a multiple of 4! Fixing..."; 0108 blockSize += (4U - blockSize % 4U); 0109 } 0110 } 0111 0112 dbgFile << "info block size" << blockSize << "(" << io.pos() << ")"; 0113 0114 if (blockSize == 0) 0115 continue; 0116 0117 // offset verifier will correct the position on the exit from 0118 // current namespace, including 'continue', 'return' and 0119 // exceptions. 0120 SETUP_OFFSET_VERIFIER(infoBlockEndVerifier, io, blockSize, 0); 0121 0122 if (keys.contains(key)) { 0123 error = "Found duplicate entry for key "; 0124 continue; 0125 } 0126 keys << key; 0127 0128 // TODO: Loading of 32 bit files is not supported yet 0129 if (key == "Lr16" /* || key == "Lr32"*/) { 0130 if (m_layerInfoBlockHandler) { 0131 int offset = m_header.version > 1 ? 8 : 4; 0132 dbgFile << "Offset for block handler: " << io.pos() << offset; 0133 io.seek(io.pos() - offset); 0134 m_layerInfoBlockHandler(io); 0135 } 0136 } else if (key == "Layr") { 0137 if (m_header.tiffStyleLayerBlock && m_layerInfoBlockHandler) { 0138 int offset = m_header.version > 1 ? 8 : 4; 0139 dbgFile << "(TIFF) Offset for block handler: " << io.pos() << offset; 0140 io.seek(io.pos() - offset); 0141 m_layerInfoBlockHandler(io); 0142 } 0143 } else if (key == "SoCo") { 0144 // Solid Color 0145 fillConfig = KisAslReader::readFillLayer(io, byteOrder); 0146 fillType = psd_fill_solid_color; 0147 } else if (key == "GdFl") { 0148 // Gradient Fill 0149 fillConfig = KisAslReader::readFillLayer(io, byteOrder); 0150 fillType = psd_fill_gradient; 0151 } else if (key == "PtFl") { 0152 // Pattern Fill 0153 fillConfig = KisAslReader::readFillLayer(io, byteOrder); 0154 fillType = psd_fill_pattern; 0155 } else if (key == "brit") { 0156 } else if (key == "levl") { 0157 } else if (key == "curv") { 0158 } else if (key == "expA") { 0159 } else if (key == "vibA") { 0160 } else if (key == "hue") { 0161 } else if (key == "hue2") { 0162 } else if (key == "blnc") { 0163 } else if (key == "blwh") { 0164 } else if (key == "phfl") { 0165 } else if (key == "mixr") { 0166 } else if (key == "clrL") { 0167 } else if (key == "nvrt") { 0168 } else if (key == "post") { 0169 } else if (key == "thrs") { 0170 } else if (key == "selc") { 0171 } else if (key == "lrFX") { 0172 // deprecated! use lfx2 instead! 0173 } else if (key == "tySh") { 0174 } else if (key == "luni") { 0175 // get the unicode layer name 0176 unicodeLayerName = readUnicodeString<byteOrder>(io); 0177 dbgFile << "\t" << "unicodeLayerName" << unicodeLayerName; 0178 } else if (key == "lyid") { 0179 quint32 id; 0180 psdread<byteOrder>(io, id); 0181 dbgFile << "\t" << "layer ID:" << id; 0182 } else if (key == "lfx2" || key == "lfxs") { 0183 // lfxs is a special variant of layer styles for group layers 0184 layerStyleXml = KisAslReader::readLfx2PsdSection(io, byteOrder); 0185 } else if (key == "Patt" || key == "Pat2" || key == "Pat3") { 0186 QDomDocument pattern = KisAslReader::readPsdSectionPattern(io, blockSize, byteOrder); 0187 embeddedPatterns << pattern; 0188 } else if (key == "Anno") { 0189 } else if (key == "clbl") { 0190 } else if (key == "infx") { 0191 } else if (key == "knko") { 0192 } else if (key == "spf") { 0193 } else if (key == "lclr") { 0194 // layer label color. 0195 quint16 col1 = 0; 0196 quint16 col2 = 0; 0197 quint16 col3 = 0; 0198 quint16 col4 = 0; 0199 psdread<byteOrder>(io, col1); 0200 psdread<byteOrder>(io, col2); 0201 psdread<byteOrder>(io, col3); 0202 psdread<byteOrder>(io, col4); 0203 dbgFile << "\t" << "layer color:" << col1 << col2 << col3 << col4; 0204 labelColor = col1; 0205 } else if (key == "fxrp") { 0206 } else if (key == "grdm") { 0207 } else if (key == "lsct") { 0208 quint32 dividerType = GARBAGE_VALUE_MARK; 0209 SAFE_READ_EX(byteOrder, io, dividerType); 0210 this->sectionDividerType = (psd_section_type)dividerType; 0211 0212 dbgFile << "Reading \"lsct\" block:"; 0213 dbgFile << ppVar(blockSize); 0214 dbgFile << ppVar(dividerType); 0215 0216 if (blockSize >= 12) { 0217 quint32 lsctSignature = GARBAGE_VALUE_MARK; 0218 const quint32 refSignature1 = 0x3842494D; // '8BIM' in little-endian 0219 SAFE_READ_SIGNATURE_EX(byteOrder, io, lsctSignature, refSignature1); 0220 0221 this->sectionDividerBlendMode = readFixedString<byteOrder>(io); 0222 0223 dbgFile << ppVar(this->sectionDividerBlendMode); 0224 } 0225 0226 // Animation 0227 if (blockSize >= 14) { 0228 /** 0229 * "I don't care 0230 * I don't care, no... !" (c) 0231 */ 0232 } 0233 0234 } else if (key == "brst") { 0235 } else if (key == "vmsk" || key == "vsms") { // If key is "vsms" then we are writing for (Photoshop CS6) and the document will have a "vscg" key 0236 0237 } else if (key == "TySh") { 0238 } else if (key == "ffxi") { 0239 } else if (key == "lnsr") { 0240 } else if (key == "shpa") { 0241 } else if (key == "shmd") { 0242 } else if (key == "lyvr") { 0243 } else if (key == "tsly") { 0244 } else if (key == "lmgm") { 0245 } else if (key == "vmgm") { 0246 } else if (key == "plLd") { // Replaced by SoLd in CS3 0247 0248 } else if (key == "linkD" || key == "lnk2" || key == "lnk3") { 0249 } else if (key == "CgEd") { 0250 } else if (key == "Txt2") { 0251 } else if (key == "pths") { 0252 } else if (key == "anFX") { 0253 } else if (key == "FMsk") { 0254 } else if (key == "SoLd") { 0255 } else if (key == "vstk") { 0256 } else if (key == "vsCg") { 0257 } else if (key == "sn2P") { 0258 } else if (key == "vogk") { 0259 } else if (key == "Mtrn" || key == "Mt16" || key == "Mt32") { // There is no data associated with these keys. 0260 0261 } else if (key == "LMsk") { 0262 // TIFFs store the global mask here. 0263 if (m_header.tiffStyleLayerBlock) { 0264 int offset = m_header.version > 1 ? 8 : 4; 0265 dbgFile << "(TIFF) Offset for block handler: " << io.pos() << offset; 0266 io.seek(io.pos() - offset); 0267 m_userMaskBlockHandler(io); 0268 } 0269 } else if (key == "FXid") { 0270 } else if (key == "FEid") { 0271 } 0272 } 0273 } 0274 0275 bool PsdAdditionalLayerInfoBlock::write(QIODevice & /*io*/, KisNodeSP /*node*/) 0276 { 0277 return true; 0278 } 0279 0280 bool PsdAdditionalLayerInfoBlock::valid() 0281 { 0282 return true; 0283 } 0284 0285 void PsdAdditionalLayerInfoBlock::writeLuniBlockEx(QIODevice &io, const QString &layerName) 0286 { 0287 switch (m_header.byteOrder) { 0288 case psd_byte_order::psdLittleEndian: 0289 writeLuniBlockExImpl<psd_byte_order::psdLittleEndian>(io, layerName); 0290 break; 0291 default: 0292 writeLuniBlockExImpl(io, layerName); 0293 break; 0294 } 0295 } 0296 0297 template<psd_byte_order byteOrder> 0298 void PsdAdditionalLayerInfoBlock::writeLuniBlockExImpl(QIODevice &io, const QString &layerName) 0299 { 0300 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io); 0301 KisAslWriterUtils::writeFixedString<byteOrder>("luni", io); 0302 KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> layerNameSizeTag(io, 2); 0303 KisAslWriterUtils::writeUnicodeString<byteOrder>(layerName, io); 0304 } 0305 0306 void PsdAdditionalLayerInfoBlock::writeLsctBlockEx(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey) 0307 { 0308 switch (m_header.byteOrder) { 0309 case psd_byte_order::psdLittleEndian: 0310 writeLsctBlockExImpl<psd_byte_order::psdLittleEndian>(io, sectionType, isPassThrough, blendModeKey); 0311 break; 0312 default: 0313 writeLsctBlockExImpl(io, sectionType, isPassThrough, blendModeKey); 0314 break; 0315 } 0316 } 0317 0318 template<psd_byte_order byteOrder> 0319 void PsdAdditionalLayerInfoBlock::writeLsctBlockExImpl(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey) 0320 { 0321 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io); 0322 KisAslWriterUtils::writeFixedString<byteOrder>("lsct", io); 0323 KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> sectionTypeSizeTag(io, 2); 0324 SAFE_WRITE_EX(byteOrder, io, (quint32)sectionType); 0325 0326 QString realBlendModeKey = isPassThrough ? QString("pass") : blendModeKey; 0327 0328 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io); 0329 KisAslWriterUtils::writeFixedString<byteOrder>(realBlendModeKey, io); 0330 } 0331 0332 void PsdAdditionalLayerInfoBlock::writeLfx2BlockEx(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat) 0333 { 0334 switch (m_header.byteOrder) { 0335 case psd_byte_order::psdLittleEndian: 0336 writeLfx2BlockExImpl<psd_byte_order::psdLittleEndian>(io, stylesXmlDoc, useLfxsLayerStyleFormat); 0337 break; 0338 default: 0339 writeLfx2BlockExImpl(io, stylesXmlDoc, useLfxsLayerStyleFormat); 0340 break; 0341 } 0342 } 0343 0344 template<psd_byte_order byteOrder> 0345 void PsdAdditionalLayerInfoBlock::writeLfx2BlockExImpl(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat) 0346 { 0347 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io); 0348 // 'lfxs' format is used for Group layers in PS 0349 KisAslWriterUtils::writeFixedString<byteOrder>(!useLfxsLayerStyleFormat ? "lfx2" : "lfxs", io); 0350 KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> lfx2SizeTag(io, 2); 0351 0352 try { 0353 KisAslWriter writer(byteOrder); 0354 writer.writePsdLfx2SectionEx(io, stylesXmlDoc); 0355 0356 } catch (KisAslWriterUtils::ASLWriteException &e) { 0357 warnKrita << "WARNING: Couldn't save layer style lfx2 block:" << PREPEND_METHOD(e.what()); 0358 0359 // TODO: make this error recoverable! 0360 throw e; 0361 } 0362 } 0363 0364 void PsdAdditionalLayerInfoBlock::writePattBlockEx(QIODevice &io, const QDomDocument &patternsXmlDoc) 0365 { 0366 switch (m_header.byteOrder) { 0367 case psd_byte_order::psdLittleEndian: 0368 writePattBlockExImpl<psd_byte_order::psdLittleEndian>(io, patternsXmlDoc); 0369 break; 0370 default: 0371 writePattBlockExImpl(io, patternsXmlDoc); 0372 break; 0373 } 0374 } 0375 0376 void PsdAdditionalLayerInfoBlock::writeLclrBlockEx(QIODevice &io, const quint16 &labelColor) 0377 { 0378 switch (m_header.byteOrder) { 0379 case psd_byte_order::psdLittleEndian: 0380 writeLclrBlockExImpl<psd_byte_order::psdLittleEndian>(io, labelColor); 0381 break; 0382 default: 0383 writeLclrBlockExImpl(io, labelColor); 0384 break; 0385 } 0386 } 0387 0388 void PsdAdditionalLayerInfoBlock::writeFillLayerBlockEx(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type) 0389 { 0390 switch (m_header.byteOrder) { 0391 case psd_byte_order::psdLittleEndian: 0392 writeFillLayerBlockExImpl<psd_byte_order::psdLittleEndian>(io, fillConfig, type); 0393 break; 0394 default: 0395 writeFillLayerBlockExImpl(io, fillConfig, type); 0396 break; 0397 } 0398 } 0399 0400 template<psd_byte_order byteOrder> 0401 void PsdAdditionalLayerInfoBlock::writePattBlockExImpl(QIODevice &io, const QDomDocument &patternsXmlDoc) 0402 { 0403 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io); 0404 KisAslWriterUtils::writeFixedString<byteOrder>("Patt", io); 0405 const quint32 padding = m_header.tiffStyleLayerBlock ? 4 : 2; 0406 KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> pattSizeTag(io, padding); 0407 0408 try { 0409 KisAslPatternsWriter writer(patternsXmlDoc, io, byteOrder); 0410 writer.writePatterns(); 0411 0412 } catch (KisAslWriterUtils::ASLWriteException &e) { 0413 warnKrita << "WARNING: Couldn't save layer style patterns block:" << PREPEND_METHOD(e.what()); 0414 0415 // TODO: make this error recoverable! 0416 throw e; 0417 } 0418 } 0419 0420 template<psd_byte_order byteOrder> 0421 void PsdAdditionalLayerInfoBlock::writeLclrBlockExImpl(QIODevice &io, const quint16 &lclr) 0422 { 0423 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io); 0424 KisAslWriterUtils::writeFixedString<byteOrder>("lclr", io); 0425 // 4x2 quint16 0426 const quint32 len = 8; 0427 SAFE_WRITE_EX(byteOrder, io, len); 0428 quint16 zero = 0; 0429 SAFE_WRITE_EX(byteOrder, io, lclr); 0430 SAFE_WRITE_EX(byteOrder, io, zero); 0431 SAFE_WRITE_EX(byteOrder, io, zero); 0432 SAFE_WRITE_EX(byteOrder, io, zero); 0433 0434 0435 } 0436 0437 template<psd_byte_order byteOrder> 0438 void PsdAdditionalLayerInfoBlock::writeFillLayerBlockExImpl(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type) 0439 { 0440 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io); 0441 if (type == psd_fill_solid_color) { 0442 KisAslWriterUtils::writeFixedString<byteOrder>("SoCo", io); 0443 } else if (type == psd_fill_gradient) { 0444 KisAslWriterUtils::writeFixedString<byteOrder>("GdFl", io); 0445 } else { 0446 KisAslWriterUtils::writeFixedString<byteOrder>("PtFl", io); 0447 } 0448 KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> fillSizeTag(io, 2); 0449 0450 try { 0451 KisAslWriter writer(byteOrder); 0452 0453 writer.writeFillLayerSectionEx(io, fillConfig); 0454 0455 } catch (KisAslWriterUtils::ASLWriteException &e) { 0456 warnKrita << "WARNING: Couldn't save fill layer block:" << PREPEND_METHOD(e.what()); 0457 0458 // TODO: make this error recoverable! 0459 throw e; 0460 } 0461 }