diff --git a/src/imageformats/psd.cpp b/src/imageformats/psd.cpp index 9155667..d06d044 100644 --- a/src/imageformats/psd.cpp +++ b/src/imageformats/psd.cpp @@ -165,6 +165,14 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img) // Ignore the other channels. channel_num = 4; } + + if (compression == 1 && header.depth == 16) { + fmt = QImage::Format_RGBX64; + if (channel_num >= 4) { + fmt = QImage::Format_RGBA64; + } + } + img = QImage(header.width, header.height, fmt); if (img.isNull()) { qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width, header.height); @@ -193,11 +201,15 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img) updateAlpha }; + typedef QRgba64(*channelUpdater16)(QRgba64, quint16); + static const channelUpdater16 updaters64[4] = { + [](QRgba64 oldPixel, quint16 redPixel) {return qRgba64((oldPixel & ~(0xFFFFull << 0)) | (quint64( redPixel) << 0));}, + [](QRgba64 oldPixel, quint16 greenPixel){return qRgba64((oldPixel & ~(0xFFFFull << 16)) | (quint64(greenPixel) << 16));}, + [](QRgba64 oldPixel, quint16 bluePixel) {return qRgba64((oldPixel & ~(0xFFFFull << 32)) | (quint64( bluePixel) << 32));}, + [](QRgba64 oldPixel, quint16 alphaPixel){return qRgba64((oldPixel & ~(0xFFFFull << 48)) | (quint64(alphaPixel) << 48));} + }; + if (compression) { - if (header.depth != 8) { - qWarning() << "RLE compressed PSD image with depth != 8 is not supported."; - return false; - } // Skip row lengths. int skip_count = header.height * header.channel_count * sizeof(quint16); if (stream.skipRawData(skip_count) != skip_count) { @@ -205,9 +217,18 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img) } for (unsigned short channel = 0; channel < channel_num; channel++) { - bool success = decodeRLEData(RLEVariant::PackBits, stream, + bool success = false; + if (header.depth == 8) { + success = decodeRLEData(RLEVariant::PackBits, stream, image_data, pixel_count, &readPixel_u8, updaters[channel]); + } else if (header.depth == 16) { + QRgba64 *image_data = reinterpret_cast(img.bits()); + success = decodeRLEData(RLEVariant::PackBits16, stream, + image_data, pixel_count * 2, + &readPixel_u8, updaters64[channel]); + } + if (!success) { qDebug() << "decodeRLEData on channel" << channel << "failed"; return false; diff --git a/src/imageformats/rle_p.h b/src/imageformats/rle_p.h index d05ef5d..92ff3f9 100644 --- a/src/imageformats/rle_p.h +++ b/src/imageformats/rle_p.h @@ -24,6 +24,11 @@ enum class RLEVariant { * of size 2, 130 of size 3, up to 255 of size 128. */ PackBits, + /** + * Same as PackBits, but treat unpacked data as + * 16-bit integers. + */ + PackBits16, /** * PIC-style RLE * @@ -67,6 +72,8 @@ static inline bool decodeRLEData(RLEVariant variant, Func2 updateItem) { unsigned offset = 0; // in dest + bool is_msb = true; // only used for 16-bit PackBits, data is big-endian + quint16 temp_data = 0; while (offset < length) { unsigned remaining = length - offset; quint8 count1; @@ -85,7 +92,7 @@ static inline bool decodeRLEData(RLEVariant variant, // 2 to 128 repetitions length = count1 - 127u; } - } else if (variant == RLEVariant::PackBits) { + } else if (variant == RLEVariant::PackBits || variant == RLEVariant::PackBits16) { if (count1 == 128u) { // Ignore value 128 continue; @@ -102,7 +109,18 @@ static inline bool decodeRLEData(RLEVariant variant, } auto datum = readData(stream); for (unsigned i = offset; i < offset + length; ++i) { - dest[i] = updateItem(dest[i], datum); + if (variant == RLEVariant::PackBits16) { + if (is_msb) { + temp_data = datum << 8; + is_msb = false; + } else { + temp_data |= datum; + dest[i >> 1] = updateItem(dest[i >> 1], temp_data); + is_msb = true; + } + } else { + dest[i] = updateItem(dest[i], datum); + } } offset += length; } else { @@ -114,7 +132,18 @@ static inline bool decodeRLEData(RLEVariant variant, } for (unsigned i = offset; i < offset + length; ++i) { auto datum = readData(stream); - dest[i] = updateItem(dest[i], datum); + if (variant == RLEVariant::PackBits16) { + if (is_msb) { + temp_data = datum << 8; + is_msb = false; + } else { + temp_data |= datum; + dest[i >> 1] = updateItem(dest[i >> 1], temp_data); + is_msb = true; + } + } else { + dest[i] = updateItem(dest[i], datum); + } } offset += length; }