Support cubemap ktx files
Change-Id: I6905c393647d31fab958cdd9471bb0a6ffe83596 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io> Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
This commit is contained in:
parent
5caf4b074e
commit
c5e9708ce6
@ -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<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));
|
||||
|
||||
const quint32 imageSize = decode(qFromUnaligned<quint32>(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)
|
||||
|
@ -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"
|
||||
|
@ -1,6 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>texturefiles/car.ktx</file>
|
||||
<file>texturefiles/cubemap_float32_rgba.ktx</file>
|
||||
<file>texturefiles/pattern.pkm</file>
|
||||
<file>texturefiles/car_mips.ktx</file>
|
||||
<file>texturefiles/newlogo_srgb.astc</file>
|
||||
|
Binary file not shown.
@ -46,6 +46,7 @@ void tst_qtexturefilereader::checkHandlers_data()
|
||||
QTest::addColumn<quint32>("glInternalFormat");
|
||||
QTest::addColumn<quint32>("glBaseInternalFormat");
|
||||
QTest::addColumn<int>("levels");
|
||||
QTest::addColumn<int>("faces");
|
||||
QTest::addColumn<QList<int>>("dataOffsets");
|
||||
QTest::addColumn<QList<int>>("dataLengths");
|
||||
|
||||
@ -56,6 +57,7 @@ void tst_qtexturefilereader::checkHandlers_data()
|
||||
<< quint32(0x8d64)
|
||||
<< quint32(0x0)
|
||||
<< 1
|
||||
<< 1
|
||||
<< (QList<int>() << 16)
|
||||
<< (QList<int>() << 2048);
|
||||
|
||||
@ -66,6 +68,7 @@ void tst_qtexturefilereader::checkHandlers_data()
|
||||
<< quint32(0x9278)
|
||||
<< quint32(0x1908)
|
||||
<< 1
|
||||
<< 1
|
||||
<< (QList<int>() << 68)
|
||||
<< (QList<int>() << 11840);
|
||||
|
||||
@ -76,9 +79,21 @@ void tst_qtexturefilereader::checkHandlers_data()
|
||||
<< quint32(0x9274)
|
||||
<< quint32(0x1907)
|
||||
<< 8
|
||||
<< 1
|
||||
<< (QList<int>() << 68 << 5992 << 7516 << 7880 << 8004 << 8056 << 8068 << 8080)
|
||||
<< (QList<int>() << 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<int>() << 96 << 24676 << 30824 << 32364 << 32752)
|
||||
<< (QList<int>() << 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<int>() << 16)
|
||||
<< (QList<int>() << 2496);
|
||||
|
||||
@ -96,6 +112,7 @@ void tst_qtexturefilereader::checkHandlers_data()
|
||||
<< quint32(0x93d9)
|
||||
<< quint32(0x0)
|
||||
<< 1
|
||||
<< 1
|
||||
<< (QList<int>() << 16)
|
||||
<< (QList<int>() << 2496);
|
||||
}
|
||||
@ -107,6 +124,7 @@ void tst_qtexturefilereader::checkHandlers()
|
||||
QFETCH(quint32, glFormat);
|
||||
QFETCH(quint32, glInternalFormat);
|
||||
QFETCH(int, levels);
|
||||
QFETCH(int, faces);
|
||||
QFETCH(QList<int>, dataOffsets);
|
||||
QFETCH(QList<int>, 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));
|
||||
|
Loading…
x
Reference in New Issue
Block a user