QTextureFileData: support key value metadata
Task-Id: QTBUG-76970 Pick-to: 6.1 Change-Id: I9dba1b373250cea4d0c806997290a7afcdc900d7 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
This commit is contained in:
parent
d66cb667ef
commit
2861cfb6f8
@ -146,7 +146,12 @@ QTextureFileData QKtxHandler::read()
|
|||||||
|
|
||||||
texData.setNumLevels(decode(header->numberOfMipmapLevels));
|
texData.setNumLevels(decode(header->numberOfMipmapLevels));
|
||||||
texData.setNumFaces(decode(header->numberOfFaces));
|
texData.setNumFaces(decode(header->numberOfFaces));
|
||||||
quint32 offset = headerSize + decode(header->bytesOfKeyValueData);
|
|
||||||
|
const quint32 bytesOfKeyValueData = decode(header->bytesOfKeyValueData);
|
||||||
|
if (headerSize + bytesOfKeyValueData < buf.length()) // oob check
|
||||||
|
texData.setKeyValueMetadata(
|
||||||
|
decodeKeyValues(QByteArrayView(buf.data() + headerSize, bytesOfKeyValueData)));
|
||||||
|
quint32 offset = headerSize + bytesOfKeyValueData;
|
||||||
|
|
||||||
constexpr int MAX_ITERATIONS = 32; // cap iterations in case of corrupt data
|
constexpr int MAX_ITERATIONS = 32; // cap iterations in case of corrupt data
|
||||||
|
|
||||||
@ -213,7 +218,39 @@ bool QKtxHandler::checkHeader(const KTXHeader &header)
|
|||||||
return is2D && (isCubeMap || isCompressedImage);
|
return is2D && (isCubeMap || isCompressedImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
quint32 QKtxHandler::decode(quint32 val)
|
QMap<QByteArray, QByteArray> QKtxHandler::decodeKeyValues(QByteArrayView view) const
|
||||||
|
{
|
||||||
|
QMap<QByteArray, QByteArray> output;
|
||||||
|
quint32 offset = 0;
|
||||||
|
while (offset < view.size() + sizeof(quint32)) {
|
||||||
|
const quint32 keyAndValueByteSize =
|
||||||
|
decode(qFromUnaligned<quint32>(view.constData() + offset));
|
||||||
|
offset += sizeof(quint32);
|
||||||
|
|
||||||
|
if (offset + keyAndValueByteSize > view.size())
|
||||||
|
break; // oob read
|
||||||
|
|
||||||
|
// 'key' is a UTF-8 string ending with a null terminator, 'value' is the rest.
|
||||||
|
// To separate the key and value we convert the complete data to utf-8 and find the first
|
||||||
|
// null terminator from the left, here we split the data into two.
|
||||||
|
const auto str = QString::fromUtf8(view.constData() + offset, keyAndValueByteSize);
|
||||||
|
const int idx = str.indexOf(QLatin1Char('\0'));
|
||||||
|
if (idx == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const QByteArray key = str.left(idx).toUtf8();
|
||||||
|
const size_t keySize = key.size() + 1; // Actual data size
|
||||||
|
const QByteArray value = QByteArray::fromRawData(view.constData() + offset + keySize,
|
||||||
|
keyAndValueByteSize - keySize);
|
||||||
|
|
||||||
|
offset = withPadding(offset + keyAndValueByteSize, 4);
|
||||||
|
output.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 QKtxHandler::decode(quint32 val) const
|
||||||
{
|
{
|
||||||
return inverseEndian ? qbswap<quint32>(val) : val;
|
return inverseEndian ? qbswap<quint32>(val) : val;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool checkHeader(const KTXHeader &header);
|
bool checkHeader(const KTXHeader &header);
|
||||||
quint32 decode(quint32 val);
|
QMap<QByteArray, QByteArray> decodeKeyValues(QByteArrayView view) const;
|
||||||
|
quint32 decode(quint32 val) const;
|
||||||
|
|
||||||
bool inverseEndian = false;
|
bool inverseEndian = false;
|
||||||
};
|
};
|
||||||
|
@ -100,6 +100,7 @@ public:
|
|||||||
quint32 baseInternalFormat = 0;
|
quint32 baseInternalFormat = 0;
|
||||||
int numFaces = 0;
|
int numFaces = 0;
|
||||||
int numLevels = 0;
|
int numLevels = 0;
|
||||||
|
QMap<QByteArray, QByteArray> keyValues;
|
||||||
};
|
};
|
||||||
|
|
||||||
QTextureFileData::QTextureFileData()
|
QTextureFileData::QTextureFileData()
|
||||||
@ -292,6 +293,17 @@ void QTextureFileData::setLogName(const QByteArray &name)
|
|||||||
d->logName = name;
|
d->logName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMap<QByteArray, QByteArray> QTextureFileData::keyValueMetadata() const
|
||||||
|
{
|
||||||
|
return d ? d->keyValues : QMap<QByteArray, QByteArray>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QTextureFileData::setKeyValueMetadata(const QMap<QByteArray, QByteArray> &keyValues)
|
||||||
|
{
|
||||||
|
if (d)
|
||||||
|
d->keyValues = keyValues;
|
||||||
|
}
|
||||||
|
|
||||||
static QByteArray glFormatName(quint32 fmt)
|
static QByteArray glFormatName(quint32 fmt)
|
||||||
{
|
{
|
||||||
return QByteArray("0x" + QByteArray::number(fmt, 16).rightJustified(4, '0'));
|
return QByteArray("0x" + QByteArray::number(fmt, 16).rightJustified(4, '0'));
|
||||||
|
@ -107,6 +107,9 @@ public:
|
|||||||
QByteArray logName() const;
|
QByteArray logName() const;
|
||||||
void setLogName(const QByteArray &name);
|
void setLogName(const QByteArray &name);
|
||||||
|
|
||||||
|
QMap<QByteArray, QByteArray> keyValueMetadata() const;
|
||||||
|
void setKeyValueMetadata(const QMap<QByteArray, QByteArray> &keyValues);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSharedDataPointer<QTextureFileDataPrivate> d;
|
QSharedDataPointer<QTextureFileDataPrivate> d;
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,7 @@ set(qtexturefilereader_resource_files
|
|||||||
"texturefiles/car.ktx"
|
"texturefiles/car.ktx"
|
||||||
"texturefiles/car_mips.ktx"
|
"texturefiles/car_mips.ktx"
|
||||||
"texturefiles/cubemap_float32_rgba.ktx"
|
"texturefiles/cubemap_float32_rgba.ktx"
|
||||||
|
"texturefiles/cubemap_metadata.ktx"
|
||||||
"texturefiles/newlogo.astc"
|
"texturefiles/newlogo.astc"
|
||||||
"texturefiles/newlogo_srgb.astc"
|
"texturefiles/newlogo_srgb.astc"
|
||||||
"texturefiles/pattern.pkm"
|
"texturefiles/pattern.pkm"
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>texturefiles/car.ktx</file>
|
<file>texturefiles/car.ktx</file>
|
||||||
<file>texturefiles/cubemap_float32_rgba.ktx</file>
|
<file>texturefiles/cubemap_float32_rgba.ktx</file>
|
||||||
|
<file>texturefiles/cubemap_metadata.ktx</file>
|
||||||
<file>texturefiles/pattern.pkm</file>
|
<file>texturefiles/pattern.pkm</file>
|
||||||
<file>texturefiles/car_mips.ktx</file>
|
<file>texturefiles/car_mips.ktx</file>
|
||||||
<file>texturefiles/newlogo_srgb.astc</file>
|
<file>texturefiles/newlogo_srgb.astc</file>
|
||||||
|
Binary file not shown.
@ -36,6 +36,7 @@ class tst_qtexturefilereader : public QObject
|
|||||||
private slots:
|
private slots:
|
||||||
void checkHandlers_data();
|
void checkHandlers_data();
|
||||||
void checkHandlers();
|
void checkHandlers();
|
||||||
|
void checkMetadata();
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_qtexturefilereader::checkHandlers_data()
|
void tst_qtexturefilereader::checkHandlers_data()
|
||||||
@ -148,6 +149,22 @@ void tst_qtexturefilereader::checkHandlers()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_qtexturefilereader::checkMetadata()
|
||||||
|
{
|
||||||
|
QFile f(":/texturefiles/cubemap_metadata.ktx");
|
||||||
|
QVERIFY(f.open(QIODevice::ReadOnly));
|
||||||
|
QTextureFileReader r(&f);
|
||||||
|
QTextureFileData d = r.read();
|
||||||
|
auto kvs = d.keyValueMetadata();
|
||||||
|
|
||||||
|
QVERIFY(kvs.contains("test A"));
|
||||||
|
QVERIFY(kvs.contains("test B"));
|
||||||
|
QVERIFY(kvs.contains("test C"));
|
||||||
|
QCOMPARE(kvs.value("test A"), QByteArrayLiteral("1\x0000"));
|
||||||
|
QCOMPARE(kvs.value("test B"), QByteArrayLiteral("2\x0000"));
|
||||||
|
QCOMPARE(kvs.value("test C"), QByteArrayLiteral("3\x0000"));
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_qtexturefilereader)
|
QTEST_MAIN(tst_qtexturefilereader)
|
||||||
|
|
||||||
#include "tst_qtexturefilereader.moc"
|
#include "tst_qtexturefilereader.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user