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 "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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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 {
|
||||||
|
Reference in New Issue
Block a user