mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-08-05 08:24:31 -04:00
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:
BIN
autotests/read/iff/ps_testcard_cmyk.iff
Normal file
BIN
autotests/read/iff/ps_testcard_cmyk.iff
Normal file
Binary file not shown.
BIN
autotests/read/iff/ps_testcard_cmyk.png
Normal file
BIN
autotests/read/iff/ps_testcard_cmyk.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
BIN
autotests/read/iff/testcard_indexed1_amiga.iff
Normal file
BIN
autotests/read/iff/testcard_indexed1_amiga.iff
Normal file
Binary file not shown.
BIN
autotests/read/iff/testcard_indexed1_amiga.png
Normal file
BIN
autotests/read/iff/testcard_indexed1_amiga.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
@ -9,6 +9,7 @@
|
||||
#include "packbits_p.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QColor>
|
||||
#include <QDebug>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
@ -16,6 +17,14 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
|
||||
|
||||
#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()
|
||||
{
|
||||
|
||||
@ -258,6 +267,8 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *
|
||||
chunk = QSharedPointer<IFFChunk>(new CAMGChunk());
|
||||
} else if (cid == CMAP_CHUNK) {
|
||||
chunk = QSharedPointer<IFFChunk>(new CMAPChunk());
|
||||
} else if (cid == CMYK_CHUNK) {
|
||||
chunk = QSharedPointer<IFFChunk>(new CMYKChunk());
|
||||
} else if (cid == COPY_CHUNK) {
|
||||
chunk = QSharedPointer<IFFChunk>(new COPYChunk());
|
||||
} else if (cid == DATE_CHUNK) {
|
||||
@ -274,6 +285,8 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *
|
||||
chunk = QSharedPointer<IFFChunk>(new FVERChunk());
|
||||
} else if (cid == HIST_CHUNK) {
|
||||
chunk = QSharedPointer<IFFChunk>(new HISTChunk());
|
||||
} else if (cid == ICCN_CHUNK) {
|
||||
chunk = QSharedPointer<IFFChunk>(new ICCNChunk());
|
||||
} else if (cid == ICCP_CHUNK) {
|
||||
chunk = QSharedPointer<IFFChunk>(new ICCPChunk());
|
||||
} else if (cid == NAME_CHUNK) {
|
||||
@ -478,21 +491,87 @@ bool CMAPChunk::isValid() const
|
||||
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)
|
||||
{
|
||||
return cacheData(d);
|
||||
}
|
||||
|
||||
QList<QRgb> CMAPChunk::palette() const
|
||||
QList<QRgb> CMAPChunk::innerPalette() const
|
||||
{
|
||||
QList<QRgb> l;
|
||||
auto &&d = data();
|
||||
for (quint32 i = 0, n = bytes() / 3; i < n; ++i) {
|
||||
l << qRgb(d.at(i * 3), d.at(i * 3 + 1), d.at(i * 3 + 2));
|
||||
for (qint32 i = 0, n = count(); i < n; ++i) {
|
||||
auto i3 = i * 3;
|
||||
l << qRgb(d.at(i3), d.at(i3 + 1), d.at(i3 + 2));
|
||||
}
|
||||
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 ***
|
||||
* ****************** */
|
||||
@ -630,20 +709,33 @@ bool BODYChunk::resetStrideRead(QIODevice *d) const
|
||||
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
|
||||
{
|
||||
auto rs = header->rowLen() * header->bitplanes();
|
||||
if (!isPbm) {
|
||||
return rs;
|
||||
return header->rowLen() * header->bitplanes();
|
||||
}
|
||||
|
||||
// I found two versions of PBM: one uses ILBM calculation, the other uses width-based.
|
||||
// As it is a proprietary extension, one of them was probably generated incorrectly.
|
||||
if (header->compression() == BMHDChunk::Compression::Uncompressed) {
|
||||
if (rs * header->height() != bytes())
|
||||
rs = header->width() * header->bitplanes() / 8;
|
||||
}
|
||||
|
||||
auto rs = header->width() * header->bitplanes() / 8;
|
||||
if (rs & 1)
|
||||
++rs;
|
||||
return rs;
|
||||
}
|
||||
|
||||
@ -655,21 +747,12 @@ QByteArray BODYChunk::deinterleave(const QByteArray &planes, const BMHDChunk *he
|
||||
|
||||
auto rowLen = qint32(header->rowLen());
|
||||
auto bitplanes = header->bitplanes();
|
||||
auto modeId = CAMGChunk::ModeIds();
|
||||
if (camg) {
|
||||
modeId = camg->modeId();
|
||||
}
|
||||
auto modeId = BODYChunk::safeModeId(header, camg, cmap);
|
||||
|
||||
QByteArray ba;
|
||||
switch (bitplanes) {
|
||||
case 1: // bitmap
|
||||
ba = QByteArray((7 + header->width() * bitplanes) / 8, char());
|
||||
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 1: // gray, indexed and rgb Ham mode
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
@ -741,14 +824,16 @@ QByteArray BODYChunk::deinterleave(const QByteArray &planes, const BMHDChunk *he
|
||||
}
|
||||
}
|
||||
} else if ((modeId & CAMGChunk::ModeId::HalfBrite) && (cmap)) {
|
||||
// From A Quick Introduction to IFF.txt:
|
||||
//
|
||||
// In HALFBRITE mode, the Amiga interprets the bit in the
|
||||
// last plane as HALFBRITE modification. The bits in the other planes are
|
||||
// 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),
|
||||
// then that pixel is displayed at half brightness. This can provide up to 64
|
||||
// absolute colors.
|
||||
ba = QByteArray(rowLen * 8 * 3, char());
|
||||
auto pal = cmap->palette();
|
||||
ba = QByteArray(rowLen * 8, char());
|
||||
auto palSize = cmap->count();
|
||||
for (qint32 i = 0, cnt = 0; i < rowLen; ++i) {
|
||||
for (qint32 j = 0; j < 8; ++j, ++cnt) {
|
||||
quint8 idx = 0, ctl = 0;
|
||||
@ -760,12 +845,8 @@ QByteArray BODYChunk::deinterleave(const QByteArray &planes, const BMHDChunk *he
|
||||
else
|
||||
ctl = 1;
|
||||
}
|
||||
if (idx < pal.size()) {
|
||||
auto cnt3 = cnt * 3;
|
||||
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;
|
||||
if (idx < palSize) {
|
||||
ba[cnt] = ctl ? idx + palSize : idx;
|
||||
} else {
|
||||
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()) {
|
||||
auto cmaps = IFFChunk::searchT<CMAPChunk>(chunks());
|
||||
auto camgs = IFFChunk::searchT<CAMGChunk>(chunks());
|
||||
|
||||
auto modeId = CAMGChunk::ModeIds();
|
||||
if (!camgs.isEmpty()) {
|
||||
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);
|
||||
if (cmaps.isEmpty()) {
|
||||
auto cmyks = IFFChunk::searchT<CMYKChunk>(chunks());
|
||||
for (auto &&cmyk : cmyks)
|
||||
cmaps.append(cmyk);
|
||||
}
|
||||
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) {
|
||||
return QImage::Format_RGB888;
|
||||
}
|
||||
if (h->bitplanes() == 32) {
|
||||
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()) {
|
||||
// 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.
|
||||
qCDebug(LOG_IFFPLUGIN) << "FORMChunk::format(): SHAM/CTBL chunk is not supported";
|
||||
return QImage::Format_Invalid;
|
||||
}
|
||||
|
||||
if (modeId & (CAMGChunk::ModeId::Ham | CAMGChunk::ModeId::HalfBrite)) {
|
||||
if (modeId & CAMGChunk::ModeId::Ham) {
|
||||
return QImage::Format_RGB888;
|
||||
}
|
||||
|
||||
@ -1001,9 +1080,6 @@ QImage::Format FORMChunk::format() const
|
||||
|
||||
return QImage::Format_Grayscale8;
|
||||
}
|
||||
if (h->bitplanes() == 1) {
|
||||
return QImage::Format_Mono;
|
||||
}
|
||||
}
|
||||
|
||||
return QImage::Format_Invalid;
|
||||
@ -1168,10 +1244,10 @@ qint32 TBHDChunk::bpc() const
|
||||
|
||||
qint32 TBHDChunk::channels() const
|
||||
{
|
||||
if (flags() == TBHDChunk::Flag::RgbA) {
|
||||
if ((flags() & TBHDChunk::Flag::RgbA) == TBHDChunk::Flag::RgbA) {
|
||||
return 4;
|
||||
}
|
||||
if (flags() == TBHDChunk::Flag::Rgb) {
|
||||
if ((flags() & TBHDChunk::Flag::Rgb) == TBHDChunk::Flag::Rgb) {
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
@ -1188,12 +1264,12 @@ quint16 TBHDChunk::tiles() const
|
||||
QImage::Format TBHDChunk::format() const
|
||||
{
|
||||
// Support for RGBA and RGB only for now.
|
||||
if (flags() == TBHDChunk::Flag::RgbA) {
|
||||
if ((flags() & TBHDChunk::Flag::RgbA) == TBHDChunk::Flag::RgbA) {
|
||||
if (bpc() == 2)
|
||||
return QImage::Format_RGBA64;
|
||||
else if (bpc() == 1)
|
||||
return QImage::Format_RGBA8888;
|
||||
} else if (flags() == TBHDChunk::Flag::Rgb) {
|
||||
} else if ((flags() & TBHDChunk::Flag::Rgb) == TBHDChunk::Flag::Rgb) {
|
||||
if (bpc() == 2)
|
||||
return QImage::Format_RGBX64;
|
||||
else if (bpc() == 1)
|
||||
@ -1516,12 +1592,12 @@ ANNOChunk::ANNOChunk()
|
||||
|
||||
bool ANNOChunk::isValid() const
|
||||
{
|
||||
return chunkId() == AUTHChunk::defaultChunkId();
|
||||
return chunkId() == ANNOChunk::defaultChunkId();
|
||||
}
|
||||
|
||||
QString ANNOChunk::value() const
|
||||
{
|
||||
return QString::fromLatin1(data()).replace(QStringLiteral("\0"), QStringLiteral(" ")).trimmed();
|
||||
return dataToString(this);
|
||||
}
|
||||
|
||||
bool ANNOChunk::innerReadStructure(QIODevice *d)
|
||||
@ -1550,7 +1626,7 @@ bool AUTHChunk::isValid() const
|
||||
|
||||
QString AUTHChunk::value() const
|
||||
{
|
||||
return QString::fromLatin1(data()).replace(QStringLiteral("\0"), QStringLiteral(" ")).trimmed();
|
||||
return dataToString(this);
|
||||
}
|
||||
|
||||
bool AUTHChunk::innerReadStructure(QIODevice *d)
|
||||
@ -1580,7 +1656,7 @@ bool COPYChunk::isValid() const
|
||||
|
||||
QString COPYChunk::value() const
|
||||
{
|
||||
return QString::fromLatin1(data()).replace(QStringLiteral("\0"), QStringLiteral(" ")).trimmed();
|
||||
return dataToString(this);
|
||||
}
|
||||
|
||||
bool COPYChunk::innerReadStructure(QIODevice *d)
|
||||
@ -1610,6 +1686,9 @@ bool DATEChunk::isValid() const
|
||||
|
||||
QDateTime DATEChunk::value() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return {};
|
||||
}
|
||||
return QDateTime::fromString(QString::fromLatin1(data()), Qt::TextDate);
|
||||
}
|
||||
|
||||
@ -1643,6 +1722,9 @@ bool EXIFChunk::isValid() const
|
||||
|
||||
MicroExif EXIFChunk::value() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return {};
|
||||
}
|
||||
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 ***
|
||||
* ****************** */
|
||||
@ -1673,6 +1785,9 @@ bool ICCPChunk::isValid() const
|
||||
|
||||
QColorSpace ICCPChunk::value() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return {};
|
||||
}
|
||||
return QColorSpace::fromIccProfile(data());
|
||||
}
|
||||
|
||||
@ -1726,6 +1841,9 @@ bool HISTChunk::isValid() const
|
||||
|
||||
QString HISTChunk::value() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return {};
|
||||
}
|
||||
return QString::fromLatin1(data());
|
||||
}
|
||||
|
||||
@ -1756,7 +1874,7 @@ bool NAMEChunk::isValid() const
|
||||
|
||||
QString NAMEChunk::value() const
|
||||
{
|
||||
return QString::fromLatin1(data()).replace(QStringLiteral("\0"), QStringLiteral(" ")).trimmed();
|
||||
return dataToString(this);
|
||||
}
|
||||
|
||||
bool NAMEChunk::innerReadStructure(QIODevice *d)
|
||||
@ -1786,6 +1904,9 @@ bool VERSChunk::isValid() const
|
||||
|
||||
QString VERSChunk::value() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return {};
|
||||
}
|
||||
return QString::fromLatin1(data());
|
||||
}
|
||||
|
||||
@ -1816,11 +1937,10 @@ bool XMP0Chunk::isValid() const
|
||||
|
||||
QString XMP0Chunk::value() const
|
||||
{
|
||||
return QString::fromUtf8(data()).replace(QStringLiteral("\0"), QStringLiteral(" ")).trimmed();
|
||||
return dataToString(this);
|
||||
}
|
||||
|
||||
bool XMP0Chunk::innerReadStructure(QIODevice *d)
|
||||
{
|
||||
return cacheData(d);
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@
|
||||
#define BODY_CHUNK QByteArray("BODY")
|
||||
#define CAMG_CHUNK QByteArray("CAMG")
|
||||
#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 CTBL_CHUNK QByteArray("CTBL") // undocumented
|
||||
@ -64,6 +65,7 @@
|
||||
#define COPY_CHUNK QByteArray("(c) ")
|
||||
#define DATE_CHUNK QByteArray("DATE")
|
||||
#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 FVER_CHUNK QByteArray("FVER")
|
||||
#define HIST_CHUNK QByteArray("HIST")
|
||||
@ -467,14 +469,64 @@ public:
|
||||
|
||||
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)
|
||||
|
||||
protected:
|
||||
virtual QList<QRgb> innerPalette() const;
|
||||
|
||||
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
|
||||
*/
|
||||
@ -589,6 +641,14 @@ public:
|
||||
*/
|
||||
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:
|
||||
/*!
|
||||
* \brief strideSize
|
||||
@ -959,6 +1019,28 @@ protected:
|
||||
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
|
||||
*/
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <QPainter>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_IFFPLUGIN, "kf.imageformats.plugins.iff", QtInfoMsg)
|
||||
Q_LOGGING_CATEGORY(LOG_IFFPLUGIN, "kf.imageformats.plugins.iff", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_IFFPLUGIN, "kf.imageformats.plugins.iff", QtWarningMsg)
|
||||
#endif
|
||||
@ -184,6 +184,12 @@ void addMetadata(QImage& img, const IFFChunk *form)
|
||||
if (!iccps.isEmpty()) {
|
||||
auto cs = iccps.first()->value();
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -223,13 +229,30 @@ bool IFFHandler::readStandardImage(QImage *image)
|
||||
}
|
||||
|
||||
// set color table
|
||||
auto cmaps = IFFChunk::searchT<CMAPChunk>(form);
|
||||
if (img.format() == QImage::Format_Indexed8) {
|
||||
if (!cmaps.isEmpty())
|
||||
if (auto &&cmap = cmaps.first())
|
||||
img.setColorTable(cmap->palette());
|
||||
const CAMGChunk *camg = nullptr;
|
||||
auto camgs = IFFChunk::searchT<CAMGChunk>(form);
|
||||
if (!camgs.isEmpty()) {
|
||||
camg = camgs.first();
|
||||
}
|
||||
|
||||
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);
|
||||
if (bodies.isEmpty()) {
|
||||
auto abits = IFFChunk::searchT<ABITChunk>(form);
|
||||
@ -239,16 +262,6 @@ bool IFFHandler::readStandardImage(QImage *image)
|
||||
if (bodies.isEmpty()) {
|
||||
img.fill(0);
|
||||
} 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();
|
||||
if (!body->resetStrideRead(device())) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage() error while reading image data";
|
||||
|
Reference in New Issue
Block a user