From 9d1c881f491363f330284ddb177cbee5f3e9952d Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Wed, 15 Aug 2018 10:58:12 +0200 Subject: [PATCH] Texture file support: add mipmap reading to ktx handler Change-Id: Ic2c10b3e64d63d2272a8a3922d2b3f99dfd45bdb Reviewed-by: Laszlo Agocs --- src/gui/util/qktxhandler.cpp | 30 ++++++++++----- .../qtexturefilereader/qtexturefilereader.qrc | 1 + .../texturefiles/car_mips.ktx | Bin 0 -> 8088 bytes .../tst_qtexturefilereader.cpp | 36 ++++++++++++------ 4 files changed, 46 insertions(+), 21 deletions(-) create mode 100644 tests/auto/gui/util/qtexturefilereader/texturefiles/car_mips.ktx diff --git a/src/gui/util/qktxhandler.cpp b/src/gui/util/qktxhandler.cpp index c7831f8143c..7eda4c46fb2 100644 --- a/src/gui/util/qktxhandler.cpp +++ b/src/gui/util/qktxhandler.cpp @@ -43,6 +43,11 @@ #include //#define KTX_DEBUG +#ifdef KTX_DEBUG +#include +#include +#include +#endif QT_BEGIN_NAMESPACE @@ -68,7 +73,7 @@ struct KTXHeader { quint32 bytesOfKeyValueData; }; -static const int headerSize = sizeof(KTXHeader); +static const quint32 headerSize = sizeof(KTXHeader); // Currently unused, declared for future reference struct KTXKeyValuePairItem { @@ -111,7 +116,8 @@ QTextureFileData QKtxHandler::read() return QTextureFileData(); QByteArray buf = device()->readAll(); - if (buf.size() < headerSize || !canRead(QByteArray(), buf)) { + const quint32 dataSize = quint32(buf.size()); + if (dataSize < headerSize || !canRead(QByteArray(), buf)) { qCDebug(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData()); return QTextureFileData(); } @@ -130,13 +136,17 @@ QTextureFileData QKtxHandler::read() texData.setGLInternalFormat(decode(header->glInternalFormat)); texData.setGLBaseInternalFormat(decode(header->glBaseInternalFormat)); - //### For now, ignore any additional mipmap levels - texData.setNumLevels(1); - int preambleSize = headerSize + decode(header->bytesOfKeyValueData); - if (buf.size() >= preambleSize + int(sizeof(KTXMipmapLevel))) { - texData.setDataOffset(preambleSize + sizeof(quint32)); // for the imageSize - const KTXMipmapLevel *level = reinterpret_cast(buf.constData() + preambleSize); - texData.setDataLength(decode(level->imageSize)); + texData.setNumLevels(decode(header->numberOfMipmapLevels)); + quint32 offset = headerSize + decode(header->bytesOfKeyValueData); + const int maxLevels = qMin(texData.numLevels(), 32); // Cap iterations in case of corrupt file. + for (int i = 0; i < maxLevels; i++) { + if (offset + sizeof(KTXMipmapLevel) > dataSize) // Corrupt file; avoid oob read + break; + const KTXMipmapLevel *level = reinterpret_cast(buf.constData() + offset); + quint32 levelLen = decode(level->imageSize); + texData.setDataOffset(offset + sizeof(KTXMipmapLevel::imageSize), i); + texData.setDataLength(levelLen, i); + offset += sizeof(KTXMipmapLevel::imageSize) + levelLen + (3 - ((levelLen + 3) % 4)); } if (!texData.isValid()) { @@ -147,7 +157,7 @@ QTextureFileData QKtxHandler::read() texData.setLogName(logName()); #ifdef KTX_DEBUG - qDebug() << "KTX file handler read" << texData.data(); + qDebug() << "KTX file handler read" << texData; #endif return texData; diff --git a/tests/auto/gui/util/qtexturefilereader/qtexturefilereader.qrc b/tests/auto/gui/util/qtexturefilereader/qtexturefilereader.qrc index 74f33d57ad6..ab882b5db25 100644 --- a/tests/auto/gui/util/qtexturefilereader/qtexturefilereader.qrc +++ b/tests/auto/gui/util/qtexturefilereader/qtexturefilereader.qrc @@ -2,5 +2,6 @@ texturefiles/car.ktx texturefiles/pattern.pkm + texturefiles/car_mips.ktx diff --git a/tests/auto/gui/util/qtexturefilereader/texturefiles/car_mips.ktx b/tests/auto/gui/util/qtexturefilereader/texturefiles/car_mips.ktx new file mode 100644 index 0000000000000000000000000000000000000000..82822e6c0b2ee78c8948fc6a130d3e144cad424c GIT binary patch literal 8088 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 o("size"); QTest::addColumn("glFormat"); QTest::addColumn("glInternalFormat"); - // todo: glBaseInternalFormat + QTest::addColumn("glBaseInternalFormat"); QTest::addColumn("levels"); - QTest::addColumn("dataOffset"); - QTest::addColumn("dataLength"); + QTest::addColumn>("dataOffsets"); + QTest::addColumn>("dataLengths"); QTest::addRow("pattern.pkm") << QStringLiteral(":/texturefiles/pattern.pkm") << QSize(64, 64) << quint32(0x0) << quint32(0x8d64) + << quint32(0x0) << 1 - << 16 - << 2048; + << (QList() << 16) + << (QList() << 2048); QTest::addRow("car.ktx") << QStringLiteral(":/texturefiles/car.ktx") << QSize(146, 80) << quint32(0x0) << quint32(0x9278) + << quint32(0x1908) << 1 - << 68 - << 11840; + << (QList() << 68) + << (QList() << 11840); + + QTest::addRow("car_mips.ktx") << QStringLiteral(":/texturefiles/car_mips.ktx") + << QSize(146, 80) + << quint32(0x0) + << quint32(0x9274) + << quint32(0x1907) + << 8 + << (QList() << 68 << 5992 << 7516 << 7880 << 8004 << 8056 << 8068 << 8080) + << (QList() << 5920 << 1520 << 360 << 120 << 48 << 8 << 8 << 8); + } void tst_qtexturefilereader::checkHandlers() @@ -73,8 +85,8 @@ void tst_qtexturefilereader::checkHandlers() QFETCH(quint32, glFormat); QFETCH(quint32, glInternalFormat); QFETCH(int, levels); - QFETCH(int, dataOffset); - QFETCH(int, dataLength); + QFETCH(QList, dataOffsets); + QFETCH(QList, dataLengths); QFile f(fileName); QVERIFY(f.open(QIODevice::ReadOnly)); @@ -88,8 +100,10 @@ void tst_qtexturefilereader::checkHandlers() QCOMPARE(tex.glFormat(), glFormat); QCOMPARE(tex.glInternalFormat(), glInternalFormat); QCOMPARE(tex.numLevels(), levels); - QCOMPARE(tex.dataOffset(), dataOffset); - QCOMPARE(tex.dataLength(), dataLength); + for (int i = 0; i < tex.numLevels(); i++) { + QCOMPARE(tex.dataOffset(i), dataOffsets.at(i)); + QCOMPARE(tex.dataLength(i), dataLengths.at(i)); + } } QTEST_MAIN(tst_qtexturefilereader)