IFF: add support for CD-i YUVS chunk (and minor code improvements)

This commit is contained in:
Mirco Miranda
2026-01-15 12:39:16 +01:00
parent 8224c0099d
commit 99e4223393
9 changed files with 98 additions and 15 deletions

View File

@ -337,6 +337,8 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *
chunk = QSharedPointer<IFFChunk>(new XBMIChunk());
} else if (cid == XMP0_CHUNK) {
chunk = QSharedPointer<IFFChunk>(new XMP0Chunk());
} else if (cid == YUVS_CHUNK) {
chunk = QSharedPointer<IFFChunk>(new YUVSChunk());
} else { // unknown chunk
chunk = QSharedPointer<IFFChunk>(new IFFChunk());
qCDebug(LOG_IFFPLUGIN) << "IFFChunk::innerFromDevice(): unknown chunk" << cid;
@ -922,11 +924,11 @@ CAMGChunk::ModeIds BODYChunk::safeModeId(const BMHDChunk *header, const CAMGChun
auto cmapCount = cmap ? cmap->count() : 0;
auto bitplanes = header->bitplanes();
if (bitplanes >= BITPLANES_HALFBRIDE_MIN && bitplanes <= BITPLANES_HALFBRIDE_MAX) {
if (cmapCount == (1 << (header->bitplanes() - 1)))
if (cmapCount == (1 << (bitplanes - 1)))
return CAMGChunk::ModeIds(CAMGChunk::ModeId::HalfBrite);
}
if (bitplanes >= BITPLANES_HAM_MIN && bitplanes <= BITPLANES_HAM_MAX) {
if (cmapCount == (1 << (header->bitplanes() - 2)))
if (cmapCount == (1 << (bitplanes - 2)))
return CAMGChunk::ModeIds(CAMGChunk::ModeId::Ham);
}
return CAMGChunk::ModeIds();
@ -1488,13 +1490,13 @@ QImage::Format FORMChunk::cdiFormat() const
}
if (h->depth() == 8) {
if (h->model() == IHDRChunk::CLut8 || h->model() == IHDRChunk::CLut7) { // CLut7: no test case
if (h->model() == IHDRChunk::CLut8 || h->model() == IHDRChunk::CLut7) {
return QImage::Format_Indexed8;
}
if (h->model() == IHDRChunk::Rgb888) { // no test case
return FORMAT_RGB_8BIT;
}
if (h->model() == IHDRChunk::DYuv && h->yuvKind() == IHDRChunk::One) {
if (h->model() == IHDRChunk::DYuv) {
return FORMAT_RGB_8BIT;
}
}
@ -1938,7 +1940,7 @@ QImage RGBAChunk::compressedTile(QIODevice *d, const TBHDChunk *header) const
}
}
} else if (bpc == 2) {
auto cs = header->channels();
auto cs = std::max(1, header->channels());
if (cs < 4) { // alpha on 64-bit images must be 0xFF
std::memset(img.bits(), 0xFF, img.sizeInBytes());
}
@ -2512,7 +2514,7 @@ IHDRChunk::Yuv IHDRChunk::yuvStart() const
if (!isValid()) {
return{};
}
return(Yuv(data().at(11), data().at(12), data().at(13)));
return Yuv(data().at(11), data().at(12), data().at(13));
}
bool IHDRChunk::innerReadStructure(QIODevice *d)
@ -2683,6 +2685,44 @@ QList<QRgb> PLTEChunk::innerPalette() const
}
/* ******************
* *** YUVS Chunk ***
* ****************** */
YUVSChunk::~YUVSChunk()
{
}
YUVSChunk::YUVSChunk()
{
}
bool YUVSChunk::isValid() const
{
return chunkId() == YUVSChunk::defaultChunkId();
}
qint32 YUVSChunk::count() const
{
return dataBytes() / 3;
}
IHDRChunk::Yuv YUVSChunk::yuvStart(qint32 y) const
{
if (!isValid() || y >= count()) {
return{};
}
return IHDRChunk::Yuv(data().at(y * 3), data().at(y * 3 + 1), data().at(y * 3 + 2));
}
bool YUVSChunk::innerReadStructure(QIODevice *d)
{
return cacheData(d);
}
/* ******************
* *** IDAT Chunk ***
* ****************** */
@ -2721,9 +2761,8 @@ inline IPARChunk::Rgb yuvToRgb(IHDRChunk::Yuv yuv) {
}
QByteArray IDATChunk::strideRead(QIODevice *d, qint32 y, const IHDRChunk *header, const IPARChunk *params) const
QByteArray IDATChunk::strideRead(QIODevice *d, qint32 y, const IHDRChunk *header, const IPARChunk *params, const YUVSChunk *yuvs) const
{
Q_UNUSED(y)
Q_UNUSED(params)
if (!isValid() || header == nullptr || d == nullptr) {
return {};
@ -2765,6 +2804,10 @@ QByteArray IDATChunk::strideRead(QIODevice *d, qint32 y, const IHDRChunk *header
};
auto yuv = header->yuvStart();
if (header->yuvKind() == IHDRChunk::Each && yuvs) {
yuv = yuvs->yuvStart(y);
}
QByteArray tmp(header->width() * 3, char());
for (auto x = 0, w = header->width() - 1; x < w; x += 2) {
// nibble order from Green Book Cap. V Par. 6.5.1.1

View File

@ -60,6 +60,7 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
#define IPAR_CHUNK QByteArray("IPAR")
#define PLTE_CHUNK QByteArray("PLTE")
#define XBMI_CHUNK QByteArray("XBMI")
#define YUVS_CHUNK QByteArray("YUVS")
// Different palette for scanline
#define BEAM_CHUNK QByteArray("BEAM")
@ -1630,6 +1631,31 @@ protected:
virtual QList<QRgb> innerPalette() const override;
};
/*!
* \brief The YUVSChunk class
*/
class YUVSChunk : public IFFChunk
{
public:
virtual ~YUVSChunk() override;
YUVSChunk();
YUVSChunk(const YUVSChunk& other) = default;
YUVSChunk& operator =(const YUVSChunk& other) = default;
virtual bool isValid() const override;
qint32 count() const;
IHDRChunk::Yuv yuvStart(qint32 y) const;
CHUNKID_DEFINE(YUVS_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The IDATChunk class
*/
@ -1657,7 +1683,8 @@ public:
QByteArray strideRead(QIODevice *d,
qint32 y,
const IHDRChunk *header,
const IPARChunk *params = nullptr) const;
const IPARChunk *params = nullptr,
const YUVSChunk *yuvs = nullptr) const;
/*!
* \brief resetStrideRead

View File

@ -334,7 +334,6 @@ bool IFFHandler::readStandardImage(QImage *image)
// show the first one (I don't have a sample with many images)
auto headers = IFFChunk::searchT<BMHDChunk>(form);
if (headers.isEmpty()) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage(): no supported image found";
return false;
}
@ -433,7 +432,6 @@ bool IFFHandler::readMayaImage(QImage *image)
// show the first one (I don't have a sample with many images)
auto headers = IFFChunk::searchT<TBHDChunk>(form);
if (headers.isEmpty()) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage(): no supported image found";
return false;
}
@ -494,7 +492,6 @@ bool IFFHandler::readCDIImage(QImage *image)
// show the first one (I don't have a sample with many images)
auto headers = IFFChunk::searchT<IHDRChunk>(form);
if (headers.isEmpty()) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readCDIImage(): no supported image found";
return false;
}
@ -525,9 +522,12 @@ bool IFFHandler::readCDIImage(QImage *image)
return false;
}
auto pars = IFFChunk::searchT<IPARChunk>(form);
auto yuvs = IFFChunk::searchT<YUVSChunk>(form);
for (auto y = 0, h = img.height(); y < h; ++y) {
auto line = reinterpret_cast<char*>(img.scanLine(y));
auto ba = body->strideRead(device(), y, header, pars.isEmpty() ? nullptr : pars.first());
auto ba = body->strideRead(device(), y, header,
pars.isEmpty() ? nullptr : pars.first(),
yuvs.isEmpty() ? nullptr : yuvs.first());
if (ba.isEmpty()) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readCDIImage(): error while reading image scanline";
return false;