mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-05-28 00:30:23 -04:00
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:
parent
348ddce987
commit
894524f7e4
@ -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.
|
||||||
|
|
||||||
|
BIN
autotests/read/psd/mch1-16bits.png
Normal file
BIN
autotests/read/psd/mch1-16bits.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
BIN
autotests/read/psd/mch1-16bits.psd
Normal file
BIN
autotests/read/psd/mch1-16bits.psd
Normal file
Binary file not shown.
BIN
autotests/read/psd/mch1-8bits.png
Normal file
BIN
autotests/read/psd/mch1-8bits.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
autotests/read/psd/mch1-8bits.psd
Normal file
BIN
autotests/read/psd/mch1-8bits.psd
Normal file
Binary file not shown.
BIN
autotests/read/psd/mch2-16bits.png
Normal file
BIN
autotests/read/psd/mch2-16bits.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
BIN
autotests/read/psd/mch2-16bits.psd
Normal file
BIN
autotests/read/psd/mch2-16bits.psd
Normal file
Binary file not shown.
BIN
autotests/read/psd/mch2-8bits.png
Normal file
BIN
autotests/read/psd/mch2-8bits.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
BIN
autotests/read/psd/mch2-8bits.psd
Normal file
BIN
autotests/read/psd/mch2-8bits.psd
Normal file
Binary file not shown.
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user