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 }