35#include "fastmath_p.h"
48typedef quint16 ushort;
66enum Signature : quint32 {
73enum ColorMode : quint16 {
84enum ImageResourceId : quint16 {
85 IRI_RESOLUTIONINFO = 0x03ED,
86 IRI_ICCPROFILE = 0x040F,
87 IRI_TRANSPARENCYINDEX = 0x0417,
88 IRI_VERSIONINFO = 0x0421,
89 IRI_XMPMETADATA = 0x0424
92enum LayerId : quint32 {
102 ushort channel_count;
109struct PSDImageResourceBlock {
119struct PSDDuotoneOptions {
127struct PSDColorModeDataSection {
128 PSDDuotoneOptions duotone;
136 qint16 layerCount = 0;
139struct PSDGlobalLayerMaskInfo {
143struct PSDAdditionalLayerInfo {
144 Signature signature = Signature();
145 LayerId
id = LayerId();
149struct PSDLayerAndMaskSection {
151 PSDLayerInfo layerInfo;
152 PSDGlobalLayerMaskInfo globalLayerMaskInfo;
155 bool isNull()
const {
159 bool hasAlpha()
const {
160 return layerInfo.layerCount < 0 ||
161 additionalLayerInfo.
contains(LI_MT16) ||
162 additionalLayerInfo.
contains(LI_MT32) ||
163 additionalLayerInfo.
contains(LI_MTRN);
166 bool atEnd(
bool isPsb)
const {
167 qint64 currentSize = 0;
168 if (layerInfo.size > -1) {
169 currentSize += layerInfo.size + 4;
173 if (globalLayerMaskInfo.size > -1) {
174 currentSize += globalLayerMaskInfo.size + 4;
176 auto aliv = additionalLayerInfo.
values();
177 for (
auto &&v : aliv) {
178 currentSize += (12 + v.size);
179 if (v.signature == S_8B64)
182 return (size <= currentSize);
190static double fixedPointToDouble(qint32 fixedPoint)
192 auto i = double(fixedPoint >> 16);
193 auto d = double((fixedPoint & 0x0000FFFF) / 65536.0);
197static qint64 readSize(
QDataStream &s,
bool psb =
false)
217 for (qint32 i32 = 0; size; size -= i32) {
218 i32 = std::min(size, qint64(std::numeric_limits<qint32>::max()));
226static bool skip_section(
QDataStream &s,
bool psb =
false)
228 auto section_length = readSize(s, psb);
229 if (section_length < 0)
231 return skip_data(s, section_length);
242static QString readPascalString(
QDataStream &s, qint32 alignBytes = 1, qint32 *size =
nullptr)
250 *size =
sizeof(stringSize);
253 if (stringSize > 0) {
265 if (
auto pad = *size % alignBytes)
278static PSDImageResourceSection readImageResourceSection(
QDataStream &s,
bool *ok =
nullptr)
280 PSDImageResourceSection irs;
292 for (
auto size = sectioSize; size > 0;) {
307 size -=
sizeof(signature);
309 if (signature != S_8BIM && signature != S_MeSa) {
310 qDebug() <<
"Invalid Image Resource Block Signature!";
321 PSDImageResourceBlock irb;
325 irb.name = readPascalString(s, 2, &bytes);
331 size -=
sizeof(dataSize);
334 if (
auto dev = s.
device())
335 irb.data = dev->read(dataSize);
339 if (quint32(read) != dataSize) {
340 qDebug() <<
"Image Resource Block Read Error!";
345 if (
auto pad = dataSize % 2) {
358PSDAdditionalLayerInfo readAdditionalLayer(
QDataStream &s,
bool *ok =
nullptr)
360 PSDAdditionalLayerInfo li;
367 *
ok = li.signature == S_8BIM || li.signature == S_8B64;
376 li.size = readSize(s, li.signature == S_8B64);
381 *
ok = skip_data(s, li.size);
386PSDLayerAndMaskSection readLayerAndMaskSection(
QDataStream &s,
bool isPsb,
bool *ok =
nullptr)
388 PSDLayerAndMaskSection lms;
398 lms.size = readSize(s, isPsb);
402 lms.layerInfo.size = readSize(s, isPsb);
403 if (lms.layerInfo.size > 0) {
404 s >> lms.layerInfo.layerCount;
405 skip_data(s, lms.layerInfo.size -
sizeof(lms.layerInfo.layerCount));
411 lms.globalLayerMaskInfo.size = readSize(s,
false);
412 if (lms.globalLayerMaskInfo.size > 0) {
413 skip_data(s, lms.globalLayerMaskInfo.size);
419 for (
bool ok =
true;
ok && !lms.atEnd(isPsb);) {
420 auto al = readAdditionalLayer(s, &ok);
422 lms.additionalLayerInfo.insert(al.id, al);
426 device->rollbackTransaction();
427 *
ok = skip_section(s, isPsb);
438PSDColorModeDataSection readColorModeDataSection(
QDataStream &s,
bool *ok =
nullptr)
440 PSDColorModeDataSection cms;
456 if (cms.duotone.data.
size() != size)
460 auto&& palette = cms.palette;
462 for (
auto&& v : vect)
464 for (qsizetype i = 0, n = vect.size()/3; i < n; ++i)
465 palette.append(qRgb(vect.at(i), vect.at(n+i), vect.at(n+n+i)));
478static bool setColorSpace(
QImage& img,
const PSDImageResourceSection& irs)
480 if (!irs.contains(IRI_ICCPROFILE))
482 auto irb = irs.value(IRI_ICCPROFILE);
497static bool setXmpData(
QImage& img,
const PSDImageResourceSection& irs)
499 if (!irs.contains(IRI_XMPMETADATA))
501 auto irb = irs.value(IRI_XMPMETADATA);
508 img.
setText(QStringLiteral(
"XML:com.adobe.xmp"), xmp);
518static bool hasMergedData(
const PSDImageResourceSection& irs)
520 if (!irs.contains(IRI_VERSIONINFO))
522 auto irb = irs.value(IRI_VERSIONINFO);
523 if (irb.data.
size() > 4)
524 return irb.data.
at(4) != 0;
535static bool setResolution(
QImage& img,
const PSDImageResourceSection& irs)
537 if (!irs.contains(IRI_RESOLUTIONINFO))
539 auto irb = irs.value(IRI_RESOLUTIONINFO);
548 auto hres = fixedPointToDouble(i32);
555 auto vres = fixedPointToDouble(i32);
569static bool setTransparencyIndex(
QImage& img,
const PSDImageResourceSection& irs)
571 if (!irs.contains(IRI_TRANSPARENCYINDEX))
573 auto irb = irs.value(IRI_TRANSPARENCYINDEX);
580 if (idx < palette.size()) {
581 auto&& v = palette[idx];
582 v = QRgb(v & ~0xFF000000);
592 s >> header.signature;
594 for (
int i = 0; i < 6; i++) {
595 s >> header.reserved[i];
597 s >> header.channel_count;
601 s >> header.color_mode;
606static bool IsValid(
const PSDHeader &header)
608 if (header.signature != 0x38425053) {
612 if (header.version != 1 && header.version != 2) {
613 qDebug() <<
"PSD header: invalid version" << header.version;
616 if (header.depth != 8 &&
617 header.depth != 16 &&
618 header.depth != 32 &&
620 qDebug() <<
"PSD header: invalid depth" << header.depth;
623 if (header.color_mode != CM_RGB &&
624 header.color_mode != CM_GRAYSCALE &&
625 header.color_mode != CM_INDEXED &&
626 header.color_mode != CM_DUOTONE &&
627 header.color_mode != CM_CMYK &&
628 header.color_mode != CM_LABCOLOR &&
629 header.color_mode != CM_MULTICHANNEL &&
630 header.color_mode != CM_BITMAP) {
631 qDebug() <<
"PSD header: invalid color mode" << header.color_mode;
636 if (header.channel_count < 1 || header.channel_count > 57) {
637 qDebug() <<
"PSD header: invalid number of channels" << header.channel_count;
640 if (header.width > 300000 || header.height > 300000) {
641 qDebug() <<
"PSD header: invalid image size" << header.width <<
"x" << header.height;
648static bool IsSupported(
const PSDHeader &header)
650 if (!IsValid(header)) {
653 if (header.version != 1 && header.version != 2) {
656 if (header.depth != 8 &&
657 header.depth != 16 &&
658 header.depth != 32 &&
662 if (header.color_mode != CM_RGB &&
663 header.color_mode != CM_GRAYSCALE &&
664 header.color_mode != CM_INDEXED &&
665 header.color_mode != CM_DUOTONE &&
666 header.color_mode != CM_CMYK &&
667 header.color_mode != CM_MULTICHANNEL &&
668 header.color_mode != CM_LABCOLOR &&
669 header.color_mode != CM_BITMAP) {
672 if (header.color_mode == CM_MULTICHANNEL &&
673 header.channel_count < 3) {
688qint64 decompress(
const char *input, qint64 ilen,
char *output, qint64 olen)
691 for (qint64 ip = 0, rr = 0, available = olen; j < olen && ip < ilen; available = olen - j) {
692 signed char n =
static_cast<signed char>(input[ip++]);
698 if (available < rr) {
705 memcpy(output + j, input + ip,
size_t(rr));
708 else if (ip < ilen) {
710 if (available < rr) {
714 memset(output + j, input[ip++],
size_t(rr));
727static QImage::Format imageFormat(
const PSDHeader &header,
bool alpha)
729 if (header.channel_count == 0) {
734 switch(header.color_mode) {
736 if (header.depth == 32)
738 else if (header.depth == 16)
743 case CM_MULTICHANNEL:
745 if (header.depth == 16)
747 else if (header.depth == 8)
751 if (header.depth == 16)
753 else if (header.depth == 8)
794inline quint8 xchg(quint8 v)
799inline quint16 xchg(quint16 v)
801#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
802 return quint16( (v>>8) | (v<<8) );
808inline quint32 xchg(quint32 v)
810#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
811 return quint32( (v>>24) | ((v & 0x00FF0000)>>8) | ((v & 0x0000FF00)<<8) | (v<<24) );
817inline float xchg(
float v)
819#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
822 quint32 f = xchg(*
reinterpret_cast<quint32*
>(pf));
824 return *
reinterpret_cast<float*
>(pi);
827 std::memcpy(&t, &v,
sizeof(quint32));
829 std::memcpy(&v, &t,
sizeof(quint32));
838inline void planarToChunchy(uchar *target,
const char *source, qint32 width, qint32 c, qint32 cn)
840 auto s =
reinterpret_cast<const T*
>(source);
841 auto t =
reinterpret_cast<T*
>(target);
842 for (qint32 x = 0; x < width; ++x) {
843 t[x * cn + c] = xchg(s[x]);
848inline void planarToChunchyFloatToUInt16(uchar *target,
const char *source, qint32 width, qint32 c, qint32 cn)
850 auto s =
reinterpret_cast<const T*
>(source);
851 auto t =
reinterpret_cast<quint16*
>(target);
852 for (qint32 x = 0; x < width; ++x) {
853 t[x * cn + c] = quint16(std::min(xchg(s[x]) * std::numeric_limits<quint16>::max() + 0.5,
double(std::numeric_limits<quint16>::max())));
857enum class PremulConversion {
864inline void premulConversion(
char *stride, qint32 width, qint32 ac, qint32 cn,
const PremulConversion &conv)
866 auto s =
reinterpret_cast<T*
>(stride);
868 auto max = qint64(std::numeric_limits<T>::is_integer ? std::numeric_limits<T>::max() : 1);
870 for (qint32 c = 0; c < ac; ++c) {
871 if (conv == PremulConversion::PS2P) {
872 for (qint32 x = 0; x < width; ++x) {
874 auto alpha = *(s + xcn + ac);
875 *(s + xcn + c) = *(s + xcn + c) + alpha - max;
878 else if (conv == PremulConversion::PS2A || (conv == PremulConversion::PSLab2A && c == 0)) {
879 for (qint32 x = 0; x < width; ++x) {
881 auto alpha = *(s + xcn + ac);
883 *(s + xcn + c) = ((*(s + xcn + c) + alpha - max) * max + alpha / 2) / alpha;
886 else if (conv == PremulConversion::PSLab2A) {
887 for (qint32 x = 0; x < width; ++x) {
889 auto alpha = *(s + xcn + ac);
891 *(s + xcn + c) = ((*(s + xcn + c) + (alpha - max + 1) / 2) * max + alpha / 2) / alpha;
897inline void monoInvert(uchar *target,
const char* source, qint32 bytes)
899 auto s =
reinterpret_cast<const quint8*
>(source);
900 auto t =
reinterpret_cast<quint8*
>(target);
901 for (qint32 x = 0; x < bytes; ++x) {
907inline void rawChannelsCopy(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width)
909 auto s =
reinterpret_cast<const T*
>(source);
910 auto t =
reinterpret_cast<T*
>(target);
911 for (qint32 c = 0, cs = std::min(targetChannels, sourceChannels); c < cs; ++c) {
912 for (qint32 x = 0; x < width; ++x) {
913 t[x * targetChannels + c] = s[x * sourceChannels + c];
919inline void cmykToRgb(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width,
bool alpha =
false)
921 auto s =
reinterpret_cast<const T*
>(source);
922 auto t =
reinterpret_cast<T*
>(target);
923 auto max = double(std::numeric_limits<T>::max());
924 auto invmax = 1.0 / max;
926 if (sourceChannels < 3) {
927 qDebug() <<
"cmykToRgb: image is not a valid CMY/CMYK!";
931 for (qint32 w = 0; w < width; ++w) {
932 auto ps = s + sourceChannels * w;
933 auto C = 1 - *(ps + 0) * invmax;
934 auto M = 1 - *(ps + 1) * invmax;
935 auto Y = 1 - *(ps + 2) * invmax;
936 auto K = sourceChannels > 3 ? 1 - *(ps + 3) * invmax : 0.0;
938 auto pt = t + targetChannels * w;
939 *(pt + 0) = T(std::min(max - (C * (1 - K) + K) * max + 0.5, max));
940 *(pt + 1) = T(std::min(max - (M * (1 - K) + K) * max + 0.5, max));
941 *(pt + 2) = T(std::min(max - (Y * (1 - K) + K) * max + 0.5, max));
942 if (targetChannels == 4) {
943 if (sourceChannels >= 5 && alpha)
944 *(pt + 3) = *(ps + 4);
946 *(pt + 3) = std::numeric_limits<T>::max();
951inline double finv(
double v)
953 return (v > 6.0 / 29.0 ? v * v * v : (v - 16.0 / 116.0) / 7.787);
956inline double gammaCorrection(
double linear)
958#ifdef PSD_FAST_LAB_CONVERSION
963 return (linear > 0.0031308 ? 1.055 * fastPow(linear, 1.0 / 2.4) - 0.055 : 12.92 * linear);
968inline void labToRgb(uchar *target, qint32 targetChannels,
const char *source, qint32 sourceChannels, qint32 width,
bool alpha =
false)
970 auto s =
reinterpret_cast<const T*
>(source);
971 auto t =
reinterpret_cast<T*
>(target);
972 auto max = double(std::numeric_limits<T>::max());
973 auto invmax = 1.0 / max;
975 if (sourceChannels < 3) {
976 qDebug() <<
"labToRgb: image is not a valid LAB!";
980 for (qint32 w = 0; w < width; ++w) {
981 auto ps = s + sourceChannels * w;
982 auto L = (*(ps + 0) * invmax) * 100.0;
983 auto A = (*(ps + 1) * invmax) * 255.0 - 128.0;
984 auto B = (*(ps + 2) * invmax) * 255.0 - 128.0;
987 auto Y = (L + 16.0) * (1.0 / 116.0);
988 auto X = A * (1.0 / 500.0) + Y;
989 auto Z = Y - B * (1.0 / 200.0);
992 X = finv(X) * 0.9504;
993 Y = finv(Y) * 1.0000;
994 Z = finv(Z) * 1.0888;
997 auto r = gammaCorrection( 3.24071 * X - 1.53726 * Y - 0.498571 * Z);
998 auto g = gammaCorrection(- 0.969258 * X + 1.87599 * Y + 0.0415557 * Z);
999 auto b = gammaCorrection( 0.0556352 * X - 0.203996 * Y + 1.05707 * Z);
1001 auto pt = t + targetChannels * w;
1002 *(pt + 0) = T(std::max(std::min(r * max + 0.5, max), 0.0));
1003 *(pt + 1) = T(std::max(std::min(g * max + 0.5, max), 0.0));
1004 *(pt + 2) = T(std::max(std::min(b * max + 0.5, max), 0.0));
1005 if (targetChannels == 4) {
1006 if (sourceChannels >= 4 && alpha)
1007 *(pt + 3) = *(ps + 3);
1009 *(pt + 3) = std::numeric_limits<T>::max();
1014bool readChannel(
QByteArray& target,
QDataStream &stream, quint32 compressedSize, quint16 compression)
1017 if (compressedSize > kMaxQVectorSize) {
1021 tmp.
resize(compressedSize);
1025 if (decompress(tmp.
data(), tmp.
size(), target.
data(), target.
size()) < 0) {
1040 auto isPsb = header.version == 2;
1044 auto cmds = readColorModeDataSection(stream, &ok);
1046 qDebug() <<
"Error while skipping Color Mode Data section";
1051 auto irs = readImageResourceSection(stream, &ok);
1053 qDebug() <<
"Error while reading Image Resources Section";
1057 if (!hasMergedData(irs)) {
1058 qDebug() <<
"No merged data found";
1063 auto lms = readLayerAndMaskSection(stream, isPsb, &ok);
1065 qDebug() <<
"Error while skipping Layer and Mask section";
1073 quint16 compression;
1074 stream >> compression;
1075 if (compression > 1) {
1076 qDebug() <<
"Unknown compression type";
1082 auto alpha = header.color_mode == CM_RGB;
1084 alpha = lms.hasAlpha();
1088 qWarning() <<
"Unsupported image format. color_mode:" << header.color_mode <<
"depth:" << header.depth <<
"channel_count:" << header.channel_count;
1092 img = imageAlloc(header.width, header.height, format);
1094 qWarning() <<
"Failed to allocate image, invalid dimensions?" <<
QSize(header.width, header.height);
1097 img.
fill(qRgb(0, 0, 0));
1098 if (!cmds.palette.isEmpty()) {
1100 setTransparencyIndex(img, irs);
1103 auto imgChannels = imageChannels(img.
format());
1104 auto channel_num = std::min(qint32(header.channel_count), imgChannels);
1105 auto raw_count = qsizetype(header.width * header.depth + 7) / 8;
1107 if (header.height > kMaxQVectorSize / header.channel_count /
sizeof(quint32)) {
1108 qWarning() <<
"LoadPSD() header height/channel_count too big" << header.height << header.channel_count;
1112 QList<quint32> strides(header.height * header.channel_count, raw_count);
1115 for (
auto&& v : strides) {
1126 auto device = stream.
device();
1128 if (!stridePositions.isEmpty()) {
1129 stridePositions[0] = device->pos();
1131 for (qsizetype i = 1, n = stridePositions.size(); i < n; ++i) {
1132 stridePositions[i] = stridePositions[i-1] + strides.at(i-1);
1137 rawStride.
resize(raw_count);
1141 auto randomAccess = (header.color_mode == CM_CMYK) ||
1142 (header.color_mode == CM_LABCOLOR) ||
1143 (header.color_mode == CM_MULTICHANNEL) ||
1150 psdScanline.
resize(qsizetype(header.width * header.depth * header.channel_count + 7) / 8);
1151 for (qint32 y = 0, h = header.height; y < h; ++y) {
1152 for (qint32 c = 0; c < header.channel_count; ++c) {
1153 auto strideNumber = c * qsizetype(h) + y;
1154 if (!device->seek(stridePositions.at(strideNumber))) {
1155 qDebug() <<
"Error while seeking the stream of channel" << c <<
"line" << y;
1158 auto&& strideSize = strides.at(strideNumber);
1159 if (!readChannel(rawStride, stream, strideSize, compression)) {
1160 qDebug() <<
"Error while reading the stream of channel" << c <<
"line" << y;
1164 auto scanLine =
reinterpret_cast<unsigned char*
>(psdScanline.
data());
1165 if (header.depth == 8) {
1166 planarToChunchy<quint8>(scanLine, rawStride.
data(), header.width, c, header.channel_count);
1168 else if (header.depth == 16) {
1169 planarToChunchy<quint16>(scanLine, rawStride.
data(), header.width, c, header.channel_count);
1171 else if (header.depth == 32) {
1172 planarToChunchy<float>(scanLine, rawStride.
data(), header.width, c, header.channel_count);
1178 auto scanLine =
reinterpret_cast<char*
>(psdScanline.
data());
1179 if (header.color_mode == CM_CMYK) {
1180 if (header.depth == 8)
1181 premulConversion<quint8>(scanLine, header.width, 4, header.channel_count, PremulConversion::PS2A);
1182 else if (header.depth == 16)
1183 premulConversion<quint16>(scanLine, header.width, 4, header.channel_count, PremulConversion::PS2A);
1185 if (header.color_mode == CM_LABCOLOR) {
1186 if (header.depth == 8)
1187 premulConversion<quint8>(scanLine, header.width, 3, header.channel_count, PremulConversion::PSLab2A);
1188 else if (header.depth == 16)
1189 premulConversion<quint16>(scanLine, header.width, 3, header.channel_count, PremulConversion::PSLab2A);
1191 if (header.color_mode == CM_RGB) {
1192 if (header.depth == 8)
1193 premulConversion<quint8>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
1194 else if (header.depth == 16)
1195 premulConversion<quint16>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
1196 else if (header.depth == 32)
1197 premulConversion<float>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
1202 if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) {
1203 if (header.depth == 8)
1204 cmykToRgb<quint8>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1205 else if (header.depth == 16)
1206 cmykToRgb<quint16>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1208 if (header.color_mode == CM_LABCOLOR) {
1209 if (header.depth == 8)
1210 labToRgb<quint8>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1211 else if (header.depth == 16)
1212 labToRgb<quint16>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width, alpha);
1214 if (header.color_mode == CM_RGB) {
1215 if (header.depth == 8)
1216 rawChannelsCopy<quint8>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width);
1217 else if (header.depth == 16)
1218 rawChannelsCopy<quint16>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width);
1219 else if (header.depth == 32)
1220 rawChannelsCopy<float>(img.
scanLine(y), imgChannels, psdScanline.
data(), header.channel_count, header.width);
1226 for (qint32 c = 0; c < channel_num; ++c) {
1227 for (qint32 y = 0, h = header.height; y < h; ++y) {
1228 auto&& strideSize = strides.at(c * qsizetype(h) + y);
1229 if (!readChannel(rawStride, stream, strideSize, compression)) {
1230 qDebug() <<
"Error while reading the stream of channel" << c <<
"line" << y;
1235 if (header.depth == 1) {
1238 else if (header.depth == 8) {
1239 planarToChunchy<quint8>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1241 else if (header.depth == 16) {
1242 planarToChunchy<quint16>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1244 else if (header.depth == 32 && header.color_mode == CM_RGB) {
1245 planarToChunchy<float>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1247 else if (header.depth == 32 && header.color_mode == CM_GRAYSCALE) {
1248 planarToChunchyFloatToUInt16<float>(scanLine, rawStride.
data(), header.width, c, imgChannels);
1256 if (!setResolution(img, irs)) {
1261 if (header.color_mode == CM_LABCOLOR) {
1263#ifdef PSD_FAST_LAB_CONVERSION
1269 else if (!setColorSpace(img, irs)) {
1274 if (!setXmpData(img, irs)) {
1281 if (!cmds.duotone.data.isEmpty()) {
1290PSDHandler::PSDHandler()
1294bool PSDHandler::canRead()
const
1296 if (canRead(device())) {
1303bool PSDHandler::read(
QImage *image)
1312 if (s.
atEnd() || !IsValid(header)) {
1318 if (!IsSupported(header)) {
1324 if (!LoadPSD(s, header, img)) {
1333bool PSDHandler::supportsOption(ImageOption option)
const
1340QVariant PSDHandler::option(ImageOption option)
const
1345 if (
auto d = device()) {
1347 d->startTransaction();
1348 auto ba = d->read(
sizeof(PSDHeader));
1349 d->rollbackTransaction();
1365bool PSDHandler::canRead(
QIODevice *device)
1368 qWarning(
"PSDHandler::canRead() called with no device");
1387 if (header.color_mode == CM_CMYK || header.color_mode == CM_LABCOLOR || header.color_mode == CM_MULTICHANNEL) {
1390 if (header.color_mode == CM_RGB && header.channel_count > 3) {
1395 return IsSupported(header);
1400 if (format ==
"psd" || format ==
"psb" || format ==
"pdd" || format ==
"psdt") {
1411 if (device->
isReadable() && PSDHandler::canRead(device)) {
1425#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(StandardShortcut 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)