From f5494d9acf986e9cd86f3ae5d3bad4b0d1549f26 Mon Sep 17 00:00:00 2001 From: Kai Uwe Broulik Date: Sun, 6 Jul 2025 16:13:50 +0200 Subject: [PATCH] iff: Support ILBM (PBM) It's another variant of IFF where the pixel data is stored contiguously rather than interleaved. --- src/imageformats/chunks.cpp | 24 +++++++++++++++++------- src/imageformats/chunks_p.h | 17 ++++++++++++----- src/imageformats/iff.cpp | 6 +++--- src/imageformats/iff.json | 4 ++-- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/imageformats/chunks.cpp b/src/imageformats/chunks.cpp index 6344ca9..df15fd6 100644 --- a/src/imageformats/chunks.cpp +++ b/src/imageformats/chunks.cpp @@ -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; } diff --git a/src/imageformats/chunks_p.h b/src/imageformats/chunks_p.h index c77deaa..69f42d9 100644 --- a/src/imageformats/chunks_p.h +++ b/src/imageformats/chunks_p.h @@ -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 diff --git a/src/imageformats/iff.cpp b/src/imageformats/iff.cpp index 2ffed32..f4a1d96 100644 --- a/src/imageformats/iff.cpp +++ b/src/imageformats/iff.cpp @@ -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(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()) { diff --git a/src/imageformats/iff.json b/src/imageformats/iff.json index a64b8cb..f1ea9d4 100644 --- a/src/imageformats/iff.json +++ b/src/imageformats/iff.json @@ -1,4 +1,4 @@ { - "Keys": [ "iff" ], - "MimeTypes": [ "application/x-iff" ] + "Keys": [ "iff", "ilbm" ], + "MimeTypes": [ "application/x-iff", "application/x-ilbm" ] }