From c5e9708ce6e8073de65efe1af31b74e8a4dcbcee Mon Sep 17 00:00:00 2001 From: Jonas Karlsson Date: Fri, 29 Jan 2021 10:45:22 +0100 Subject: [PATCH] Support cubemap ktx files Change-Id: I6905c393647d31fab958cdd9471bb0a6ffe83596 Reviewed-by: Eirik Aavitsland Reviewed-by: Laszlo Agocs --- src/gui/util/qktxhandler.cpp | 52 ++++++++++++------ .../util/qtexturefilereader/CMakeLists.txt | 1 + .../qtexturefilereader/qtexturefilereader.qrc | 1 + .../texturefiles/cubemap_float32_rgba.ktx | Bin 0 -> 32848 bytes .../tst_qtexturefilereader.cpp | 20 +++++++ 5 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 tests/auto/gui/util/qtexturefilereader/texturefiles/cubemap_float32_rgba.ktx diff --git a/src/gui/util/qktxhandler.cpp b/src/gui/util/qktxhandler.cpp index 31c572db542..27fb69a3489 100644 --- a/src/gui/util/qktxhandler.cpp +++ b/src/gui/util/qktxhandler.cpp @@ -104,6 +104,13 @@ struct KTXMipmapLevel { */ }; +// Returns the nearest multiple of 'rounding' greater than or equal to 'value' +constexpr quint32 withPadding(quint32 value, quint32 rounding) +{ + Q_ASSERT(rounding > 1); + return value + (rounding - 1) - ((value + (rounding - 1)) % rounding); +} + bool QKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block) { Q_UNUSED(suffix); @@ -138,17 +145,25 @@ QTextureFileData QKtxHandler::read() texData.setGLBaseInternalFormat(decode(header->glBaseInternalFormat)); texData.setNumLevels(decode(header->numberOfMipmapLevels)); - texData.setNumFaces(1); + texData.setNumFaces(decode(header->numberOfFaces)); 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 + + constexpr int MAX_ITERATIONS = 32; // cap iterations in case of corrupt data + + for (int level = 0; level < qMin(texData.numLevels(), MAX_ITERATIONS); level++) { + if (offset + sizeof(quint32) > 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)); + + const quint32 imageSize = decode(qFromUnaligned(buf.constData() + offset)); + offset += sizeof(quint32); + + for (int face = 0; face < qMin(texData.numFaces(), MAX_ITERATIONS); face++) { + texData.setDataOffset(offset, level, face); + texData.setDataLength(imageSize, level, face); + + // Add image data and padding to offset + offset += withPadding(imageSize, 4); + } } if (!texData.isValid()) { @@ -176,9 +191,12 @@ bool QKtxHandler::checkHeader(const KTXHeader &header) qDebug("Header of %s:", logName().constData()); qDebug(" glType: 0x%x (%s)", decode(header.glType), ptme.valueToKey(decode(header.glType))); qDebug(" glTypeSize: %u", decode(header.glTypeSize)); - qDebug(" glFormat: 0x%x (%s)", decode(header.glFormat), tfme.valueToKey(decode(header.glFormat))); - qDebug(" glInternalFormat: 0x%x (%s)", decode(header.glInternalFormat), tfme.valueToKey(decode(header.glInternalFormat))); - qDebug(" glBaseInternalFormat: 0x%x (%s)", decode(header.glBaseInternalFormat), tfme.valueToKey(decode(header.glBaseInternalFormat))); + qDebug(" glFormat: 0x%x (%s)", decode(header.glFormat), + tfme.valueToKey(decode(header.glFormat))); + qDebug(" glInternalFormat: 0x%x (%s)", decode(header.glInternalFormat), + tfme.valueToKey(decode(header.glInternalFormat))); + qDebug(" glBaseInternalFormat: 0x%x (%s)", decode(header.glBaseInternalFormat), + tfme.valueToKey(decode(header.glBaseInternalFormat))); qDebug(" pixelWidth: %u", decode(header.pixelWidth)); qDebug(" pixelHeight: %u", decode(header.pixelHeight)); qDebug(" pixelDepth: %u", decode(header.pixelDepth)); @@ -187,10 +205,12 @@ bool QKtxHandler::checkHeader(const KTXHeader &header) qDebug(" numberOfMipmapLevels: %u", decode(header.numberOfMipmapLevels)); qDebug(" bytesOfKeyValueData: %u", decode(header.bytesOfKeyValueData)); #endif - return ((decode(header.glType) == 0) && - (decode(header.glFormat) == 0) && - (decode(header.pixelDepth) == 0) && - (decode(header.numberOfFaces) == 1)); + const bool isCompressedImage = decode(header.glType) == 0 && decode(header.glFormat) == 0 + && decode(header.pixelDepth) == 0; + const bool isCubeMap = decode(header.numberOfFaces) == 6; + const bool is2D = decode(header.pixelDepth) == 0 && decode(header.numberOfArrayElements) == 0; + + return is2D && (isCubeMap || isCompressedImage); } quint32 QKtxHandler::decode(quint32 val) diff --git a/tests/auto/gui/util/qtexturefilereader/CMakeLists.txt b/tests/auto/gui/util/qtexturefilereader/CMakeLists.txt index 91083348f76..e1731a45d55 100644 --- a/tests/auto/gui/util/qtexturefilereader/CMakeLists.txt +++ b/tests/auto/gui/util/qtexturefilereader/CMakeLists.txt @@ -16,6 +16,7 @@ qt_internal_add_test(tst_qtexturefilereader set(qtexturefilereader_resource_files "texturefiles/car.ktx" "texturefiles/car_mips.ktx" + "texturefiles/cubemap_float32_rgba.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 8aab86e1ffe..a014c0367a8 100644 --- a/tests/auto/gui/util/qtexturefilereader/qtexturefilereader.qrc +++ b/tests/auto/gui/util/qtexturefilereader/qtexturefilereader.qrc @@ -1,6 +1,7 @@ texturefiles/car.ktx + texturefiles/cubemap_float32_rgba.ktx texturefiles/pattern.pkm texturefiles/car_mips.ktx texturefiles/newlogo_srgb.astc diff --git a/tests/auto/gui/util/qtexturefilereader/texturefiles/cubemap_float32_rgba.ktx b/tests/auto/gui/util/qtexturefilereader/texturefiles/cubemap_float32_rgba.ktx new file mode 100644 index 0000000000000000000000000000000000000000..25b48b13709a27b7374691eb7b97ee3b44f56da6 GIT binary patch literal 32848 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 B2igDt 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 6ff3497a74d..cd90914bd96 100644 --- a/tests/auto/gui/util/qtexturefilereader/tst_qtexturefilereader.cpp +++ b/tests/auto/gui/util/qtexturefilereader/tst_qtexturefilereader.cpp @@ -46,6 +46,7 @@ void tst_qtexturefilereader::checkHandlers_data() QTest::addColumn("glInternalFormat"); QTest::addColumn("glBaseInternalFormat"); QTest::addColumn("levels"); + QTest::addColumn("faces"); QTest::addColumn>("dataOffsets"); QTest::addColumn>("dataLengths"); @@ -56,6 +57,7 @@ void tst_qtexturefilereader::checkHandlers_data() << quint32(0x8d64) << quint32(0x0) << 1 + << 1 << (QList() << 16) << (QList() << 2048); @@ -66,6 +68,7 @@ void tst_qtexturefilereader::checkHandlers_data() << quint32(0x9278) << quint32(0x1908) << 1 + << 1 << (QList() << 68) << (QList() << 11840); @@ -76,9 +79,21 @@ void tst_qtexturefilereader::checkHandlers_data() << quint32(0x9274) << quint32(0x1907) << 8 + << 1 << (QList() << 68 << 5992 << 7516 << 7880 << 8004 << 8056 << 8068 << 8080) << (QList() << 5920 << 1520 << 360 << 120 << 48 << 8 << 8 << 8); + QTest::addRow("cubemap_float32_rgba.ktx") + << QStringLiteral(":/texturefiles/cubemap_float32_rgba.ktx") + << QSize(16, 16) + << quint32(0x1908) + << quint32(0x8814) + << quint32(0x1908) + << 5 + << 6 + << (QList() << 96 << 24676 << 30824 << 32364 << 32752) + << (QList() << 4096 << 1024 << 256 << 64 << 16); + QTest::addRow("newlogo.astc") << QStringLiteral(":/texturefiles/newlogo.astc") << QSize(111, 78) @@ -86,6 +101,7 @@ void tst_qtexturefilereader::checkHandlers_data() << quint32(0x93b9) << quint32(0x0) << 1 + << 1 << (QList() << 16) << (QList() << 2496); @@ -96,6 +112,7 @@ void tst_qtexturefilereader::checkHandlers_data() << quint32(0x93d9) << quint32(0x0) << 1 + << 1 << (QList() << 16) << (QList() << 2496); } @@ -107,6 +124,7 @@ void tst_qtexturefilereader::checkHandlers() QFETCH(quint32, glFormat); QFETCH(quint32, glInternalFormat); QFETCH(int, levels); + QFETCH(int, faces); QFETCH(QList, dataOffsets); QFETCH(QList, dataLengths); @@ -122,6 +140,8 @@ void tst_qtexturefilereader::checkHandlers() QCOMPARE(tex.glFormat(), glFormat); QCOMPARE(tex.glInternalFormat(), glInternalFormat); QCOMPARE(tex.numLevels(), levels); + QCOMPARE(tex.numFaces(), faces); + for (int i = 0; i < tex.numLevels(); i++) { QCOMPARE(tex.dataOffset(i), dataOffsets.at(i)); QCOMPARE(tex.dataLength(i), dataLengths.at(i));