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"