IFF: add support for uncompressed ACBM type

This commit is contained in:
Mirco Miranda
2025-07-24 07:37:58 +02:00
parent 16b669d63d
commit fd864e6f55
5 changed files with 119 additions and 7 deletions

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -8,6 +8,7 @@
#include "chunks_p.h" #include "chunks_p.h"
#include "packbits_p.h" #include "packbits_p.h"
#include <QBuffer>
#include <QDebug> #include <QDebug>
#include <QLoggingCategory> #include <QLoggingCategory>
@ -243,7 +244,9 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *
for (; !d->atEnd() && (nextChunkPos == 0 || d->pos() < nextChunkPos);) { for (; !d->atEnd() && (nextChunkPos == 0 || d->pos() < nextChunkPos);) {
auto cid = d->peek(4); auto cid = d->peek(4);
QSharedPointer<IFFChunk> chunk; QSharedPointer<IFFChunk> chunk;
if (cid == ANNO_CHUNK) { if (cid == ABIT_CHUNK) {
chunk = QSharedPointer<IFFChunk>(new ABITChunk());
} else if (cid == ANNO_CHUNK) {
chunk = QSharedPointer<IFFChunk>(new ANNOChunk()); chunk = QSharedPointer<IFFChunk>(new ANNOChunk());
} else if (cid == AUTH_CHUNK) { } else if (cid == AUTH_CHUNK) {
chunk = QSharedPointer<IFFChunk>(new AUTHChunk()); chunk = QSharedPointer<IFFChunk>(new AUTHChunk());
@ -285,7 +288,7 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *
chunk = QSharedPointer<IFFChunk>(new XMP0Chunk()); chunk = QSharedPointer<IFFChunk>(new XMP0Chunk());
} else { // unknown chunk } else { // unknown chunk
chunk = QSharedPointer<IFFChunk>(new IFFChunk()); chunk = QSharedPointer<IFFChunk>(new IFFChunk());
qCInfo(LOG_IFFPLUGIN) << "IFFChunk::innerFromDevice: unknown chunk" << cid; qCDebug(LOG_IFFPLUGIN) << "IFFChunk::innerFromDevice: unknown chunk" << cid;
} }
// change the alignment to the one of main chunk (required for unknown Maya IFF chunks) // change the alignment to the one of main chunk (required for unknown Maya IFF chunks)
@ -596,7 +599,7 @@ bool BODYChunk::isValid() const
QByteArray BODYChunk::strideRead(QIODevice *d, const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap, bool isPbm) const QByteArray BODYChunk::strideRead(QIODevice *d, const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap, bool isPbm) const
{ {
if (!isValid() || header == nullptr) { if (!isValid() || header == nullptr || d == nullptr) {
return {}; return {};
} }
@ -849,6 +852,68 @@ QByteArray BODYChunk::deinterleave(const QByteArray &planes, const BMHDChunk *he
return ba; return ba;
} }
/* ******************
* *** ABIT Chunk ***
* ****************** */
ABITChunk::~ABITChunk()
{
}
ABITChunk::ABITChunk() : BODYChunk()
{
}
bool ABITChunk::isValid() const
{
return chunkId() == ABITChunk::defaultChunkId();
}
QByteArray ABITChunk::strideRead(QIODevice *d, const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap, bool isPbm) const
{
if (!isValid() || header == nullptr || d == nullptr) {
return {};
}
if (header->compression() != BMHDChunk::Compression::Uncompressed || isPbm) {
return {};
}
// convert ABIT data to an ILBM line on the fly
auto ilbmLine = QByteArray(strideSize(header, isPbm), char());
auto rowSize = header->rowLen();
auto height = header->height();
if (_y >= height) {
return {};
}
for (qint32 plane = 0, planes = qint32(header->bitplanes()); plane < planes; ++plane) {
if (!seek(d, qint64(plane) * rowSize * height + _y * rowSize))
return {};
auto offset = qint64(plane) * rowSize;
if (offset + rowSize > ilbmLine.size())
return {};
if (d->read(ilbmLine.data() + offset, rowSize) != rowSize)
return {};
}
// next line on the next run
++_y;
// decode the ILBM line
QBuffer buf;
buf.setData(ilbmLine);
if (!buf.open(QBuffer::ReadOnly)) {
return {};
}
return BODYChunk::strideRead(&buf, header, camg, cmap, isPbm);
}
bool ABITChunk::resetStrideRead(QIODevice *d) const
{
_y = 0;
return BODYChunk::resetStrideRead(d);
}
/* ****************** /* ******************
* *** FORM Chunk *** * *** FORM Chunk ***
* ****************** */ * ****************** */
@ -883,6 +948,8 @@ bool FORMChunk::innerReadStructure(QIODevice *d)
setChunks(IFFChunk::innerFromDevice(d, &ok, this)); setChunks(IFFChunk::innerFromDevice(d, &ok, this));
} else if (_type == PBM__FORM_TYPE) { } else if (_type == PBM__FORM_TYPE) {
setChunks(IFFChunk::innerFromDevice(d, &ok, this)); setChunks(IFFChunk::innerFromDevice(d, &ok, this));
} else if (_type == ACBM_FORM_TYPE) {
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
} }
return ok; return ok;
} }

View File

@ -44,6 +44,7 @@
#define PRO8_CHUNK QByteArray("PRO8") #define PRO8_CHUNK QByteArray("PRO8")
// FORM ILBM IFF // FORM ILBM IFF
#define ABIT_CHUNK QByteArray("ABIT")
#define BMHD_CHUNK QByteArray("BMHD") #define BMHD_CHUNK QByteArray("BMHD")
#define BODY_CHUNK QByteArray("BODY") #define BODY_CHUNK QByteArray("BODY")
#define CAMG_CHUNK QByteArray("CAMG") #define CAMG_CHUNK QByteArray("CAMG")
@ -70,6 +71,7 @@
#define VERS_CHUNK QByteArray("VERS") #define VERS_CHUNK QByteArray("VERS")
#define XMP0_CHUNK QByteArray("XMP0") // https://aminet.net/package/docs/misc/IFF-metadata #define XMP0_CHUNK QByteArray("XMP0") // https://aminet.net/package/docs/misc/IFF-metadata
#define ACBM_FORM_TYPE QByteArray("ACBM")
#define ILBM_FORM_TYPE QByteArray("ILBM") #define ILBM_FORM_TYPE QByteArray("ILBM")
#define PBM__FORM_TYPE QByteArray("PBM ") #define PBM__FORM_TYPE QByteArray("PBM ")
#define CIMG_FOR4_TYPE QByteArray("CIMG") #define CIMG_FOR4_TYPE QByteArray("CIMG")
@ -573,20 +575,21 @@ public:
* \return The scanline as requested for QImage. * \return The scanline as requested for QImage.
* \warning Call resetStrideRead() once before this one. * \warning Call resetStrideRead() once before this one.
*/ */
QByteArray strideRead(QIODevice *d, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, bool isPbm = false) const; virtual QByteArray strideRead(QIODevice *d, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, bool isPbm = false) const;
/*! /*!
* \brief resetStrideRead * \brief resetStrideRead
* Reset the stride read set the position at the beginning of the data and reset all buffers. * Reset the stride read set the position at the beginning of the data and reset all buffers.
* \param d The device * \param d The device.
* \param header The BMHDChunk chunk (mandatory) * \param header The BMHDChunk chunk (mandatory)
* \param camg The CAMG chunk (optional) * \param camg The CAMG chunk (optional)
* \return True on success, otherwise false. * \return True on success, otherwise false.
* \sa strideRead * \sa strideRead
* \note Must be called once before strideRead().
*/ */
bool resetStrideRead(QIODevice *d) const; virtual bool resetStrideRead(QIODevice *d) const;
private: protected:
/*! /*!
* \brief strideSize * \brief strideSize
* \param isPbm Set true if the image is PBM. * \param isPbm Set true if the image is PBM.
@ -596,9 +599,35 @@ private:
QByteArray deinterleave(const QByteArray &planes, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, bool isPbm = false) const; QByteArray deinterleave(const QByteArray &planes, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, bool isPbm = false) const;
private:
mutable QByteArray _readBuffer; mutable QByteArray _readBuffer;
}; };
/*!
* \brief The ABITChunk class
*/
class ABITChunk : public BODYChunk
{
public:
virtual ~ABITChunk() override;
ABITChunk();
ABITChunk(const ABITChunk& other) = default;
ABITChunk& operator =(const ABITChunk& other) = default;
virtual bool isValid() const override;
CHUNKID_DEFINE(ABIT_CHUNK)
virtual QByteArray strideRead(QIODevice *d, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, bool isPbm = false) const override;
virtual bool resetStrideRead(QIODevice *d) const override;
private:
mutable qint32 _y;
};
/*! /*!
* \brief The FORMChunk class * \brief The FORMChunk class
*/ */

View File

@ -91,6 +91,17 @@ bool IFFHandler::canRead(QIODevice *device)
return false; return false;
} }
// I avoid parsing obviously incorrect files
auto cid = device->peek(4);
if (cid != CAT__CHUNK &&
cid != FORM_CHUNK &&
cid != LIST_CHUNK &&
cid != CAT4_CHUNK &&
cid != FOR4_CHUNK &&
cid != LIS4_CHUNK) {
return false;
}
auto ok = false; auto ok = false;
auto pos = device->pos(); auto pos = device->pos();
auto chunks = IFFChunk::fromDevice(device, &ok); auto chunks = IFFChunk::fromDevice(device, &ok);
@ -220,6 +231,11 @@ bool IFFHandler::readStandardImage(QImage *image)
} }
auto bodies = IFFChunk::searchT<BODYChunk>(form); auto bodies = IFFChunk::searchT<BODYChunk>(form);
if (bodies.isEmpty()) {
auto abits = IFFChunk::searchT<ABITChunk>(form);
for (auto &&abit : abits)
bodies.append(abit);
}
if (bodies.isEmpty()) { if (bodies.isEmpty()) {
img.fill(0); img.fill(0);
} else { } else {