File indexing completed on 2024-05-12 15:59:39
0001 /* 0002 * SPDX-FileCopyrightText: 2009 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 #include "psd_header.h" 0008 0009 #include <QByteArray> 0010 #include <QIODevice> 0011 #include <QtEndian> 0012 #include <psd_utils.h> 0013 0014 struct Header { 0015 char signature[4]; // 8PBS 0016 char version[2]; // 1 or 2 0017 char padding[6]; 0018 char nChannels[2]; // 1 - 56 0019 char height[4]; // 1-30,000 or 1 - 300,000 0020 char width[4]; // 1-30,000 or 1 - 300,000 0021 char channelDepth[2]; // 1, 8, 16 0022 char colormode[2]; // 0-9 0023 }; 0024 0025 PSDHeader::PSDHeader() 0026 : version(0) 0027 , nChannels(0) 0028 , height(0) 0029 , width(0) 0030 , channelDepth(0) 0031 , colormode(COLORMODE_UNKNOWN) 0032 , byteOrder(psd_byte_order::psdBigEndian) 0033 , tiffStyleLayerBlock(false) 0034 { 0035 } 0036 0037 bool PSDHeader::read(QIODevice &device) 0038 { 0039 Header header; 0040 quint64 bytesRead = device.read((char *)&header, sizeof(Header)); 0041 if (bytesRead != sizeof(Header)) { 0042 error = "Could not read header: not enough bytes"; 0043 return false; 0044 } 0045 0046 signature = QString(header.signature); 0047 memcpy(&version, header.version, 2); 0048 version = qFromBigEndian(version); 0049 memcpy(&nChannels, header.nChannels, 2); 0050 nChannels = qFromBigEndian(nChannels); 0051 memcpy(&height, header.height, 4); 0052 height = qFromBigEndian(height); 0053 memcpy(&width, header.width, 4); 0054 width = qFromBigEndian(width); 0055 memcpy(&channelDepth, header.channelDepth, 2); 0056 channelDepth = qFromBigEndian(channelDepth); 0057 memcpy(&colormode, header.colormode, 2); 0058 colormode = (psd_color_mode)qFromBigEndian((quint16)colormode); 0059 0060 return valid(); 0061 } 0062 0063 bool PSDHeader::write(QIODevice &device) 0064 { 0065 if (!valid()) 0066 return false; 0067 if (!psdwrite(device, signature)) 0068 return false; 0069 if (!psdwrite(device, version)) 0070 return false; 0071 if (!psdpad(device, 6)) 0072 return false; 0073 if (!psdwrite(device, nChannels)) 0074 return false; 0075 if (!psdwrite(device, height)) 0076 return false; 0077 if (!psdwrite(device, width)) 0078 return false; 0079 if (!psdwrite(device, channelDepth)) 0080 return false; 0081 if (!psdwrite(device, (quint16)colormode)) 0082 return false; 0083 return true; 0084 } 0085 0086 bool PSDHeader::valid() 0087 { 0088 if (signature != "8BPS") { 0089 error = "Not a PhotoShop document. Signature is: " + signature; 0090 return false; 0091 } 0092 if (version < 1 || version > 2) { 0093 error = QString("Wrong version: %1").arg(version); 0094 return false; 0095 } 0096 if (nChannels < 1 || nChannels > 56) { 0097 error = QString("Channel count out of range: %1").arg(nChannels); 0098 return false; 0099 } 0100 if (version == 1) { 0101 if (height < 1 || height > 30000) { 0102 error = QString("Height out of range: %1").arg(height); 0103 return false; 0104 } 0105 if (width < 1 || width > 30000) { 0106 error = QString("Width out of range: %1").arg(width); 0107 return false; 0108 } 0109 } else /* if (version == 2) */ { 0110 if (height < 1 || height > 300000) { 0111 error = QString("Height out of range: %1").arg(height); 0112 return false; 0113 } 0114 if (width < 1 || width > 300000) { 0115 error = QString("Width out of range: %1").arg(width); 0116 return false; 0117 } 0118 } 0119 if (channelDepth != 1 && channelDepth != 8 && channelDepth != 16) { 0120 error = QString("Channel depth incorrect: %1").arg(channelDepth); 0121 return false; 0122 } 0123 if (colormode < 0 || colormode > 9) { 0124 error = QString("Colormode is out of range: %1").arg(colormode); 0125 return false; 0126 } 0127 0128 return true; 0129 } 0130 0131 QDebug operator<<(QDebug dbg, const PSDHeader &header) 0132 { 0133 #ifndef NODEBUG 0134 dbg.nospace() << "(valid: " << const_cast<PSDHeader *>(&header)->valid(); 0135 dbg.nospace() << ", signature: " << header.signature; 0136 dbg.nospace() << ", version:" << header.version; 0137 dbg.nospace() << ", number of channels: " << header.nChannels; 0138 dbg.nospace() << ", height: " << header.height; 0139 dbg.nospace() << ", width: " << header.width; 0140 dbg.nospace() << ", channel depth: " << header.channelDepth; 0141 dbg.nospace() << ", color mode: "; 0142 switch (header.colormode) { 0143 case (Bitmap): 0144 dbg.nospace() << "Bitmap"; 0145 break; 0146 case (Grayscale): 0147 dbg.nospace() << "Grayscale"; 0148 break; 0149 case (Indexed): 0150 dbg.nospace() << "Indexed"; 0151 break; 0152 case (RGB): 0153 dbg.nospace() << "RGB"; 0154 break; 0155 case (CMYK): 0156 dbg.nospace() << "CMYK"; 0157 break; 0158 case (MultiChannel): 0159 dbg.nospace() << "MultiChannel"; 0160 break; 0161 case (DuoTone): 0162 dbg.nospace() << "DuoTone"; 0163 break; 0164 case (Lab): 0165 dbg.nospace() << "Lab"; 0166 break; 0167 default: 0168 dbg.nospace() << "Unknown"; 0169 }; 0170 dbg.nospace() << ")"; 0171 #endif 0172 return dbg.nospace(); 0173 }