Texture file support: add mipmap reading to ktx handler

Change-Id: Ic2c10b3e64d63d2272a8a3922d2b3f99dfd45bdb
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
This commit is contained in:
Eirik Aavitsland 2018-08-15 10:58:12 +02:00
parent 307d0b32f0
commit 9d1c881f49
4 changed files with 46 additions and 21 deletions

View File

@ -43,6 +43,11 @@
#include <QSize> #include <QSize>
//#define KTX_DEBUG //#define KTX_DEBUG
#ifdef KTX_DEBUG
#include <QDebug>
#include <QMetaEnum>
#include <QOpenGLTexture>
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -68,7 +73,7 @@ struct KTXHeader {
quint32 bytesOfKeyValueData; quint32 bytesOfKeyValueData;
}; };
static const int headerSize = sizeof(KTXHeader); static const quint32 headerSize = sizeof(KTXHeader);
// Currently unused, declared for future reference // Currently unused, declared for future reference
struct KTXKeyValuePairItem { struct KTXKeyValuePairItem {
@ -111,7 +116,8 @@ QTextureFileData QKtxHandler::read()
return QTextureFileData(); return QTextureFileData();
QByteArray buf = device()->readAll(); 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()); qCDebug(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData());
return QTextureFileData(); return QTextureFileData();
} }
@ -130,13 +136,17 @@ QTextureFileData QKtxHandler::read()
texData.setGLInternalFormat(decode(header->glInternalFormat)); texData.setGLInternalFormat(decode(header->glInternalFormat));
texData.setGLBaseInternalFormat(decode(header->glBaseInternalFormat)); texData.setGLBaseInternalFormat(decode(header->glBaseInternalFormat));
//### For now, ignore any additional mipmap levels texData.setNumLevels(decode(header->numberOfMipmapLevels));
texData.setNumLevels(1); quint32 offset = headerSize + decode(header->bytesOfKeyValueData);
int preambleSize = headerSize + decode(header->bytesOfKeyValueData); const int maxLevels = qMin(texData.numLevels(), 32); // Cap iterations in case of corrupt file.
if (buf.size() >= preambleSize + int(sizeof(KTXMipmapLevel))) { for (int i = 0; i < maxLevels; i++) {
texData.setDataOffset(preambleSize + sizeof(quint32)); // for the imageSize if (offset + sizeof(KTXMipmapLevel) > dataSize) // Corrupt file; avoid oob read
const KTXMipmapLevel *level = reinterpret_cast<const KTXMipmapLevel *>(buf.constData() + preambleSize); break;
texData.setDataLength(decode(level->imageSize)); const KTXMipmapLevel *level = reinterpret_cast<const KTXMipmapLevel *>(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()) { if (!texData.isValid()) {
@ -147,7 +157,7 @@ QTextureFileData QKtxHandler::read()
texData.setLogName(logName()); texData.setLogName(logName());
#ifdef KTX_DEBUG #ifdef KTX_DEBUG
qDebug() << "KTX file handler read" << texData.data(); qDebug() << "KTX file handler read" << texData;
#endif #endif
return texData; return texData;

View File

@ -2,5 +2,6 @@
<qresource prefix="/"> <qresource prefix="/">
<file>texturefiles/car.ktx</file> <file>texturefiles/car.ktx</file>
<file>texturefiles/pattern.pkm</file> <file>texturefiles/pattern.pkm</file>
<file>texturefiles/car_mips.ktx</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -44,26 +44,38 @@ void tst_qtexturefilereader::checkHandlers_data()
QTest::addColumn<QSize>("size"); QTest::addColumn<QSize>("size");
QTest::addColumn<quint32>("glFormat"); QTest::addColumn<quint32>("glFormat");
QTest::addColumn<quint32>("glInternalFormat"); QTest::addColumn<quint32>("glInternalFormat");
// todo: glBaseInternalFormat QTest::addColumn<quint32>("glBaseInternalFormat");
QTest::addColumn<int>("levels"); QTest::addColumn<int>("levels");
QTest::addColumn<int>("dataOffset"); QTest::addColumn<QList<int>>("dataOffsets");
QTest::addColumn<int>("dataLength"); QTest::addColumn<QList<int>>("dataLengths");
QTest::addRow("pattern.pkm") << QStringLiteral(":/texturefiles/pattern.pkm") QTest::addRow("pattern.pkm") << QStringLiteral(":/texturefiles/pattern.pkm")
<< QSize(64, 64) << QSize(64, 64)
<< quint32(0x0) << quint32(0x0)
<< quint32(0x8d64) << quint32(0x8d64)
<< quint32(0x0)
<< 1 << 1
<< 16 << (QList<int>() << 16)
<< 2048; << (QList<int>() << 2048);
QTest::addRow("car.ktx") << QStringLiteral(":/texturefiles/car.ktx") QTest::addRow("car.ktx") << QStringLiteral(":/texturefiles/car.ktx")
<< QSize(146, 80) << QSize(146, 80)
<< quint32(0x0) << quint32(0x0)
<< quint32(0x9278) << quint32(0x9278)
<< quint32(0x1908)
<< 1 << 1
<< 68 << (QList<int>() << 68)
<< 11840; << (QList<int>() << 11840);
QTest::addRow("car_mips.ktx") << QStringLiteral(":/texturefiles/car_mips.ktx")
<< QSize(146, 80)
<< quint32(0x0)
<< quint32(0x9274)
<< quint32(0x1907)
<< 8
<< (QList<int>() << 68 << 5992 << 7516 << 7880 << 8004 << 8056 << 8068 << 8080)
<< (QList<int>() << 5920 << 1520 << 360 << 120 << 48 << 8 << 8 << 8);
} }
void tst_qtexturefilereader::checkHandlers() void tst_qtexturefilereader::checkHandlers()
@ -73,8 +85,8 @@ void tst_qtexturefilereader::checkHandlers()
QFETCH(quint32, glFormat); QFETCH(quint32, glFormat);
QFETCH(quint32, glInternalFormat); QFETCH(quint32, glInternalFormat);
QFETCH(int, levels); QFETCH(int, levels);
QFETCH(int, dataOffset); QFETCH(QList<int>, dataOffsets);
QFETCH(int, dataLength); QFETCH(QList<int>, dataLengths);
QFile f(fileName); QFile f(fileName);
QVERIFY(f.open(QIODevice::ReadOnly)); QVERIFY(f.open(QIODevice::ReadOnly));
@ -88,8 +100,10 @@ void tst_qtexturefilereader::checkHandlers()
QCOMPARE(tex.glFormat(), glFormat); QCOMPARE(tex.glFormat(), glFormat);
QCOMPARE(tex.glInternalFormat(), glInternalFormat); QCOMPARE(tex.glInternalFormat(), glInternalFormat);
QCOMPARE(tex.numLevels(), levels); QCOMPARE(tex.numLevels(), levels);
QCOMPARE(tex.dataOffset(), dataOffset); for (int i = 0; i < tex.numLevels(); i++) {
QCOMPARE(tex.dataLength(), dataLength); QCOMPARE(tex.dataOffset(i), dataOffsets.at(i));
QCOMPARE(tex.dataLength(i), dataLengths.at(i));
}
} }
QTEST_MAIN(tst_qtexturefilereader) QTEST_MAIN(tst_qtexturefilereader)