mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-05-28 00:30:23 -04:00
Add support for RLE-compressed, 16 bits per channel PSD files.
This commit is contained in:
parent
b742cb7cc7
commit
5825c83235
@ -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<QRgba64*>(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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user