mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-05-28 00:30:23 -04:00
Simplified read/verify header process
Where possible, QIODevice::peek has been used instead of transactions or instead of using ungetchar() for sequential access devices and seek() for random access devices. Furthermore: - RAS format gained the ability of read on sequential devices. - Removed unused code in XCF (still related to ungetchar and sequential devices). - These changes should prevent errors like the ones fixed by MR !258
This commit is contained in:
parent
fee0165bef
commit
97120b2537
@ -266,31 +266,16 @@ PCXHEADER::PCXHEADER()
|
|||||||
|
|
||||||
bool peekHeader(QIODevice *d, PCXHEADER& h)
|
bool peekHeader(QIODevice *d, PCXHEADER& h)
|
||||||
{
|
{
|
||||||
qint64 oldPos = d->pos();
|
auto head = d->peek(sizeof(PCXHEADER));
|
||||||
QByteArray head = d->read(sizeof(PCXHEADER));
|
if (size_t(head.size()) < sizeof(PCXHEADER)) {
|
||||||
int readBytes = head.size();
|
|
||||||
|
|
||||||
if (d->isSequential()) {
|
|
||||||
for (int pos = readBytes -1; pos >= 0; --pos) {
|
|
||||||
d->ungetChar(head[pos]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
d->seek(oldPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (readBytes < sizeof(PCXHEADER)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ok = false;
|
QDataStream ds(head);
|
||||||
{ // datastream is destroyed before working on device
|
ds.setByteOrder(QDataStream::LittleEndian);
|
||||||
QDataStream ds(head);
|
ds >> h;
|
||||||
ds.setByteOrder(QDataStream::LittleEndian);
|
|
||||||
ds >> h;
|
|
||||||
ok = ds.status() == QDataStream::Ok && h.isValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok;
|
return ds.status() == QDataStream::Ok && h.isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool readLine(QDataStream &s, QByteArray &buf, const PCXHEADER &header)
|
static bool readLine(QDataStream &s, QByteArray &buf, const PCXHEADER &header)
|
||||||
|
@ -121,10 +121,8 @@ public:
|
|||||||
|
|
||||||
bool peek(QIODevice *d)
|
bool peek(QIODevice *d)
|
||||||
{
|
{
|
||||||
d->startTransaction();
|
m_rawHeader = d->peek(512);
|
||||||
auto ok = read(d);
|
return isValid();
|
||||||
d->rollbackTransaction();
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool jumpToImageData(QIODevice *d) const
|
bool jumpToImageData(QIODevice *d) const
|
||||||
|
@ -341,12 +341,8 @@ bool QOIHandler::canRead(QIODevice *device)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
device->startTransaction();
|
auto head = device->peek(QOI_HEADER_SIZE);
|
||||||
QByteArray head = device->read(QOI_HEADER_SIZE);
|
if (head.size() < QOI_HEADER_SIZE) {
|
||||||
qsizetype readBytes = head.size();
|
|
||||||
device->rollbackTransaction();
|
|
||||||
|
|
||||||
if (readBytes < QOI_HEADER_SIZE) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,12 +426,7 @@ QVariant QOIHandler::option(ImageOption option) const
|
|||||||
if (IsSupported(header)) {
|
if (IsSupported(header)) {
|
||||||
v = QVariant::fromValue(QSize(header.Width, header.Height));
|
v = QVariant::fromValue(QSize(header.Width, header.Height));
|
||||||
} else if (auto d = device()) {
|
} else if (auto d = device()) {
|
||||||
// transactions works on both random and sequential devices
|
QDataStream s(d->peek(sizeof(QoiHeader)));
|
||||||
d->startTransaction();
|
|
||||||
auto ba = d->read(sizeof(QoiHeader));
|
|
||||||
d->rollbackTransaction();
|
|
||||||
|
|
||||||
QDataStream s(ba);
|
|
||||||
s.setByteOrder(QDataStream::BigEndian);
|
s.setByteOrder(QDataStream::BigEndian);
|
||||||
s >> header;
|
s >> header;
|
||||||
if (s.status() == QDataStream::Ok && IsSupported(header)) {
|
if (s.status() == QDataStream::Ok && IsSupported(header)) {
|
||||||
@ -449,12 +440,7 @@ QVariant QOIHandler::option(ImageOption option) const
|
|||||||
if (IsSupported(header)) {
|
if (IsSupported(header)) {
|
||||||
v = QVariant::fromValue(imageFormat(header));
|
v = QVariant::fromValue(imageFormat(header));
|
||||||
} else if (auto d = device()) {
|
} else if (auto d = device()) {
|
||||||
// transactions works on both random and sequential devices
|
QDataStream s(d->peek(sizeof(QoiHeader)));
|
||||||
d->startTransaction();
|
|
||||||
auto ba = d->read(sizeof(QoiHeader));
|
|
||||||
d->rollbackTransaction();
|
|
||||||
|
|
||||||
QDataStream s(ba);
|
|
||||||
s.setByteOrder(QDataStream::BigEndian);
|
s.setByteOrder(QDataStream::BigEndian);
|
||||||
s >> header;
|
s >> header;
|
||||||
if (s.status() == QDataStream::Ok && IsSupported(header)) {
|
if (s.status() == QDataStream::Ok && IsSupported(header)) {
|
||||||
|
@ -202,8 +202,6 @@ private:
|
|||||||
|
|
||||||
static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img)
|
static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img)
|
||||||
{
|
{
|
||||||
s.device()->seek(RasHeader::SIZE);
|
|
||||||
|
|
||||||
// The width of a scan line is always a multiple of 16 bits, padded when necessary.
|
// The width of a scan line is always a multiple of 16 bits, padded when necessary.
|
||||||
auto rasLineSize = (qint64(ras.Width) * ras.Depth + 7) / 8;
|
auto rasLineSize = (qint64(ras.Width) * ras.Depth + 7) / 8;
|
||||||
if (rasLineSize & 1)
|
if (rasLineSize & 1)
|
||||||
@ -368,18 +366,8 @@ bool RASHandler::canRead(QIODevice *device)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device->isSequential()) {
|
auto head = device->peek(RasHeader::SIZE); // header is exactly 32 bytes, always FIXME
|
||||||
// qWarning("Reading ras files from sequential devices not supported");
|
if (head.size() < RasHeader::SIZE) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
qint64 oldPos = device->pos();
|
|
||||||
QByteArray head = device->read(RasHeader::SIZE); // header is exactly 32 bytes, always FIXME
|
|
||||||
int readBytes = head.size(); // this should always be 32 bytes
|
|
||||||
|
|
||||||
device->seek(oldPos);
|
|
||||||
|
|
||||||
if (readBytes < RasHeader::SIZE) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,9 +399,7 @@ bool RASHandler::read(QImage *outImage)
|
|||||||
}
|
}
|
||||||
|
|
||||||
QImage img;
|
QImage img;
|
||||||
bool result = LoadRAS(s, ras, img);
|
if (!LoadRAS(s, ras, img)) {
|
||||||
|
|
||||||
if (result == false) {
|
|
||||||
// qDebug() << "Error loading RAS file.";
|
// qDebug() << "Error loading RAS file.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -443,12 +429,7 @@ QVariant RASHandler::option(ImageOption option) const
|
|||||||
v = QVariant::fromValue(QSize(header.Width, header.Height));
|
v = QVariant::fromValue(QSize(header.Width, header.Height));
|
||||||
}
|
}
|
||||||
else if (auto dev = device()) {
|
else if (auto dev = device()) {
|
||||||
// transactions works on both random and sequential devices
|
QDataStream s(dev->peek(RasHeader::SIZE));
|
||||||
dev->startTransaction();
|
|
||||||
auto ba = dev->read(RasHeader::SIZE);
|
|
||||||
dev->rollbackTransaction();
|
|
||||||
|
|
||||||
QDataStream s(ba);
|
|
||||||
s.setByteOrder(QDataStream::BigEndian);
|
s.setByteOrder(QDataStream::BigEndian);
|
||||||
s >> header;
|
s >> header;
|
||||||
if (s.status() == QDataStream::Ok && IsSupported(header)) {
|
if (s.status() == QDataStream::Ok && IsSupported(header)) {
|
||||||
@ -463,12 +444,7 @@ QVariant RASHandler::option(ImageOption option) const
|
|||||||
v = QVariant::fromValue(imageFormat(header));
|
v = QVariant::fromValue(imageFormat(header));
|
||||||
}
|
}
|
||||||
else if (auto dev = device()) {
|
else if (auto dev = device()) {
|
||||||
// transactions works on both random and sequential devices
|
QDataStream s(dev->peek(RasHeader::SIZE));
|
||||||
dev->startTransaction();
|
|
||||||
auto ba = dev->read(RasHeader::SIZE);
|
|
||||||
dev->rollbackTransaction();
|
|
||||||
|
|
||||||
QDataStream s(ba);
|
|
||||||
s.setByteOrder(QDataStream::BigEndian);
|
s.setByteOrder(QDataStream::BigEndian);
|
||||||
s >> header;
|
s >> header;
|
||||||
if (s.status() == QDataStream::Ok && IsSupported(header)) {
|
if (s.status() == QDataStream::Ok && IsSupported(header)) {
|
||||||
|
@ -578,30 +578,8 @@ bool SGIImagePrivate::isSupported() const
|
|||||||
|
|
||||||
bool SGIImagePrivate::peekHeader(QIODevice *device)
|
bool SGIImagePrivate::peekHeader(QIODevice *device)
|
||||||
{
|
{
|
||||||
qint64 pos = 0;
|
QDataStream ds(device->peek(512));
|
||||||
if (!device->isSequential()) {
|
return SGIImagePrivate::readHeader(ds, this) && isValid();
|
||||||
pos = device->pos();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ok = false;
|
|
||||||
QByteArray header;
|
|
||||||
{ // datastream is destroyed before working on device
|
|
||||||
header = device->read(512);
|
|
||||||
QDataStream ds(header);
|
|
||||||
ok = SGIImagePrivate::readHeader(ds, this) && isValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!device->isSequential()) {
|
|
||||||
return device->seek(pos) && ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sequential device undo
|
|
||||||
auto head = header.data();
|
|
||||||
auto readBytes = header.size();
|
|
||||||
while (readBytes > 0) {
|
|
||||||
device->ungetChar(head[readBytes-- - 1]);
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize SGIImagePrivate::size() const
|
QSize SGIImagePrivate::size() const
|
||||||
|
@ -195,26 +195,13 @@ static QImage::Format imageFormat(const TgaHeader &head)
|
|||||||
*/
|
*/
|
||||||
static bool peekHeader(QIODevice *device, TgaHeader &header)
|
static bool peekHeader(QIODevice *device, TgaHeader &header)
|
||||||
{
|
{
|
||||||
qint64 oldPos = device->pos();
|
auto head = device->peek(TgaHeader::SIZE);
|
||||||
QByteArray head = device->read(TgaHeader::SIZE);
|
if (head.size() < TgaHeader::SIZE) {
|
||||||
int readBytes = head.size();
|
|
||||||
|
|
||||||
if (device->isSequential()) {
|
|
||||||
for (int pos = readBytes - 1; pos >= 0; --pos) {
|
|
||||||
device->ungetChar(head[pos]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
device->seek(oldPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (readBytes < TgaHeader::SIZE) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDataStream stream(head);
|
QDataStream stream(head);
|
||||||
stream.setByteOrder(QDataStream::LittleEndian);
|
stream.setByteOrder(QDataStream::LittleEndian);
|
||||||
stream >> header;
|
stream >> header;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,22 +548,6 @@ bool TGAHandler::canRead(QIODevice *device)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 oldPos = device->pos();
|
|
||||||
QByteArray head = device->read(TgaHeader::SIZE);
|
|
||||||
int readBytes = head.size();
|
|
||||||
|
|
||||||
if (device->isSequential()) {
|
|
||||||
for (int pos = readBytes - 1; pos >= 0; --pos) {
|
|
||||||
device->ungetChar(head[pos]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
device->seek(oldPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (readBytes < TgaHeader::SIZE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TgaHeader tga;
|
TgaHeader tga;
|
||||||
if (!peekHeader(device, tga)) {
|
if (!peekHeader(device, tga)) {
|
||||||
qWarning("TGAHandler::canRead() error while reading the header");
|
qWarning("TGAHandler::canRead() error while reading the header");
|
||||||
|
@ -4229,66 +4229,43 @@ bool XCFHandler::canRead(QIODevice *device)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const qint64 oldPos = device->pos();
|
const qint64 oldPos = device->pos();
|
||||||
if (!device->isSequential()) {
|
|
||||||
QDataStream ds(device);
|
|
||||||
XCFImageFormat::XCFImage::Header header;
|
|
||||||
bool failed = !XCFImageFormat::readXCFHeader(ds, &header);
|
|
||||||
ds.setDevice(nullptr);
|
|
||||||
device->seek(oldPos);
|
|
||||||
if (failed) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (header.precision) {
|
QDataStream ds(device);
|
||||||
case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
|
XCFImageFormat::XCFImage::Header header;
|
||||||
case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
|
bool failed = !XCFImageFormat::readXCFHeader(ds, &header);
|
||||||
case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
|
ds.setDevice(nullptr);
|
||||||
case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
|
|
||||||
break;
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
|
|
||||||
case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
|
|
||||||
default:
|
|
||||||
qCDebug(XCFPLUGIN) << "unsupported precision" << header.precision;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
device->seek(oldPos);
|
||||||
}
|
if (failed) {
|
||||||
|
|
||||||
char head[8];
|
|
||||||
qint64 readBytes = device->read(head, sizeof(head));
|
|
||||||
if (readBytes != sizeof(head)) {
|
|
||||||
if (device->isSequential()) {
|
|
||||||
while (readBytes > 0) {
|
|
||||||
device->ungetChar(head[readBytes-- - 1]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
device->seek(oldPos);
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device->isSequential()) {
|
switch (header.precision) {
|
||||||
while (readBytes > 0) {
|
case XCFImageFormat::GIMP_PRECISION_HALF_LINEAR:
|
||||||
device->ungetChar(head[readBytes-- - 1]);
|
case XCFImageFormat::GIMP_PRECISION_HALF_NON_LINEAR:
|
||||||
}
|
case XCFImageFormat::GIMP_PRECISION_HALF_PERCEPTUAL:
|
||||||
} else {
|
case XCFImageFormat::GIMP_PRECISION_FLOAT_LINEAR:
|
||||||
device->seek(oldPos);
|
case XCFImageFormat::GIMP_PRECISION_FLOAT_NON_LINEAR:
|
||||||
|
case XCFImageFormat::GIMP_PRECISION_FLOAT_PERCEPTUAL:
|
||||||
|
case XCFImageFormat::GIMP_PRECISION_U8_LINEAR:
|
||||||
|
case XCFImageFormat::GIMP_PRECISION_U8_NON_LINEAR:
|
||||||
|
case XCFImageFormat::GIMP_PRECISION_U8_PERCEPTUAL:
|
||||||
|
case XCFImageFormat::GIMP_PRECISION_U16_LINEAR:
|
||||||
|
case XCFImageFormat::GIMP_PRECISION_U16_NON_LINEAR:
|
||||||
|
case XCFImageFormat::GIMP_PRECISION_U16_PERCEPTUAL:
|
||||||
|
case XCFImageFormat::GIMP_PRECISION_U32_LINEAR:
|
||||||
|
case XCFImageFormat::GIMP_PRECISION_U32_NON_LINEAR:
|
||||||
|
case XCFImageFormat::GIMP_PRECISION_U32_PERCEPTUAL:
|
||||||
|
break;
|
||||||
|
case XCFImageFormat::GIMP_PRECISION_DOUBLE_LINEAR:
|
||||||
|
case XCFImageFormat::GIMP_PRECISION_DOUBLE_NON_LINEAR:
|
||||||
|
case XCFImageFormat::GIMP_PRECISION_DOUBLE_PERCEPTUAL:
|
||||||
|
default:
|
||||||
|
qCDebug(XCFPLUGIN) << "unsupported precision" << header.precision;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return qstrncmp(head, "gimp xcf", 8) == 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QImageIOPlugin::Capabilities XCFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
QImageIOPlugin::Capabilities XCFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||||
|
Loading…
Reference in New Issue
Block a user