29#include "fastmath_p.h"
30#include "microexif_p.h"
32#include "scanlineconverter_p.h"
44typedef quint16 ushort;
77#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0) || defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
78# define CMYK_FORMAT QImage::Format_Invalid
80# define CMYK_FORMAT QImage::Format_CMYK8888
83#define NATIVE_CMYK (CMYK_FORMAT != QImage::Format_Invalid)
92enum ColorMode : quint16 {
103enum ImageResourceId : quint16 {
104 IRI_RESOLUTIONINFO = 0x03ED,
105 IRI_ICCPROFILE = 0x040F,
106 IRI_TRANSPARENCYINDEX = 0x0417,
107 IRI_ALPHAIDENTIFIERS = 0x041D,
108 IRI_VERSIONINFO = 0x0421,
109 IRI_EXIFDATA1 = 0x0422,
110 IRI_EXIFDATA3 = 0x0423,
111 IRI_XMPMETADATA = 0x0424
114enum LayerId : quint32 {
115 LI_MT16 = 0x4D743136,
116 LI_MT32 = 0x4D743332,
122 memset(
this, 0,
sizeof(PSDHeader));
128 ushort channel_count;
135struct PSDImageResourceBlock {
145struct PSDDuotoneOptions {
153struct PSDColorModeDataSection {
154 PSDDuotoneOptions duotone;
162 qint16 layerCount = 0;
165struct PSDGlobalLayerMaskInfo {
169struct PSDAdditionalLayerInfo {
170 Signature signature = Signature();
171 LayerId
id = LayerId();
175struct PSDLayerAndMaskSection {
177 PSDLayerInfo layerInfo;
178 PSDGlobalLayerMaskInfo globalLayerMaskInfo;
179 QHash<LayerId, PSDAdditionalLayerInfo> additionalLayerInfo;
181 bool isNull()
const {
185 bool hasAlpha()
const {
186 return layerInfo.layerCount < 0 ||
187 additionalLayerInfo.contains(LI_MT16) ||
188 additionalLayerInfo.contains(LI_MT32) ||
189 additionalLayerInfo.contains(LI_MTRN);
192 bool atEnd(
bool isPsb)
const {
193 qint64 currentSize = 0;
194 if (layerInfo.size > -1) {
195 currentSize += layerInfo.size + 4;
199 if (globalLayerMaskInfo.size > -1) {
200 currentSize += globalLayerMaskInfo.size + 4;
202 auto aliv = additionalLayerInfo.values();
203 for (
auto &&v : aliv) {
204 currentSize += (12 + v.size);
205 if (v.signature == S_8B64)
208 return (size <= currentSize);
216static double fixedPointToDouble(qint32 fixedPoint)
218 auto i = double(fixedPoint >> 16);
219 auto d = double((fixedPoint & 0x0000FFFF) / 65536.0);
223static qint64 readSize(
QDataStream &s,
bool psb =
false)
242 for (qint32 i32 = 0; size; size -= i32) {
243 i32 = std::min(size, qint64(std::numeric_limits<qint32>::max()));
251static bool skip_section(
QDataStream &s,
bool psb =
false)
253 auto section_length = readSize(s, psb);
254 if (section_length < 0)
256 return skip_data(s, section_length);
267static QString readPascalString(
QDataStream &s, qint32 alignBytes = 1, qint32 *size =
nullptr)
275 *size =
sizeof(stringSize);
278 if (stringSize > 0) {
290 if (
auto pad = *size % alignBytes)
303static PSDImageResourceSection readImageResourceSection(
QDataStream &s,
bool *ok =
nullptr)
305 PSDImageResourceSection irs;
317 for (
auto size = sectioSize; size > 0;) {
332 size -=
sizeof(signature);
334 if (signature != S_8BIM && signature != S_MeSa) {
335 qDebug() <<
"Invalid Image Resource Block Signature!";
346 PSDImageResourceBlock irb;
350 irb.name = readPascalString(s, 2, &bytes);
356 size -=
sizeof(dataSize);
359 if (
auto dev = s.
device())
360 irb.data = dev->read(dataSize);
364 if (quint32(read) != dataSize) {
365 qDebug() <<
"Image Resource Block Read Error!";
370 if (
auto pad = dataSize % 2) {
383PSDAdditionalLayerInfo readAdditionalLayer(
QDataStream &s,
bool *ok =
nullptr)
385 PSDAdditionalLayerInfo li;
392 *
ok = li.signature == S_8BIM || li.signature == S_8B64;
401 li.size = readSize(s, li.signature == S_8B64);
406 *
ok = skip_data(s, li.size);
411PSDLayerAndMaskSection readLayerAndMaskSection(
QDataStream &s,
bool isPsb,
bool *ok =
nullptr)
413 PSDLayerAndMaskSection lms;
423 lms.size = readSize(s, isPsb);
427 lms.layerInfo.size = readSize(s, isPsb);
428 if (lms.layerInfo.size > 0) {
429 s >> lms.layerInfo.layerCount;
430 skip_data(s, lms.layerInfo.size -
sizeof(lms.layerInfo.layerCount));
436 lms.globalLayerMaskInfo.size = readSize(s,
false);
437 if (lms.globalLayerMaskInfo.size > 0) {
438 skip_data(s, lms.globalLayerMaskInfo.size);
444 for (
bool ok =
true;
ok && !lms.atEnd(isPsb);) {
445 auto al = readAdditionalLayer(s, &ok);
447 lms.additionalLayerInfo.insert(al.id, al);
452 device->rollbackTransaction();
453 *
ok = skip_section(s, isPsb);
464PSDColorModeDataSection readColorModeDataSection(
QDataStream &s,
bool *ok =
nullptr)
466 PSDColorModeDataSection cms;
482 if (cms.duotone.data.
size() != size)
485 auto &&palette = cms.palette;
487 for (
auto &&v : vect)
489 for (qsizetype i = 0, n = vect.size()/3; i < n; ++i)
490 palette.append(qRgb(vect.at(i), vect.at(n+i), vect.at(n+n+i)));
503static bool setColorSpace(
QImage &img,
const PSDImageResourceSection &irs)
505 if (!irs.contains(IRI_ICCPROFILE) || img.
isNull())
507 auto irb = irs.value(IRI_ICCPROFILE);
522static bool setXmpData(
QImage &img,
const PSDImageResourceSection &irs)
524 if (!irs.contains(IRI_XMPMETADATA))
526 auto irb = irs.value(IRI_XMPMETADATA);
533 img.
setText(QStringLiteral(META_KEY_XMP_ADOBE), xmp);
544static bool setExifData(
QImage &img,
const MicroExif &exif)
548 exif.toImageMetadata(img);
558static bool HasMergedData(
const PSDImageResourceSection &irs)
560 if (!irs.contains(IRI_VERSIONINFO))
562 auto irb = irs.value(IRI_VERSIONINFO);
563 if (irb.data.
size() > 4)
564 return irb.data.
at(4) != 0;
575static bool setResolution(
QImage &img,
const PSDImageResourceSection &irs)
577 if (!irs.contains(IRI_RESOLUTIONINFO))
579 auto irb = irs.value(IRI_RESOLUTIONINFO);
588 auto hres = fixedPointToDouble(i32);
595 auto vres = fixedPointToDouble(i32);
609static bool setTransparencyIndex(
QImage &img,
const PSDImageResourceSection &irs)
611 if (!irs.contains(IRI_TRANSPARENCYINDEX))
613 auto irb = irs.value(IRI_TRANSPARENCYINDEX);
620 if (idx < palette.size()) {
621 auto &&v = palette[idx];
622 v = QRgb(v & ~0xFF000000);
632 s >> header.signature;
634 for (
int i = 0; i < 6; i++) {
635 s >> header.reserved[i];
637 s >> header.channel_count;
641 s >> header.color_mode;
646static bool IsValid(
const PSDHeader &header)
648 if (header.signature != 0x38425053) {
652 if (header.version != 1 && header.version != 2) {
653 qDebug() <<
"PSD header: invalid version" << header.version;
656 if (header.depth != 8 &&
657 header.depth != 16 &&
658 header.depth != 32 &&
660 qDebug() <<
"PSD header: invalid depth" << header.depth;
663 if (header.color_mode != CM_RGB &&
664 header.color_mode != CM_GRAYSCALE &&
665 header.color_mode != CM_INDEXED &&
666 header.color_mode != CM_DUOTONE &&
667 header.color_mode != CM_CMYK &&
668 header.color_mode != CM_LABCOLOR &&
669 header.color_mode != CM_MULTICHANNEL &&
670 header.color_mode != CM_BITMAP) {
671 qDebug() <<
"PSD header: invalid color mode" << header.color_mode;
676 if (header.channel_count < 1 || header.channel_count > 57) {
677 qDebug() <<
"PSD header: invalid number of channels" << header.channel_count;
680 if (header.width > 300000 || header.height > 300000) {
681 qDebug() <<
"PSD header: invalid image size" << header.width <<
"x" << header.height;
688static bool IsSupported(
const PSDHeader &header)
690 if (!IsValid(header)) {
693 if (header.version != 1 && header.version != 2) {
696 if (header.depth != 8 &&
697 header.depth != 16 &&
698 header.depth != 32 &&
702 if (header.color_mode != CM_RGB &&
703 header.color_mode != CM_GRAYSCALE &&
704 header.color_mode != CM_INDEXED &&
705 header.color_mode != CM_DUOTONE &&
706 header.color_mode != CM_CMYK &&
707 header.color_mode != CM_MULTICHANNEL &&
708 header.color_mode != CM_LABCOLOR &&
709 header.color_mode != CM_BITMAP) {
724qint64 decompress(
const char *input, qint64 ilen,
char *output, qint64 olen)
727 for (qint64 ip = 0, rr = 0, available = olen; j < olen && ip < ilen; available = olen - j) {
728 signed char n =
static_cast<signed char>(input[ip++]);
734 if (available < rr) {
741 memcpy(output + j, input + ip,
size_t(rr));
743 }
else if (ip < ilen) {
745 if (available < rr) {
749 memset(output + j, input[ip++],
size_t(rr));
762static QImage::Format imageFormat(
const PSDHeader &header,
bool alpha)
764 if (header.channel_count == 0) {
769 switch(header.color_mode) {
771 if (header.depth == 32) {
773 }
else if (header.depth == 16) {
779 case CM_MULTICHANNEL:
781 if (NATIVE_CMYK && header.channel_count == 4 && (header.depth == 16 || header.depth == 8)) {
782 format = CMYK_FORMAT;
783 }
else if (header.depth == 16) {
784 if (header.channel_count == 1)
788 }
else if (header.depth == 8) {
789 if (header.channel_count == 1)
796 if (header.depth == 16) {
798 }
else if (header.depth == 8) {
840inline quint8 xchg(quint8 v)
845inline quint16 xchg(quint16 v)
847#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
848 return quint16( (v>>8) | (v<<8) );
854inline quint32 xchg(quint32 v)
856#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
857 return quint32( (v>>24) | ((v & 0x00FF0000)>>8) | ((v & 0x0000FF00)<<8) | (v<<24) );
863inline float xchg(
float v)
865#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
868 quint32 f = xchg(*
reinterpret_cast<quint32*
>(pf));
870 return *
reinterpret_cast<float*
>(pi);
873 std::memcpy(&t, &v,
sizeof(quint32));
875 std::memcpy(&v, &t,
sizeof(quint32));
884inline void planarToChunchy(uchar *target,
const char *source, qint32 width, qint32 c, qint32 cn)
886 auto s =
reinterpret_cast<const T*
>(source);
887 auto t =
reinterpret_cast<T*
>(target);
888 for (qint32 x = 0; x < width; ++x) {
889 t[x * cn + c] = xchg(s[x]);
894inline void planarToChunchyCMYK(uchar *target,
const char *source, qint32 width, qint32 c, qint32 cn)
896 auto s =
reinterpret_cast<const T*
>(source);
897 auto t =
reinterpret_cast<quint8*
>(target);
898 const T d = std::numeric_limits<T>::max() / std::numeric_limits<quint8>::max();
899 for (qint32 x = 0; x < width; ++x) {
900 t[x * cn + c] = quint8((std::numeric_limits<T>::max() - xchg(s[x])) / d);
906inline void planarToChunchyFloatToUInt16(uchar *target,
const char *source, qint32 width, qint32 c, qint32 cn)
908 auto s =
reinterpret_cast<const T*
>(source);
909 auto t =
reinterpret_cast<quint16*
>(target);
910 for (qint32 x = 0; x < width; ++x) {
911 t[x * cn + c] = quint16(std::min(xchg(s[x]) * std::numeric_limits<quint16>::max() + 0.5,
double(std::numeric_limits<quint16>::max())));
915enum class PremulConversion {
922inline void premulConversion(
char *stride, qint32 width, qint32 ac, qint32 cn,
const PremulConversion &conv)
924 auto s =
reinterpret_cast<T*
>(stride);
926 auto max = qint64(std::numeric_limits<T>::is_integer ? std::numeric_limits<T>::max() : 1);
928 for (qint32 c = 0; c < ac; ++c) {
929 if (conv == PremulConversion::PS2P) {
930 for (qint32 x = 0; x < width; ++x) {
932 auto alpha = *(s + xcn + ac);
933 *(s + xcn + c) = *(s + xcn + c) + alpha - max;
935 }
else if (conv == PremulConversion::PS2A || (conv == PremulConversion::PSLab2A && c == 0)) {
936 for (qint32 x = 0; x < width; ++x) {
938 auto alpha = *(s + xcn + ac);
940 *(s + xcn + c) = ((*(s + xcn + c) + alpha - max) * max + alpha / 2) / alpha;
942 }
else if (conv == PremulConversion::PSLab2A) {
943 for (qint32 x = 0; x < width; ++x) {
945 auto alpha = *(s + xcn + ac);
947 *(s + xcn + c) = ((*(s + xcn + c) + (alpha - max + 1) / 2) * max + alpha / 2) / alpha;
953inline void monoInvert(uchar *target,
const char* source, qint32 bytes)
955 auto s =
reinterpret_cast<const quint8*
>(source);
956 auto t =
reinterpret_cast<quint8*
>(target);
957 for (qint32 x = 0; x < bytes; ++x) {
963inline void rawChannelsCopyToCMYK(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width)
965 auto s =
reinterpret_cast<const T*
>(source);
966 auto t =
reinterpret_cast<quint8*
>(target);
967 const T d = std::numeric_limits<T>::max() / std::numeric_limits<quint8>::max();
968 for (qint32 c = 0, cs = std::min(targetChannels, sourceChannels); c < cs; ++c) {
969 for (qint32 x = 0; x < width; ++x) {
970 t[x * targetChannels + c] = (std::numeric_limits<T>::max() - s[x * sourceChannels + c]) / d;
976inline void rawChannelsCopy(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width)
978 auto s =
reinterpret_cast<const T*
>(source);
979 auto t =
reinterpret_cast<T*
>(target);
980 for (qint32 c = 0, cs = std::min(targetChannels, sourceChannels); c < cs; ++c) {
981 for (qint32 x = 0; x < width; ++x) {
982 t[x * targetChannels + c] = s[x * sourceChannels + c];
988inline void rawChannelCopy(uchar *target, qint32 targetChannels, qint32 targetChannel,
const char *source, qint32 sourceChannels, qint32 sourceChannel, qint32 width)
990 auto s =
reinterpret_cast<const T*
>(source);
991 auto t =
reinterpret_cast<T*
>(target);
992 for (qint32 x = 0; x < width; ++x) {
993 t[x * targetChannels + targetChannel] = s[x * sourceChannels + sourceChannel];
999inline void cmykToRgb(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width,
bool alpha =
false)
1001 auto s =
reinterpret_cast<const T*
>(source);
1002 auto t =
reinterpret_cast<T*
>(target);
1003 auto max = double(std::numeric_limits<T>::max());
1004 auto invmax = 1.0 / max;
1006 if (sourceChannels < 2) {
1007 qDebug() <<
"cmykToRgb: image is not a valid MCH/CMYK!";
1011 for (qint32 w = 0; w < width; ++w) {
1012 auto ps = s + sourceChannels * w;
1013 auto C = 1 - *(ps + 0) * invmax;
1014 auto M = sourceChannels > 1 ? 1 - *(ps + 1) * invmax : 0.0;
1015 auto Y = sourceChannels > 2 ? 1 - *(ps + 2) * invmax : 0.0;
1016 auto K = sourceChannels > 3 ? 1 - *(ps + 3) * invmax : 0.0;
1018 auto pt = t + targetChannels * w;
1019 *(pt + 0) = T(std::min(max - (C * (1 - K) + K) * max + 0.5, max));
1020 *(pt + 1) = targetChannels > 1 ? T(std::min(max - (M * (1 - K) + K) * max + 0.5, max)) : std::numeric_limits<T>::max();
1021 *(pt + 2) = targetChannels > 2 ? T(std::min(max - (Y * (1 - K) + K) * max + 0.5, max)) : std::numeric_limits<T>::max();
1022 if (targetChannels == 4) {
1023 if (sourceChannels >= 5 && alpha)
1024 *(pt + 3) = *(ps + 4);
1026 *(pt + 3) = std::numeric_limits<T>::max();
1031inline double finv(
double v)
1033 return (v > 6.0 / 29.0 ? v * v * v : (v - 16.0 / 116.0) / 7.787);
1036inline double gammaCorrection(
double linear)
1038#ifdef PSD_FAST_LAB_CONVERSION
1043 return (linear > 0.0031308 ? 1.055 * fastPow(linear, 1.0 / 2.4) - 0.055 : 12.92 * linear);
1048inline void labToRgb(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width,
bool alpha =
false)
1050 auto s =
reinterpret_cast<const T*
>(source);
1051 auto t =
reinterpret_cast<T*
>(target);
1052 auto max = double(std::numeric_limits<T>::max());
1053 auto invmax = 1.0 / max;
1055 if (sourceChannels < 3) {
1056 qDebug() <<
"labToRgb: image is not a valid LAB!";
1060 for (qint32 w = 0; w < width; ++w) {
1061 auto ps = s + sourceChannels * w;
1062 auto L = (*(ps + 0) * invmax) * 100.0;
1063 auto A = (*(ps + 1) * invmax) * 255.0 - 128.0;
1064 auto B = (*(ps + 2) * invmax) * 255.0 - 128.0;
1067 auto Y = (L + 16.0) * (1.0 / 116.0);
1068 auto X = A * (1.0 / 500.0) + Y;
1069 auto Z = Y - B * (1.0 / 200.0);
1072 X = finv(X) * 0.9504;
1073 Y = finv(Y) * 1.0000;
1074 Z = finv(Z) * 1.0888;
1077 auto r = gammaCorrection( 3.24071 * X - 1.53726 * Y - 0.498571 * Z);
1078 auto g = gammaCorrection(- 0.969258 * X + 1.87599 * Y + 0.0415557 * Z);
1079 auto b = gammaCorrection( 0.0556352 * X - 0.203996 * Y + 1.05707 * Z);
1081 auto pt = t + targetChannels * w;
1082 *(pt + 0) = T(std::max(std::min(r * max + 0.5, max), 0.0));
1083 *(pt + 1) = T(std::max(std::min(g * max + 0.5, max), 0.0));
1084 *(pt + 2) = T(std::max(std::min(b * max + 0.5, max), 0.0));
1085 if (targetChannels == 4) {
1086 if (sourceChannels >= 4 && alpha)
1087 *(pt + 3) = *(ps + 3);
1089 *(pt + 3) = std::numeric_limits<T>::max();
1094bool readChannel(
QByteArray &target,
QDataStream &stream, quint32 compressedSize, quint16 compression)
1097 if (compressedSize > kMaxQVectorSize) {
1101 tmp.
resize(compressedSize);
1105 if (decompress(tmp.
data(), tmp.
size(), target.
data(), target.
size()) < 0) {
1117class PSDHandlerPrivate
1123 ~PSDHandlerPrivate()
1129 return m_header.version == 2;
1132 bool isValid()
const
1134 return IsValid(m_header);
1137 bool isSupported()
const
1139 return IsSupported(m_header);
1142 bool hasAlpha()
const
1146#ifdef PSD_FORCE_RGBA
1147 auto alpha = m_header.color_mode == CM_RGB;
1151 if (m_irs.contains(IRI_ALPHAIDENTIFIERS)) {
1152 auto irb = m_irs.value(IRI_ALPHAIDENTIFIERS);
1153 if (irb.data.
size() >= 4) {
1154 QDataStream s(irb.data);
1160 }
else if (!m_lms.isNull()) {
1161 alpha = m_lms.hasAlpha();
1166 bool hasMergedData()
const
1168 return HasMergedData(m_irs);
1174 return QSize(m_header.width, m_header.height);
1180 return imageFormat(m_header, hasAlpha());
1185 return m_exif.transformation();
1188 bool readInfo(QDataStream &stream)
1196 if (stream.
atEnd() || !IsValid(m_header)) {
1202 if (!IsSupported(m_header)) {
1208 m_cmds = readColorModeDataSection(stream, &ok);
1210 qDebug() <<
"Error while skipping Color Mode Data section";
1215 m_irs = readImageResourceSection(stream, &ok);
1217 qDebug() <<
"Error while reading Image Resources Section";
1221 if (!hasMergedData()) {
1222 qDebug() <<
"No merged data found";
1227 m_lms = readLayerAndMaskSection(stream, isPsb(), &ok);
1229 qDebug() <<
"Error while skipping Layer and Mask section";
1234 if (m_irs.contains(IRI_EXIFDATA1)) {
1235 m_exif = MicroExif::fromByteArray(m_irs.value(IRI_EXIFDATA1).data);
1242 PSDColorModeDataSection m_cmds;
1243 PSDImageResourceSection m_irs;
1244 PSDLayerAndMaskSection m_lms;
1250PSDHandler::PSDHandler()
1252 , d(new PSDHandlerPrivate)
1256bool PSDHandler::canRead()
const
1258 if (canRead(device())) {
1265bool PSDHandler::read(
QImage *image)
1270 if (!d->isValid()) {
1271 if (!d->readInfo(stream))
1275 auto &&header = d->m_header;
1276 auto &&cmds = d->m_cmds;
1277 auto &&irs = d->m_irs;
1279 auto isPsb = d->isPsb();
1280 auto alpha = d->hasAlpha();
1287 quint16 compression;
1288 stream >> compression;
1289 if (compression > 1) {
1290 qDebug() <<
"Unknown compression type";
1296 qWarning() <<
"Unsupported image format. color_mode:" << header.color_mode <<
"depth:" << header.depth <<
"channel_count:" << header.channel_count;
1300 img = imageAlloc(d->size(), format);
1302 qWarning() <<
"Failed to allocate image, invalid dimensions?" <<
QSize(header.width, header.height);
1305 img.
fill(qRgb(0, 0, 0));
1306 if (!cmds.palette.isEmpty()) {
1308 setTransparencyIndex(img, irs);
1311 auto imgChannels = imageChannels(img.
format());
1312 auto channel_num = std::min(qint32(header.channel_count), imgChannels);
1313 auto raw_count = qsizetype(header.width * header.depth + 7) / 8;
1314 auto native_cmyk = img.
format() == CMYK_FORMAT;
1316 if (header.height > kMaxQVectorSize / header.channel_count /
sizeof(quint32)) {
1317 qWarning() <<
"LoadPSD() header height/channel_count too big" << header.height << header.channel_count;
1321 QList<quint32> strides(header.height * header.channel_count, raw_count);
1324 for (
auto &&v : strides) {
1335 auto device = stream.
device();
1337 if (!stridePositions.isEmpty()) {
1338 stridePositions[0] = device->pos();
1340 for (qsizetype i = 1, n = stridePositions.size(); i < n; ++i) {
1341 stridePositions[i] = stridePositions[i-1] + strides.at(i-1);
1346 rawStride.
resize(raw_count);
1350 auto randomAccess = (header.color_mode == CM_CMYK && !native_cmyk) ||
1351 (header.color_mode == CM_MULTICHANNEL && header.channel_count != 1 && !native_cmyk) ||
1352 (header.color_mode == CM_LABCOLOR) ||
1359 ScanLineConverter iccConv(img.
format());
1360#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) && !defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
1361 if (header.color_mode == CM_CMYK && img.
format() != QImage::Format_CMYK8888) {
1362 auto tmpi =
QImage(header.width, 1, QImage::Format_CMYK8888);
1363 if (setColorSpace(tmpi, irs))
1371 psdScanline.
resize(qsizetype(header.width * header.depth * header.channel_count + 7) / 8);
1372 for (qint32 y = 0, h = header.height; y < h; ++y) {
1373 for (qint32 c = 0; c < header.channel_count; ++c) {
1374 auto strideNumber = c * qsizetype(h) + y;
1375 if (!device->seek(stridePositions.at(strideNumber))) {
1376 qDebug() <<
"Error while seeking the stream of channel" << c <<
"line" << y;
1379 auto &&strideSize = strides.at(strideNumber);
1380 if (!readChannel(rawStride, stream, strideSize, compression)) {
1381 qDebug() <<
"Error while reading the stream of channel" << c <<
"line" << y;
1385 auto scanLine =
reinterpret_cast<unsigned char*
>(psdScanline.
data());
1386 if (header.depth == 8) {
1387 planarToChunchy<quint8>(scanLine, rawStride.
data(), header.width, c, header.channel_count);
1388 }
else if (header.depth == 16) {
1389 planarToChunchy<quint16>(scanLine, rawStride.
data(), header.width, c, header.channel_count);
1390 }
else if (header.depth == 32) {
1391 planarToChunchy<float>(scanLine, rawStride.
data(), header.width, c, header.channel_count);
1397 auto scanLine =
reinterpret_cast<char*
>(psdScanline.
data());
1398 if (header.color_mode == CM_CMYK) {
1399 if (header.depth == 8)
1400 premulConversion<quint8>(scanLine, header.width, 4, header.channel_count, PremulConversion::PS2A);
1401 else if (header.depth == 16)
1402 premulConversion<quint16>(scanLine, header.width, 4, header.channel_count, PremulConversion::PS2A);
1404 if (header.color_mode == CM_LABCOLOR) {
1405 if (header.depth == 8)
1406 premulConversion<quint8>(scanLine, header.width, 3, header.channel_count, PremulConversion::PSLab2A);
1407 else if (header.depth == 16)
1408 premulConversion<quint16>(scanLine, header.width, 3, header.channel_count, PremulConversion::PSLab2A);
1410 if (header.color_mode == CM_RGB) {
1411 if (header.depth == 8)
1412 premulConversion<quint8>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
1413 else if (header.depth == 16)
1414 premulConversion<quint16>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
1415 else if (header.depth == 32)
1416 premulConversion<float>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
1421 if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) {
1423 if (header.depth == 8)
1424 cmykToRgb<quint8>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1425 else if (header.depth == 16)
1426 cmykToRgb<quint16>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1427 }
else if (header.depth == 8) {
1428 rawChannelsCopyToCMYK<quint8>(tmpCmyk.
bits(), 4, psdScanline.
data(), header.channel_count, header.width);
1429 if (
auto rgbPtr = iccConv.convertedScanLine(tmpCmyk, 0))
1431 if (imgChannels == 4 && header.channel_count >= 5)
1432 rawChannelCopy<quint8>(img.
scanLine(y), imgChannels, 3, psdScanline.
data(), header.channel_count, 4, header.width);
1433 }
else if (header.depth == 16) {
1434 rawChannelsCopyToCMYK<quint16>(tmpCmyk.
bits(), 4, psdScanline.
data(), header.channel_count, header.width);
1435 if (
auto rgbPtr = iccConv.convertedScanLine(tmpCmyk, 0))
1437 if (imgChannels == 4 && header.channel_count >= 5)
1438 rawChannelCopy<quint16>(img.
scanLine(y), imgChannels, 3, psdScanline.
data(), header.channel_count, 4, header.width);
1441 if (header.color_mode == CM_LABCOLOR) {
1442 if (header.depth == 8)
1443 labToRgb<quint8>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1444 else if (header.depth == 16)
1445 labToRgb<quint16>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1447 if (header.color_mode == CM_RGB) {
1448 if (header.depth == 8)
1449 rawChannelsCopy<quint8>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width);
1450 else if (header.depth == 16)
1451 rawChannelsCopy<quint16>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width);
1452 else if (header.depth == 32)
1453 rawChannelsCopy<float>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width);
1458 for (qint32 c = 0; c < channel_num; ++c) {
1459 for (qint32 y = 0, h = header.height; y < h; ++y) {
1460 auto&& strideSize = strides.at(c * qsizetype(h) + y);
1461 if (!readChannel(rawStride, stream, strideSize, compression)) {
1462 qDebug() <<
"Error while reading the stream of channel" << c <<
"line" << y;
1467 if (header.depth == 1) {
1470 }
else if (header.depth == 8) {
1473 planarToChunchyCMYK<quint8>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1475 planarToChunchy<quint8>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1476 }
else if (header.depth == 16) {
1479 planarToChunchyCMYK<quint16>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1481 planarToChunchy<quint16>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1482 }
else if (header.depth == 32 && header.color_mode == CM_RGB) {
1484 planarToChunchy<float>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1485 }
else if (header.depth == 32 && header.color_mode == CM_GRAYSCALE) {
1487 planarToChunchyFloatToUInt16<float>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1494 if (!setResolution(img, irs)) {
1499 if (header.color_mode == CM_LABCOLOR) {
1501#ifdef PSD_FAST_LAB_CONVERSION
1506 }
else if (!setColorSpace(img, irs)) {
1511 if (!setXmpData(img, irs)) {
1516 if (!setExifData(img, d->m_exif)) {
1523 if (!cmds.duotone.data.isEmpty()) {
1531bool PSDHandler::supportsOption(ImageOption option)
const
1544QVariant PSDHandler::option(ImageOption option)
const
1548 if (
auto dev = device()) {
1549 if (!d->isValid()) {
1576 auto descr = d->m_exif.description();
1577 if (!descr.isEmpty())
1585bool PSDHandler::canRead(
QIODevice *device)
1588 qWarning(
"PSDHandler::canRead() called with no device");
1592 auto ba = device->
peek(
sizeof(PSDHeader));
1604 if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) {
1605 if (header.channel_count != 4 || !NATIVE_CMYK)
1608 if (header.color_mode == CM_LABCOLOR) {
1611 if (header.color_mode == CM_RGB && header.channel_count > 3) {
1616 return IsSupported(header);
1621 if (format ==
"psd" || format ==
"psb" || format ==
"pdd" || format ==
"psdt") {
1632 if (device->
isReadable() && PSDHandler::canRead(device)) {
1646#include "moc_psd_p.cpp"
QFlags< Capability > Capabilities
QVariant read(const QByteArray &data, int versionOverride=0)
char at(qsizetype i) const const
bool isEmpty() const const
void resize(qsizetype newSize, char c)
qsizetype size() const const
QColorSpace fromIccProfile(const QByteArray &iccProfile)
QIODevice * device() const const
int readRawData(char *s, int len)
void setByteOrder(ByteOrder bo)
Status status() const const
qsizetype bytesPerLine() const const
QList< QRgb > colorTable() const const
void fill(Qt::GlobalColor color)
bool hasAlphaChannel() const const
bool isNull() const const
void setColorSpace(const QColorSpace &colorSpace)
void setColorTable(const QList< QRgb > &colors)
void setDotsPerMeterX(int x)
void setDotsPerMeterY(int y)
void setText(const QString &key, const QString &text)
void setDevice(QIODevice *device)
bool isOpen() const const
bool isReadable() const const
virtual bool isSequential() const const
QByteArray peek(qint64 maxSize)
QByteArray read(qint64 maxSize)
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
QVariant fromValue(T &&value)