File indexing completed on 2024-04-28 15:25:43
0001 /* 0002 Photoshop File Format support for QImage. 0003 0004 SPDX-FileCopyrightText: 2003 Ignacio CastaƱo <castano@ludicon.com> 0005 SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org> 0006 SPDX-FileCopyrightText: 2022-2023 Mirco Miranda <mircomir@outlook.com> 0007 0008 SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 0011 /* 0012 * The early version of this code was based on Thacher Ulrich PSD loading code 0013 * released into the public domain. See: http://tulrich.com/geekstuff/ 0014 */ 0015 0016 /* 0017 * Documentation on this file format is available at 0018 * http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ 0019 */ 0020 0021 /* 0022 * Limitations of the current code: 0023 * - 32-bit float image are converted to 16-bit integer image. 0024 * - Other color spaces cannot directly be read due to lack of QImage support for 0025 * color spaces other than RGB (and Grayscale). Where possible, a conversion 0026 * to RGB is done: 0027 * - CMYK images are converted using an approximated way that ignores the color 0028 * information (ICC profile). 0029 * - LAB images are converted to sRGB using literature formulas. 0030 * 0031 * NOTE: The best way to convert between different color spaces is to use a 0032 * color management engine (e.g. LittleCMS). 0033 */ 0034 0035 #include "fastmath_p.h" 0036 #include "psd_p.h" 0037 #include "util_p.h" 0038 0039 #include <QDataStream> 0040 #include <QDebug> 0041 #include <QImage> 0042 #include <QColorSpace> 0043 0044 #include <cmath> 0045 #include <cstring> 0046 0047 typedef quint32 uint; 0048 typedef quint16 ushort; 0049 typedef quint8 uchar; 0050 0051 /* The fast LAB conversion converts the image to linear sRgb instead to sRgb. 0052 * This should not be a problem because the Qt's QColorSpace supports the linear 0053 * sRgb colorspace. 0054 * 0055 * Using linear conversion, the loading speed is slightly improved. Anyway, if you are using 0056 * an software that discard color info, you should comment it. 0057 * 0058 * At the time I'm writing (07/2022), Gwenview and Krita supports linear sRgb but KDE 0059 * preview creator does not. This is the why, for now, it is disabled. 0060 */ 0061 //#define PSD_FAST_LAB_CONVERSION 0062 0063 namespace // Private. 0064 { 0065 0066 enum Signature : quint32 { 0067 S_8BIM = 0x3842494D, // '8BIM' 0068 S_8B64 = 0x38423634, // '8B64' 0069 0070 S_MeSa = 0x4D655361 // 'MeSa' 0071 }; 0072 0073 enum ColorMode : quint16 { 0074 CM_BITMAP = 0, 0075 CM_GRAYSCALE = 1, 0076 CM_INDEXED = 2, 0077 CM_RGB = 3, 0078 CM_CMYK = 4, 0079 CM_MULTICHANNEL = 7, 0080 CM_DUOTONE = 8, 0081 CM_LABCOLOR = 9, 0082 }; 0083 0084 enum ImageResourceId : quint16 { 0085 IRI_RESOLUTIONINFO = 0x03ED, 0086 IRI_ICCPROFILE = 0x040F, 0087 IRI_TRANSPARENCYINDEX = 0x0417, 0088 IRI_VERSIONINFO = 0x0421, 0089 IRI_XMPMETADATA = 0x0424 0090 }; 0091 0092 enum LayerId : quint32 { 0093 LI_MT16 = 0x4D743136, // 'Mt16', 0094 LI_MT32 = 0x4D743332, // 'Mt32', 0095 LI_MTRN = 0x4D74726E // 'Mtrn' 0096 }; 0097 0098 struct PSDHeader { 0099 uint signature; 0100 ushort version; 0101 uchar reserved[6]; 0102 ushort channel_count; 0103 uint height; 0104 uint width; 0105 ushort depth; 0106 ushort color_mode; 0107 }; 0108 0109 struct PSDImageResourceBlock { 0110 QString name; 0111 QByteArray data; 0112 }; 0113 0114 /*! 0115 * \brief The PSDDuotoneOptions struct 0116 * \note You can decode the duotone data using the "Duotone Options" 0117 * file format found in the "Photoshop File Format" specs. 0118 */ 0119 struct PSDDuotoneOptions { 0120 QByteArray data; 0121 }; 0122 0123 /*! 0124 * \brief The PSDColorModeDataSection struct 0125 * Only indexed color and duotone have color mode data. 0126 */ 0127 struct PSDColorModeDataSection { 0128 PSDDuotoneOptions duotone; 0129 QVector<QRgb> palette; 0130 }; 0131 0132 using PSDImageResourceSection = QHash<quint16, PSDImageResourceBlock>; 0133 0134 struct PSDLayerInfo { 0135 qint64 size = -1; 0136 qint16 layerCount = 0; 0137 }; 0138 0139 struct PSDGlobalLayerMaskInfo { 0140 qint64 size = -1; 0141 }; 0142 0143 struct PSDAdditionalLayerInfo { 0144 Signature signature = Signature(); 0145 LayerId id = LayerId(); 0146 qint64 size = -1; 0147 }; 0148 0149 struct PSDLayerAndMaskSection { 0150 qint64 size = -1; 0151 PSDLayerInfo layerInfo; 0152 PSDGlobalLayerMaskInfo globalLayerMaskInfo; 0153 QHash<LayerId, PSDAdditionalLayerInfo> additionalLayerInfo; 0154 0155 bool isNull() const { 0156 return (size <= 0); 0157 } 0158 0159 bool hasAlpha() const { 0160 return layerInfo.layerCount < 0 || 0161 additionalLayerInfo.contains(LI_MT16) || 0162 additionalLayerInfo.contains(LI_MT32) || 0163 additionalLayerInfo.contains(LI_MTRN); 0164 } 0165 0166 bool atEnd(bool isPsb) const { 0167 qint64 currentSize = 0; 0168 if (layerInfo.size > -1) { 0169 currentSize += layerInfo.size + 4; 0170 if (isPsb) 0171 currentSize += 4; 0172 } 0173 if (globalLayerMaskInfo.size > -1) { 0174 currentSize += globalLayerMaskInfo.size + 4; 0175 } 0176 auto aliv = additionalLayerInfo.values(); 0177 for (auto &&v : aliv) { 0178 currentSize += (12 + v.size); 0179 if (v.signature == S_8B64) 0180 currentSize += 4; 0181 } 0182 return (size <= currentSize); 0183 } 0184 }; 0185 0186 /*! 0187 * \brief fixedPointToDouble 0188 * Converts a fixed point number to floating point one. 0189 */ 0190 static double fixedPointToDouble(qint32 fixedPoint) 0191 { 0192 auto i = double(fixedPoint >> 16); 0193 auto d = double((fixedPoint & 0x0000FFFF) / 65536.0); 0194 return (i+d); 0195 } 0196 0197 static qint64 readSize(QDataStream &s, bool psb = false) 0198 { 0199 qint64 size = 0; 0200 if (!psb) { 0201 quint32 tmp; 0202 s >> tmp; 0203 size = tmp; 0204 } 0205 else { 0206 s >> size; 0207 } 0208 if (s.status() != QDataStream::Ok) { 0209 size = -1; 0210 } 0211 return size; 0212 } 0213 0214 static bool skip_data(QDataStream &s, qint64 size) 0215 { 0216 // Skip mode data. 0217 for (qint32 i32 = 0; size; size -= i32) { 0218 i32 = std::min(size, qint64(std::numeric_limits<qint32>::max())); 0219 i32 = s.skipRawData(i32); 0220 if (i32 < 1) 0221 return false; 0222 } 0223 return true; 0224 } 0225 0226 static bool skip_section(QDataStream &s, bool psb = false) 0227 { 0228 auto section_length = readSize(s, psb); 0229 if (section_length < 0) 0230 return false; 0231 return skip_data(s, section_length); 0232 } 0233 0234 /*! 0235 * \brief readPascalString 0236 * Reads the Pascal string as defined in the PSD specification. 0237 * \param s The stream. 0238 * \param alignBytes Alignment of the string. 0239 * \param size Number of stream bytes used. 0240 * \return The string read. 0241 */ 0242 static QString readPascalString(QDataStream &s, qint32 alignBytes = 1, qint32 *size = nullptr) 0243 { 0244 qint32 tmp = 0; 0245 if (size == nullptr) 0246 size = &tmp; 0247 0248 quint8 stringSize; 0249 s >> stringSize; 0250 *size = sizeof(stringSize); 0251 0252 QString str; 0253 if (stringSize > 0) { 0254 QByteArray ba; 0255 ba.resize(stringSize); 0256 auto read = s.readRawData(ba.data(), ba.size()); 0257 if (read > 0) { 0258 *size += read; 0259 str = QString::fromLatin1(ba); 0260 } 0261 } 0262 0263 // align 0264 if (alignBytes > 1) 0265 if (auto pad = *size % alignBytes) 0266 *size += s.skipRawData(alignBytes - pad); 0267 0268 return str; 0269 } 0270 0271 /*! 0272 * \brief readImageResourceSection 0273 * Reads the image resource section. 0274 * \param s The stream. 0275 * \param ok Pointer to the operation result variable. 0276 * \return The image resource section raw data. 0277 */ 0278 static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok = nullptr) 0279 { 0280 PSDImageResourceSection irs; 0281 0282 bool tmp = true; 0283 if (ok == nullptr) 0284 ok = &tmp; 0285 *ok = true; 0286 0287 // Section size 0288 qint32 sectioSize; 0289 s >> sectioSize; 0290 0291 // Reading Image resource block 0292 for (auto size = sectioSize; size > 0;) { 0293 // Length Description 0294 // ------------------------------------------------------------------- 0295 // 4 Signature: '8BIM' 0296 // 2 Unique identifier for the resource. Image resource IDs 0297 // contains a list of resource IDs used by Photoshop. 0298 // Variable Name: Pascal string, padded to make the size even 0299 // (a null name consists of two bytes of 0) 0300 // 4 Actual size of resource data that follows 0301 // Variable The resource data, described in the sections on the 0302 // individual resource types. It is padded to make the size 0303 // even. 0304 0305 quint32 signature; 0306 s >> signature; 0307 size -= sizeof(signature); 0308 // NOTE: MeSa signature is not documented but found in some old PSD take from Photoshop 7.0 CD. 0309 if (signature != S_8BIM && signature != S_MeSa) { // 8BIM and MeSa 0310 qDebug() << "Invalid Image Resource Block Signature!"; 0311 *ok = false; 0312 break; 0313 } 0314 0315 // id 0316 quint16 id; 0317 s >> id; 0318 size -= sizeof(id); 0319 0320 // getting data 0321 PSDImageResourceBlock irb; 0322 0323 // name 0324 qint32 bytes = 0; 0325 irb.name = readPascalString(s, 2, &bytes); 0326 size -= bytes; 0327 0328 // data read 0329 quint32 dataSize; 0330 s >> dataSize; 0331 size -= sizeof(dataSize); 0332 // NOTE: Qt device::read() and QDataStream::readRawData() could read less data than specified. 0333 // The read code should be improved. 0334 if (auto dev = s.device()) 0335 irb.data = dev->read(dataSize); 0336 auto read = irb.data.size(); 0337 if (read > 0) 0338 size -= read; 0339 if (quint32(read) != dataSize) { 0340 qDebug() << "Image Resource Block Read Error!"; 0341 *ok = false; 0342 break; 0343 } 0344 0345 if (auto pad = dataSize % 2) { 0346 auto skipped = s.skipRawData(pad); 0347 if (skipped > 0) 0348 size -= skipped; 0349 } 0350 0351 // insert IRB 0352 irs.insert(id, irb); 0353 } 0354 0355 return irs; 0356 } 0357 0358 PSDAdditionalLayerInfo readAdditionalLayer(QDataStream &s, bool *ok = nullptr) 0359 { 0360 PSDAdditionalLayerInfo li; 0361 0362 bool tmp = true; 0363 if (ok == nullptr) 0364 ok = &tmp; 0365 0366 s >> li.signature; 0367 *ok = li.signature == S_8BIM || li.signature == S_8B64; 0368 if (!*ok) 0369 return li; 0370 0371 s >> li.id; 0372 *ok = s.status() == QDataStream::Ok; 0373 if (!*ok) 0374 return li; 0375 0376 li.size = readSize(s, li.signature == S_8B64); 0377 *ok = li.size >= 0; 0378 if (!*ok) 0379 return li; 0380 0381 *ok = skip_data(s, li.size); 0382 0383 return li; 0384 } 0385 0386 PSDLayerAndMaskSection readLayerAndMaskSection(QDataStream &s, bool isPsb, bool *ok = nullptr) 0387 { 0388 PSDLayerAndMaskSection lms; 0389 0390 bool tmp = true; 0391 if (ok == nullptr) 0392 ok = &tmp; 0393 *ok = true; 0394 0395 auto device = s.device(); 0396 device->startTransaction(); 0397 0398 lms.size = readSize(s, isPsb); 0399 0400 // read layer info 0401 if (s.status() == QDataStream::Ok && !lms.atEnd(isPsb)) { 0402 lms.layerInfo.size = readSize(s, isPsb); 0403 if (lms.layerInfo.size > 0) { 0404 s >> lms.layerInfo.layerCount; 0405 skip_data(s, lms.layerInfo.size - sizeof(lms.layerInfo.layerCount)); 0406 } 0407 } 0408 0409 // read global layer mask info 0410 if (s.status() == QDataStream::Ok && !lms.atEnd(isPsb)) { 0411 lms.globalLayerMaskInfo.size = readSize(s, false); // always 32-bits 0412 if (lms.globalLayerMaskInfo.size > 0) { 0413 skip_data(s, lms.globalLayerMaskInfo.size); 0414 } 0415 } 0416 0417 // read additional layer info 0418 if (s.status() == QDataStream::Ok) { 0419 for (bool ok = true; ok && !lms.atEnd(isPsb);) { 0420 auto al = readAdditionalLayer(s, &ok); 0421 if (ok) 0422 lms.additionalLayerInfo.insert(al.id, al); 0423 } 0424 } 0425 0426 device->rollbackTransaction(); 0427 *ok = skip_section(s, isPsb); 0428 return lms; 0429 } 0430 0431 /*! 0432 * \brief readColorModeDataSection 0433 * Read the color mode section 0434 * \param s The stream. 0435 * \param ok Pointer to the operation result variable. 0436 * \return The color mode section. 0437 */ 0438 PSDColorModeDataSection readColorModeDataSection(QDataStream &s, bool *ok = nullptr) 0439 { 0440 PSDColorModeDataSection cms; 0441 0442 bool tmp = false; 0443 if (ok == nullptr) 0444 ok = &tmp; 0445 *ok = true; 0446 0447 qint32 size; 0448 s >> size; 0449 if (size != 768) { // read the duotone data (524 bytes) 0450 // NOTE: A RGB/Gray float image has a 112 bytes ColorModeData that could be 0451 // the "32-bit Toning Options" of Photoshop (starts with 'hdrt'). 0452 // Official Adobe specification tells "Only indexed color and duotone 0453 // (see the mode field in the File header section) have color mode data.". 0454 // See test case images 32bit_grayscale.psd and 32bit-rgb.psd 0455 cms.duotone.data = s.device()->read(size); 0456 if (cms.duotone.data.size() != size) 0457 *ok = false; 0458 } 0459 else { // read the palette (768 bytes) 0460 auto&& palette = cms.palette; 0461 QVector<quint8> vect(size); 0462 for (auto&& v : vect) 0463 s >> v; 0464 for (qsizetype i = 0, n = vect.size()/3; i < n; ++i) 0465 palette.append(qRgb(vect.at(i), vect.at(n+i), vect.at(n+n+i))); 0466 } 0467 0468 return cms; 0469 } 0470 0471 /*! 0472 * \brief setColorSpace 0473 * Set the color space to the image. 0474 * \param img The image. 0475 * \param irs The image resource section. 0476 * \return True on success, otherwise false. 0477 */ 0478 static bool setColorSpace(QImage& img, const PSDImageResourceSection& irs) 0479 { 0480 if (!irs.contains(IRI_ICCPROFILE)) 0481 return false; 0482 auto irb = irs.value(IRI_ICCPROFILE); 0483 auto cs = QColorSpace::fromIccProfile(irb.data); 0484 if (!cs.isValid()) 0485 return false; 0486 img.setColorSpace(cs); 0487 return true; 0488 } 0489 0490 /*! 0491 * \brief setXmpData 0492 * Adds XMP metadata to QImage. 0493 * \param img The image. 0494 * \param irs The image resource section. 0495 * \return True on success, otherwise false. 0496 */ 0497 static bool setXmpData(QImage& img, const PSDImageResourceSection& irs) 0498 { 0499 if (!irs.contains(IRI_XMPMETADATA)) 0500 return false; 0501 auto irb = irs.value(IRI_XMPMETADATA); 0502 auto xmp = QString::fromUtf8(irb.data); 0503 if (xmp.isEmpty()) 0504 return false; 0505 // NOTE: "XML:com.adobe.xmp" is the meta set by Qt reader when an 0506 // XMP packet is found (e.g. when reading a PNG saved by Photoshop). 0507 // I'm reusing the same key because a programs could search for it. 0508 img.setText(QStringLiteral("XML:com.adobe.xmp"), xmp); 0509 return true; 0510 } 0511 0512 /*! 0513 * \brief hasMergedData 0514 * Checks if merged image data are available. 0515 * \param irs The image resource section. 0516 * \return True on success or if the block does not exist, otherwise false. 0517 */ 0518 static bool hasMergedData(const PSDImageResourceSection& irs) 0519 { 0520 if (!irs.contains(IRI_VERSIONINFO)) 0521 return true; 0522 auto irb = irs.value(IRI_VERSIONINFO); 0523 if (irb.data.size() > 4) 0524 return irb.data.at(4) != 0; 0525 return false; 0526 } 0527 0528 /*! 0529 * \brief setResolution 0530 * Set the image resolution. 0531 * \param img The image. 0532 * \param irs The image resource section. 0533 * \return True on success, otherwise false. 0534 */ 0535 static bool setResolution(QImage& img, const PSDImageResourceSection& irs) 0536 { 0537 if (!irs.contains(IRI_RESOLUTIONINFO)) 0538 return false; 0539 auto irb = irs.value(IRI_RESOLUTIONINFO); 0540 0541 QDataStream s(irb.data); 0542 s.setByteOrder(QDataStream::BigEndian); 0543 0544 qint32 i32; 0545 s >> i32; // Horizontal resolution in pixels per inch. 0546 if (i32 <= 0) 0547 return false; 0548 auto hres = fixedPointToDouble(i32); 0549 0550 s.skipRawData(4); // Display data (not used here) 0551 0552 s >> i32; // Vertial resolution in pixels per inch. 0553 if (i32 <= 0) 0554 return false; 0555 auto vres = fixedPointToDouble(i32); 0556 0557 img.setDotsPerMeterX(hres * 1000 / 25.4); 0558 img.setDotsPerMeterY(vres * 1000 / 25.4); 0559 return true; 0560 } 0561 0562 /*! 0563 * \brief setTransparencyIndex 0564 * Search for transparency index block and, if found, changes the alpha of the value at the given index. 0565 * \param img The image. 0566 * \param irs The image resource section. 0567 * \return True on success, otherwise false. 0568 */ 0569 static bool setTransparencyIndex(QImage& img, const PSDImageResourceSection& irs) 0570 { 0571 if (!irs.contains(IRI_TRANSPARENCYINDEX)) 0572 return false; 0573 auto irb = irs.value(IRI_TRANSPARENCYINDEX); 0574 QDataStream s(irb.data); 0575 s.setByteOrder(QDataStream::BigEndian); 0576 quint16 idx; 0577 s >> idx; 0578 0579 auto palette = img.colorTable(); 0580 if (idx < palette.size()) { 0581 auto&& v = palette[idx]; 0582 v = QRgb(v & ~0xFF000000); 0583 img.setColorTable(palette); 0584 return true; 0585 } 0586 0587 return false; 0588 } 0589 0590 static QDataStream &operator>>(QDataStream &s, PSDHeader &header) 0591 { 0592 s >> header.signature; 0593 s >> header.version; 0594 for (int i = 0; i < 6; i++) { 0595 s >> header.reserved[i]; 0596 } 0597 s >> header.channel_count; 0598 s >> header.height; 0599 s >> header.width; 0600 s >> header.depth; 0601 s >> header.color_mode; 0602 return s; 0603 } 0604 0605 // Check that the header is a valid PSD (as written in the PSD specification). 0606 static bool IsValid(const PSDHeader &header) 0607 { 0608 if (header.signature != 0x38425053) { // '8BPS' 0609 //qDebug() << "PSD header: invalid signature" << header.signature; 0610 return false; 0611 } 0612 if (header.version != 1 && header.version != 2) { 0613 qDebug() << "PSD header: invalid version" << header.version; 0614 return false; 0615 } 0616 if (header.depth != 8 && 0617 header.depth != 16 && 0618 header.depth != 32 && 0619 header.depth != 1) { 0620 qDebug() << "PSD header: invalid depth" << header.depth; 0621 return false; 0622 } 0623 if (header.color_mode != CM_RGB && 0624 header.color_mode != CM_GRAYSCALE && 0625 header.color_mode != CM_INDEXED && 0626 header.color_mode != CM_DUOTONE && 0627 header.color_mode != CM_CMYK && 0628 header.color_mode != CM_LABCOLOR && 0629 header.color_mode != CM_MULTICHANNEL && 0630 header.color_mode != CM_BITMAP) { 0631 qDebug() << "PSD header: invalid color mode" << header.color_mode; 0632 return false; 0633 } 0634 // Specs tells: "Supported range is 1 to 56" but the limit is 57: 0635 // Photoshop does not make you add more (see also 53alphas.psd test case). 0636 if (header.channel_count < 1 || header.channel_count > 57) { 0637 qDebug() << "PSD header: invalid number of channels" << header.channel_count; 0638 return false; 0639 } 0640 if (header.width > 300000 || header.height > 300000) { 0641 qDebug() << "PSD header: invalid image size" << header.width << "x" << header.height; 0642 return false; 0643 } 0644 return true; 0645 } 0646 0647 // Check that the header is supported by this plugin. 0648 static bool IsSupported(const PSDHeader &header) 0649 { 0650 if (!IsValid(header)) { 0651 return false; 0652 } 0653 if (header.version != 1 && header.version != 2) { 0654 return false; 0655 } 0656 if (header.depth != 8 && 0657 header.depth != 16 && 0658 header.depth != 32 && 0659 header.depth != 1) { 0660 return false; 0661 } 0662 if (header.color_mode != CM_RGB && 0663 header.color_mode != CM_GRAYSCALE && 0664 header.color_mode != CM_INDEXED && 0665 header.color_mode != CM_DUOTONE && 0666 header.color_mode != CM_CMYK && 0667 header.color_mode != CM_MULTICHANNEL && 0668 header.color_mode != CM_LABCOLOR && 0669 header.color_mode != CM_BITMAP) { 0670 return false; 0671 } 0672 if (header.color_mode == CM_MULTICHANNEL && 0673 header.channel_count < 3) { 0674 return false; 0675 } 0676 return true; 0677 } 0678 0679 /*! 0680 * \brief decompress 0681 * Fast PackBits decompression. 0682 * \param input The compressed input buffer. 0683 * \param ilen The input buffer size. 0684 * \param output The uncompressed target buffer. 0685 * \param olen The target buffer size. 0686 * \return The number of valid bytes in the target buffer. 0687 */ 0688 qint64 decompress(const char *input, qint64 ilen, char *output, qint64 olen) 0689 { 0690 qint64 j = 0; 0691 for (qint64 ip = 0, rr = 0, available = olen; j < olen && ip < ilen; available = olen - j) { 0692 signed char n = static_cast<signed char>(input[ip++]); 0693 if (n == -128) 0694 continue; 0695 0696 if (n >= 0) { 0697 rr = qint64(n) + 1; 0698 if (available < rr) { 0699 --ip; 0700 break; 0701 } 0702 0703 if (ip + rr > ilen) 0704 return -1; 0705 memcpy(output + j, input + ip, size_t(rr)); 0706 ip += rr; 0707 } 0708 else if (ip < ilen) { 0709 rr = qint64(1-n); 0710 if (available < rr) { 0711 --ip; 0712 break; 0713 } 0714 memset(output + j, input[ip++], size_t(rr)); 0715 } 0716 0717 j += rr; 0718 } 0719 return j; 0720 } 0721 0722 /*! 0723 * \brief imageFormat 0724 * \param header The PSD header. 0725 * \return The Qt image format. 0726 */ 0727 static QImage::Format imageFormat(const PSDHeader &header, bool alpha) 0728 { 0729 if (header.channel_count == 0) { 0730 return QImage::Format_Invalid; 0731 } 0732 0733 auto format = QImage::Format_Invalid; 0734 switch(header.color_mode) { 0735 case CM_RGB: 0736 if (header.depth == 16 || header.depth == 32) 0737 format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64_Premultiplied; 0738 else 0739 format = header.channel_count < 4 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888_Premultiplied; 0740 break; 0741 case CM_MULTICHANNEL: // Treat MCH as CMYK (number of channel check is done in IsSupported()) 0742 case CM_CMYK: // Photoshop supports CMYK/MCH 8-bits and 16-bits only 0743 if (header.depth == 16) 0744 format = header.channel_count < 5 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64; 0745 else if (header.depth == 8) 0746 format = header.channel_count < 5 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888; 0747 break; 0748 case CM_LABCOLOR: // Photoshop supports LAB 8-bits and 16-bits only 0749 if (header.depth == 16) 0750 format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64; 0751 else if (header.depth == 8) 0752 format = header.channel_count < 4 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888; 0753 break; 0754 case CM_GRAYSCALE: 0755 case CM_DUOTONE: 0756 format = header.depth == 8 ? QImage::Format_Grayscale8 : QImage::Format_Grayscale16; 0757 break; 0758 case CM_INDEXED: 0759 format = header.depth == 8 ? QImage::Format_Indexed8 : QImage::Format_Invalid; 0760 break; 0761 case CM_BITMAP: 0762 format = header.depth == 1 ? QImage::Format_Mono : QImage::Format_Invalid; 0763 break; 0764 } 0765 return format; 0766 } 0767 0768 /*! 0769 * \brief imageChannels 0770 * \param format The Qt image format. 0771 * \return The number of channels of the image format. 0772 */ 0773 static qint32 imageChannels(const QImage::Format& format) 0774 { 0775 qint32 c = 4; 0776 switch(format) { 0777 case QImage::Format_RGB888: 0778 c = 3; 0779 break; 0780 case QImage::Format_Grayscale8: 0781 case QImage::Format_Grayscale16: 0782 case QImage::Format_Indexed8: 0783 case QImage::Format_Mono: 0784 c = 1; 0785 break; 0786 default: 0787 break; 0788 } 0789 return c; 0790 } 0791 0792 inline quint8 xchg(quint8 v) { 0793 return v; 0794 } 0795 0796 inline quint16 xchg(quint16 v) { 0797 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN 0798 return quint16( (v>>8) | (v<<8) ); 0799 #else 0800 return v; // never tested 0801 #endif 0802 } 0803 0804 inline quint32 xchg(quint32 v) { 0805 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN 0806 return quint32( (v>>24) | ((v & 0x00FF0000)>>8) | ((v & 0x0000FF00)<<8) | (v<<24) ); 0807 #else 0808 return v; // never tested 0809 #endif 0810 } 0811 0812 inline float xchg(float v) 0813 { 0814 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN 0815 # ifdef Q_CC_MSVC 0816 float *pf = &v; 0817 quint32 f = xchg(*reinterpret_cast<quint32*>(pf)); 0818 quint32 *pi = &f; 0819 return *reinterpret_cast<float*>(pi); 0820 # else 0821 quint32 t; 0822 std::memcpy(&t, &v, sizeof(quint32)); 0823 t = xchg(t); 0824 std::memcpy(&v, &t, sizeof(quint32)); 0825 return v; 0826 # endif 0827 #else 0828 return v; // never tested 0829 #endif 0830 } 0831 0832 template<class T> 0833 inline void planarToChunchy(uchar *target, const char *source, qint32 width, qint32 c, qint32 cn) 0834 { 0835 auto s = reinterpret_cast<const T*>(source); 0836 auto t = reinterpret_cast<T*>(target); 0837 for (qint32 x = 0; x < width; ++x) { 0838 t[x * cn + c] = xchg(s[x]); 0839 } 0840 } 0841 0842 template<class T> 0843 inline void planarToChunchyFloatToUInt16(uchar *target, const char *source, qint32 width, qint32 c, qint32 cn) 0844 { 0845 auto s = reinterpret_cast<const T*>(source); 0846 auto t = reinterpret_cast<quint16*>(target); 0847 for (qint32 x = 0; x < width; ++x) { 0848 t[x * cn + c] = quint16(std::min(xchg(s[x]) * std::numeric_limits<quint16>::max() + 0.5, double(std::numeric_limits<quint16>::max()))); 0849 } 0850 } 0851 0852 enum class PremulConversion { 0853 PS2P, // Photoshop premul to qimage premul (required by RGB) 0854 PS2A, // Photoshop premul to unassociated alpha (required by RGB, CMYK and L* components of LAB) 0855 PSLab2A // Photoshop premul to unassociated alpha (required by a* and b* components of LAB) 0856 }; 0857 0858 template<class T> 0859 inline void premulConversion(char *stride, qint32 width, qint32 ac, qint32 cn, const PremulConversion &conv) 0860 { 0861 auto s = reinterpret_cast<T *>(stride); 0862 auto max = qint64(std::numeric_limits<T>::max()); 0863 0864 for (qint32 c = 0; c < ac; ++c) { 0865 if (conv == PremulConversion::PS2P) { 0866 for (qint32 x = 0; x < width; ++x) { 0867 auto xcn = x * cn; 0868 auto alpha = *(s + xcn + ac); 0869 *(s + xcn + c) = *(s + xcn + c) + alpha - max; 0870 } 0871 } else if (conv == PremulConversion::PS2A || (conv == PremulConversion::PSLab2A && c == 0)) { 0872 for (qint32 x = 0; x < width; ++x) { 0873 auto xcn = x * cn; 0874 auto alpha = *(s + xcn + ac); 0875 if (alpha > 0) 0876 *(s + xcn + c) = ((*(s + xcn + c) + alpha - max) * max + alpha / 2) / alpha; 0877 } 0878 } else if (conv == PremulConversion::PSLab2A) { 0879 for (qint32 x = 0; x < width; ++x) { 0880 auto xcn = x * cn; 0881 auto alpha = *(s + xcn + ac); 0882 if (alpha > 0) 0883 *(s + xcn + c) = ((*(s + xcn + c) + (alpha - max + 1) / 2) * max + alpha / 2) / alpha; 0884 } 0885 } 0886 } 0887 } 0888 0889 inline void monoInvert(uchar *target, const char* source, qint32 bytes) 0890 { 0891 auto s = reinterpret_cast<const quint8*>(source); 0892 auto t = reinterpret_cast<quint8*>(target); 0893 for (qint32 x = 0; x < bytes; ++x) { 0894 t[x] = ~s[x]; 0895 } 0896 } 0897 0898 template<class T> 0899 inline void rawChannelsCopy(uchar *target, qint32 targetChannels, const char *source, qint32 sourceChannels, qint32 width) 0900 { 0901 auto s = reinterpret_cast<const T *>(source); 0902 auto t = reinterpret_cast<T *>(target); 0903 for (qint32 c = 0, cs = std::min(targetChannels, sourceChannels); c < cs; ++c) { 0904 for (qint32 x = 0; x < width; ++x) { 0905 t[x * targetChannels + c] = s[x * sourceChannels + c]; 0906 } 0907 } 0908 } 0909 0910 template<class T> 0911 inline void cmykToRgb(uchar *target, qint32 targetChannels, const char *source, qint32 sourceChannels, qint32 width, bool alpha = false) 0912 { 0913 auto s = reinterpret_cast<const T*>(source); 0914 auto t = reinterpret_cast<T*>(target); 0915 auto max = double(std::numeric_limits<T>::max()); 0916 auto invmax = 1.0 / max; // speed improvements by ~10% 0917 0918 if (sourceChannels < 3) { 0919 qDebug() << "cmykToRgb: image is not a valid CMY/CMYK!"; 0920 return; 0921 } 0922 0923 for (qint32 w = 0; w < width; ++w) { 0924 auto ps = s + sourceChannels * w; 0925 auto C = 1 - *(ps + 0) * invmax; 0926 auto M = 1 - *(ps + 1) * invmax; 0927 auto Y = 1 - *(ps + 2) * invmax; 0928 auto K = sourceChannels > 3 ? 1 - *(ps + 3) * invmax : 0.0; 0929 0930 auto pt = t + targetChannels * w; 0931 *(pt + 0) = T(std::min(max - (C * (1 - K) + K) * max + 0.5, max)); 0932 *(pt + 1) = T(std::min(max - (M * (1 - K) + K) * max + 0.5, max)); 0933 *(pt + 2) = T(std::min(max - (Y * (1 - K) + K) * max + 0.5, max)); 0934 if (targetChannels == 4) { 0935 if (sourceChannels >= 5 && alpha) 0936 *(pt + 3) = *(ps + 4); 0937 else 0938 *(pt + 3) = std::numeric_limits<T>::max(); 0939 } 0940 } 0941 } 0942 0943 inline double finv(double v) 0944 { 0945 return (v > 6.0 / 29.0 ? v * v * v : (v - 16.0 / 116.0) / 7.787); 0946 } 0947 0948 inline double gammaCorrection(double linear) 0949 { 0950 #ifdef PSD_FAST_LAB_CONVERSION 0951 return linear; 0952 #else 0953 // Replacing fastPow with std::pow the conversion time is 2/3 times longer: using fastPow 0954 // there are minimal differences in the conversion that are not visually noticeable. 0955 return (linear > 0.0031308 ? 1.055 * fastPow(linear, 1.0 / 2.4) - 0.055 : 12.92 * linear); 0956 #endif 0957 } 0958 0959 template<class T> 0960 inline void labToRgb(uchar *target, qint32 targetChannels, const char *source, qint32 sourceChannels, qint32 width, bool alpha = false) 0961 { 0962 auto s = reinterpret_cast<const T*>(source); 0963 auto t = reinterpret_cast<T*>(target); 0964 auto max = double(std::numeric_limits<T>::max()); 0965 auto invmax = 1.0 / max; 0966 0967 if (sourceChannels < 3) { 0968 qDebug() << "labToRgb: image is not a valid LAB!"; 0969 return; 0970 } 0971 0972 for (qint32 w = 0; w < width; ++w) { 0973 auto ps = s + sourceChannels * w; 0974 auto L = (*(ps + 0) * invmax) * 100.0; 0975 auto A = (*(ps + 1) * invmax) * 255.0 - 128.0; 0976 auto B = (*(ps + 2) * invmax) * 255.0 - 128.0; 0977 0978 // converting LAB to XYZ (D65 illuminant) 0979 auto Y = (L + 16.0) * (1.0 / 116.0); 0980 auto X = A * (1.0 / 500.0) + Y; 0981 auto Z = Y - B * (1.0 / 200.0); 0982 0983 // NOTE: use the constants of the illuminant of the target RGB color space 0984 X = finv(X) * 0.9504; // D50: * 0.9642 0985 Y = finv(Y) * 1.0000; // D50: * 1.0000 0986 Z = finv(Z) * 1.0888; // D50: * 0.8251 0987 0988 // converting XYZ to sRGB (sRGB illuminant is D65) 0989 auto r = gammaCorrection( 3.24071 * X - 1.53726 * Y - 0.498571 * Z); 0990 auto g = gammaCorrection(- 0.969258 * X + 1.87599 * Y + 0.0415557 * Z); 0991 auto b = gammaCorrection( 0.0556352 * X - 0.203996 * Y + 1.05707 * Z); 0992 0993 auto pt = t + targetChannels * w; 0994 *(pt + 0) = T(std::max(std::min(r * max + 0.5, max), 0.0)); 0995 *(pt + 1) = T(std::max(std::min(g * max + 0.5, max), 0.0)); 0996 *(pt + 2) = T(std::max(std::min(b * max + 0.5, max), 0.0)); 0997 if (targetChannels == 4) { 0998 if (sourceChannels >= 4 && alpha) 0999 *(pt + 3) = *(ps + 3); 1000 else 1001 *(pt + 3) = std::numeric_limits<T>::max(); 1002 } 1003 } 1004 } 1005 1006 bool readChannel(QByteArray& target, QDataStream &stream, quint32 compressedSize, quint16 compression) 1007 { 1008 if (compression) { 1009 if (compressedSize > kMaxQVectorSize) { 1010 return false; 1011 } 1012 QByteArray tmp; 1013 tmp.resize(compressedSize); 1014 if (stream.readRawData(tmp.data(), tmp.size()) != tmp.size()) { 1015 return false; 1016 } 1017 if (decompress(tmp.data(), tmp.size(), target.data(), target.size()) < 0) { 1018 return false; 1019 } 1020 } 1021 else if (stream.readRawData(target.data(), target.size()) != target.size()) { 1022 return false; 1023 } 1024 1025 return stream.status() == QDataStream::Ok; 1026 } 1027 1028 // Load the PSD image. 1029 static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img) 1030 { 1031 // Checking for PSB 1032 auto isPsb = header.version == 2; 1033 bool ok = false; 1034 1035 // Color Mode Data section 1036 auto cmds = readColorModeDataSection(stream, &ok); 1037 if (!ok) { 1038 qDebug() << "Error while skipping Color Mode Data section"; 1039 return false; 1040 } 1041 1042 // Image Resources Section 1043 auto irs = readImageResourceSection(stream, &ok); 1044 if (!ok) { 1045 qDebug() << "Error while reading Image Resources Section"; 1046 return false; 1047 } 1048 // Checking for merged image (Photoshop compatibility data) 1049 if (!hasMergedData(irs)) { 1050 qDebug() << "No merged data found"; 1051 return false; 1052 } 1053 1054 // Layer and Mask section 1055 auto lms = readLayerAndMaskSection(stream, isPsb, &ok); 1056 if (!ok) { 1057 qDebug() << "Error while skipping Layer and Mask section"; 1058 return false; 1059 } 1060 1061 // Find out if the data is compressed. 1062 // Known values: 1063 // 0: no compression 1064 // 1: RLE compressed 1065 quint16 compression; 1066 stream >> compression; 1067 if (compression > 1) { 1068 qDebug() << "Unknown compression type"; 1069 return false; 1070 } 1071 1072 // Try to identify the nature of spots: note that this is just one of many ways to identify the presence 1073 // of alpha channels: should work in most cases where colorspaces != RGB/Gray 1074 auto alpha = header.color_mode == CM_RGB; 1075 if (!lms.isNull()) 1076 alpha = lms.hasAlpha(); 1077 1078 const QImage::Format format = imageFormat(header, alpha); 1079 if (format == QImage::Format_Invalid) { 1080 qWarning() << "Unsupported image format. color_mode:" << header.color_mode << "depth:" << header.depth << "channel_count:" << header.channel_count; 1081 return false; 1082 } 1083 1084 img = imageAlloc(header.width, header.height, format); 1085 if (img.isNull()) { 1086 qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width, header.height); 1087 return false; 1088 } 1089 img.fill(qRgb(0, 0, 0)); 1090 if (!cmds.palette.isEmpty()) { 1091 img.setColorTable(cmds.palette); 1092 setTransparencyIndex(img, irs); 1093 } 1094 1095 auto imgChannels = imageChannels(img.format()); 1096 auto channel_num = std::min(qint32(header.channel_count), imgChannels); 1097 auto raw_count = qsizetype(header.width * header.depth + 7) / 8; 1098 1099 if (header.height > kMaxQVectorSize / header.channel_count / sizeof(quint32)) { 1100 qWarning() << "LoadPSD() header height/channel_count too big" << header.height << header.channel_count; 1101 return false; 1102 } 1103 1104 QVector<quint32> strides(header.height * header.channel_count, raw_count); 1105 // Read the compressed stride sizes 1106 if (compression) { 1107 for (auto&& v : strides) { 1108 if (isPsb) { 1109 stream >> v; 1110 continue; 1111 } 1112 quint16 tmp; 1113 stream >> tmp; 1114 v = tmp; 1115 } 1116 } 1117 // calculate the absolute file positions of each stride (required when a colorspace conversion should be done) 1118 auto device = stream.device(); 1119 QVector<quint64> stridePositions(strides.size()); 1120 if (!stridePositions.isEmpty()) { 1121 stridePositions[0] = device->pos(); 1122 } 1123 for (qsizetype i = 1, n = stridePositions.size(); i < n; ++i) { 1124 stridePositions[i] = stridePositions[i-1] + strides.at(i-1); 1125 } 1126 1127 // Read the image 1128 QByteArray rawStride; 1129 rawStride.resize(raw_count); 1130 1131 // clang-format off 1132 // checks the need of color conversion (that requires random access to the image) 1133 auto randomAccess = (header.color_mode == CM_CMYK) || 1134 (header.color_mode == CM_LABCOLOR) || 1135 (header.color_mode == CM_MULTICHANNEL) || 1136 (header.color_mode != CM_INDEXED && img.hasAlphaChannel()); 1137 // clang-format on 1138 1139 if (randomAccess) { 1140 // In order to make a colorspace transformation, we need all channels of a scanline 1141 QByteArray psdScanline; 1142 psdScanline.resize(qsizetype(header.width * std::min(header.depth, quint16(16)) * header.channel_count + 7) / 8); 1143 for (qint32 y = 0, h = header.height; y < h; ++y) { 1144 for (qint32 c = 0; c < header.channel_count; ++c) { 1145 auto strideNumber = c * qsizetype(h) + y; 1146 if (!device->seek(stridePositions.at(strideNumber))) { 1147 qDebug() << "Error while seeking the stream of channel" << c << "line" << y; 1148 return false; 1149 } 1150 auto&& strideSize = strides.at(strideNumber); 1151 if (!readChannel(rawStride, stream, strideSize, compression)) { 1152 qDebug() << "Error while reading the stream of channel" << c << "line" << y; 1153 return false; 1154 } 1155 1156 auto scanLine = reinterpret_cast<unsigned char*>(psdScanline.data()); 1157 if (header.depth == 8) { 1158 planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, header.channel_count); 1159 } else if (header.depth == 16) { 1160 planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, header.channel_count); 1161 } else if (header.depth == 32) { 1162 planarToChunchyFloatToUInt16<float>(scanLine, rawStride.data(), header.width, c, header.channel_count); 1163 } 1164 } 1165 1166 // Convert premultiplied data to unassociated data 1167 if (img.hasAlphaChannel()) { 1168 if (header.color_mode == CM_CMYK) { 1169 if (header.depth == 8) 1170 premulConversion<quint8>(psdScanline.data(), header.width, 4, header.channel_count, PremulConversion::PS2A); 1171 else if (header.depth == 16) 1172 premulConversion<quint16>(psdScanline.data(), header.width, 4, header.channel_count, PremulConversion::PS2A); 1173 } 1174 if (header.color_mode == CM_LABCOLOR) { 1175 if (header.depth == 8) 1176 premulConversion<quint8>(psdScanline.data(), header.width, 3, header.channel_count, PremulConversion::PSLab2A); 1177 else if (header.depth == 16) 1178 premulConversion<quint16>(psdScanline.data(), header.width, 3, header.channel_count, PremulConversion::PSLab2A); 1179 } 1180 if (header.color_mode == CM_RGB) { 1181 if (header.depth == 8) 1182 premulConversion<quint8>(psdScanline.data(), header.width, 3, header.channel_count, PremulConversion::PS2P); 1183 else if (header.depth == 16 || header.depth == 32) 1184 premulConversion<quint16>(psdScanline.data(), header.width, 3, header.channel_count, PremulConversion::PS2P); 1185 } 1186 } 1187 1188 // Conversion to RGB 1189 if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) { 1190 if (header.depth == 8) 1191 cmykToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha); 1192 else if (header.depth == 16) 1193 cmykToRgb<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha); 1194 } 1195 if (header.color_mode == CM_LABCOLOR) { 1196 if (header.depth == 8) 1197 labToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha); 1198 else if (header.depth == 16) 1199 labToRgb<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha); 1200 } 1201 if (header.color_mode == CM_RGB) { 1202 if (header.depth == 8) 1203 rawChannelsCopy<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width); 1204 else if (header.depth == 16 || header.depth == 32) 1205 rawChannelsCopy<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width); 1206 } 1207 } 1208 } else { 1209 // Linear read (no position jumps): optimized code usable only for the colorspaces supported by QImage 1210 for (qint32 c = 0; c < channel_num; ++c) { 1211 for (qint32 y = 0, h = header.height; y < h; ++y) { 1212 auto&& strideSize = strides.at(c * qsizetype(h) + y); 1213 if (!readChannel(rawStride, stream, strideSize, compression)) { 1214 qDebug() << "Error while reading the stream of channel" << c << "line" << y; 1215 return false; 1216 } 1217 1218 auto scanLine = img.scanLine(y); 1219 if (header.depth == 1) { // Bitmap 1220 monoInvert(scanLine, rawStride.data(), std::min(rawStride.size(), img.bytesPerLine())); 1221 } else if (header.depth == 8) { // 8-bits images: Indexed, Grayscale, RGB/RGBA 1222 planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, imgChannels); 1223 } else if (header.depth == 16) { // 16-bits integer images: Grayscale, RGB/RGBA 1224 planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, imgChannels); 1225 } else if (header.depth == 32) { // 32-bits float images: Grayscale, RGB/RGBA (coverted to equivalent integer 16-bits) 1226 planarToChunchyFloatToUInt16<float>(scanLine, rawStride.data(), header.width, c, imgChannels); 1227 } 1228 } 1229 } 1230 } 1231 1232 // LAB conversion generates a sRGB image 1233 if (header.color_mode == CM_LABCOLOR) { 1234 #ifdef PSD_FAST_LAB_CONVERSION 1235 img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear)); 1236 #else 1237 img.setColorSpace(QColorSpace(QColorSpace::SRgb)); 1238 #endif 1239 } 1240 1241 // Resolution info 1242 if (!setResolution(img, irs)) { 1243 // qDebug() << "No resolution info found!"; 1244 } 1245 1246 // ICC profile 1247 if (!setColorSpace(img, irs)) { 1248 // qDebug() << "No colorspace info set!"; 1249 } 1250 1251 // XMP data 1252 if (!setXmpData(img, irs)) { 1253 // qDebug() << "No XMP data found!"; 1254 } 1255 1256 // Duotone images: color data contains the duotone specification (not documented). 1257 // Other applications that read Photoshop files can treat a duotone image as a gray image, 1258 // and just preserve the contents of the duotone information when reading and writing the file. 1259 if (!cmds.duotone.data.isEmpty()) { 1260 img.setText(QStringLiteral("PSDDuotoneOptions"), QString::fromUtf8(cmds.duotone.data.toHex())); 1261 } 1262 1263 return true; 1264 } 1265 1266 } // Private 1267 1268 PSDHandler::PSDHandler() 1269 { 1270 } 1271 1272 bool PSDHandler::canRead() const 1273 { 1274 if (canRead(device())) { 1275 setFormat("psd"); 1276 return true; 1277 } 1278 return false; 1279 } 1280 1281 bool PSDHandler::read(QImage *image) 1282 { 1283 QDataStream s(device()); 1284 s.setByteOrder(QDataStream::BigEndian); 1285 1286 PSDHeader header; 1287 s >> header; 1288 1289 // Check image file format. 1290 if (s.atEnd() || !IsValid(header)) { 1291 // qDebug() << "This PSD file is not valid."; 1292 return false; 1293 } 1294 1295 // Check if it's a supported format. 1296 if (!IsSupported(header)) { 1297 // qDebug() << "This PSD file is not supported."; 1298 return false; 1299 } 1300 1301 QImage img; 1302 if (!LoadPSD(s, header, img)) { 1303 // qDebug() << "Error loading PSD file."; 1304 return false; 1305 } 1306 1307 *image = img; 1308 return true; 1309 } 1310 1311 bool PSDHandler::supportsOption(ImageOption option) const 1312 { 1313 if (option == QImageIOHandler::Size) 1314 return true; 1315 return false; 1316 } 1317 1318 QVariant PSDHandler::option(ImageOption option) const 1319 { 1320 QVariant v; 1321 1322 if (option == QImageIOHandler::Size) { 1323 if (auto d = device()) { 1324 // transactions works on both random and sequential devices 1325 d->startTransaction(); 1326 auto ba = d->read(sizeof(PSDHeader)); 1327 d->rollbackTransaction(); 1328 1329 QDataStream s(ba); 1330 s.setByteOrder(QDataStream::BigEndian); 1331 1332 PSDHeader header; 1333 s >> header; 1334 1335 if (s.status() == QDataStream::Ok && IsValid(header)) 1336 v = QVariant::fromValue(QSize(header.width, header.height)); 1337 } 1338 } 1339 1340 return v; 1341 } 1342 1343 bool PSDHandler::canRead(QIODevice *device) 1344 { 1345 if (!device) { 1346 qWarning("PSDHandler::canRead() called with no device"); 1347 return false; 1348 } 1349 1350 device->startTransaction(); 1351 1352 QDataStream s(device); 1353 s.setByteOrder(QDataStream::BigEndian); 1354 1355 PSDHeader header; 1356 s >> header; 1357 1358 device->rollbackTransaction(); 1359 1360 if (s.status() != QDataStream::Ok) { 1361 return false; 1362 } 1363 1364 if (device->isSequential()) { 1365 if (header.color_mode == CM_CMYK || header.color_mode == CM_LABCOLOR || header.color_mode == CM_MULTICHANNEL) { 1366 return false; 1367 } 1368 if (header.color_mode == CM_RGB && header.channel_count > 3) { 1369 return false; // supposing extra channel as alpha 1370 } 1371 } 1372 1373 return IsSupported(header); 1374 } 1375 1376 QImageIOPlugin::Capabilities PSDPlugin::capabilities(QIODevice *device, const QByteArray &format) const 1377 { 1378 if (format == "psd" || format == "psb" || format == "pdd" || format == "psdt") { 1379 return Capabilities(CanRead); 1380 } 1381 if (!format.isEmpty()) { 1382 return {}; 1383 } 1384 if (!device->isOpen()) { 1385 return {}; 1386 } 1387 1388 Capabilities cap; 1389 if (device->isReadable() && PSDHandler::canRead(device)) { 1390 cap |= CanRead; 1391 } 1392 return cap; 1393 } 1394 1395 QImageIOHandler *PSDPlugin::create(QIODevice *device, const QByteArray &format) const 1396 { 1397 QImageIOHandler *handler = new PSDHandler; 1398 handler->setDevice(device); 1399 handler->setFormat(format); 1400 return handler; 1401 } 1402 1403 #include "moc_psd_p.cpp"