iff: Support ILBM (PBM)

It's another variant of IFF where the pixel data is stored
contiguously rather than interleaved.
This commit is contained in:
Kai Uwe Broulik
2025-07-06 16:13:50 +02:00
parent ea1983a7d1
commit f5494d9acf
4 changed files with 34 additions and 17 deletions

View File

@ -547,9 +547,9 @@ bool BODYChunk::isValid() const
return chunkId() == BODYChunk::defaultChunkId();
}
QByteArray BODYChunk::strideRead(QIODevice *d, const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap) const
QByteArray BODYChunk::strideRead(QIODevice *d, const FORMChunk *form, const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap) const
{
if (!isValid() || header == nullptr) {
if (!isValid() || form == nullptr || header == nullptr) {
return {};
}
@ -571,7 +571,11 @@ QByteArray BODYChunk::strideRead(QIODevice *d, const BMHDChunk *header, const CA
auto planes = _readBuffer.left(readSize);
_readBuffer.remove(0, readSize);
return BODYChunk::deinterleave(planes, header, camg, cmap);
if (form->formType() == FORMChunk::FormType::Pbm) {
return planes;
} else {
return BODYChunk::deinterleave(planes, header, camg, cmap);
}
}
bool BODYChunk::resetStrideRead(QIODevice *d) const
@ -773,15 +777,21 @@ bool FORMChunk::innerReadStructure(QIODevice *d)
if (bytes() < 4) {
return false;
}
_type = d->read(4);
auto ok = true;
if (_type == QByteArray("ILBM")) {
const auto type = d->read(4);
auto ok = false;
if (type == QByteArrayLiteral("ILBM")) {
_type = FormType::Ilbm;
} else if (type == QByteArrayLiteral("PBM ")) {
_type = FormType::Pbm;
}
if (_type != FormType::Unknown) {
setChunks(IFFChunk::innerFromDevice(d, &ok, alignBytes(), recursionCounter()));
}
return ok;
}
QByteArray FORMChunk::formType() const
FORMChunk::FormType FORMChunk::formType() const
{
return _type;
}

View File

@ -451,6 +451,7 @@ protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
class FORMChunk;
/*!
* \brief The BODYChunk class
@ -476,7 +477,7 @@ public:
* \return The scanline as requested for QImage.
* \warning Call resetStrideRead() once before this one.
*/
QByteArray strideRead(QIODevice *d, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr) const;
QByteArray strideRead(QIODevice *d, const FORMChunk *form, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr) const;
/*!
* \brief resetStrideRead
@ -500,9 +501,13 @@ private:
*/
class FORMChunk : public IFFChunk
{
QByteArray _type;
public:
enum class FormType {
Unknown,
Ilbm,
Pbm,
};
virtual ~FORMChunk() override;
FORMChunk();
FORMChunk(const FORMChunk& other) = default;
@ -512,7 +517,7 @@ public:
bool isSupported() const;
QByteArray formType() const;
FormType formType() const;
QImage::Format format() const;
@ -522,8 +527,10 @@ public:
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
private:
FormType _type = FormType::Unknown;
};
/*!
* \brief The FOR4Chunk class

View File

@ -74,7 +74,7 @@ IFFHandler::IFFHandler()
bool IFFHandler::canRead() const
{
if (canRead(device())) {
setFormat("iff");
setFormat("iff"); // TODO ilbm based on header?
return true;
}
return false;
@ -192,7 +192,7 @@ bool IFFHandler::readStandardImage(QImage *image)
}
for (auto y = 0, h = img.height(); y < h; ++y) {
auto line = reinterpret_cast<char*>(img.scanLine(y));
auto ba = body->strideRead(device(), header, camg, cmap);
auto ba = body->strideRead(device(), form, header, camg, cmap);
if (ba.isEmpty()) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage() error while reading image scanline";
return false;
@ -334,7 +334,7 @@ QVariant IFFHandler::option(ImageOption option) const
QImageIOPlugin::Capabilities IFFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
{
if (format == "iff") {
if (format == "iff" || format == "ilbm") {
return Capabilities(CanRead);
}
if (!format.isEmpty()) {

View File

@ -1,4 +1,4 @@
{
"Keys": [ "iff" ],
"MimeTypes": [ "application/x-iff" ]
"Keys": [ "iff", "ilbm" ],
"MimeTypes": [ "application/x-iff", "application/x-ilbm" ]
}