mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2026-02-05 03:40:09 -05:00
Compare commits
3 Commits
v6.16.0-rc
...
work/kbrou
| Author | SHA1 | Date | |
|---|---|---|---|
| f5494d9acf | |||
| ea1983a7d1 | |||
| 775b53f1f6 |
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(KF_VERSION "6.16.0") # handled by release scripts
|
||||
set(KF_VERSION "6.17.0") # handled by release scripts
|
||||
set(KF_DEP_VERSION "6.16.0") # handled by release scripts
|
||||
project(KImageFormats VERSION ${KF_VERSION})
|
||||
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#define RECURSION_PROTECTION 10
|
||||
|
||||
IFFChunk::~IFFChunk()
|
||||
{
|
||||
|
||||
@ -20,6 +22,7 @@ IFFChunk::IFFChunk()
|
||||
, _size{0}
|
||||
, _align{2}
|
||||
, _dataPos{0}
|
||||
, _recursionCnt{0}
|
||||
{
|
||||
}
|
||||
|
||||
@ -49,12 +52,16 @@ qint32 IFFChunk::alignBytes() const
|
||||
bool IFFChunk::readStructure(QIODevice *d)
|
||||
{
|
||||
auto ok = readInfo(d);
|
||||
ok = ok && innerReadStructure(d);
|
||||
if (recursionCounter() > RECURSION_PROTECTION - 1) {
|
||||
ok = ok && IFFChunk::innerReadStructure(d); // force default implementation (no more recursion)
|
||||
} else {
|
||||
ok = ok && innerReadStructure(d);
|
||||
}
|
||||
if (ok) {
|
||||
auto pos = _dataPos + _size;
|
||||
if (auto align = pos % alignBytes())
|
||||
pos += alignBytes() - align;
|
||||
ok = d->seek(pos);
|
||||
ok = pos < d->pos() ? false : d->seek(pos);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
@ -169,7 +176,17 @@ void IFFChunk::setChunks(const ChunkList &chunks)
|
||||
_chunks = chunks;
|
||||
}
|
||||
|
||||
IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, qint32 alignBytes)
|
||||
qint32 IFFChunk::recursionCounter() const
|
||||
{
|
||||
return _recursionCnt;
|
||||
}
|
||||
|
||||
void IFFChunk::setRecursionCounter(qint32 cnt)
|
||||
{
|
||||
_recursionCnt = cnt;
|
||||
}
|
||||
|
||||
IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, qint32 alignBytes, qint32 recursionCnt)
|
||||
{
|
||||
auto tmp = false;
|
||||
if (ok == nullptr) {
|
||||
@ -181,6 +198,10 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, qint32 ali
|
||||
return {};
|
||||
}
|
||||
|
||||
if (recursionCnt > RECURSION_PROTECTION) {
|
||||
return {};
|
||||
}
|
||||
|
||||
IFFChunk::ChunkList list;
|
||||
for (; !d->atEnd();) {
|
||||
auto cid = d->peek(4);
|
||||
@ -229,6 +250,7 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, qint32 ali
|
||||
chunk->setAlignBytes(alignBytes);
|
||||
}
|
||||
|
||||
chunk->setRecursionCounter(recursionCnt + 1);
|
||||
if (!chunk->readStructure(d)) {
|
||||
*ok = false;
|
||||
return {};
|
||||
@ -243,7 +265,7 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, qint32 ali
|
||||
|
||||
IFFChunk::ChunkList IFFChunk::fromDevice(QIODevice *d, bool *ok)
|
||||
{
|
||||
return innerFromDevice(d, ok, 2);
|
||||
return innerFromDevice(d, ok, 2, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -525,9 +547,9 @@ bool BODYChunk::isValid() const
|
||||
return chunkId() == BODYChunk::defaultChunkId();
|
||||
}
|
||||
|
||||
QByteArray BODYChunk::strideRead(QIODevice *d, const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap) const
|
||||
QByteArray BODYChunk::strideRead(QIODevice *d, const FORMChunk *form, const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap) const
|
||||
{
|
||||
if (!isValid() || header == nullptr) {
|
||||
if (!isValid() || form == nullptr || header == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -549,7 +571,11 @@ QByteArray BODYChunk::strideRead(QIODevice *d, const BMHDChunk *header, const CA
|
||||
|
||||
auto planes = _readBuffer.left(readSize);
|
||||
_readBuffer.remove(0, readSize);
|
||||
return BODYChunk::deinterleave(planes, header, camg, cmap);
|
||||
if (form->formType() == FORMChunk::FormType::Pbm) {
|
||||
return planes;
|
||||
} else {
|
||||
return BODYChunk::deinterleave(planes, header, camg, cmap);
|
||||
}
|
||||
}
|
||||
|
||||
bool BODYChunk::resetStrideRead(QIODevice *d) const
|
||||
@ -751,15 +777,21 @@ bool FORMChunk::innerReadStructure(QIODevice *d)
|
||||
if (bytes() < 4) {
|
||||
return false;
|
||||
}
|
||||
_type = d->read(4);
|
||||
auto ok = true;
|
||||
if (_type == QByteArray("ILBM")) {
|
||||
setChunks(IFFChunk::innerFromDevice(d, &ok, alignBytes()));
|
||||
const auto type = d->read(4);
|
||||
auto ok = false;
|
||||
if (type == QByteArrayLiteral("ILBM")) {
|
||||
_type = FormType::Ilbm;
|
||||
} else if (type == QByteArrayLiteral("PBM ")) {
|
||||
_type = FormType::Pbm;
|
||||
}
|
||||
|
||||
if (_type != FormType::Unknown) {
|
||||
setChunks(IFFChunk::innerFromDevice(d, &ok, alignBytes(), recursionCounter()));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
QByteArray FORMChunk::formType() const
|
||||
FORMChunk::FormType FORMChunk::formType() const
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
@ -857,9 +889,9 @@ bool FOR4Chunk::innerReadStructure(QIODevice *d)
|
||||
_type = d->read(4);
|
||||
auto ok = true;
|
||||
if (_type == QByteArray("CIMG")) {
|
||||
setChunks(IFFChunk::innerFromDevice(d, &ok, alignBytes()));
|
||||
setChunks(IFFChunk::innerFromDevice(d, &ok, alignBytes(), recursionCounter()));
|
||||
} else if (_type == QByteArray("TBMP")) {
|
||||
setChunks(IFFChunk::innerFromDevice(d, &ok, alignBytes()));
|
||||
setChunks(IFFChunk::innerFromDevice(d, &ok, alignBytes(), recursionCounter()));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
@ -256,6 +256,14 @@ protected:
|
||||
*/
|
||||
void setChunks(const ChunkList &chunks);
|
||||
|
||||
/*!
|
||||
* \brief recursionCounter
|
||||
* Protection against stack overflow due to broken data.
|
||||
* \return The current recursion counter.
|
||||
*/
|
||||
qint32 recursionCounter() const;
|
||||
void setRecursionCounter(qint32 cnt);
|
||||
|
||||
inline quint16 ui16(quint8 c1, quint8 c2) const {
|
||||
return (quint16(c2) << 8) | quint16(c1);
|
||||
}
|
||||
@ -272,7 +280,7 @@ protected:
|
||||
return qint32(ui32(c1, c2, c3, c4));
|
||||
}
|
||||
|
||||
static ChunkList innerFromDevice(QIODevice *d, bool *ok, qint32 alignBytes);
|
||||
static ChunkList innerFromDevice(QIODevice *d, bool *ok, qint32 alignBytes, qint32 recursionCnt);
|
||||
|
||||
private:
|
||||
char _chunkId[4];
|
||||
@ -287,6 +295,8 @@ private:
|
||||
|
||||
ChunkList _chunks;
|
||||
|
||||
qint32 _recursionCnt;
|
||||
|
||||
|
||||
};
|
||||
|
||||
@ -441,6 +451,7 @@ protected:
|
||||
virtual bool innerReadStructure(QIODevice *d) override;
|
||||
};
|
||||
|
||||
class FORMChunk;
|
||||
|
||||
/*!
|
||||
* \brief The BODYChunk class
|
||||
@ -466,7 +477,7 @@ public:
|
||||
* \return The scanline as requested for QImage.
|
||||
* \warning Call resetStrideRead() once before this one.
|
||||
*/
|
||||
QByteArray strideRead(QIODevice *d, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr) const;
|
||||
QByteArray strideRead(QIODevice *d, const FORMChunk *form, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr) const;
|
||||
|
||||
/*!
|
||||
* \brief resetStrideRead
|
||||
@ -490,9 +501,13 @@ private:
|
||||
*/
|
||||
class FORMChunk : public IFFChunk
|
||||
{
|
||||
QByteArray _type;
|
||||
|
||||
public:
|
||||
enum class FormType {
|
||||
Unknown,
|
||||
Ilbm,
|
||||
Pbm,
|
||||
};
|
||||
|
||||
virtual ~FORMChunk() override;
|
||||
FORMChunk();
|
||||
FORMChunk(const FORMChunk& other) = default;
|
||||
@ -502,7 +517,7 @@ public:
|
||||
|
||||
bool isSupported() const;
|
||||
|
||||
QByteArray formType() const;
|
||||
FormType formType() const;
|
||||
|
||||
QImage::Format format() const;
|
||||
|
||||
@ -512,8 +527,10 @@ public:
|
||||
|
||||
protected:
|
||||
virtual bool innerReadStructure(QIODevice *d) override;
|
||||
};
|
||||
|
||||
private:
|
||||
FormType _type = FormType::Unknown;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief The FOR4Chunk class
|
||||
|
||||
@ -74,7 +74,7 @@ IFFHandler::IFFHandler()
|
||||
bool IFFHandler::canRead() const
|
||||
{
|
||||
if (canRead(device())) {
|
||||
setFormat("iff");
|
||||
setFormat("iff"); // TODO ilbm based on header?
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -192,7 +192,7 @@ bool IFFHandler::readStandardImage(QImage *image)
|
||||
}
|
||||
for (auto y = 0, h = img.height(); y < h; ++y) {
|
||||
auto line = reinterpret_cast<char*>(img.scanLine(y));
|
||||
auto ba = body->strideRead(device(), header, camg, cmap);
|
||||
auto ba = body->strideRead(device(), form, header, camg, cmap);
|
||||
if (ba.isEmpty()) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage() error while reading image scanline";
|
||||
return false;
|
||||
@ -334,7 +334,7 @@ QVariant IFFHandler::option(ImageOption option) const
|
||||
|
||||
QImageIOPlugin::Capabilities IFFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
if (format == "iff") {
|
||||
if (format == "iff" || format == "ilbm") {
|
||||
return Capabilities(CanRead);
|
||||
}
|
||||
if (!format.isEmpty()) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{
|
||||
"Keys": [ "iff" ],
|
||||
"MimeTypes": [ "application/x-iff" ]
|
||||
"Keys": [ "iff", "ilbm" ],
|
||||
"MimeTypes": [ "application/x-iff", "application/x-ilbm" ]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user