35#include "fastmath_p.h"
37#include "scanlineconverter_p.h"
49typedef quint16 ushort;
74#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0) || defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
75# define CMYK_FORMAT QImage::Format_Invalid
77# define CMYK_FORMAT QImage::Format_CMYK8888
80#define NATIVE_CMYK (CMYK_FORMAT != QImage::Format_Invalid)
82enum Signature : quint32 {
89enum ColorMode : quint16 {
100enum ImageResourceId : quint16 {
101 IRI_RESOLUTIONINFO = 0x03ED,
102 IRI_ICCPROFILE = 0x040F,
103 IRI_TRANSPARENCYINDEX = 0x0417,
104 IRI_VERSIONINFO = 0x0421,
105 IRI_XMPMETADATA = 0x0424
108enum LayerId : quint32 {
109 LI_MT16 = 0x4D743136,
110 LI_MT32 = 0x4D743332,
116 memset(
this, 0,
sizeof(PSDHeader));
122 ushort channel_count;
129struct PSDImageResourceBlock {
139struct PSDDuotoneOptions {
147struct PSDColorModeDataSection {
148 PSDDuotoneOptions duotone;
156 qint16 layerCount = 0;
159struct PSDGlobalLayerMaskInfo {
163struct PSDAdditionalLayerInfo {
164 Signature signature = Signature();
165 LayerId
id = LayerId();
169struct PSDLayerAndMaskSection {
171 PSDLayerInfo layerInfo;
172 PSDGlobalLayerMaskInfo globalLayerMaskInfo;
175 bool isNull()
const {
179 bool hasAlpha()
const {
180 return layerInfo.layerCount < 0 ||
181 additionalLayerInfo.
contains(LI_MT16) ||
182 additionalLayerInfo.
contains(LI_MT32) ||
183 additionalLayerInfo.
contains(LI_MTRN);
186 bool atEnd(
bool isPsb)
const {
187 qint64 currentSize = 0;
188 if (layerInfo.size > -1) {
189 currentSize += layerInfo.size + 4;
193 if (globalLayerMaskInfo.size > -1) {
194 currentSize += globalLayerMaskInfo.size + 4;
196 auto aliv = additionalLayerInfo.
values();
197 for (
auto &&v : aliv) {
198 currentSize += (12 + v.size);
199 if (v.signature == S_8B64)
202 return (size <= currentSize);
210static double fixedPointToDouble(qint32 fixedPoint)
212 auto i = double(fixedPoint >> 16);
213 auto d = double((fixedPoint & 0x0000FFFF) / 65536.0);
217static qint64 readSize(
QDataStream &s,
bool psb =
false)
237 for (qint32 i32 = 0; size; size -= i32) {
238 i32 = std::min(size, qint64(std::numeric_limits<qint32>::max()));
246static bool skip_section(
QDataStream &s,
bool psb =
false)
248 auto section_length = readSize(s, psb);
249 if (section_length < 0)
251 return skip_data(s, section_length);
262static QString readPascalString(
QDataStream &s, qint32 alignBytes = 1, qint32 *size =
nullptr)
270 *size =
sizeof(stringSize);
273 if (stringSize > 0) {
285 if (
auto pad = *size % alignBytes)
298static PSDImageResourceSection readImageResourceSection(
QDataStream &s,
bool *ok =
nullptr)
300 PSDImageResourceSection irs;
312 for (
auto size = sectioSize; size > 0;) {
327 size -=
sizeof(signature);
329 if (signature != S_8BIM && signature != S_MeSa) {
330 qDebug() <<
"Invalid Image Resource Block Signature!";
341 PSDImageResourceBlock irb;
345 irb.name = readPascalString(s, 2, &bytes);
351 size -=
sizeof(dataSize);
354 if (
auto dev = s.
device())
355 irb.data = dev->read(dataSize);
359 if (quint32(read) != dataSize) {
360 qDebug() <<
"Image Resource Block Read Error!";
365 if (
auto pad = dataSize % 2) {
378PSDAdditionalLayerInfo readAdditionalLayer(
QDataStream &s,
bool *ok =
nullptr)
380 PSDAdditionalLayerInfo li;
387 *
ok = li.signature == S_8BIM || li.signature == S_8B64;
396 li.size = readSize(s, li.signature == S_8B64);
401 *
ok = skip_data(s, li.size);
406PSDLayerAndMaskSection readLayerAndMaskSection(
QDataStream &s,
bool isPsb,
bool *ok =
nullptr)
408 PSDLayerAndMaskSection lms;
418 lms.size = readSize(s, isPsb);
422 lms.layerInfo.size = readSize(s, isPsb);
423 if (lms.layerInfo.size > 0) {
424 s >> lms.layerInfo.layerCount;
425 skip_data(s, lms.layerInfo.size -
sizeof(lms.layerInfo.layerCount));
431 lms.globalLayerMaskInfo.size = readSize(s,
false);
432 if (lms.globalLayerMaskInfo.size > 0) {
433 skip_data(s, lms.globalLayerMaskInfo.size);
439 for (
bool ok =
true;
ok && !lms.atEnd(isPsb);) {
440 auto al = readAdditionalLayer(s, &ok);
442 lms.additionalLayerInfo.insert(al.id, al);
446 device->rollbackTransaction();
447 *
ok = skip_section(s, isPsb);
458PSDColorModeDataSection readColorModeDataSection(
QDataStream &s,
bool *ok =
nullptr)
460 PSDColorModeDataSection cms;
476 if (cms.duotone.data.
size() != size)
480 auto&& palette = cms.palette;
482 for (
auto&& v : vect)
484 for (qsizetype i = 0, n = vect.size()/3; i < n; ++i)
485 palette.append(qRgb(vect.at(i), vect.at(n+i), vect.at(n+n+i)));
498static bool setColorSpace(
QImage& img,
const PSDImageResourceSection& irs)
500 if (!irs.contains(IRI_ICCPROFILE) || img.
isNull())
502 auto irb = irs.value(IRI_ICCPROFILE);
517static bool setXmpData(
QImage& img,
const PSDImageResourceSection& irs)
519 if (!irs.contains(IRI_XMPMETADATA))
521 auto irb = irs.value(IRI_XMPMETADATA);
528 img.
setText(QStringLiteral(META_KEY_XMP_ADOBE), xmp);
538static bool hasMergedData(
const PSDImageResourceSection& irs)
540 if (!irs.contains(IRI_VERSIONINFO))
542 auto irb = irs.value(IRI_VERSIONINFO);
543 if (irb.data.
size() > 4)
544 return irb.data.
at(4) != 0;
555static bool setResolution(
QImage& img,
const PSDImageResourceSection& irs)
557 if (!irs.contains(IRI_RESOLUTIONINFO))
559 auto irb = irs.value(IRI_RESOLUTIONINFO);
568 auto hres = fixedPointToDouble(i32);
575 auto vres = fixedPointToDouble(i32);
589static bool setTransparencyIndex(
QImage& img,
const PSDImageResourceSection& irs)
591 if (!irs.contains(IRI_TRANSPARENCYINDEX))
593 auto irb = irs.value(IRI_TRANSPARENCYINDEX);
600 if (idx < palette.size()) {
601 auto&& v = palette[idx];
602 v = QRgb(v & ~0xFF000000);
612 s >> header.signature;
614 for (
int i = 0; i < 6; i++) {
615 s >> header.reserved[i];
617 s >> header.channel_count;
621 s >> header.color_mode;
626static bool IsValid(
const PSDHeader &header)
628 if (header.signature != 0x38425053) {
632 if (header.version != 1 && header.version != 2) {
633 qDebug() <<
"PSD header: invalid version" << header.version;
636 if (header.depth != 8 &&
637 header.depth != 16 &&
638 header.depth != 32 &&
640 qDebug() <<
"PSD header: invalid depth" << header.depth;
643 if (header.color_mode != CM_RGB &&
644 header.color_mode != CM_GRAYSCALE &&
645 header.color_mode != CM_INDEXED &&
646 header.color_mode != CM_DUOTONE &&
647 header.color_mode != CM_CMYK &&
648 header.color_mode != CM_LABCOLOR &&
649 header.color_mode != CM_MULTICHANNEL &&
650 header.color_mode != CM_BITMAP) {
651 qDebug() <<
"PSD header: invalid color mode" << header.color_mode;
656 if (header.channel_count < 1 || header.channel_count > 57) {
657 qDebug() <<
"PSD header: invalid number of channels" << header.channel_count;
660 if (header.width > 300000 || header.height > 300000) {
661 qDebug() <<
"PSD header: invalid image size" << header.width <<
"x" << header.height;
668static bool IsSupported(
const PSDHeader &header)
670 if (!IsValid(header)) {
673 if (header.version != 1 && header.version != 2) {
676 if (header.depth != 8 &&
677 header.depth != 16 &&
678 header.depth != 32 &&
682 if (header.color_mode != CM_RGB &&
683 header.color_mode != CM_GRAYSCALE &&
684 header.color_mode != CM_INDEXED &&
685 header.color_mode != CM_DUOTONE &&
686 header.color_mode != CM_CMYK &&
687 header.color_mode != CM_MULTICHANNEL &&
688 header.color_mode != CM_LABCOLOR &&
689 header.color_mode != CM_BITMAP) {
692 if (header.color_mode == CM_MULTICHANNEL &&
693 header.channel_count < 3) {
708qint64 decompress(
const char *input, qint64 ilen,
char *output, qint64 olen)
711 for (qint64 ip = 0, rr = 0, available = olen; j < olen && ip < ilen; available = olen - j) {
712 signed char n =
static_cast<signed char>(input[ip++]);
718 if (available < rr) {
725 memcpy(output + j, input + ip,
size_t(rr));
728 else if (ip < ilen) {
730 if (available < rr) {
734 memset(output + j, input[ip++],
size_t(rr));
747static QImage::Format imageFormat(
const PSDHeader &header,
bool alpha)
749 if (header.channel_count == 0) {
754 switch(header.color_mode) {
756 if (header.depth == 32)
758 else if (header.depth == 16)
763 case CM_MULTICHANNEL:
765 if (NATIVE_CMYK && header.channel_count == 4 && (header.depth == 16 || header.depth == 8))
766 format = CMYK_FORMAT;
767 else if (header.depth == 16)
769 else if (header.depth == 8)
773 if (header.depth == 16)
775 else if (header.depth == 8)
816inline quint8 xchg(quint8 v)
821inline quint16 xchg(quint16 v)
823#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
824 return quint16( (v>>8) | (v<<8) );
830inline quint32 xchg(quint32 v)
832#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
833 return quint32( (v>>24) | ((v & 0x00FF0000)>>8) | ((v & 0x0000FF00)<<8) | (v<<24) );
839inline float xchg(
float v)
841#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
844 quint32 f = xchg(*
reinterpret_cast<quint32*
>(pf));
846 return *
reinterpret_cast<float*
>(pi);
849 std::memcpy(&t, &v,
sizeof(quint32));
851 std::memcpy(&v, &t,
sizeof(quint32));
860inline void planarToChunchy(uchar *target,
const char *source, qint32 width, qint32 c, qint32 cn)
862 auto s =
reinterpret_cast<const T*
>(source);
863 auto t =
reinterpret_cast<T*
>(target);
864 for (qint32 x = 0; x < width; ++x) {
865 t[x * cn + c] = xchg(s[x]);
870inline void planarToChunchyCMYK(uchar *target,
const char *source, qint32 width, qint32 c, qint32 cn)
872 auto s =
reinterpret_cast<const T*
>(source);
873 auto t =
reinterpret_cast<quint8*
>(target);
874 const T d = std::numeric_limits<T>::max() / std::numeric_limits<quint8>::max();
875 for (qint32 x = 0; x < width; ++x) {
876 t[x * cn + c] = quint8((std::numeric_limits<T>::max() - xchg(s[x])) / d);
882inline void planarToChunchyFloatToUInt16(uchar *target,
const char *source, qint32 width, qint32 c, qint32 cn)
884 auto s =
reinterpret_cast<const T*
>(source);
885 auto t =
reinterpret_cast<quint16*
>(target);
886 for (qint32 x = 0; x < width; ++x) {
887 t[x * cn + c] = quint16(std::min(xchg(s[x]) * std::numeric_limits<quint16>::max() + 0.5,
double(std::numeric_limits<quint16>::max())));
891enum class PremulConversion {
898inline void premulConversion(
char *stride, qint32 width, qint32 ac, qint32 cn,
const PremulConversion &conv)
900 auto s =
reinterpret_cast<T*
>(stride);
902 auto max = qint64(std::numeric_limits<T>::is_integer ? std::numeric_limits<T>::max() : 1);
904 for (qint32 c = 0; c < ac; ++c) {
905 if (conv == PremulConversion::PS2P) {
906 for (qint32 x = 0; x < width; ++x) {
908 auto alpha = *(s + xcn + ac);
909 *(s + xcn + c) = *(s + xcn + c) + alpha - max;
912 else if (conv == PremulConversion::PS2A || (conv == PremulConversion::PSLab2A && c == 0)) {
913 for (qint32 x = 0; x < width; ++x) {
915 auto alpha = *(s + xcn + ac);
917 *(s + xcn + c) = ((*(s + xcn + c) + alpha - max) * max + alpha / 2) / alpha;
920 else if (conv == PremulConversion::PSLab2A) {
921 for (qint32 x = 0; x < width; ++x) {
923 auto alpha = *(s + xcn + ac);
925 *(s + xcn + c) = ((*(s + xcn + c) + (alpha - max + 1) / 2) * max + alpha / 2) / alpha;
931inline void monoInvert(uchar *target,
const char* source, qint32 bytes)
933 auto s =
reinterpret_cast<const quint8*
>(source);
934 auto t =
reinterpret_cast<quint8*
>(target);
935 for (qint32 x = 0; x < bytes; ++x) {
941inline void rawChannelsCopyToCMYK(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width)
943 auto s =
reinterpret_cast<const T*
>(source);
944 auto t =
reinterpret_cast<quint8*
>(target);
945 const T d = std::numeric_limits<T>::max() / std::numeric_limits<quint8>::max();
946 for (qint32 c = 0, cs = std::min(targetChannels, sourceChannels); c < cs; ++c) {
947 for (qint32 x = 0; x < width; ++x) {
948 t[x * targetChannels + c] = (std::numeric_limits<T>::max() - s[x * sourceChannels + c]) / d;
954inline void rawChannelsCopy(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width)
956 auto s =
reinterpret_cast<const T*
>(source);
957 auto t =
reinterpret_cast<T*
>(target);
958 for (qint32 c = 0, cs = std::min(targetChannels, sourceChannels); c < cs; ++c) {
959 for (qint32 x = 0; x < width; ++x) {
960 t[x * targetChannels + c] = s[x * sourceChannels + c];
966inline void rawChannelCopy(uchar *target, qint32 targetChannels, qint32 targetChannel,
const char *source, qint32 sourceChannels, qint32 sourceChannel, qint32 width)
968 auto s =
reinterpret_cast<const T*
>(source);
969 auto t =
reinterpret_cast<T*
>(target);
970 for (qint32 x = 0; x < width; ++x) {
971 t[x * targetChannels + targetChannel] = s[x * sourceChannels + sourceChannel];
977inline void cmykToRgb(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width,
bool alpha =
false)
979 auto s =
reinterpret_cast<const T*
>(source);
980 auto t =
reinterpret_cast<T*
>(target);
981 auto max = double(std::numeric_limits<T>::max());
982 auto invmax = 1.0 / max;
984 if (sourceChannels < 3) {
985 qDebug() <<
"cmykToRgb: image is not a valid CMY/CMYK!";
989 for (qint32 w = 0; w < width; ++w) {
990 auto ps = s + sourceChannels * w;
991 auto C = 1 - *(ps + 0) * invmax;
992 auto M = 1 - *(ps + 1) * invmax;
993 auto Y = 1 - *(ps + 2) * invmax;
994 auto K = sourceChannels > 3 ? 1 - *(ps + 3) * invmax : 0.0;
996 auto pt = t + targetChannels * w;
997 *(pt + 0) = T(std::min(max - (C * (1 - K) + K) * max + 0.5, max));
998 *(pt + 1) = T(std::min(max - (M * (1 - K) + K) * max + 0.5, max));
999 *(pt + 2) = T(std::min(max - (Y * (1 - K) + K) * max + 0.5, max));
1000 if (targetChannels == 4) {
1001 if (sourceChannels >= 5 && alpha)
1002 *(pt + 3) = *(ps + 4);
1004 *(pt + 3) = std::numeric_limits<T>::max();
1009inline double finv(
double v)
1011 return (v > 6.0 / 29.0 ? v * v * v : (v - 16.0 / 116.0) / 7.787);
1014inline double gammaCorrection(
double linear)
1016#ifdef PSD_FAST_LAB_CONVERSION
1021 return (linear > 0.0031308 ? 1.055 * fastPow(linear, 1.0 / 2.4) - 0.055 : 12.92 * linear);
1026inline void labToRgb(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width,
bool alpha =
false)
1028 auto s =
reinterpret_cast<const T*
>(source);
1029 auto t =
reinterpret_cast<T*
>(target);
1030 auto max = double(std::numeric_limits<T>::max());
1031 auto invmax = 1.0 / max;
1033 if (sourceChannels < 3) {
1034 qDebug() <<
"labToRgb: image is not a valid LAB!";
1038 for (qint32 w = 0; w < width; ++w) {
1039 auto ps = s + sourceChannels * w;
1040 auto L = (*(ps + 0) * invmax) * 100.0;
1041 auto A = (*(ps + 1) * invmax) * 255.0 - 128.0;
1042 auto B = (*(ps + 2) * invmax) * 255.0 - 128.0;
1045 auto Y = (L + 16.0) * (1.0 / 116.0);
1046 auto X = A * (1.0 / 500.0) + Y;
1047 auto Z = Y - B * (1.0 / 200.0);
1050 X = finv(X) * 0.9504;
1051 Y = finv(Y) * 1.0000;
1052 Z = finv(Z) * 1.0888;
1055 auto r = gammaCorrection( 3.24071 * X - 1.53726 * Y - 0.498571 * Z);
1056 auto g = gammaCorrection(- 0.969258 * X + 1.87599 * Y + 0.0415557 * Z);
1057 auto b = gammaCorrection( 0.0556352 * X - 0.203996 * Y + 1.05707 * Z);
1059 auto pt = t + targetChannels * w;
1060 *(pt + 0) = T(std::max(std::min(r * max + 0.5, max), 0.0));
1061 *(pt + 1) = T(std::max(std::min(g * max + 0.5, max), 0.0));
1062 *(pt + 2) = T(std::max(std::min(b * max + 0.5, max), 0.0));
1063 if (targetChannels == 4) {
1064 if (sourceChannels >= 4 && alpha)
1065 *(pt + 3) = *(ps + 3);
1067 *(pt + 3) = std::numeric_limits<T>::max();
1072bool readChannel(
QByteArray& target,
QDataStream &stream, quint32 compressedSize, quint16 compression)
1075 if (compressedSize > kMaxQVectorSize) {
1079 tmp.
resize(compressedSize);
1083 if (decompress(tmp.
data(), tmp.
size(), target.
data(), target.
size()) < 0) {
1098 auto isPsb = header.version == 2;
1102 auto cmds = readColorModeDataSection(stream, &ok);
1104 qDebug() <<
"Error while skipping Color Mode Data section";
1109 auto irs = readImageResourceSection(stream, &ok);
1111 qDebug() <<
"Error while reading Image Resources Section";
1115 if (!hasMergedData(irs)) {
1116 qDebug() <<
"No merged data found";
1121 auto lms = readLayerAndMaskSection(stream, isPsb, &ok);
1123 qDebug() <<
"Error while skipping Layer and Mask section";
1131 quint16 compression;
1132 stream >> compression;
1133 if (compression > 1) {
1134 qDebug() <<
"Unknown compression type";
1140 auto alpha = header.color_mode == CM_RGB;
1142 alpha = lms.hasAlpha();
1146 qWarning() <<
"Unsupported image format. color_mode:" << header.color_mode <<
"depth:" << header.depth <<
"channel_count:" << header.channel_count;
1150 img = imageAlloc(header.width, header.height, format);
1152 qWarning() <<
"Failed to allocate image, invalid dimensions?" <<
QSize(header.width, header.height);
1155 img.
fill(qRgb(0, 0, 0));
1156 if (!cmds.palette.isEmpty()) {
1158 setTransparencyIndex(img, irs);
1161 auto imgChannels = imageChannels(img.
format());
1162 auto channel_num = std::min(qint32(header.channel_count), imgChannels);
1163 auto raw_count = qsizetype(header.width * header.depth + 7) / 8;
1164 auto native_cmyk = img.
format() == CMYK_FORMAT;
1166 if (header.height > kMaxQVectorSize / header.channel_count /
sizeof(quint32)) {
1167 qWarning() <<
"LoadPSD() header height/channel_count too big" << header.height << header.channel_count;
1171 QList<quint32> strides(header.height * header.channel_count, raw_count);
1174 for (
auto&& v : strides) {
1185 auto device = stream.
device();
1187 if (!stridePositions.isEmpty()) {
1188 stridePositions[0] = device->pos();
1190 for (qsizetype i = 1, n = stridePositions.size(); i < n; ++i) {
1191 stridePositions[i] = stridePositions[i-1] + strides.at(i-1);
1196 rawStride.
resize(raw_count);
1200 auto randomAccess = (header.color_mode == CM_CMYK && !native_cmyk) ||
1201 (header.color_mode == CM_MULTICHANNEL && !native_cmyk) ||
1202 (header.color_mode == CM_LABCOLOR) ||
1209 ScanLineConverter iccConv(img.
format());
1210#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) && !defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
1211 if (header.color_mode == CM_CMYK && img.
format() != QImage::Format_CMYK8888) {
1212 auto tmpi =
QImage(header.width, 1, QImage::Format_CMYK8888);
1213 if (setColorSpace(tmpi, irs))
1221 psdScanline.
resize(qsizetype(header.width * header.depth * header.channel_count + 7) / 8);
1222 for (qint32 y = 0, h = header.height; y < h; ++y) {
1223 for (qint32 c = 0; c < header.channel_count; ++c) {
1224 auto strideNumber = c * qsizetype(h) + y;
1225 if (!device->seek(stridePositions.at(strideNumber))) {
1226 qDebug() <<
"Error while seeking the stream of channel" << c <<
"line" << y;
1229 auto&& strideSize = strides.at(strideNumber);
1230 if (!readChannel(rawStride, stream, strideSize, compression)) {
1231 qDebug() <<
"Error while reading the stream of channel" << c <<
"line" << y;
1235 auto scanLine =
reinterpret_cast<unsigned char*
>(psdScanline.
data());
1236 if (header.depth == 8) {
1237 planarToChunchy<quint8>(scanLine, rawStride.
data(), header.width, c, header.channel_count);
1239 else if (header.depth == 16) {
1240 planarToChunchy<quint16>(scanLine, rawStride.
data(), header.width, c, header.channel_count);
1242 else if (header.depth == 32) {
1243 planarToChunchy<float>(scanLine, rawStride.
data(), header.width, c, header.channel_count);
1249 auto scanLine =
reinterpret_cast<char*
>(psdScanline.
data());
1250 if (header.color_mode == CM_CMYK) {
1251 if (header.depth == 8)
1252 premulConversion<quint8>(scanLine, header.width, 4, header.channel_count, PremulConversion::PS2A);
1253 else if (header.depth == 16)
1254 premulConversion<quint16>(scanLine, header.width, 4, header.channel_count, PremulConversion::PS2A);
1256 if (header.color_mode == CM_LABCOLOR) {
1257 if (header.depth == 8)
1258 premulConversion<quint8>(scanLine, header.width, 3, header.channel_count, PremulConversion::PSLab2A);
1259 else if (header.depth == 16)
1260 premulConversion<quint16>(scanLine, header.width, 3, header.channel_count, PremulConversion::PSLab2A);
1262 if (header.color_mode == CM_RGB) {
1263 if (header.depth == 8)
1264 premulConversion<quint8>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
1265 else if (header.depth == 16)
1266 premulConversion<quint16>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
1267 else if (header.depth == 32)
1268 premulConversion<float>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
1273 if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) {
1275 if (header.depth == 8)
1276 cmykToRgb<quint8>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1277 else if (header.depth == 16)
1278 cmykToRgb<quint16>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1280 else if (header.depth == 8) {
1281 rawChannelsCopyToCMYK<quint8>(tmpCmyk.
bits(), 4, psdScanline.
data(), header.channel_count, header.width);
1282 if (
auto rgbPtr = iccConv.convertedScanLine(tmpCmyk, 0))
1284 if (imgChannels == 4 && header.channel_count >= 5)
1285 rawChannelCopy<quint8>(img.
scanLine(y), imgChannels, 3, psdScanline.
data(), header.channel_count, 4, header.width);
1287 else if (header.depth == 16) {
1288 rawChannelsCopyToCMYK<quint16>(tmpCmyk.
bits(), 4, psdScanline.
data(), header.channel_count, header.width);
1289 if (
auto rgbPtr = iccConv.convertedScanLine(tmpCmyk, 0))
1291 if (imgChannels == 4 && header.channel_count >= 5)
1292 rawChannelCopy<quint16>(img.
scanLine(y), imgChannels, 3, psdScanline.
data(), header.channel_count, 4, header.width);
1295 if (header.color_mode == CM_LABCOLOR) {
1296 if (header.depth == 8)
1297 labToRgb<quint8>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1298 else if (header.depth == 16)
1299 labToRgb<quint16>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1301 if (header.color_mode == CM_RGB) {
1302 if (header.depth == 8)
1303 rawChannelsCopy<quint8>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width);
1304 else if (header.depth == 16)
1305 rawChannelsCopy<quint16>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width);
1306 else if (header.depth == 32)
1307 rawChannelsCopy<float>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width);
1313 for (qint32 c = 0; c < channel_num; ++c) {
1314 for (qint32 y = 0, h = header.height; y < h; ++y) {
1315 auto&& strideSize = strides.at(c * qsizetype(h) + y);
1316 if (!readChannel(rawStride, stream, strideSize, compression)) {
1317 qDebug() <<
"Error while reading the stream of channel" << c <<
"line" << y;
1322 if (header.depth == 1) {
1325 else if (header.depth == 8) {
1327 planarToChunchyCMYK<quint8>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1329 planarToChunchy<quint8>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1331 else if (header.depth == 16) {
1333 planarToChunchyCMYK<quint16>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1335 planarToChunchy<quint16>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1337 else if (header.depth == 32 && header.color_mode == CM_RGB) {
1338 planarToChunchy<float>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1340 else if (header.depth == 32 && header.color_mode == CM_GRAYSCALE) {
1341 planarToChunchyFloatToUInt16<float>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1348 if (!setResolution(img, irs)) {
1353 if (header.color_mode == CM_LABCOLOR) {
1355#ifdef PSD_FAST_LAB_CONVERSION
1361 else if (!setColorSpace(img, irs)) {
1366 if (!setXmpData(img, irs)) {
1373 if (!cmds.duotone.data.isEmpty()) {
1382class PSDHandlerPrivate
1385 PSDHandlerPrivate() {}
1386 ~PSDHandlerPrivate() {}
1390PSDHandler::PSDHandler()
1392 , d(new PSDHandlerPrivate)
1396bool PSDHandler::canRead()
const
1398 if (canRead(device())) {
1405bool PSDHandler::read(
QImage *image)
1410 auto&& header = d->m_header;
1414 if (s.
atEnd() || !IsValid(header)) {
1420 if (!IsSupported(header)) {
1426 if (!LoadPSD(s, header, img)) {
1435bool PSDHandler::supportsOption(ImageOption option)
const
1442QVariant PSDHandler::option(ImageOption option)
const
1447 auto&& header = d->m_header;
1448 if (IsValid(header)) {
1451 else if (
auto dev = device()) {
1453 dev->startTransaction();
1454 auto ba = dev->read(
sizeof(PSDHeader));
1455 dev->rollbackTransaction();
1469bool PSDHandler::canRead(
QIODevice *device)
1472 qWarning(
"PSDHandler::canRead() called with no device");
1491 if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) {
1492 if (header.channel_count != 4 || !NATIVE_CMYK)
1495 if (header.color_mode == CM_LABCOLOR) {
1498 if (header.color_mode == CM_RGB && header.channel_count > 3) {
1503 return IsSupported(header);
1508 if (format ==
"psd" || format ==
"psb" || format ==
"pdd" || format ==
"psdt") {
1519 if (device->
isReadable() && PSDHandler::canRead(device)) {
1533#include "moc_psd_p.cpp"
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
KCOREADDONS_EXPORT unsigned int 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 read(qint64 maxSize)
void rollbackTransaction()
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
QVariant fromValue(T &&value)