summaryrefslogtreecommitdiffstats
path: root/src/gui/util/qktxhandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/util/qktxhandler.cpp')
-rw-r--r--src/gui/util/qktxhandler.cpp52
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)