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 "packbits_p.h"
#include <QBuffer>
#include <QDebug>
#include <QLoggingCategory>
@ -243,7 +244,9 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *
for (; !d->atEnd() && (nextChunkPos == 0 || d->pos() < nextChunkPos);) {
auto cid = d->peek(4);
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());
} else if (cid == AUTH_CHUNK) {
chunk = QSharedPointer<IFFChunk>(new AUTHChunk());
@ -285,7 +288,7 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *
chunk = QSharedPointer<IFFChunk>(new XMP0Chunk());
} else { // unknown chunk
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)
@ -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
{
if (!isValid() || header == nullptr) {
if (!isValid() || header == nullptr || d == nullptr) {
return {};
}
@ -849,6 +852,68 @@ QByteArray BODYChunk::deinterleave(const QByteArray &planes, const BMHDChunk *he
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 ***
* ****************** */
@ -883,6 +948,8 @@ bool FORMChunk::innerReadStructure(QIODevice *d)
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
} else if (_type == PBM__FORM_TYPE) {
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
} else if (_type == ACBM_FORM_TYPE) {
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
}
return ok;
}

View File

@ -44,6 +44,7 @@
#define PRO8_CHUNK QByteArray("PRO8")
// FORM ILBM IFF
#define ABIT_CHUNK QByteArray("ABIT")
#define BMHD_CHUNK QByteArray("BMHD")
#define BODY_CHUNK QByteArray("BODY")
#define CAMG_CHUNK QByteArray("CAMG")
@ -70,6 +71,7 @@
#define VERS_CHUNK QByteArray("VERS")
#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 PBM__FORM_TYPE QByteArray("PBM ")
#define CIMG_FOR4_TYPE QByteArray("CIMG")
@ -573,20 +575,21 @@ 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, 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
* 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 camg The CAMG chunk (optional)
* \return True on success, otherwise false.
* \sa strideRead
* \note Must be called once before strideRead().
*/
bool resetStrideRead(QIODevice *d) const;
virtual bool resetStrideRead(QIODevice *d) const;
private:
protected:
/*!
* \brief strideSize
* \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;
private:
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
*/

View File

@ -91,6 +91,17 @@ bool IFFHandler::canRead(QIODevice *device)
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 pos = device->pos();
auto chunks = IFFChunk::fromDevice(device, &ok);
@ -220,6 +231,11 @@ bool IFFHandler::readStandardImage(QImage *image)
}
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()) {
img.fill(0);
} else {