mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-07-18 03:54:18 -04:00
Make PSD image reader endianess-agnostic.
By using the same strategy as the SoftImage PIC handler (and sharing some code with it), we should avoid reading the image data incorrectly on big-endian architectures. REVIEW: 122650 BUG: 337918
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Softimage PIC support for QImage
|
||||
* Softimage PIC support for QImage.
|
||||
*
|
||||
* Copyright 1998 Halfdan Ingvarsson
|
||||
* Copyright 2007 Ruben Lopez <r.lopez@bren.es>
|
||||
* Copyright 2014 Alex Merry <alex.merry@kde.org>
|
||||
@ -27,6 +28,8 @@
|
||||
|
||||
#include "pic_p.h"
|
||||
|
||||
#include "rle_p.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
@ -167,82 +170,6 @@ static QDataStream &operator<< (QDataStream &s, const QList<PicChannel> &channel
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes data written in mixed run-length encoding format.
|
||||
*
|
||||
* This is intended to be used with lambda functions.
|
||||
*
|
||||
* Note that this functions expects that, at the current location in @p stream,
|
||||
* exactly @p length items have been encoded as a unit (and so it will not be
|
||||
* partway through a run when it has decoded @p length items). If this is not
|
||||
* the case, it will return @c false.
|
||||
*
|
||||
* @param stream The stream to read the data from.
|
||||
* @param data The location to write the data.
|
||||
* @param length The number of items to read.
|
||||
* @param readItem A function that takes a QDataStream reference and reads a
|
||||
* single item.
|
||||
* @param updateItem A function that takes an item from @p data and an item
|
||||
* read by @p readItem and produces the item that should be
|
||||
* written to @p data.
|
||||
*
|
||||
* @returns @c true if @p length items in mixed RLE were successfully read
|
||||
* into @p data, @c false otherwise.
|
||||
*/
|
||||
template<typename Item, typename Func1, typename Func2>
|
||||
static bool decodeMixedRLEData(QDataStream &stream,
|
||||
Item *data,
|
||||
quint16 length,
|
||||
Func1 readItem,
|
||||
Func2 updateItem)
|
||||
{
|
||||
unsigned offset = 0; // in data
|
||||
while (offset < length) {
|
||||
unsigned remaining = length - offset;
|
||||
quint8 count1;
|
||||
stream >> count1;
|
||||
|
||||
if (count1 >= 128u) {
|
||||
unsigned length;
|
||||
if (count1 == 128u) {
|
||||
// If the value is exactly 128, it means that it is more than
|
||||
// 127 repetitions
|
||||
quint16 count2;
|
||||
stream >> count2;
|
||||
length = count2;
|
||||
} else {
|
||||
// If last bit is 1, then it is 2 to 127 repetitions
|
||||
length = count1 - 127u;
|
||||
}
|
||||
if (length > remaining) {
|
||||
qDebug() << "Row overrun:" << length << ">" << remaining;
|
||||
return false;
|
||||
}
|
||||
Item item = readItem(stream);
|
||||
for (unsigned i = offset; i < offset + length; ++i) {
|
||||
data[i] = updateItem(data[i], item);
|
||||
}
|
||||
offset += length;
|
||||
} else {
|
||||
// No repetitions
|
||||
unsigned length = count1 + 1u;
|
||||
if (length > remaining) {
|
||||
qDebug() << "Row overrun:" << length << ">" << remaining;
|
||||
return false;
|
||||
}
|
||||
for (unsigned i = offset; i < offset + length; ++i) {
|
||||
Item item = readItem(stream);
|
||||
data[i] = updateItem(data[i], item);
|
||||
}
|
||||
offset += length;
|
||||
}
|
||||
}
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
qDebug() << "DataStream status was" << stream.status();;;;
|
||||
}
|
||||
return stream.status() == QDataStream::Ok;
|
||||
}
|
||||
|
||||
static bool readRow(QDataStream &stream, QRgb *row, quint16 width, QList<PicChannel> channels)
|
||||
{
|
||||
Q_FOREACH(const PicChannel &channel, channels) {
|
||||
@ -273,8 +200,10 @@ static bool readRow(QDataStream &stream, QRgb *row, quint16 width, QList<PicChan
|
||||
qAlpha((channel.code & ALPHA) ? newPixel : oldPixel));
|
||||
};
|
||||
if (channel.encoding == MixedRLE) {
|
||||
if (!decodeMixedRLEData(stream, row, width, readPixel, updatePixel)) {
|
||||
qDebug() << "decodeMixedRLEData failed";
|
||||
bool success = decodeRLEData(RLEVariant::PIC, stream, row, width,
|
||||
readPixel, updatePixel);
|
||||
if (!success) {
|
||||
qDebug() << "decodeRLEData failed";
|
||||
return false;
|
||||
}
|
||||
} else if (channel.encoding == Uncompressed) {
|
||||
@ -294,67 +223,6 @@ static bool readRow(QDataStream &stream, QRgb *row, quint16 width, QList<PicChan
|
||||
return stream.status() == QDataStream::Ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes data in mixed run-length encoding format.
|
||||
*
|
||||
* This is intended to be used with lambda functions.
|
||||
*
|
||||
* @param stream The stream to write the data to.
|
||||
* @param data The data to be written.
|
||||
* @param length The number of items to write.
|
||||
* @param itemsEqual A function that takes two items and returns whether
|
||||
* @p writeItem would write them identically.
|
||||
* @param writeItem A function that takes a QDataStream reference and an item
|
||||
* and writes the item to the data stream.
|
||||
*/
|
||||
template<typename Item, typename Func1, typename Func2>
|
||||
static void encodeMixedRLEData(QDataStream &stream, const Item *data, unsigned length, Func1 itemsEqual, Func2 writeItem)
|
||||
{
|
||||
unsigned offset = 0;
|
||||
while (offset < length) {
|
||||
const Item *chunkStart = data + offset;
|
||||
unsigned maxChunk = qMin(length - offset, 65535u);
|
||||
|
||||
const Item *chunkEnd = chunkStart + 1;
|
||||
quint16 chunkLength = 1;
|
||||
while (chunkLength < maxChunk && itemsEqual(*chunkStart, *chunkEnd)) {
|
||||
++chunkEnd;
|
||||
++chunkLength;
|
||||
}
|
||||
|
||||
if (chunkLength > 127) {
|
||||
// Sequence of > 127 identical pixels
|
||||
stream << quint8(128);
|
||||
stream << quint16(chunkLength);
|
||||
writeItem(stream, *chunkStart);
|
||||
} else if (chunkLength > 1) {
|
||||
// Sequence of < 128 identical pixels
|
||||
stream << quint8(chunkLength + 127);
|
||||
writeItem(stream, *chunkStart);
|
||||
} else {
|
||||
// find a string of up to 128 values, each different from the one
|
||||
// that follows it
|
||||
if (maxChunk > 128) {
|
||||
maxChunk = 128;
|
||||
}
|
||||
chunkLength = 1;
|
||||
chunkEnd = chunkStart + 1;
|
||||
while (chunkLength < maxChunk &&
|
||||
(chunkLength + 1u == maxChunk ||
|
||||
!itemsEqual(*chunkEnd, *(chunkEnd+1))))
|
||||
{
|
||||
++chunkEnd;
|
||||
++chunkLength;
|
||||
}
|
||||
stream << quint8(chunkLength - 1);
|
||||
for (unsigned i = 0; i < chunkLength; ++i) {
|
||||
writeItem(stream, *(chunkStart + i));
|
||||
}
|
||||
}
|
||||
offset += chunkLength;
|
||||
}
|
||||
}
|
||||
|
||||
bool SoftimagePICHandler::canRead() const
|
||||
{
|
||||
if (!SoftimagePICHandler::canRead(device())) {
|
||||
@ -445,7 +313,8 @@ bool SoftimagePICHandler::write(const QImage &_image)
|
||||
<< quint8(qBlue(pixel));
|
||||
};
|
||||
if (m_compression) {
|
||||
encodeMixedRLEData(stream, row, image.width(), rgbEqual, writeRgb);
|
||||
encodeRLEData(RLEVariant::PIC, stream, row, image.width(),
|
||||
rgbEqual, writeRgb);
|
||||
} else {
|
||||
for (int i = 0; i < image.width(); ++i) {
|
||||
writeRgb(stream, row[i]);
|
||||
@ -461,7 +330,8 @@ bool SoftimagePICHandler::write(const QImage &_image)
|
||||
str << quint8(qAlpha(pixel));
|
||||
};
|
||||
if (m_compression) {
|
||||
encodeMixedRLEData(stream, row, image.width(), alphaEqual, writeAlpha);
|
||||
encodeRLEData(RLEVariant::PIC, stream, row, image.width(),
|
||||
alphaEqual, writeAlpha);
|
||||
} else {
|
||||
for (int i = 0; i < image.width(); ++i) {
|
||||
writeAlpha(stream, row[i]);
|
||||
|
Reference in New Issue
Block a user