From 2861cfb6f851d7cee503b19f0e800a33374db66c Mon Sep 17 00:00:00 2001 From: Jonas Karlsson Date: Thu, 4 Feb 2021 15:37:53 +0100 Subject: [PATCH] QTextureFileData: support key value metadata Task-Id: QTBUG-76970 Pick-to: 6.1 Change-Id: I9dba1b373250cea4d0c806997290a7afcdc900d7 Reviewed-by: Eirik Aavitsland --- src/gui/util/qktxhandler.cpp | 41 +++++++++++++++++- src/gui/util/qktxhandler_p.h | 3 +- src/gui/util/qtexturefiledata.cpp | 12 +++++ src/gui/util/qtexturefiledata_p.h | 3 ++ .../util/qtexturefilereader/CMakeLists.txt | 1 + .../qtexturefilereader/qtexturefilereader.qrc | 1 + .../texturefiles/cubemap_metadata.ktx | Bin 0 -> 4916 bytes .../tst_qtexturefilereader.cpp | 17 ++++++++ 8 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 tests/auto/gui/util/qtexturefilereader/texturefiles/cubemap_metadata.ktx diff --git a/src/gui/util/qktxhandler.cpp b/src/gui/util/qktxhandler.cpp index 27fb69a3489..cd8ca3635e7 100644 --- a/src/gui/util/qktxhandler.cpp +++ b/src/gui/util/qktxhandler.cpp @@ -146,7 +146,12 @@ QTextureFileData QKtxHandler::read() texData.setNumLevels(decode(header->numberOfMipmapLevels)); 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 @@ -213,7 +218,39 @@ bool QKtxHandler::checkHeader(const KTXHeader &header) return is2D && (isCubeMap || isCompressedImage); } -quint32 QKtxHandler::decode(quint32 val) +QMap QKtxHandler::decodeKeyValues(QByteArrayView view) const +{ + QMap output; + quint32 offset = 0; + while (offset < view.size() + sizeof(quint32)) { + const quint32 keyAndValueByteSize = + decode(qFromUnaligned(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(val) : val; } diff --git a/src/gui/util/qktxhandler_p.h b/src/gui/util/qktxhandler_p.h index 19f7b0e79a4..4298433f367 100644 --- a/src/gui/util/qktxhandler_p.h +++ b/src/gui/util/qktxhandler_p.h @@ -68,7 +68,8 @@ public: private: bool checkHeader(const KTXHeader &header); - quint32 decode(quint32 val); + QMap decodeKeyValues(QByteArrayView view) const; + quint32 decode(quint32 val) const; bool inverseEndian = false; }; diff --git a/src/gui/util/qtexturefiledata.cpp b/src/gui/util/qtexturefiledata.cpp index 0a3958aaf24..6549206a26f 100644 --- a/src/gui/util/qtexturefiledata.cpp +++ b/src/gui/util/qtexturefiledata.cpp @@ -100,6 +100,7 @@ public: quint32 baseInternalFormat = 0; int numFaces = 0; int numLevels = 0; + QMap keyValues; }; QTextureFileData::QTextureFileData() @@ -292,6 +293,17 @@ void QTextureFileData::setLogName(const QByteArray &name) d->logName = name; } +QMap QTextureFileData::keyValueMetadata() const +{ + return d ? d->keyValues : QMap(); +} + +void QTextureFileData::setKeyValueMetadata(const QMap &keyValues) +{ + if (d) + d->keyValues = keyValues; +} + static QByteArray glFormatName(quint32 fmt) { return QByteArray("0x" + QByteArray::number(fmt, 16).rightJustified(4, '0')); diff --git a/src/gui/util/qtexturefiledata_p.h b/src/gui/util/qtexturefiledata_p.h index f6a03c0550c..e553733d697 100644 --- a/src/gui/util/qtexturefiledata_p.h +++ b/src/gui/util/qtexturefiledata_p.h @@ -107,6 +107,9 @@ public: QByteArray logName() const; void setLogName(const QByteArray &name); + QMap keyValueMetadata() const; + void setKeyValueMetadata(const QMap &keyValues); + private: QSharedDataPointer d; }; diff --git a/tests/auto/gui/util/qtexturefilereader/CMakeLists.txt b/tests/auto/gui/util/qtexturefilereader/CMakeLists.txt index e1731a45d55..de809f577d6 100644 --- a/tests/auto/gui/util/qtexturefilereader/CMakeLists.txt +++ b/tests/auto/gui/util/qtexturefilereader/CMakeLists.txt @@ -17,6 +17,7 @@ set(qtexturefilereader_resource_files "texturefiles/car.ktx" "texturefiles/car_mips.ktx" "texturefiles/cubemap_float32_rgba.ktx" + "texturefiles/cubemap_metadata.ktx" "texturefiles/newlogo.astc" "texturefiles/newlogo_srgb.astc" "texturefiles/pattern.pkm" diff --git a/tests/auto/gui/util/qtexturefilereader/qtexturefilereader.qrc b/tests/auto/gui/util/qtexturefilereader/qtexturefilereader.qrc index a014c0367a8..c4cef6cc443 100644 --- a/tests/auto/gui/util/qtexturefilereader/qtexturefilereader.qrc +++ b/tests/auto/gui/util/qtexturefilereader/qtexturefilereader.qrc @@ -2,6 +2,7 @@ texturefiles/car.ktx texturefiles/cubemap_float32_rgba.ktx + texturefiles/cubemap_metadata.ktx texturefiles/pattern.pkm texturefiles/car_mips.ktx texturefiles/newlogo_srgb.astc diff --git a/tests/auto/gui/util/qtexturefilereader/texturefiles/cubemap_metadata.ktx b/tests/auto/gui/util/qtexturefilereader/texturefiles/cubemap_metadata.ktx new file mode 100644 index 0000000000000000000000000000000000000000..a29f68a7c911f5d027ad386288db91190db0893c GIT binary patch literal 4916 zcmd^@K~BRk5Jji7LJ-7`4PE634G0NPH^7oDmAC*Wpoi#L;0V23XU4J+uAePdL{22< zXTJS^?8(fc{eFFW?}uT3*)6--$$W8fC9)8avuly%E?}pzjd-2v!%W9Y$GMK}$LF?x zl0hGT;%B+}6~D->#r<4wYaeSwTNq~ZSktHTE&Am%6HaRPkmWpDeoFdu-k1B=3c55- z*Vp1ypT;$PI-ly(I9+eC)fb`Z|FQnQ9>e<-VFMAEra1YLeddW zK9MIK@li)eI^xME@>!kMzw;S%U7-M_Y>CqNJo5=ewsl)ZwCFmh5jHN z@lE=Txc=l`Gj9D!Xdk4|zo?s%pJm9-n?t8{o{)6JlTYMHM|{)~l8$)tiF{5s;`-a% z>Bg;p0sUBo{vaLkP5NmD{k$0rWx@SHI^vu38*}~HFL~P^V6~n>Hz_~s3;##t7_0RR Kx=Hzsxc&}KL7r#; literal 0 HcmV?d00001 diff --git a/tests/auto/gui/util/qtexturefilereader/tst_qtexturefilereader.cpp b/tests/auto/gui/util/qtexturefilereader/tst_qtexturefilereader.cpp index cd90914bd96..89144eb245e 100644 --- a/tests/auto/gui/util/qtexturefilereader/tst_qtexturefilereader.cpp +++ b/tests/auto/gui/util/qtexturefilereader/tst_qtexturefilereader.cpp @@ -36,6 +36,7 @@ class tst_qtexturefilereader : public QObject private slots: void checkHandlers_data(); void checkHandlers(); + void checkMetadata(); }; 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) #include "tst_qtexturefilereader.moc"