mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2026-02-24 00:12:59 -05:00
Decode Atari ST VDAT chunks
This commit is contained in:
@ -331,6 +331,8 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *
|
|||||||
chunk = QSharedPointer<IFFChunk>(new SHAMChunk());
|
chunk = QSharedPointer<IFFChunk>(new SHAMChunk());
|
||||||
} else if (cid == TBHD_CHUNK) {
|
} else if (cid == TBHD_CHUNK) {
|
||||||
chunk = QSharedPointer<IFFChunk>(new TBHDChunk());
|
chunk = QSharedPointer<IFFChunk>(new TBHDChunk());
|
||||||
|
} else if (cid == VDAT_CHUNK) {
|
||||||
|
chunk = QSharedPointer<IFFChunk>(new VDATChunk());
|
||||||
} else if (cid == VERS_CHUNK) {
|
} else if (cid == VERS_CHUNK) {
|
||||||
chunk = QSharedPointer<IFFChunk>(new VERSChunk());
|
chunk = QSharedPointer<IFFChunk>(new VERSChunk());
|
||||||
} else if (cid == XBMI_CHUNK) {
|
} else if (cid == XBMI_CHUNK) {
|
||||||
@ -853,6 +855,23 @@ inline qint64 rgbNDecompress(QIODevice *input, char *output, qint64 olen)
|
|||||||
return j;
|
return j;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline qint64 vdatDecompress(const IFFChunk *chunk, const BMHDChunk *header, qint32 y, char *output, qint64 olen)
|
||||||
|
{
|
||||||
|
auto vdats = IFFChunk::searchT<VDATChunk>(chunk);
|
||||||
|
auto rowLen = header->rowLen();
|
||||||
|
if (olen < rowLen * vdats.size()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (qint32 i = 0, n = vdats.size(); i < n; ++i) {
|
||||||
|
auto&& uc = vdats.at(i)->uncompressedData(header);
|
||||||
|
if (y * rowLen > uc.size() - rowLen)
|
||||||
|
return -1;
|
||||||
|
std::memcpy(output + (i * rowLen), uc.data() + (y * rowLen), rowLen);
|
||||||
|
}
|
||||||
|
return vdats.size() * rowLen;
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray BODYChunk::strideRead(QIODevice *d, qint32 y, const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap, const IPALChunk *ipal, const QByteArray& formType) const
|
QByteArray BODYChunk::strideRead(QIODevice *d, qint32 y, const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap, const IPALChunk *ipal, const QByteArray& formType) const
|
||||||
{
|
{
|
||||||
if (!isValid() || header == nullptr || d == nullptr) {
|
if (!isValid() || header == nullptr || d == nullptr) {
|
||||||
@ -878,6 +897,8 @@ QByteArray BODYChunk::strideRead(QIODevice *d, qint32 y, const BMHDChunk *header
|
|||||||
// WARNING: The online spec says it's the same as TIFF but that's
|
// WARNING: The online spec says it's the same as TIFF but that's
|
||||||
// not accurate: the RLE -128 code is not a noop.
|
// not accurate: the RLE -128 code is not a noop.
|
||||||
rr = packbitsDecompress(d, buf.data(), buf.size(), true);
|
rr = packbitsDecompress(d, buf.data(), buf.size(), true);
|
||||||
|
} else if (header->compression() == BMHDChunk::Compression::Vdat) {
|
||||||
|
rr = vdatDecompress(this, header, y, buf.data(), buf.size());
|
||||||
} else if (header->compression() == BMHDChunk::Compression::RgbN8) {
|
} else if (header->compression() == BMHDChunk::Compression::RgbN8) {
|
||||||
if (isRgb8)
|
if (isRgb8)
|
||||||
rr = rgb8Decompress(d, buf.data(), buf.size());
|
rr = rgb8Decompress(d, buf.data(), buf.size());
|
||||||
@ -984,6 +1005,15 @@ QByteArray BODYChunk::rgbN(const QByteArray &planes, qint32, const BMHDChunk *he
|
|||||||
return planes;
|
return planes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BODYChunk::innerReadStructure(QIODevice *d)
|
||||||
|
{
|
||||||
|
auto ok = true;
|
||||||
|
if (d->peek(4) == VDAT_CHUNK) {
|
||||||
|
setChunks(IFFChunk::innerFromDevice(d, &ok, this));
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray BODYChunk::deinterleave(const QByteArray &planes, qint32 y, const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap, const IPALChunk *ipal) const
|
QByteArray BODYChunk::deinterleave(const QByteArray &planes, qint32 y, const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap, const IPALChunk *ipal) const
|
||||||
{
|
{
|
||||||
if (planes.size() != strideSize(header, ILBM_FORM_TYPE)) {
|
if (planes.size() != strideSize(header, ILBM_FORM_TYPE)) {
|
||||||
@ -2371,6 +2401,123 @@ bool NAMEChunk::innerReadStructure(QIODevice *d)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ******************
|
||||||
|
* *** VDAT Chunk ***
|
||||||
|
* ****************** */
|
||||||
|
|
||||||
|
VDATChunk::~VDATChunk()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
VDATChunk::VDATChunk()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VDATChunk::isValid() const
|
||||||
|
{
|
||||||
|
return chunkId() == VDATChunk::defaultChunkId();
|
||||||
|
}
|
||||||
|
|
||||||
|
static QByteArray decompressVdat(const QByteArray &comp)
|
||||||
|
{
|
||||||
|
QByteArray out;
|
||||||
|
auto ok = true;
|
||||||
|
auto cpos = 0;
|
||||||
|
|
||||||
|
auto readU16BE = [&](const QByteArray &src, int &pos, bool *ok) -> quint16 {
|
||||||
|
if (pos + 2 > src.size()) {
|
||||||
|
*ok = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
auto v = quint16((quint8(src[pos]) << 8) | quint8(src[pos + 1]));
|
||||||
|
pos += 2;
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto readI8 = [&](const QByteArray &src, int &pos, bool *ok) -> qint8 {
|
||||||
|
if (pos >= src.size()) {
|
||||||
|
*ok = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return qint8(src[pos++]);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto emitWord = [&](quint16 w) {
|
||||||
|
out.append(char(w & 0xFF));
|
||||||
|
out.append(char(w >> 8));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto cmdCnt = readU16BE(comp, cpos, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
return{};
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode command stream
|
||||||
|
auto dpos = cmdCnt + (cmdCnt & 1);
|
||||||
|
for (auto n = cmdCnt; cpos < n && dpos < comp.size() && ok;) {
|
||||||
|
auto cmd = readI8(comp, cpos, &ok);
|
||||||
|
if (cmd == 0) {
|
||||||
|
auto count = readU16BE(comp, dpos, &ok);
|
||||||
|
for (auto i = 0; i < count; ++i)
|
||||||
|
emitWord(readU16BE(comp, dpos, &ok));
|
||||||
|
} else if (cmd == 1) {
|
||||||
|
auto count = readU16BE(comp, dpos, &ok);
|
||||||
|
auto value = readU16BE(comp, dpos, &ok);
|
||||||
|
for (auto i = 0; i < count; ++i)
|
||||||
|
emitWord(value);
|
||||||
|
} else if (cmd < 0) {
|
||||||
|
auto count = -qint32(cmd);
|
||||||
|
for (auto i = 0; i < count; ++i)
|
||||||
|
emitWord(readU16BE(comp, dpos, &ok));
|
||||||
|
} else {
|
||||||
|
auto count = quint16(cmd);
|
||||||
|
auto value = readU16BE(comp, dpos, &ok);
|
||||||
|
for (auto i = 0; i < count; ++i)
|
||||||
|
emitWord(value);
|
||||||
|
}
|
||||||
|
if (!ok) {
|
||||||
|
return{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QByteArray vdatToIlbmPlane(const QByteArray &vdatData, const BMHDChunk *header)
|
||||||
|
{
|
||||||
|
QByteArray ba(vdatData.size(), char());
|
||||||
|
auto rowLen = header->rowLen();
|
||||||
|
for (auto x = 0, n = 0; x < rowLen; x += 2) {
|
||||||
|
for (auto y = 0, off = x, h = header->height(); y < h; y++, off += rowLen) {
|
||||||
|
if ((off + 1 >= ba.size()) || n + 1 >= vdatData.size()) {
|
||||||
|
return{};
|
||||||
|
}
|
||||||
|
ba[off + 1] = vdatData.at(n++);
|
||||||
|
ba[off] = vdatData.at(n++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray &VDATChunk::uncompressedData(const BMHDChunk *header) const
|
||||||
|
{
|
||||||
|
if (uncompressed.isEmpty()) {
|
||||||
|
auto tmp = decompressVdat(data());
|
||||||
|
if (tmp.size() == header->rowLen() * header->height()) {
|
||||||
|
uncompressed = vdatToIlbmPlane(tmp, header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uncompressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool VDATChunk::innerReadStructure(QIODevice *d)
|
||||||
|
{
|
||||||
|
return cacheData(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ******************
|
/* ******************
|
||||||
* *** VERS Chunk ***
|
* *** VERS Chunk ***
|
||||||
* ****************** */
|
* ****************** */
|
||||||
|
|||||||
@ -84,6 +84,7 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
|
|||||||
#define FVER_CHUNK QByteArray("FVER")
|
#define FVER_CHUNK QByteArray("FVER")
|
||||||
#define HIST_CHUNK QByteArray("HIST")
|
#define HIST_CHUNK QByteArray("HIST")
|
||||||
#define NAME_CHUNK QByteArray("NAME")
|
#define NAME_CHUNK QByteArray("NAME")
|
||||||
|
#define VDAT_CHUNK QByteArray("VDAT")
|
||||||
#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
|
||||||
|
|
||||||
@ -431,7 +432,8 @@ public:
|
|||||||
enum Compression {
|
enum Compression {
|
||||||
Uncompressed = 0, /**< Image data are uncompressed. */
|
Uncompressed = 0, /**< Image data are uncompressed. */
|
||||||
Rle = 1, /**< Image data are RLE compressed. */
|
Rle = 1, /**< Image data are RLE compressed. */
|
||||||
RgbN8 = 4 /**< RGB8/RGBN compresson. */
|
Vdat = 2, /**< Image data are VDAT compressed. */
|
||||||
|
RgbN8 = 4 /**< Image data are RGB8/RGBN compressed. */
|
||||||
};
|
};
|
||||||
enum Masking {
|
enum Masking {
|
||||||
None = 0, /**< Designates an opaque rectangular image. */
|
None = 0, /**< Designates an opaque rectangular image. */
|
||||||
@ -815,6 +817,8 @@ protected:
|
|||||||
|
|
||||||
QByteArray rgbN(const QByteArray &planes, qint32 y, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, const IPALChunk *ipal = nullptr) const;
|
QByteArray rgbN(const QByteArray &planes, qint32 y, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, const IPALChunk *ipal = nullptr) const;
|
||||||
|
|
||||||
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable QByteArray _readBuffer;
|
mutable QByteArray _readBuffer;
|
||||||
};
|
};
|
||||||
@ -1369,6 +1373,32 @@ protected:
|
|||||||
virtual bool innerReadStructure(QIODevice *d) override;
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The VDATChunk class
|
||||||
|
*/
|
||||||
|
class VDATChunk : public IFFChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~VDATChunk() override;
|
||||||
|
VDATChunk();
|
||||||
|
VDATChunk(const VDATChunk& other) = default;
|
||||||
|
VDATChunk& operator =(const VDATChunk& other) = default;
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(VDAT_CHUNK)
|
||||||
|
|
||||||
|
const QByteArray &uncompressedData(const BMHDChunk *header) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable QByteArray uncompressed;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The VERSChunk class
|
* \brief The VERSChunk class
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user