mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-07-26 22:04:18 -04:00
IFF: add support for uncompressed ACBM type
This commit is contained in:
BIN
autotests/read/iff/testcard_acbm.iff
Normal file
BIN
autotests/read/iff/testcard_acbm.iff
Normal file
Binary file not shown.
BIN
autotests/read/iff/testcard_acbm.png
Normal file
BIN
autotests/read/iff/testcard_acbm.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user