PSD: Added support to MCH1 and MCH2

- Multichannel images are treat as CMYK image when the number of channels are more than 1: when exists, channel 5 is used as alpha. Channels higher than 5 are discarded.
- Multichannel images are treat as Grayscaleimage when the number of channels are equals to 1.
- Device transactions removed (where possible)
- Fix clang-format issues
This commit is contained in:
Mirco Miranda 2024-12-23 23:58:40 +00:00 committed by Albert Astals Cid
parent 348ddce987
commit 894524f7e4
10 changed files with 71 additions and 89 deletions

View File

@ -220,7 +220,8 @@ The following defines can be defined in cmake to modify the behavior of the plug
PSD support has the following limitations: PSD support has the following limitations:
- Only images saved by Photoshop using compatibility mode enabled (Photoshop default) can be decoded. - Only images saved by Photoshop using compatibility mode enabled (Photoshop default) can be decoded.
- Multichannel images are treated as CMY/CMYK and are only loaded if they have 3 or more channels. - Multichannel images are treated as CMYK if they have 2 or more channels.
- Multichannel images are treated as Grayscale if they have 1 channel.
- Duotone images are treated as grayscale images. - Duotone images are treated as grayscale images.
- Extra channels other than alpha are discarded. - Extra channels other than alpha are discarded.

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

View File

@ -3,7 +3,7 @@
SPDX-FileCopyrightText: 2003 Ignacio Castaño <castano@ludicon.com> SPDX-FileCopyrightText: 2003 Ignacio Castaño <castano@ludicon.com>
SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org> SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
SPDX-FileCopyrightText: 2022-2023 Mirco Miranda <mircomir@outlook.com> SPDX-FileCopyrightText: 2022-2024 Mirco Miranda <mircomir@outlook.com>
SPDX-License-Identifier: LGPL-2.0-or-later SPDX-License-Identifier: LGPL-2.0-or-later
*/ */
@ -11,25 +11,19 @@
/* /*
* The early version of this code was based on Thacher Ulrich PSD loading code * The early version of this code was based on Thacher Ulrich PSD loading code
* released into the public domain. See: http://tulrich.com/geekstuff/ * released into the public domain. See: http://tulrich.com/geekstuff/
*/ *
/*
* Documentation on this file format is available at * Documentation on this file format is available at
* http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ * http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/
*/ *
/*
* Limitations of the current code: * Limitations of the current code:
* - Color spaces other than RGB/Grayscale cannot be read due to lack of QImage * - Color spaces other than RGB/Grayscale cannot be read due to lack of QImage
* support. Where possible, a conversion to RGB is done: * support. Where possible, a conversion to RGB is done:
* - CMYK images are converted using an approximated way that ignores the color * - CMYK images are converted using an approximated way that ignores the color
* information (ICC profile) with Qt less than 6.8. * information (ICC profile) with Qt less than 6.8.
* - LAB images are converted to sRGB using literature formulas. * - LAB images are converted to sRGB using literature formulas.
* - MULICHANNEL images more than 3 channels are converted as CMYK images. * - MULICHANNEL images with 1 channel are treat as Grayscale images.
* - DUOTONE images are considered as Grayscale images. * - MULICHANNEL images with more than 1 channels are treat as CMYK images.
* * - DUOTONE images are treat as Grayscale images.
* NOTE: The best way to convert between different color spaces is to use a
* color management engine (e.g. LittleCMS).
*/ */
#include "fastmath_p.h" #include "fastmath_p.h"
@ -59,14 +53,14 @@ typedef quint8 uchar;
* At the time I'm writing (07/2022), Gwenview and Krita supports linear sRgb but KDE * At the time I'm writing (07/2022), Gwenview and Krita supports linear sRgb but KDE
* preview creator does not. This is the why, for now, it is disabled. * preview creator does not. This is the why, for now, it is disabled.
*/ */
//#define PSD_FAST_LAB_CONVERSION // #define PSD_FAST_LAB_CONVERSION
/* /*
* Since Qt version 6.8, the 8-bit CMYK format is natively supported. * Since Qt version 6.8, the 8-bit CMYK format is natively supported.
* If you encounter problems with native CMYK support you can continue to force the plugin to convert * If you encounter problems with native CMYK support you can continue to force the plugin to convert
* to RGB as in previous versions by defining PSD_NATIVE_CMYK_SUPPORT_DISABLED. * to RGB as in previous versions by defining PSD_NATIVE_CMYK_SUPPORT_DISABLED.
*/ */
//#define PSD_NATIVE_CMYK_SUPPORT_DISABLED // #define PSD_NATIVE_CMYK_SUPPORT_DISABLED
namespace // Private. namespace // Private.
{ {
@ -221,8 +215,7 @@ static qint64 readSize(QDataStream &s, bool psb = false)
quint32 tmp; quint32 tmp;
s >> tmp; s >> tmp;
size = tmp; size = tmp;
} } else {
else {
s >> size; s >> size;
} }
if (s.status() != QDataStream::Ok) { if (s.status() != QDataStream::Ok) {
@ -438,8 +431,9 @@ PSDLayerAndMaskSection readLayerAndMaskSection(QDataStream &s, bool isPsb, bool
if (s.status() == QDataStream::Ok) { if (s.status() == QDataStream::Ok) {
for (bool ok = true; ok && !lms.atEnd(isPsb);) { for (bool ok = true; ok && !lms.atEnd(isPsb);) {
auto al = readAdditionalLayer(s, &ok); auto al = readAdditionalLayer(s, &ok);
if (ok) if (ok) {
lms.additionalLayerInfo.insert(al.id, al); lms.additionalLayerInfo.insert(al.id, al);
}
} }
} }
@ -475,8 +469,7 @@ PSDColorModeDataSection readColorModeDataSection(QDataStream &s, bool *ok = null
cms.duotone.data = s.device()->read(size); cms.duotone.data = s.device()->read(size);
if (cms.duotone.data.size() != size) if (cms.duotone.data.size() != size)
*ok = false; *ok = false;
} } else { // read the palette (768 bytes)
else { // read the palette (768 bytes)
auto&& palette = cms.palette; auto&& palette = cms.palette;
QList<quint8> vect(size); QList<quint8> vect(size);
for (auto&& v : vect) for (auto&& v : vect)
@ -626,7 +619,7 @@ static QDataStream &operator>>(QDataStream &s, PSDHeader &header)
static bool IsValid(const PSDHeader &header) static bool IsValid(const PSDHeader &header)
{ {
if (header.signature != 0x38425053) { // '8BPS' if (header.signature != 0x38425053) { // '8BPS'
//qDebug() << "PSD header: invalid signature" << header.signature; // qDebug() << "PSD header: invalid signature" << header.signature;
return false; return false;
} }
if (header.version != 1 && header.version != 2) { if (header.version != 1 && header.version != 2) {
@ -689,10 +682,6 @@ static bool IsSupported(const PSDHeader &header)
header.color_mode != CM_BITMAP) { header.color_mode != CM_BITMAP) {
return false; return false;
} }
if (header.color_mode == CM_MULTICHANNEL &&
header.channel_count < 3) {
return false;
}
return true; return true;
} }
@ -724,8 +713,7 @@ qint64 decompress(const char *input, qint64 ilen, char *output, qint64 olen)
return -1; return -1;
memcpy(output + j, input + ip, size_t(rr)); memcpy(output + j, input + ip, size_t(rr));
ip += rr; ip += rr;
} } else if (ip < ilen) {
else if (ip < ilen) {
rr = qint64(1-n); rr = qint64(1-n);
if (available < rr) { if (available < rr) {
--ip; --ip;
@ -753,27 +741,36 @@ static QImage::Format imageFormat(const PSDHeader &header, bool alpha)
auto format = QImage::Format_Invalid; auto format = QImage::Format_Invalid;
switch(header.color_mode) { switch(header.color_mode) {
case CM_RGB: case CM_RGB:
if (header.depth == 32) if (header.depth == 32) {
format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX32FPx4 : QImage::Format_RGBA32FPx4_Premultiplied; format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX32FPx4 : QImage::Format_RGBA32FPx4_Premultiplied;
else if (header.depth == 16) } else if (header.depth == 16) {
format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64_Premultiplied; format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64_Premultiplied;
else } else {
format = header.channel_count < 4 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888_Premultiplied; format = header.channel_count < 4 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888_Premultiplied;
}
break; break;
case CM_MULTICHANNEL: // Treat MCH as CMYK (number of channel check is done in IsSupported()) case CM_MULTICHANNEL: // Treat MCH as CMYK or Grayscale
case CM_CMYK: // Photoshop supports CMYK/MCH 8-bits and 16-bits only case CM_CMYK: // Photoshop supports CMYK/MCH 8-bits and 16-bits only
if (NATIVE_CMYK && header.channel_count == 4 && (header.depth == 16 || header.depth == 8)) if (NATIVE_CMYK && header.channel_count == 4 && (header.depth == 16 || header.depth == 8)) {
format = CMYK_FORMAT; format = CMYK_FORMAT;
else if (header.depth == 16) } else if (header.depth == 16) {
format = header.channel_count < 5 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64; if (header.channel_count == 1)
else if (header.depth == 8) format = QImage::Format_Grayscale16;
format = header.channel_count < 5 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888; else
format = header.channel_count < 5 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64;
} else if (header.depth == 8) {
if (header.channel_count == 1)
format = QImage::Format_Grayscale8;
else
format = header.channel_count < 5 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888;
}
break; break;
case CM_LABCOLOR: // Photoshop supports LAB 8-bits and 16-bits only case CM_LABCOLOR: // Photoshop supports LAB 8-bits and 16-bits only
if (header.depth == 16) if (header.depth == 16) {
format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64; format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64;
else if (header.depth == 8) } else if (header.depth == 8) {
format = header.channel_count < 4 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888; format = header.channel_count < 4 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888;
}
break; break;
case CM_GRAYSCALE: case CM_GRAYSCALE:
case CM_DUOTONE: case CM_DUOTONE:
@ -908,16 +905,14 @@ inline void premulConversion(char *stride, qint32 width, qint32 ac, qint32 cn, c
auto alpha = *(s + xcn + ac); auto alpha = *(s + xcn + ac);
*(s + xcn + c) = *(s + xcn + c) + alpha - max; *(s + xcn + c) = *(s + xcn + c) + alpha - max;
} }
} } else if (conv == PremulConversion::PS2A || (conv == PremulConversion::PSLab2A && c == 0)) {
else if (conv == PremulConversion::PS2A || (conv == PremulConversion::PSLab2A && c == 0)) {
for (qint32 x = 0; x < width; ++x) { for (qint32 x = 0; x < width; ++x) {
auto xcn = x * cn; auto xcn = x * cn;
auto alpha = *(s + xcn + ac); auto alpha = *(s + xcn + ac);
if (alpha > 0) if (alpha > 0)
*(s + xcn + c) = ((*(s + xcn + c) + alpha - max) * max + alpha / 2) / alpha; *(s + xcn + c) = ((*(s + xcn + c) + alpha - max) * max + alpha / 2) / alpha;
} }
} } else if (conv == PremulConversion::PSLab2A) {
else if (conv == PremulConversion::PSLab2A) {
for (qint32 x = 0; x < width; ++x) { for (qint32 x = 0; x < width; ++x) {
auto xcn = x * cn; auto xcn = x * cn;
auto alpha = *(s + xcn + ac); auto alpha = *(s + xcn + ac);
@ -981,22 +976,22 @@ inline void cmykToRgb(uchar *target, qint32 targetChannels, const char *source,
auto max = double(std::numeric_limits<T>::max()); auto max = double(std::numeric_limits<T>::max());
auto invmax = 1.0 / max; // speed improvements by ~10% auto invmax = 1.0 / max; // speed improvements by ~10%
if (sourceChannels < 3) { if (sourceChannels < 2) {
qDebug() << "cmykToRgb: image is not a valid CMY/CMYK!"; qDebug() << "cmykToRgb: image is not a valid MCH/CMYK!";
return; return;
} }
for (qint32 w = 0; w < width; ++w) { for (qint32 w = 0; w < width; ++w) {
auto ps = s + sourceChannels * w; auto ps = s + sourceChannels * w;
auto C = 1 - *(ps + 0) * invmax; auto C = 1 - *(ps + 0) * invmax;
auto M = 1 - *(ps + 1) * invmax; auto M = sourceChannels > 1 ? 1 - *(ps + 1) * invmax : 0.0;
auto Y = 1 - *(ps + 2) * invmax; auto Y = sourceChannels > 2 ? 1 - *(ps + 2) * invmax : 0.0;
auto K = sourceChannels > 3 ? 1 - *(ps + 3) * invmax : 0.0; auto K = sourceChannels > 3 ? 1 - *(ps + 3) * invmax : 0.0;
auto pt = t + targetChannels * w; auto pt = t + targetChannels * w;
*(pt + 0) = T(std::min(max - (C * (1 - K) + K) * max + 0.5, max)); *(pt + 0) = T(std::min(max - (C * (1 - K) + K) * max + 0.5, max));
*(pt + 1) = T(std::min(max - (M * (1 - K) + K) * max + 0.5, max)); *(pt + 1) = targetChannels > 1 ? T(std::min(max - (M * (1 - K) + K) * max + 0.5, max)) : std::numeric_limits<T>::max();
*(pt + 2) = T(std::min(max - (Y * (1 - K) + K) * max + 0.5, max)); *(pt + 2) = targetChannels > 2 ? T(std::min(max - (Y * (1 - K) + K) * max + 0.5, max)) : std::numeric_limits<T>::max();
if (targetChannels == 4) { if (targetChannels == 4) {
if (sourceChannels >= 5 && alpha) if (sourceChannels >= 5 && alpha)
*(pt + 3) = *(ps + 4); *(pt + 3) = *(ps + 4);
@ -1083,8 +1078,7 @@ bool readChannel(QByteArray& target, QDataStream &stream, quint32 compressedSize
if (decompress(tmp.data(), tmp.size(), target.data(), target.size()) < 0) { if (decompress(tmp.data(), tmp.size(), target.data(), target.size()) < 0) {
return false; return false;
} }
} } else if (stream.readRawData(target.data(), target.size()) != target.size()) {
else if (stream.readRawData(target.data(), target.size()) != target.size()) {
return false; return false;
} }
@ -1198,7 +1192,7 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
// clang-format off // clang-format off
// checks the need of color conversion (that requires random access to the image) // checks the need of color conversion (that requires random access to the image)
auto randomAccess = (header.color_mode == CM_CMYK && !native_cmyk) || auto randomAccess = (header.color_mode == CM_CMYK && !native_cmyk) ||
(header.color_mode == CM_MULTICHANNEL && !native_cmyk) || (header.color_mode == CM_MULTICHANNEL && header.channel_count != 1 && !native_cmyk) ||
(header.color_mode == CM_LABCOLOR) || (header.color_mode == CM_LABCOLOR) ||
(header.color_mode != CM_INDEXED && img.hasAlphaChannel()); (header.color_mode != CM_INDEXED && img.hasAlphaChannel());
// clang-format on // clang-format on
@ -1235,11 +1229,9 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
auto scanLine = reinterpret_cast<unsigned char*>(psdScanline.data()); auto scanLine = reinterpret_cast<unsigned char*>(psdScanline.data());
if (header.depth == 8) { if (header.depth == 8) {
planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, header.channel_count); planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, header.channel_count);
} } else if (header.depth == 16) {
else if (header.depth == 16) {
planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, header.channel_count); planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, header.channel_count);
} } else if (header.depth == 32) {
else if (header.depth == 32) {
planarToChunchy<float>(scanLine, rawStride.data(), header.width, c, header.channel_count); planarToChunchy<float>(scanLine, rawStride.data(), header.width, c, header.channel_count);
} }
} }
@ -1276,15 +1268,13 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
cmykToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha); cmykToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
else if (header.depth == 16) else if (header.depth == 16)
cmykToRgb<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha); cmykToRgb<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
} } else if (header.depth == 8) {
else if (header.depth == 8) {
rawChannelsCopyToCMYK<quint8>(tmpCmyk.bits(), 4, psdScanline.data(), header.channel_count, header.width); rawChannelsCopyToCMYK<quint8>(tmpCmyk.bits(), 4, psdScanline.data(), header.channel_count, header.width);
if (auto rgbPtr = iccConv.convertedScanLine(tmpCmyk, 0)) if (auto rgbPtr = iccConv.convertedScanLine(tmpCmyk, 0))
std::memcpy(img.scanLine(y), rgbPtr, img.bytesPerLine()); std::memcpy(img.scanLine(y), rgbPtr, img.bytesPerLine());
if (imgChannels == 4 && header.channel_count >= 5) if (imgChannels == 4 && header.channel_count >= 5)
rawChannelCopy<quint8>(img.scanLine(y), imgChannels, 3, psdScanline.data(), header.channel_count, 4, header.width); rawChannelCopy<quint8>(img.scanLine(y), imgChannels, 3, psdScanline.data(), header.channel_count, 4, header.width);
} } else if (header.depth == 16) {
else if (header.depth == 16) {
rawChannelsCopyToCMYK<quint16>(tmpCmyk.bits(), 4, psdScanline.data(), header.channel_count, header.width); rawChannelsCopyToCMYK<quint16>(tmpCmyk.bits(), 4, psdScanline.data(), header.channel_count, header.width);
if (auto rgbPtr = iccConv.convertedScanLine(tmpCmyk, 0)) if (auto rgbPtr = iccConv.convertedScanLine(tmpCmyk, 0))
std::memcpy(img.scanLine(y), rgbPtr, img.bytesPerLine()); std::memcpy(img.scanLine(y), rgbPtr, img.bytesPerLine());
@ -1307,8 +1297,7 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
rawChannelsCopy<float>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width); rawChannelsCopy<float>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width);
} }
} }
} } else {
else {
// Linear read (no position jumps): optimized code usable only for the colorspaces supported by QImage // Linear read (no position jumps): optimized code usable only for the colorspaces supported by QImage
for (qint32 c = 0; c < channel_num; ++c) { for (qint32 c = 0; c < channel_num; ++c) {
for (qint32 y = 0, h = header.height; y < h; ++y) { for (qint32 y = 0, h = header.height; y < h; ++y) {
@ -1319,25 +1308,26 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
} }
auto scanLine = img.scanLine(y); auto scanLine = img.scanLine(y);
if (header.depth == 1) { // Bitmap if (header.depth == 1) {
// Bitmap
monoInvert(scanLine, rawStride.data(), std::min(rawStride.size(), img.bytesPerLine())); monoInvert(scanLine, rawStride.data(), std::min(rawStride.size(), img.bytesPerLine()));
} } else if (header.depth == 8) {
else if (header.depth == 8) { // 8-bits images: Indexed, Grayscale, RGB/RGBA, CMYK, MCH4 // 8-bits images: Indexed, Grayscale, RGB/RGBA, CMYK, MCH1, MCH4
if (native_cmyk) if (native_cmyk)
planarToChunchyCMYK<quint8>(scanLine, rawStride.data(), header.width, c, imgChannels); planarToChunchyCMYK<quint8>(scanLine, rawStride.data(), header.width, c, imgChannels);
else else
planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, imgChannels); planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, imgChannels);
} } else if (header.depth == 16) {
else if (header.depth == 16) { // 16-bits integer images: Grayscale, RGB/RGBA, CMYK, MCH4 // 16-bits integer images: Grayscale, RGB/RGBA, CMYK, MCH1, MCH4
if (native_cmyk) if (native_cmyk)
planarToChunchyCMYK<quint16>(scanLine, rawStride.data(), header.width, c, imgChannels); planarToChunchyCMYK<quint16>(scanLine, rawStride.data(), header.width, c, imgChannels);
else else
planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, imgChannels); planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, imgChannels);
} } else if (header.depth == 32 && header.color_mode == CM_RGB) {
else if (header.depth == 32 && header.color_mode == CM_RGB) { // 32-bits float images: RGB/RGBA // 32-bits float images: RGB/RGBA
planarToChunchy<float>(scanLine, rawStride.data(), header.width, c, imgChannels); planarToChunchy<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
} } else if (header.depth == 32 && header.color_mode == CM_GRAYSCALE) {
else if (header.depth == 32 && header.color_mode == CM_GRAYSCALE) { // 32-bits float images: Grayscale (coverted to equivalent integer 16-bits) // 32-bits float images: Grayscale (coverted to equivalent integer 16-bits)
planarToChunchyFloatToUInt16<float>(scanLine, rawStride.data(), header.width, c, imgChannels); planarToChunchyFloatToUInt16<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
} }
} }
@ -1357,8 +1347,7 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
#else #else
img.setColorSpace(QColorSpace(QColorSpace::SRgb)); img.setColorSpace(QColorSpace(QColorSpace::SRgb));
#endif #endif
} } else if (!setColorSpace(img, irs)) {
else if (!setColorSpace(img, irs)) {
// qDebug() << "No colorspace info set!"; // qDebug() << "No colorspace info set!";
} }
@ -1412,19 +1401,19 @@ bool PSDHandler::read(QImage *image)
// Check image file format. // Check image file format.
if (s.atEnd() || !IsValid(header)) { if (s.atEnd() || !IsValid(header)) {
// qDebug() << "This PSD file is not valid."; // qDebug() << "This PSD file is not valid.";
return false; return false;
} }
// Check if it's a supported format. // Check if it's a supported format.
if (!IsSupported(header)) { if (!IsSupported(header)) {
// qDebug() << "This PSD file is not supported."; // qDebug() << "This PSD file is not supported.";
return false; return false;
} }
QImage img; QImage img;
if (!LoadPSD(s, header, img)) { if (!LoadPSD(s, header, img)) {
// qDebug() << "Error loading PSD file."; // qDebug() << "Error loading PSD file.";
return false; return false;
} }
@ -1447,13 +1436,8 @@ QVariant PSDHandler::option(ImageOption option) const
auto&& header = d->m_header; auto&& header = d->m_header;
if (IsValid(header)) { if (IsValid(header)) {
v = QVariant::fromValue(QSize(header.width, header.height)); v = QVariant::fromValue(QSize(header.width, header.height));
} } else if (auto dev = device()) {
else if (auto dev = device()) { auto ba = dev->peek(sizeof(PSDHeader));
// transactions works on both random and sequential devices
dev->startTransaction();
auto ba = dev->read(sizeof(PSDHeader));
dev->rollbackTransaction();
QDataStream s(ba); QDataStream s(ba);
s.setByteOrder(QDataStream::BigEndian); s.setByteOrder(QDataStream::BigEndian);
@ -1473,16 +1457,13 @@ bool PSDHandler::canRead(QIODevice *device)
return false; return false;
} }
device->startTransaction(); auto ba = device->peek(sizeof(PSDHeader));
QDataStream s(ba);
QDataStream s(device);
s.setByteOrder(QDataStream::BigEndian); s.setByteOrder(QDataStream::BigEndian);
PSDHeader header; PSDHeader header;
s >> header; s >> header;
device->rollbackTransaction();
if (s.status() != QDataStream::Ok) { if (s.status() != QDataStream::Ok) {
return false; return false;
} }