diff options
Diffstat (limited to 'src/gui/util/qktxhandler.cpp')
-rw-r--r-- | src/gui/util/qktxhandler.cpp | 52 |
1 files changed, 36 insertions, 16 deletions
diff --git a/src/gui/util/qktxhandler.cpp b/src/gui/util/qktxhandler.cpp index 31c572db54..27fb69a348 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<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) |