diff --git a/README.md b/README.md index 574daac..1c1f971 100644 --- a/README.md +++ b/README.md @@ -357,11 +357,15 @@ The plugin supports the following image data: type 4. - FORM PBM: PBM is a chunky version of IFF pictures. It supports 8-bit images with color map only. -- FORM IMAG (Compact Disc-Interactive): It supports CLut4, CLut8 and DYuv - formats. +- FORM IMAG (Compact Disc-Interactive): It supports CLut4, CLut7, CLut8 and + DYuv formats. - FOR4 CIMG (Maya Image File Format): It supports 24/48-bit RGB and 32/64-bit RGBA images. +> [!note] +> The plugin only supports the IFF, ILBM, and LBM file extensions. You'll +> need to rename files with different extensions to open them. + ### The JP2 plugin **This plugin can be disabled by setting `KIMAGEFORMATS_JP2` to `OFF` diff --git a/autotests/read/iff/cdi_dyuv_each.iff b/autotests/read/iff/cdi_dyuv_each.iff new file mode 100644 index 0000000..3dc7d7d Binary files /dev/null and b/autotests/read/iff/cdi_dyuv_each.iff differ diff --git a/autotests/read/iff/cdi_dyuv_each.iff.json b/autotests/read/iff/cdi_dyuv_each.iff.json new file mode 100644 index 0000000..9fa9798 --- /dev/null +++ b/autotests/read/iff/cdi_dyuv_each.iff.json @@ -0,0 +1,9 @@ +[ + { + "fileName" : "cdi_dyuv_each.iff", + "resolution" : { + "dotsPerMeterX" : 3937, + "dotsPerMeterY" : 5249 + } + } +] diff --git a/autotests/read/iff/cdi_dyuv_each.png b/autotests/read/iff/cdi_dyuv_each.png new file mode 100644 index 0000000..c26e738 Binary files /dev/null and b/autotests/read/iff/cdi_dyuv_each.png differ diff --git a/autotests/read/iff/cdi_dyuv_one.iff b/autotests/read/iff/cdi_dyuv_one.iff new file mode 100644 index 0000000..95f1efe Binary files /dev/null and b/autotests/read/iff/cdi_dyuv_one.iff differ diff --git a/autotests/read/iff/cdi_dyuv_one.png b/autotests/read/iff/cdi_dyuv_one.png new file mode 100644 index 0000000..7f40017 Binary files /dev/null and b/autotests/read/iff/cdi_dyuv_one.png differ diff --git a/src/imageformats/chunks.cpp b/src/imageformats/chunks.cpp index 6ab0a83..046f078 100644 --- a/src/imageformats/chunks.cpp +++ b/src/imageformats/chunks.cpp @@ -337,6 +337,8 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk * chunk = QSharedPointer(new XBMIChunk()); } else if (cid == XMP0_CHUNK) { chunk = QSharedPointer(new XMP0Chunk()); + } else if (cid == YUVS_CHUNK) { + chunk = QSharedPointer(new YUVSChunk()); } else { // unknown chunk chunk = QSharedPointer(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 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 diff --git a/src/imageformats/chunks_p.h b/src/imageformats/chunks_p.h index 55391ad..94854ff 100644 --- a/src/imageformats/chunks_p.h +++ b/src/imageformats/chunks_p.h @@ -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 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 diff --git a/src/imageformats/iff.cpp b/src/imageformats/iff.cpp index 422908c..f02d7a8 100644 --- a/src/imageformats/iff.cpp +++ b/src/imageformats/iff.cpp @@ -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(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(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(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(form); + auto yuvs = IFFChunk::searchT(form); for (auto y = 0, h = img.height(); y < h; ++y) { auto line = reinterpret_cast(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;