29#include "fastmath_p.h"
31#include "scanlineconverter_p.h"
43typedef quint16 ushort;
68#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0) || defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
69# define CMYK_FORMAT QImage::Format_Invalid
71# define CMYK_FORMAT QImage::Format_CMYK8888
74#define NATIVE_CMYK (CMYK_FORMAT != QImage::Format_Invalid)
76enum Signature : quint32 {
83enum ColorMode : quint16 {
94enum ImageResourceId : quint16 {
95 IRI_RESOLUTIONINFO = 0x03ED,
96 IRI_ICCPROFILE = 0x040F,
97 IRI_TRANSPARENCYINDEX = 0x0417,
98 IRI_VERSIONINFO = 0x0421,
99 IRI_XMPMETADATA = 0x0424
102enum LayerId : quint32 {
103 LI_MT16 = 0x4D743136,
104 LI_MT32 = 0x4D743332,
110 memset(
this, 0,
sizeof(PSDHeader));
116 ushort channel_count;
123struct PSDImageResourceBlock {
133struct PSDDuotoneOptions {
141struct PSDColorModeDataSection {
142 PSDDuotoneOptions duotone;
150 qint16 layerCount = 0;
153struct PSDGlobalLayerMaskInfo {
157struct PSDAdditionalLayerInfo {
158 Signature signature = Signature();
159 LayerId
id = LayerId();
163struct PSDLayerAndMaskSection {
165 PSDLayerInfo layerInfo;
166 PSDGlobalLayerMaskInfo globalLayerMaskInfo;
169 bool isNull()
const {
173 bool hasAlpha()
const {
174 return layerInfo.layerCount < 0 ||
175 additionalLayerInfo.
contains(LI_MT16) ||
176 additionalLayerInfo.
contains(LI_MT32) ||
177 additionalLayerInfo.
contains(LI_MTRN);
180 bool atEnd(
bool isPsb)
const {
181 qint64 currentSize = 0;
182 if (layerInfo.size > -1) {
183 currentSize += layerInfo.size + 4;
187 if (globalLayerMaskInfo.size > -1) {
188 currentSize += globalLayerMaskInfo.size + 4;
190 auto aliv = additionalLayerInfo.
values();
191 for (
auto &&v : aliv) {
192 currentSize += (12 + v.size);
193 if (v.signature == S_8B64)
196 return (size <= currentSize);
204static double fixedPointToDouble(qint32 fixedPoint)
206 auto i = double(fixedPoint >> 16);
207 auto d = double((fixedPoint & 0x0000FFFF) / 65536.0);
211static qint64 readSize(
QDataStream &s,
bool psb =
false)
230 for (qint32 i32 = 0; size; size -= i32) {
231 i32 = std::min(size, qint64(std::numeric_limits<qint32>::max()));
239static bool skip_section(
QDataStream &s,
bool psb =
false)
241 auto section_length = readSize(s, psb);
242 if (section_length < 0)
244 return skip_data(s, section_length);
255static QString readPascalString(
QDataStream &s, qint32 alignBytes = 1, qint32 *size =
nullptr)
263 *size =
sizeof(stringSize);
266 if (stringSize > 0) {
278 if (
auto pad = *size % alignBytes)
291static PSDImageResourceSection readImageResourceSection(
QDataStream &s,
bool *ok =
nullptr)
293 PSDImageResourceSection irs;
305 for (
auto size = sectioSize; size > 0;) {
320 size -=
sizeof(signature);
322 if (signature != S_8BIM && signature != S_MeSa) {
323 qDebug() <<
"Invalid Image Resource Block Signature!";
334 PSDImageResourceBlock irb;
338 irb.name = readPascalString(s, 2, &bytes);
344 size -=
sizeof(dataSize);
347 if (
auto dev = s.
device())
348 irb.data = dev->read(dataSize);
352 if (quint32(read) != dataSize) {
353 qDebug() <<
"Image Resource Block Read Error!";
358 if (
auto pad = dataSize % 2) {
371PSDAdditionalLayerInfo readAdditionalLayer(
QDataStream &s,
bool *ok =
nullptr)
373 PSDAdditionalLayerInfo li;
380 *
ok = li.signature == S_8BIM || li.signature == S_8B64;
389 li.size = readSize(s, li.signature == S_8B64);
394 *
ok = skip_data(s, li.size);
399PSDLayerAndMaskSection readLayerAndMaskSection(
QDataStream &s,
bool isPsb,
bool *ok =
nullptr)
401 PSDLayerAndMaskSection lms;
411 lms.size = readSize(s, isPsb);
415 lms.layerInfo.size = readSize(s, isPsb);
416 if (lms.layerInfo.size > 0) {
417 s >> lms.layerInfo.layerCount;
418 skip_data(s, lms.layerInfo.size -
sizeof(lms.layerInfo.layerCount));
424 lms.globalLayerMaskInfo.size = readSize(s,
false);
425 if (lms.globalLayerMaskInfo.size > 0) {
426 skip_data(s, lms.globalLayerMaskInfo.size);
432 for (
bool ok =
true;
ok && !lms.atEnd(isPsb);) {
433 auto al = readAdditionalLayer(s, &ok);
435 lms.additionalLayerInfo.insert(al.id, al);
440 device->rollbackTransaction();
441 *
ok = skip_section(s, isPsb);
452PSDColorModeDataSection readColorModeDataSection(
QDataStream &s,
bool *ok =
nullptr)
454 PSDColorModeDataSection cms;
470 if (cms.duotone.data.
size() != size)
473 auto&& palette = cms.palette;
475 for (
auto&& v : vect)
477 for (qsizetype i = 0, n = vect.size()/3; i < n; ++i)
478 palette.append(qRgb(vect.at(i), vect.at(n+i), vect.at(n+n+i)));
491static bool setColorSpace(
QImage& img,
const PSDImageResourceSection& irs)
493 if (!irs.contains(IRI_ICCPROFILE) || img.
isNull())
495 auto irb = irs.value(IRI_ICCPROFILE);
510static bool setXmpData(
QImage& img,
const PSDImageResourceSection& irs)
512 if (!irs.contains(IRI_XMPMETADATA))
514 auto irb = irs.value(IRI_XMPMETADATA);
521 img.
setText(QStringLiteral(META_KEY_XMP_ADOBE), xmp);
531static bool hasMergedData(
const PSDImageResourceSection& irs)
533 if (!irs.contains(IRI_VERSIONINFO))
535 auto irb = irs.value(IRI_VERSIONINFO);
536 if (irb.data.
size() > 4)
537 return irb.data.
at(4) != 0;
548static bool setResolution(
QImage& img,
const PSDImageResourceSection& irs)
550 if (!irs.contains(IRI_RESOLUTIONINFO))
552 auto irb = irs.value(IRI_RESOLUTIONINFO);
561 auto hres = fixedPointToDouble(i32);
568 auto vres = fixedPointToDouble(i32);
582static bool setTransparencyIndex(
QImage& img,
const PSDImageResourceSection& irs)
584 if (!irs.contains(IRI_TRANSPARENCYINDEX))
586 auto irb = irs.value(IRI_TRANSPARENCYINDEX);
593 if (idx < palette.size()) {
594 auto&& v = palette[idx];
595 v = QRgb(v & ~0xFF000000);
605 s >> header.signature;
607 for (
int i = 0; i < 6; i++) {
608 s >> header.reserved[i];
610 s >> header.channel_count;
614 s >> header.color_mode;
619static bool IsValid(
const PSDHeader &header)
621 if (header.signature != 0x38425053) {
625 if (header.version != 1 && header.version != 2) {
626 qDebug() <<
"PSD header: invalid version" << header.version;
629 if (header.depth != 8 &&
630 header.depth != 16 &&
631 header.depth != 32 &&
633 qDebug() <<
"PSD header: invalid depth" << header.depth;
636 if (header.color_mode != CM_RGB &&
637 header.color_mode != CM_GRAYSCALE &&
638 header.color_mode != CM_INDEXED &&
639 header.color_mode != CM_DUOTONE &&
640 header.color_mode != CM_CMYK &&
641 header.color_mode != CM_LABCOLOR &&
642 header.color_mode != CM_MULTICHANNEL &&
643 header.color_mode != CM_BITMAP) {
644 qDebug() <<
"PSD header: invalid color mode" << header.color_mode;
649 if (header.channel_count < 1 || header.channel_count > 57) {
650 qDebug() <<
"PSD header: invalid number of channels" << header.channel_count;
653 if (header.width > 300000 || header.height > 300000) {
654 qDebug() <<
"PSD header: invalid image size" << header.width <<
"x" << header.height;
661static bool IsSupported(
const PSDHeader &header)
663 if (!IsValid(header)) {
666 if (header.version != 1 && header.version != 2) {
669 if (header.depth != 8 &&
670 header.depth != 16 &&
671 header.depth != 32 &&
675 if (header.color_mode != CM_RGB &&
676 header.color_mode != CM_GRAYSCALE &&
677 header.color_mode != CM_INDEXED &&
678 header.color_mode != CM_DUOTONE &&
679 header.color_mode != CM_CMYK &&
680 header.color_mode != CM_MULTICHANNEL &&
681 header.color_mode != CM_LABCOLOR &&
682 header.color_mode != CM_BITMAP) {
697qint64 decompress(
const char *input, qint64 ilen,
char *output, qint64 olen)
700 for (qint64 ip = 0, rr = 0, available = olen; j < olen && ip < ilen; available = olen - j) {
701 signed char n =
static_cast<signed char>(input[ip++]);
707 if (available < rr) {
714 memcpy(output + j, input + ip,
size_t(rr));
716 }
else if (ip < ilen) {
718 if (available < rr) {
722 memset(output + j, input[ip++],
size_t(rr));
735static QImage::Format imageFormat(
const PSDHeader &header,
bool alpha)
737 if (header.channel_count == 0) {
742 switch(header.color_mode) {
744 if (header.depth == 32) {
746 }
else if (header.depth == 16) {
752 case CM_MULTICHANNEL:
754 if (NATIVE_CMYK && header.channel_count == 4 && (header.depth == 16 || header.depth == 8)) {
755 format = CMYK_FORMAT;
756 }
else if (header.depth == 16) {
757 if (header.channel_count == 1)
761 }
else if (header.depth == 8) {
762 if (header.channel_count == 1)
769 if (header.depth == 16) {
771 }
else if (header.depth == 8) {
813inline quint8 xchg(quint8 v)
818inline quint16 xchg(quint16 v)
820#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
821 return quint16( (v>>8) | (v<<8) );
827inline quint32 xchg(quint32 v)
829#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
830 return quint32( (v>>24) | ((v & 0x00FF0000)>>8) | ((v & 0x0000FF00)<<8) | (v<<24) );
836inline float xchg(
float v)
838#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
841 quint32 f = xchg(*
reinterpret_cast<quint32*
>(pf));
843 return *
reinterpret_cast<float*
>(pi);
846 std::memcpy(&t, &v,
sizeof(quint32));
848 std::memcpy(&v, &t,
sizeof(quint32));
857inline void planarToChunchy(uchar *target,
const char *source, qint32 width, qint32 c, qint32 cn)
859 auto s =
reinterpret_cast<const T*
>(source);
860 auto t =
reinterpret_cast<T*
>(target);
861 for (qint32 x = 0; x < width; ++x) {
862 t[x * cn + c] = xchg(s[x]);
867inline void planarToChunchyCMYK(uchar *target,
const char *source, qint32 width, qint32 c, qint32 cn)
869 auto s =
reinterpret_cast<const T*
>(source);
870 auto t =
reinterpret_cast<quint8*
>(target);
871 const T d = std::numeric_limits<T>::max() / std::numeric_limits<quint8>::max();
872 for (qint32 x = 0; x < width; ++x) {
873 t[x * cn + c] = quint8((std::numeric_limits<T>::max() - xchg(s[x])) / d);
879inline void planarToChunchyFloatToUInt16(uchar *target,
const char *source, qint32 width, qint32 c, qint32 cn)
881 auto s =
reinterpret_cast<const T*
>(source);
882 auto t =
reinterpret_cast<quint16*
>(target);
883 for (qint32 x = 0; x < width; ++x) {
884 t[x * cn + c] = quint16(std::min(xchg(s[x]) * std::numeric_limits<quint16>::max() + 0.5,
double(std::numeric_limits<quint16>::max())));
888enum class PremulConversion {
895inline void premulConversion(
char *stride, qint32 width, qint32 ac, qint32 cn,
const PremulConversion &conv)
897 auto s =
reinterpret_cast<T*
>(stride);
899 auto max = qint64(std::numeric_limits<T>::is_integer ? std::numeric_limits<T>::max() : 1);
901 for (qint32 c = 0; c < ac; ++c) {
902 if (conv == PremulConversion::PS2P) {
903 for (qint32 x = 0; x < width; ++x) {
905 auto alpha = *(s + xcn + ac);
906 *(s + xcn + c) = *(s + xcn + c) + alpha - max;
908 }
else if (conv == PremulConversion::PS2A || (conv == PremulConversion::PSLab2A && c == 0)) {
909 for (qint32 x = 0; x < width; ++x) {
911 auto alpha = *(s + xcn + ac);
913 *(s + xcn + c) = ((*(s + xcn + c) + alpha - max) * max + alpha / 2) / alpha;
915 }
else if (conv == PremulConversion::PSLab2A) {
916 for (qint32 x = 0; x < width; ++x) {
918 auto alpha = *(s + xcn + ac);
920 *(s + xcn + c) = ((*(s + xcn + c) + (alpha - max + 1) / 2) * max + alpha / 2) / alpha;
926inline void monoInvert(uchar *target,
const char* source, qint32 bytes)
928 auto s =
reinterpret_cast<const quint8*
>(source);
929 auto t =
reinterpret_cast<quint8*
>(target);
930 for (qint32 x = 0; x < bytes; ++x) {
936inline void rawChannelsCopyToCMYK(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width)
938 auto s =
reinterpret_cast<const T*
>(source);
939 auto t =
reinterpret_cast<quint8*
>(target);
940 const T d = std::numeric_limits<T>::max() / std::numeric_limits<quint8>::max();
941 for (qint32 c = 0, cs = std::min(targetChannels, sourceChannels); c < cs; ++c) {
942 for (qint32 x = 0; x < width; ++x) {
943 t[x * targetChannels + c] = (std::numeric_limits<T>::max() - s[x * sourceChannels + c]) / d;
949inline void rawChannelsCopy(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width)
951 auto s =
reinterpret_cast<const T*
>(source);
952 auto t =
reinterpret_cast<T*
>(target);
953 for (qint32 c = 0, cs = std::min(targetChannels, sourceChannels); c < cs; ++c) {
954 for (qint32 x = 0; x < width; ++x) {
955 t[x * targetChannels + c] = s[x * sourceChannels + c];
961inline void rawChannelCopy(uchar *target, qint32 targetChannels, qint32 targetChannel,
const char *source, qint32 sourceChannels, qint32 sourceChannel, qint32 width)
963 auto s =
reinterpret_cast<const T*
>(source);
964 auto t =
reinterpret_cast<T*
>(target);
965 for (qint32 x = 0; x < width; ++x) {
966 t[x * targetChannels + targetChannel] = s[x * sourceChannels + sourceChannel];
972inline void cmykToRgb(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width,
bool alpha =
false)
974 auto s =
reinterpret_cast<const T*
>(source);
975 auto t =
reinterpret_cast<T*
>(target);
976 auto max = double(std::numeric_limits<T>::max());
977 auto invmax = 1.0 / max;
979 if (sourceChannels < 2) {
980 qDebug() <<
"cmykToRgb: image is not a valid MCH/CMYK!";
984 for (qint32 w = 0; w < width; ++w) {
985 auto ps = s + sourceChannels * w;
986 auto C = 1 - *(ps + 0) * invmax;
987 auto M = sourceChannels > 1 ? 1 - *(ps + 1) * invmax : 0.0;
988 auto Y = sourceChannels > 2 ? 1 - *(ps + 2) * invmax : 0.0;
989 auto K = sourceChannels > 3 ? 1 - *(ps + 3) * invmax : 0.0;
991 auto pt = t + targetChannels * w;
992 *(pt + 0) = T(std::min(max - (C * (1 - K) + K) * max + 0.5, max));
993 *(pt + 1) = targetChannels > 1 ? T(std::min(max - (M * (1 - K) + K) * max + 0.5, max)) : std::numeric_limits<T>::max();
994 *(pt + 2) = targetChannels > 2 ? T(std::min(max - (Y * (1 - K) + K) * max + 0.5, max)) : std::numeric_limits<T>::max();
995 if (targetChannels == 4) {
996 if (sourceChannels >= 5 && alpha)
997 *(pt + 3) = *(ps + 4);
999 *(pt + 3) = std::numeric_limits<T>::max();
1004inline double finv(
double v)
1006 return (v > 6.0 / 29.0 ? v * v * v : (v - 16.0 / 116.0) / 7.787);
1009inline double gammaCorrection(
double linear)
1011#ifdef PSD_FAST_LAB_CONVERSION
1016 return (linear > 0.0031308 ? 1.055 * fastPow(linear, 1.0 / 2.4) - 0.055 : 12.92 * linear);
1021inline void labToRgb(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width,
bool alpha =
false)
1023 auto s =
reinterpret_cast<const T*
>(source);
1024 auto t =
reinterpret_cast<T*
>(target);
1025 auto max = double(std::numeric_limits<T>::max());
1026 auto invmax = 1.0 / max;
1028 if (sourceChannels < 3) {
1029 qDebug() <<
"labToRgb: image is not a valid LAB!";
1033 for (qint32 w = 0; w < width; ++w) {
1034 auto ps = s + sourceChannels * w;
1035 auto L = (*(ps + 0) * invmax) * 100.0;
1036 auto A = (*(ps + 1) * invmax) * 255.0 - 128.0;
1037 auto B = (*(ps + 2) * invmax) * 255.0 - 128.0;
1040 auto Y = (L + 16.0) * (1.0 / 116.0);
1041 auto X = A * (1.0 / 500.0) + Y;
1042 auto Z = Y - B * (1.0 / 200.0);
1045 X = finv(X) * 0.9504;
1046 Y = finv(Y) * 1.0000;
1047 Z = finv(Z) * 1.0888;
1050 auto r = gammaCorrection( 3.24071 * X - 1.53726 * Y - 0.498571 * Z);
1051 auto g = gammaCorrection(- 0.969258 * X + 1.87599 * Y + 0.0415557 * Z);
1052 auto b = gammaCorrection( 0.0556352 * X - 0.203996 * Y + 1.05707 * Z);
1054 auto pt = t + targetChannels * w;
1055 *(pt + 0) = T(std::max(std::min(r * max + 0.5, max), 0.0));
1056 *(pt + 1) = T(std::max(std::min(g * max + 0.5, max), 0.0));
1057 *(pt + 2) = T(std::max(std::min(b * max + 0.5, max), 0.0));
1058 if (targetChannels == 4) {
1059 if (sourceChannels >= 4 && alpha)
1060 *(pt + 3) = *(ps + 3);
1062 *(pt + 3) = std::numeric_limits<T>::max();
1067bool readChannel(
QByteArray& target,
QDataStream &stream, quint32 compressedSize, quint16 compression)
1070 if (compressedSize > kMaxQVectorSize) {
1074 tmp.
resize(compressedSize);
1078 if (decompress(tmp.
data(), tmp.
size(), target.
data(), target.
size()) < 0) {
1092 auto isPsb = header.version == 2;
1096 auto cmds = readColorModeDataSection(stream, &ok);
1098 qDebug() <<
"Error while skipping Color Mode Data section";
1103 auto irs = readImageResourceSection(stream, &ok);
1105 qDebug() <<
"Error while reading Image Resources Section";
1109 if (!hasMergedData(irs)) {
1110 qDebug() <<
"No merged data found";
1115 auto lms = readLayerAndMaskSection(stream, isPsb, &ok);
1117 qDebug() <<
"Error while skipping Layer and Mask section";
1125 quint16 compression;
1126 stream >> compression;
1127 if (compression > 1) {
1128 qDebug() <<
"Unknown compression type";
1134 auto alpha = header.color_mode == CM_RGB;
1136 alpha = lms.hasAlpha();
1140 qWarning() <<
"Unsupported image format. color_mode:" << header.color_mode <<
"depth:" << header.depth <<
"channel_count:" << header.channel_count;
1144 img = imageAlloc(header.width, header.height, format);
1146 qWarning() <<
"Failed to allocate image, invalid dimensions?" <<
QSize(header.width, header.height);
1149 img.
fill(qRgb(0, 0, 0));
1150 if (!cmds.palette.isEmpty()) {
1152 setTransparencyIndex(img, irs);
1155 auto imgChannels = imageChannels(img.
format());
1156 auto channel_num = std::min(qint32(header.channel_count), imgChannels);
1157 auto raw_count = qsizetype(header.width * header.depth + 7) / 8;
1158 auto native_cmyk = img.
format() == CMYK_FORMAT;
1160 if (header.height > kMaxQVectorSize / header.channel_count /
sizeof(quint32)) {
1161 qWarning() <<
"LoadPSD() header height/channel_count too big" << header.height << header.channel_count;
1165 QList<quint32> strides(header.height * header.channel_count, raw_count);
1168 for (
auto&& v : strides) {
1179 auto device = stream.
device();
1181 if (!stridePositions.isEmpty()) {
1182 stridePositions[0] = device->pos();
1184 for (qsizetype i = 1, n = stridePositions.size(); i < n; ++i) {
1185 stridePositions[i] = stridePositions[i-1] + strides.at(i-1);
1190 rawStride.
resize(raw_count);
1194 auto randomAccess = (header.color_mode == CM_CMYK && !native_cmyk) ||
1195 (header.color_mode == CM_MULTICHANNEL && header.channel_count != 1 && !native_cmyk) ||
1196 (header.color_mode == CM_LABCOLOR) ||
1203 ScanLineConverter iccConv(img.
format());
1204#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) && !defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
1205 if (header.color_mode == CM_CMYK && img.
format() != QImage::Format_CMYK8888) {
1206 auto tmpi =
QImage(header.width, 1, QImage::Format_CMYK8888);
1207 if (setColorSpace(tmpi, irs))
1215 psdScanline.
resize(qsizetype(header.width * header.depth * header.channel_count + 7) / 8);
1216 for (qint32 y = 0, h = header.height; y < h; ++y) {
1217 for (qint32 c = 0; c < header.channel_count; ++c) {
1218 auto strideNumber = c * qsizetype(h) + y;
1219 if (!device->seek(stridePositions.at(strideNumber))) {
1220 qDebug() <<
"Error while seeking the stream of channel" << c <<
"line" << y;
1223 auto&& strideSize = strides.at(strideNumber);
1224 if (!readChannel(rawStride, stream, strideSize, compression)) {
1225 qDebug() <<
"Error while reading the stream of channel" << c <<
"line" << y;
1229 auto scanLine =
reinterpret_cast<unsigned char*
>(psdScanline.
data());
1230 if (header.depth == 8) {
1231 planarToChunchy<quint8>(scanLine, rawStride.
data(), header.width, c, header.channel_count);
1232 }
else if (header.depth == 16) {
1233 planarToChunchy<quint16>(scanLine, rawStride.
data(), header.width, c, header.channel_count);
1234 }
else if (header.depth == 32) {
1235 planarToChunchy<float>(scanLine, rawStride.
data(), header.width, c, header.channel_count);
1241 auto scanLine =
reinterpret_cast<char*
>(psdScanline.
data());
1242 if (header.color_mode == CM_CMYK) {
1243 if (header.depth == 8)
1244 premulConversion<quint8>(scanLine, header.width, 4, header.channel_count, PremulConversion::PS2A);
1245 else if (header.depth == 16)
1246 premulConversion<quint16>(scanLine, header.width, 4, header.channel_count, PremulConversion::PS2A);
1248 if (header.color_mode == CM_LABCOLOR) {
1249 if (header.depth == 8)
1250 premulConversion<quint8>(scanLine, header.width, 3, header.channel_count, PremulConversion::PSLab2A);
1251 else if (header.depth == 16)
1252 premulConversion<quint16>(scanLine, header.width, 3, header.channel_count, PremulConversion::PSLab2A);
1254 if (header.color_mode == CM_RGB) {
1255 if (header.depth == 8)
1256 premulConversion<quint8>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
1257 else if (header.depth == 16)
1258 premulConversion<quint16>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
1259 else if (header.depth == 32)
1260 premulConversion<float>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
1265 if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) {
1267 if (header.depth == 8)
1268 cmykToRgb<quint8>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1269 else if (header.depth == 16)
1270 cmykToRgb<quint16>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1271 }
else if (header.depth == 8) {
1272 rawChannelsCopyToCMYK<quint8>(tmpCmyk.
bits(), 4, psdScanline.
data(), header.channel_count, header.width);
1273 if (
auto rgbPtr = iccConv.convertedScanLine(tmpCmyk, 0))
1275 if (imgChannels == 4 && header.channel_count >= 5)
1276 rawChannelCopy<quint8>(img.
scanLine(y), imgChannels, 3, psdScanline.
data(), header.channel_count, 4, header.width);
1277 }
else if (header.depth == 16) {
1278 rawChannelsCopyToCMYK<quint16>(tmpCmyk.
bits(), 4, psdScanline.
data(), header.channel_count, header.width);
1279 if (
auto rgbPtr = iccConv.convertedScanLine(tmpCmyk, 0))
1281 if (imgChannels == 4 && header.channel_count >= 5)
1282 rawChannelCopy<quint16>(img.
scanLine(y), imgChannels, 3, psdScanline.
data(), header.channel_count, 4, header.width);
1285 if (header.color_mode == CM_LABCOLOR) {
1286 if (header.depth == 8)
1287 labToRgb<quint8>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1288 else if (header.depth == 16)
1289 labToRgb<quint16>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1291 if (header.color_mode == CM_RGB) {
1292 if (header.depth == 8)
1293 rawChannelsCopy<quint8>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width);
1294 else if (header.depth == 16)
1295 rawChannelsCopy<quint16>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width);
1296 else if (header.depth == 32)
1297 rawChannelsCopy<float>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width);
1302 for (qint32 c = 0; c < channel_num; ++c) {
1303 for (qint32 y = 0, h = header.height; y < h; ++y) {
1304 auto&& strideSize = strides.at(c * qsizetype(h) + y);
1305 if (!readChannel(rawStride, stream, strideSize, compression)) {
1306 qDebug() <<
"Error while reading the stream of channel" << c <<
"line" << y;
1311 if (header.depth == 1) {
1314 }
else if (header.depth == 8) {
1317 planarToChunchyCMYK<quint8>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1319 planarToChunchy<quint8>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1320 }
else if (header.depth == 16) {
1323 planarToChunchyCMYK<quint16>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1325 planarToChunchy<quint16>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1326 }
else if (header.depth == 32 && header.color_mode == CM_RGB) {
1328 planarToChunchy<float>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1329 }
else if (header.depth == 32 && header.color_mode == CM_GRAYSCALE) {
1331 planarToChunchyFloatToUInt16<float>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1338 if (!setResolution(img, irs)) {
1343 if (header.color_mode == CM_LABCOLOR) {
1345#ifdef PSD_FAST_LAB_CONVERSION
1350 }
else if (!setColorSpace(img, irs)) {
1355 if (!setXmpData(img, irs)) {
1362 if (!cmds.duotone.data.isEmpty()) {
1371class PSDHandlerPrivate
1374 PSDHandlerPrivate() {}
1375 ~PSDHandlerPrivate() {}
1379PSDHandler::PSDHandler()
1381 , d(new PSDHandlerPrivate)
1385bool PSDHandler::canRead()
const
1387 if (canRead(device())) {
1394bool PSDHandler::read(
QImage *image)
1399 auto&& header = d->m_header;
1403 if (s.
atEnd() || !IsValid(header)) {
1409 if (!IsSupported(header)) {
1415 if (!LoadPSD(s, header, img)) {
1424bool PSDHandler::supportsOption(ImageOption option)
const
1431QVariant PSDHandler::option(ImageOption option)
const
1436 auto&& header = d->m_header;
1437 if (IsValid(header)) {
1439 }
else if (
auto dev = device()) {
1440 auto ba = dev->peek(
sizeof(PSDHeader));
1453bool PSDHandler::canRead(
QIODevice *device)
1456 qWarning(
"PSDHandler::canRead() called with no device");
1460 auto ba = device->
peek(
sizeof(PSDHeader));
1472 if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) {
1473 if (header.channel_count != 4 || !NATIVE_CMYK)
1476 if (header.color_mode == CM_LABCOLOR) {
1479 if (header.color_mode == CM_RGB && header.channel_count > 3) {
1484 return IsSupported(header);
1489 if (format ==
"psd" || format ==
"psb" || format ==
"pdd" || format ==
"psdt") {
1500 if (device->
isReadable() && PSDHandler::canRead(device)) {
1514#include "moc_psd_p.cpp"
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
KDB_EXPORT KDbVersionInfo version()
QFlags< Capability > Capabilities
QVariant read(const QByteArray &data, int versionOverride=0)
QString name(StandardAction id)
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
bool contains(const Key &key) const const
QList< T > values() 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)