IFF: Fix halfbride detection, 1-bitplane colors and PBM line size calculation. It also ignore ZBuffer flag on Maya images (like Photoshop does) amd adds CMYK palette support.

This commit is contained in:
Mirco Miranda
2025-08-01 15:29:52 +02:00
parent cd39c36621
commit c2a1d4b401
7 changed files with 290 additions and 75 deletions

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -9,6 +9,7 @@
#include "packbits_p.h" #include "packbits_p.h"
#include <QBuffer> #include <QBuffer>
#include <QColor>
#include <QDebug> #include <QDebug>
#include <QLoggingCategory> #include <QLoggingCategory>
@ -16,6 +17,14 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
#define RECURSION_PROTECTION 10 #define RECURSION_PROTECTION 10
static QString dataToString(const IFFChunk *chunk)
{
if (chunk == nullptr || !chunk->isValid()) {
return {};
}
return QString::fromUtf8(chunk->data()).replace(QStringLiteral("\0"), QStringLiteral(" ")).trimmed();
}
IFFChunk::~IFFChunk() IFFChunk::~IFFChunk()
{ {
@ -258,6 +267,8 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *
chunk = QSharedPointer<IFFChunk>(new CAMGChunk()); chunk = QSharedPointer<IFFChunk>(new CAMGChunk());
} else if (cid == CMAP_CHUNK) { } else if (cid == CMAP_CHUNK) {
chunk = QSharedPointer<IFFChunk>(new CMAPChunk()); chunk = QSharedPointer<IFFChunk>(new CMAPChunk());
} else if (cid == CMYK_CHUNK) {
chunk = QSharedPointer<IFFChunk>(new CMYKChunk());
} else if (cid == COPY_CHUNK) { } else if (cid == COPY_CHUNK) {
chunk = QSharedPointer<IFFChunk>(new COPYChunk()); chunk = QSharedPointer<IFFChunk>(new COPYChunk());
} else if (cid == DATE_CHUNK) { } else if (cid == DATE_CHUNK) {
@ -274,6 +285,8 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *
chunk = QSharedPointer<IFFChunk>(new FVERChunk()); chunk = QSharedPointer<IFFChunk>(new FVERChunk());
} else if (cid == HIST_CHUNK) { } else if (cid == HIST_CHUNK) {
chunk = QSharedPointer<IFFChunk>(new HISTChunk()); chunk = QSharedPointer<IFFChunk>(new HISTChunk());
} else if (cid == ICCN_CHUNK) {
chunk = QSharedPointer<IFFChunk>(new ICCNChunk());
} else if (cid == ICCP_CHUNK) { } else if (cid == ICCP_CHUNK) {
chunk = QSharedPointer<IFFChunk>(new ICCPChunk()); chunk = QSharedPointer<IFFChunk>(new ICCPChunk());
} else if (cid == NAME_CHUNK) { } else if (cid == NAME_CHUNK) {
@ -478,21 +491,87 @@ bool CMAPChunk::isValid() const
return chunkId() == CMAPChunk::defaultChunkId(); return chunkId() == CMAPChunk::defaultChunkId();
} }
qint32 CMAPChunk::count() const
{
if (!isValid()) {
return 0;
}
return bytes() / 3;
}
QList<QRgb> CMAPChunk::palette(bool halfbride) const
{
auto p = innerPalette();
if (!halfbride) {
return p;
}
auto tmp = p;
for(auto &&v : tmp) {
p << qRgb(qRed(v) / 2, qGreen(v) / 2, qBlue(v) / 2);
}
return p;
}
bool CMAPChunk::innerReadStructure(QIODevice *d) bool CMAPChunk::innerReadStructure(QIODevice *d)
{ {
return cacheData(d); return cacheData(d);
} }
QList<QRgb> CMAPChunk::palette() const QList<QRgb> CMAPChunk::innerPalette() const
{ {
QList<QRgb> l; QList<QRgb> l;
auto &&d = data(); auto &&d = data();
for (quint32 i = 0, n = bytes() / 3; i < n; ++i) { for (qint32 i = 0, n = count(); i < n; ++i) {
l << qRgb(d.at(i * 3), d.at(i * 3 + 1), d.at(i * 3 + 2)); auto i3 = i * 3;
l << qRgb(d.at(i3), d.at(i3 + 1), d.at(i3 + 2));
} }
return l; return l;
} }
/* ******************
* *** CMYK Chunk ***
* ****************** */
CMYKChunk::~CMYKChunk()
{
}
CMYKChunk::CMYKChunk() : CMAPChunk()
{
}
bool CMYKChunk::isValid() const
{
return chunkId() == CMYKChunk::defaultChunkId();
}
qint32 CMYKChunk::count() const
{
if (!isValid()) {
return 0;
}
return bytes() / 4;
}
QList<QRgb> CMYKChunk::innerPalette() const
{
QList<QRgb> l;
auto &&d = data();
for (qint32 i = 0, n = count(); i < n; ++i) {
auto i4 = i * 4;
auto C = quint8(d.at(i4)) / 255.;
auto M = quint8(d.at(i4 + 1)) / 255.;
auto Y = quint8(d.at(i4 + 2)) / 255.;
auto K = quint8(d.at(i4 + 3)) / 255.;
l << QColor::fromCmykF(C, M, Y, K).toRgb().rgb();
}
return l;
}
/* ****************** /* ******************
* *** CAMG Chunk *** * *** CAMG Chunk ***
* ****************** */ * ****************** */
@ -630,20 +709,33 @@ bool BODYChunk::resetStrideRead(QIODevice *d) const
return seek(d); return seek(d);
} }
CAMGChunk::ModeIds BODYChunk::safeModeId(const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap)
{
if (camg) {
return camg->modeId();
}
if (header == nullptr) {
return CAMGChunk::ModeIds();
}
if (cmap && cmap->count() == (1 << (header->bitplanes() - 1))) {
return CAMGChunk::ModeIds(CAMGChunk::ModeId::HalfBrite);
}
if (header->bitplanes() == 6) {
// If no CAMG chunk is present, and image is 6 planes deep,
// assume HAM and you'll probably be right.
return CAMGChunk::ModeIds(CAMGChunk::ModeId::Ham);
}
return CAMGChunk::ModeIds();
}
quint32 BODYChunk::strideSize(const BMHDChunk *header, bool isPbm) const quint32 BODYChunk::strideSize(const BMHDChunk *header, bool isPbm) const
{ {
auto rs = header->rowLen() * header->bitplanes();
if (!isPbm) { if (!isPbm) {
return rs; return header->rowLen() * header->bitplanes();
} }
auto rs = header->width() * header->bitplanes() / 8;
// I found two versions of PBM: one uses ILBM calculation, the other uses width-based. if (rs & 1)
// As it is a proprietary extension, one of them was probably generated incorrectly. ++rs;
if (header->compression() == BMHDChunk::Compression::Uncompressed) {
if (rs * header->height() != bytes())
rs = header->width() * header->bitplanes() / 8;
}
return rs; return rs;
} }
@ -655,21 +747,12 @@ QByteArray BODYChunk::deinterleave(const QByteArray &planes, const BMHDChunk *he
auto rowLen = qint32(header->rowLen()); auto rowLen = qint32(header->rowLen());
auto bitplanes = header->bitplanes(); auto bitplanes = header->bitplanes();
auto modeId = CAMGChunk::ModeIds(); auto modeId = BODYChunk::safeModeId(header, camg, cmap);
if (camg) {
modeId = camg->modeId();
}
QByteArray ba; QByteArray ba;
switch (bitplanes) { switch (bitplanes) {
case 1: // bitmap case 1: // gray, indexed and rgb Ham mode
ba = QByteArray((7 + header->width() * bitplanes) / 8, char()); case 2:
for (qint32 i = 0, n = std::min(planes.size(), ba.size()); i < n; ++i) {
ba[i] = ~planes.at(i);
}
break;
case 2: // gray, indexed and rgb Ham mode
case 3: case 3:
case 4: case 4:
case 5: case 5:
@ -741,14 +824,16 @@ QByteArray BODYChunk::deinterleave(const QByteArray &planes, const BMHDChunk *he
} }
} }
} else if ((modeId & CAMGChunk::ModeId::HalfBrite) && (cmap)) { } else if ((modeId & CAMGChunk::ModeId::HalfBrite) && (cmap)) {
// From A Quick Introduction to IFF.txt:
//
// In HALFBRITE mode, the Amiga interprets the bit in the // In HALFBRITE mode, the Amiga interprets the bit in the
// last plane as HALFBRITE modification. The bits in the other planes are // last plane as HALFBRITE modification. The bits in the other planes are
// treated as normal color register numbers (RGB values for each color register // treated as normal color register numbers (RGB values for each color register
// is specified in the CMAP chunk). If the bit in the last plane is set (1), // is specified in the CMAP chunk). If the bit in the last plane is set (1),
// then that pixel is displayed at half brightness. This can provide up to 64 // then that pixel is displayed at half brightness. This can provide up to 64
// absolute colors. // absolute colors.
ba = QByteArray(rowLen * 8 * 3, char()); ba = QByteArray(rowLen * 8, char());
auto pal = cmap->palette(); auto palSize = cmap->count();
for (qint32 i = 0, cnt = 0; i < rowLen; ++i) { for (qint32 i = 0, cnt = 0; i < rowLen; ++i) {
for (qint32 j = 0; j < 8; ++j, ++cnt) { for (qint32 j = 0; j < 8; ++j, ++cnt) {
quint8 idx = 0, ctl = 0; quint8 idx = 0, ctl = 0;
@ -760,12 +845,8 @@ QByteArray BODYChunk::deinterleave(const QByteArray &planes, const BMHDChunk *he
else else
ctl = 1; ctl = 1;
} }
if (idx < pal.size()) { if (idx < palSize) {
auto cnt3 = cnt * 3; ba[cnt] = ctl ? idx + palSize : idx;
auto div = ctl ? 2 : 1;
ba[cnt3] = qRed(pal.at(idx)) / div;
ba[cnt3 + 1] = qGreen(pal.at(idx)) / div;
ba[cnt3 + 2] = qBlue(pal.at(idx)) / div;
} else { } else {
qCWarning(LOG_IFFPLUGIN) << "BODYChunk::deinterleave: palette index" << idx << "is out of range"; qCWarning(LOG_IFFPLUGIN) << "BODYChunk::deinterleave: palette index" << idx << "is out of range";
} }
@ -968,30 +1049,28 @@ QImage::Format FORMChunk::format() const
if (auto &&h = headers.first()) { if (auto &&h = headers.first()) {
auto cmaps = IFFChunk::searchT<CMAPChunk>(chunks()); auto cmaps = IFFChunk::searchT<CMAPChunk>(chunks());
auto camgs = IFFChunk::searchT<CAMGChunk>(chunks()); if (cmaps.isEmpty()) {
auto cmyks = IFFChunk::searchT<CMYKChunk>(chunks());
auto modeId = CAMGChunk::ModeIds(); for (auto &&cmyk : cmyks)
if (!camgs.isEmpty()) { cmaps.append(cmyk);
modeId = camgs.first()->modeId();
} else if (h->bitplanes() == 6) {
// If no CAMG chunk is present, and image is 6 planes deep,
// assume HAM and you'll probably be right.
modeId = CAMGChunk::ModeIds(CAMGChunk::ModeId::Ham);
} }
auto camgs = IFFChunk::searchT<CAMGChunk>(chunks());
auto modeId = BODYChunk::safeModeId(h, camgs.isEmpty() ? nullptr : camgs.first(), cmaps.isEmpty() ? nullptr : cmaps.first());
if (h->bitplanes() == 24) { if (h->bitplanes() == 24) {
return QImage::Format_RGB888; return QImage::Format_RGB888;
} }
if (h->bitplanes() == 32) { if (h->bitplanes() == 32) {
return QImage::Format_RGBA8888; return QImage::Format_RGBA8888;
} }
if (h->bitplanes() >= 2 && h->bitplanes() <= 8) { if (h->bitplanes() >= 1 && h->bitplanes() <= 8) {
if (!IFFChunk::search(SHAM_CHUNK, chunks()).isEmpty() || !IFFChunk::search(CTBL_CHUNK, chunks()).isEmpty()) { if (!IFFChunk::search(SHAM_CHUNK, chunks()).isEmpty() || !IFFChunk::search(CTBL_CHUNK, chunks()).isEmpty()) {
// Images with the SHAM or CTBL chunk do not load correctly: it seems they contains // Images with the SHAM or CTBL chunk do not load correctly: it seems they contains
// a color table but I didn't find any specs. // a color table but I didn't find any specs.
qCDebug(LOG_IFFPLUGIN) << "FORMChunk::format(): SHAM/CTBL chunk is not supported";
return QImage::Format_Invalid; return QImage::Format_Invalid;
} }
if (modeId & (CAMGChunk::ModeId::Ham | CAMGChunk::ModeId::HalfBrite)) { if (modeId & CAMGChunk::ModeId::Ham) {
return QImage::Format_RGB888; return QImage::Format_RGB888;
} }
@ -1001,9 +1080,6 @@ QImage::Format FORMChunk::format() const
return QImage::Format_Grayscale8; return QImage::Format_Grayscale8;
} }
if (h->bitplanes() == 1) {
return QImage::Format_Mono;
}
} }
return QImage::Format_Invalid; return QImage::Format_Invalid;
@ -1168,10 +1244,10 @@ qint32 TBHDChunk::bpc() const
qint32 TBHDChunk::channels() const qint32 TBHDChunk::channels() const
{ {
if (flags() == TBHDChunk::Flag::RgbA) { if ((flags() & TBHDChunk::Flag::RgbA) == TBHDChunk::Flag::RgbA) {
return 4; return 4;
} }
if (flags() == TBHDChunk::Flag::Rgb) { if ((flags() & TBHDChunk::Flag::Rgb) == TBHDChunk::Flag::Rgb) {
return 3; return 3;
} }
return 0; return 0;
@ -1188,12 +1264,12 @@ quint16 TBHDChunk::tiles() const
QImage::Format TBHDChunk::format() const QImage::Format TBHDChunk::format() const
{ {
// Support for RGBA and RGB only for now. // Support for RGBA and RGB only for now.
if (flags() == TBHDChunk::Flag::RgbA) { if ((flags() & TBHDChunk::Flag::RgbA) == TBHDChunk::Flag::RgbA) {
if (bpc() == 2) if (bpc() == 2)
return QImage::Format_RGBA64; return QImage::Format_RGBA64;
else if (bpc() == 1) else if (bpc() == 1)
return QImage::Format_RGBA8888; return QImage::Format_RGBA8888;
} else if (flags() == TBHDChunk::Flag::Rgb) { } else if ((flags() & TBHDChunk::Flag::Rgb) == TBHDChunk::Flag::Rgb) {
if (bpc() == 2) if (bpc() == 2)
return QImage::Format_RGBX64; return QImage::Format_RGBX64;
else if (bpc() == 1) else if (bpc() == 1)
@ -1516,12 +1592,12 @@ ANNOChunk::ANNOChunk()
bool ANNOChunk::isValid() const bool ANNOChunk::isValid() const
{ {
return chunkId() == AUTHChunk::defaultChunkId(); return chunkId() == ANNOChunk::defaultChunkId();
} }
QString ANNOChunk::value() const QString ANNOChunk::value() const
{ {
return QString::fromLatin1(data()).replace(QStringLiteral("\0"), QStringLiteral(" ")).trimmed(); return dataToString(this);
} }
bool ANNOChunk::innerReadStructure(QIODevice *d) bool ANNOChunk::innerReadStructure(QIODevice *d)
@ -1550,7 +1626,7 @@ bool AUTHChunk::isValid() const
QString AUTHChunk::value() const QString AUTHChunk::value() const
{ {
return QString::fromLatin1(data()).replace(QStringLiteral("\0"), QStringLiteral(" ")).trimmed(); return dataToString(this);
} }
bool AUTHChunk::innerReadStructure(QIODevice *d) bool AUTHChunk::innerReadStructure(QIODevice *d)
@ -1580,7 +1656,7 @@ bool COPYChunk::isValid() const
QString COPYChunk::value() const QString COPYChunk::value() const
{ {
return QString::fromLatin1(data()).replace(QStringLiteral("\0"), QStringLiteral(" ")).trimmed(); return dataToString(this);
} }
bool COPYChunk::innerReadStructure(QIODevice *d) bool COPYChunk::innerReadStructure(QIODevice *d)
@ -1610,6 +1686,9 @@ bool DATEChunk::isValid() const
QDateTime DATEChunk::value() const QDateTime DATEChunk::value() const
{ {
if (!isValid()) {
return {};
}
return QDateTime::fromString(QString::fromLatin1(data()), Qt::TextDate); return QDateTime::fromString(QString::fromLatin1(data()), Qt::TextDate);
} }
@ -1643,6 +1722,9 @@ bool EXIFChunk::isValid() const
MicroExif EXIFChunk::value() const MicroExif EXIFChunk::value() const
{ {
if (!isValid()) {
return {};
}
return MicroExif::fromByteArray(data().mid(6)); return MicroExif::fromByteArray(data().mid(6));
} }
@ -1652,6 +1734,36 @@ bool EXIFChunk::innerReadStructure(QIODevice *d)
} }
/* ******************
* *** ICCN Chunk ***
* ****************** */
ICCNChunk::~ICCNChunk()
{
}
ICCNChunk::ICCNChunk()
{
}
bool ICCNChunk::isValid() const
{
return chunkId() == ICCNChunk::defaultChunkId();
}
QString ICCNChunk::value() const
{
return dataToString(this);
}
bool ICCNChunk::innerReadStructure(QIODevice *d)
{
return cacheData(d);
}
/* ****************** /* ******************
* *** ICCP Chunk *** * *** ICCP Chunk ***
* ****************** */ * ****************** */
@ -1673,6 +1785,9 @@ bool ICCPChunk::isValid() const
QColorSpace ICCPChunk::value() const QColorSpace ICCPChunk::value() const
{ {
if (!isValid()) {
return {};
}
return QColorSpace::fromIccProfile(data()); return QColorSpace::fromIccProfile(data());
} }
@ -1726,6 +1841,9 @@ bool HISTChunk::isValid() const
QString HISTChunk::value() const QString HISTChunk::value() const
{ {
if (!isValid()) {
return {};
}
return QString::fromLatin1(data()); return QString::fromLatin1(data());
} }
@ -1756,7 +1874,7 @@ bool NAMEChunk::isValid() const
QString NAMEChunk::value() const QString NAMEChunk::value() const
{ {
return QString::fromLatin1(data()).replace(QStringLiteral("\0"), QStringLiteral(" ")).trimmed(); return dataToString(this);
} }
bool NAMEChunk::innerReadStructure(QIODevice *d) bool NAMEChunk::innerReadStructure(QIODevice *d)
@ -1786,6 +1904,9 @@ bool VERSChunk::isValid() const
QString VERSChunk::value() const QString VERSChunk::value() const
{ {
if (!isValid()) {
return {};
}
return QString::fromLatin1(data()); return QString::fromLatin1(data());
} }
@ -1816,11 +1937,10 @@ bool XMP0Chunk::isValid() const
QString XMP0Chunk::value() const QString XMP0Chunk::value() const
{ {
return QString::fromUtf8(data()).replace(QStringLiteral("\0"), QStringLiteral(" ")).trimmed(); return dataToString(this);
} }
bool XMP0Chunk::innerReadStructure(QIODevice *d) bool XMP0Chunk::innerReadStructure(QIODevice *d)
{ {
return cacheData(d); return cacheData(d);
} }

View File

@ -49,6 +49,7 @@
#define BODY_CHUNK QByteArray("BODY") #define BODY_CHUNK QByteArray("BODY")
#define CAMG_CHUNK QByteArray("CAMG") #define CAMG_CHUNK QByteArray("CAMG")
#define CMAP_CHUNK QByteArray("CMAP") #define CMAP_CHUNK QByteArray("CMAP")
#define CMYK_CHUNK QByteArray("CMYK") // https://wiki.amigaos.net/wiki/ILBM_IFF_Interleaved_Bitmap#ILBM.CMYK
#define DPI__CHUNK QByteArray("DPI ") #define DPI__CHUNK QByteArray("DPI ")
#define CTBL_CHUNK QByteArray("CTBL") // undocumented #define CTBL_CHUNK QByteArray("CTBL") // undocumented
@ -64,6 +65,7 @@
#define COPY_CHUNK QByteArray("(c) ") #define COPY_CHUNK QByteArray("(c) ")
#define DATE_CHUNK QByteArray("DATE") #define DATE_CHUNK QByteArray("DATE")
#define EXIF_CHUNK QByteArray("EXIF") // https://aminet.net/package/docs/misc/IFF-metadata #define EXIF_CHUNK QByteArray("EXIF") // https://aminet.net/package/docs/misc/IFF-metadata
#define ICCN_CHUNK QByteArray("ICCN") // https://aminet.net/package/docs/misc/IFF-metadata
#define ICCP_CHUNK QByteArray("ICCP") // https://aminet.net/package/docs/misc/IFF-metadata #define ICCP_CHUNK QByteArray("ICCP") // https://aminet.net/package/docs/misc/IFF-metadata
#define FVER_CHUNK QByteArray("FVER") #define FVER_CHUNK QByteArray("FVER")
#define HIST_CHUNK QByteArray("HIST") #define HIST_CHUNK QByteArray("HIST")
@ -467,14 +469,64 @@ public:
virtual bool isValid() const override; virtual bool isValid() const override;
QList<QRgb> palette() const; /*!
* \brief count
* \return The number of color in the palette.
*/
virtual qint32 count() const;
/*!
* \brief palette
* \param halfbride When True, the new palette values are appended using the halfbride method.
* \return The color palette.
* \note If \a halfbride is true, the returned palette size is count() * 2.
*/
QList<QRgb> palette(bool halfbride = false) const;
CHUNKID_DEFINE(CMAP_CHUNK) CHUNKID_DEFINE(CMAP_CHUNK)
protected: protected:
virtual QList<QRgb> innerPalette() const;
virtual bool innerReadStructure(QIODevice *d) override; virtual bool innerReadStructure(QIODevice *d) override;
}; };
/*!
* \brief The CMYKChunk class
*
* This chunk would allow color specification in terms of Cyan,
* Magenta, Yellow, and Black as opposed to the current CMAP which uses RGB.
* The format would be the same as the CMAP chunk with the exception that this
* chunk uses four color components as opposed to three. The number of colors
* contained within would be chunk length/4. This chunk would be used in addition
* to the CMAP chunk.
*/
class CMYKChunk : public CMAPChunk
{
public:
virtual ~CMYKChunk() override;
CMYKChunk();
CMYKChunk(const CMYKChunk& other) = default;
CMYKChunk& operator =(const CMYKChunk& other) = default;
virtual bool isValid() const override;
/*!
* \brief count
* \return The number of color in the palette.
*/
virtual qint32 count() const override;
CHUNKID_DEFINE(CMYK_CHUNK)
protected:
/*!
* \brief palette
* \return The CMYK color palette converted to RGB one.
*/
virtual QList<QRgb> innerPalette() const override;
};
/*! /*!
* \brief The CAMGChunk class * \brief The CAMGChunk class
*/ */
@ -589,6 +641,14 @@ public:
*/ */
virtual bool resetStrideRead(QIODevice *d) const; virtual bool resetStrideRead(QIODevice *d) const;
/*!
* \brief safeModeId
* \param header The header.
* \param camg The CAMG chunk.
* \return The most likely ModeId if not explicitly specified.
*/
static CAMGChunk::ModeIds safeModeId(const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap = nullptr);
protected: protected:
/*! /*!
* \brief strideSize * \brief strideSize
@ -959,6 +1019,28 @@ protected:
virtual bool innerReadStructure(QIODevice *d) override; virtual bool innerReadStructure(QIODevice *d) override;
}; };
/*!
* \brief The NAMEChunk class
*/
class ICCNChunk : public IFFChunk
{
public:
virtual ~ICCNChunk() override;
ICCNChunk();
ICCNChunk(const ICCNChunk& other) = default;
ICCNChunk& operator =(const ICCNChunk& other) = default;
virtual bool isValid() const override;
QString value() const;
CHUNKID_DEFINE(ICCN_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*! /*!
* \brief The ICCPChunk class * \brief The ICCPChunk class
*/ */

View File

@ -15,7 +15,7 @@
#include <QPainter> #include <QPainter>
#ifdef QT_DEBUG #ifdef QT_DEBUG
Q_LOGGING_CATEGORY(LOG_IFFPLUGIN, "kf.imageformats.plugins.iff", QtInfoMsg) Q_LOGGING_CATEGORY(LOG_IFFPLUGIN, "kf.imageformats.plugins.iff", QtDebugMsg)
#else #else
Q_LOGGING_CATEGORY(LOG_IFFPLUGIN, "kf.imageformats.plugins.iff", QtWarningMsg) Q_LOGGING_CATEGORY(LOG_IFFPLUGIN, "kf.imageformats.plugins.iff", QtWarningMsg)
#endif #endif
@ -184,6 +184,12 @@ void addMetadata(QImage& img, const IFFChunk *form)
if (!iccps.isEmpty()) { if (!iccps.isEmpty()) {
auto cs = iccps.first()->value(); auto cs = iccps.first()->value();
if (cs.isValid()) { if (cs.isValid()) {
auto iccns = IFFChunk::searchT<ICCNChunk>(form);
if (!iccns.isEmpty()) {
auto desc = iccns.first()->value();
if (!desc.isEmpty())
cs.setDescription(desc);
}
img.setColorSpace(cs); img.setColorSpace(cs);
} }
} }
@ -223,13 +229,30 @@ bool IFFHandler::readStandardImage(QImage *image)
} }
// set color table // set color table
auto cmaps = IFFChunk::searchT<CMAPChunk>(form); const CAMGChunk *camg = nullptr;
if (img.format() == QImage::Format_Indexed8) { auto camgs = IFFChunk::searchT<CAMGChunk>(form);
if (!cmaps.isEmpty()) if (!camgs.isEmpty()) {
if (auto &&cmap = cmaps.first()) camg = camgs.first();
img.setColorTable(cmap->palette());
} }
const CMAPChunk *cmap = nullptr;
auto cmaps = IFFChunk::searchT<CMAPChunk>(form);
if (cmaps.isEmpty()) {
auto cmyks = IFFChunk::searchT<CMYKChunk>(form);
for (auto &&cmyk : cmyks)
cmaps.append(cmyk);
}
if (!cmaps.isEmpty()) {
cmap = cmaps.first();
}
if (img.format() == QImage::Format_Indexed8) {
if (cmap) {
auto halfbride = BODYChunk::safeModeId(header, camg, cmap) & CAMGChunk::ModeId::HalfBrite ? true : false;
img.setColorTable(cmap->palette(halfbride));
}
}
// reading image data
auto bodies = IFFChunk::searchT<BODYChunk>(form); auto bodies = IFFChunk::searchT<BODYChunk>(form);
if (bodies.isEmpty()) { if (bodies.isEmpty()) {
auto abits = IFFChunk::searchT<ABITChunk>(form); auto abits = IFFChunk::searchT<ABITChunk>(form);
@ -239,16 +262,6 @@ bool IFFHandler::readStandardImage(QImage *image)
if (bodies.isEmpty()) { if (bodies.isEmpty()) {
img.fill(0); img.fill(0);
} else { } else {
const CAMGChunk *camg = nullptr;
auto camgs = IFFChunk::searchT<CAMGChunk>(form);
if (!camgs.isEmpty()) {
camg = camgs.first();
}
const CMAPChunk *cmap = nullptr;
if (!cmaps.isEmpty())
cmap = cmaps.first();
auto &&body = bodies.first(); auto &&body = bodies.first();
if (!body->resetStrideRead(device())) { if (!body->resetStrideRead(device())) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage() error while reading image data"; qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage() error while reading image data";