Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qktxhandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qktxhandler_p.h"
6#include <QtEndian>
7#include <QSize>
8#include <QMap>
9#include <QtCore/qiodevice.h>
10
11//#define KTX_DEBUG
12#ifdef KTX_DEBUG
13#include <QDebug>
14#include <QMetaEnum>
15#include <QOpenGLTexture>
16#endif
17
19
20using namespace Qt::StringLiterals;
21
22#define KTX_IDENTIFIER_LENGTH 12
23static const char ktxIdentifier[KTX_IDENTIFIER_LENGTH] = { '\xAB', 'K', 'T', 'X', ' ', '1', '1', '\xBB', '\r', '\n', '\x1A', '\n' };
24static const quint32 platformEndianIdentifier = 0x04030201;
25static const quint32 inversePlatformEndianIdentifier = 0x01020304;
26
27struct KTXHeader {
28 quint8 identifier[KTX_IDENTIFIER_LENGTH]; // Must match ktxIdentifier
29 quint32 endianness; // Either platformEndianIdentifier or inversePlatformEndianIdentifier, other values not allowed.
42};
43
44static const quint32 qktxh_headerSize = sizeof(KTXHeader);
45
46// Currently unused, declared for future reference
49 /*
50 quint8 keyAndValue[keyAndValueByteSize];
51 quint8 valuePadding[3 - ((keyAndValueByteSize + 3) % 4)];
52 */
53};
54
57 /*
58 for each array_element in numberOfArrayElements*
59 for each face in numberOfFaces
60 for each z_slice in pixelDepth*
61 for each row or row_of_blocks in pixelHeight*
62 for each pixel or block_of_pixels in pixelWidth
63 Byte data[format-specific-number-of-bytes]**
64 end
65 end
66 end
67 Byte cubePadding[0-3]
68 end
69 end
70 quint8 mipPadding[3 - ((imageSize + 3) % 4)]
71 */
72};
73
74// Returns the nearest multiple of 'rounding' greater than or equal to 'value'
76{
77 Q_ASSERT(rounding > 1);
78 return value + (rounding - 1) - ((value + (rounding - 1)) % rounding);
79}
80
82
83bool QKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block)
84{
85 Q_UNUSED(suffix);
86
88}
89
91{
92 if (!device())
93 return QTextureFileData();
94
95 const QByteArray buf = device()->readAll();
96 const quint32 dataSize = quint32(buf.size());
98 qCDebug(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData());
99 return QTextureFileData();
100 }
101
102 const KTXHeader *header = reinterpret_cast<const KTXHeader *>(buf.data());
103 if (!checkHeader(*header)) {
104 qCDebug(lcQtGuiTextureIO, "Unsupported KTX file format in %s", logName().constData());
105 return QTextureFileData();
106 }
107
108 QTextureFileData texData;
109 texData.setData(buf);
110
111 texData.setSize(QSize(decode(header->pixelWidth), decode(header->pixelHeight)));
112 texData.setGLFormat(decode(header->glFormat));
113 texData.setGLInternalFormat(decode(header->glInternalFormat));
114 texData.setGLBaseInternalFormat(decode(header->glBaseInternalFormat));
115
116 texData.setNumLevels(decode(header->numberOfMipmapLevels));
117 texData.setNumFaces(decode(header->numberOfFaces));
118
119 const quint32 bytesOfKeyValueData = decode(header->bytesOfKeyValueData);
120 if (qktxh_headerSize + bytesOfKeyValueData < quint64(buf.size())) // oob check
121 texData.setKeyValueMetadata(decodeKeyValues(
122 QByteArrayView(buf.data() + qktxh_headerSize, bytesOfKeyValueData)));
123 quint32 offset = qktxh_headerSize + bytesOfKeyValueData;
124
125 constexpr int MAX_ITERATIONS = 32; // cap iterations in case of corrupt data
126
127 for (int level = 0; level < qMin(texData.numLevels(), MAX_ITERATIONS); level++) {
128 if (offset + sizeof(quint32) > dataSize) // Corrupt file; avoid oob read
129 break;
130
131 const quint32 imageSize = decode(qFromUnaligned<quint32>(buf.data() + offset));
132 offset += sizeof(quint32);
133
134 for (int face = 0; face < qMin(texData.numFaces(), MAX_ITERATIONS); face++) {
135 texData.setDataOffset(offset, level, face);
137
138 // Add image data and padding to offset
140 }
141 }
142
143 if (!texData.isValid()) {
144 qCDebug(lcQtGuiTextureIO, "Invalid values in header of KTX file %s", logName().constData());
145 return QTextureFileData();
146 }
147
148 texData.setLogName(logName());
149
150#ifdef KTX_DEBUG
151 qDebug() << "KTX file handler read" << texData;
152#endif
153
154 return texData;
155}
156
157bool QKtxHandler::checkHeader(const KTXHeader &header)
158{
160 return false;
161 inverseEndian = (header.endianness == inversePlatformEndianIdentifier);
162#ifdef KTX_DEBUG
163 QMetaEnum tfme = QMetaEnum::fromType<QOpenGLTexture::TextureFormat>();
164 QMetaEnum ptme = QMetaEnum::fromType<QOpenGLTexture::PixelType>();
165 qDebug("Header of %s:", logName().constData());
166 qDebug(" glType: 0x%x (%s)", decode(header.glType), ptme.valueToKey(decode(header.glType)));
167 qDebug(" glTypeSize: %u", decode(header.glTypeSize));
168 qDebug(" glFormat: 0x%x (%s)", decode(header.glFormat),
169 tfme.valueToKey(decode(header.glFormat)));
170 qDebug(" glInternalFormat: 0x%x (%s)", decode(header.glInternalFormat),
171 tfme.valueToKey(decode(header.glInternalFormat)));
172 qDebug(" glBaseInternalFormat: 0x%x (%s)", decode(header.glBaseInternalFormat),
173 tfme.valueToKey(decode(header.glBaseInternalFormat)));
174 qDebug(" pixelWidth: %u", decode(header.pixelWidth));
175 qDebug(" pixelHeight: %u", decode(header.pixelHeight));
176 qDebug(" pixelDepth: %u", decode(header.pixelDepth));
177 qDebug(" numberOfArrayElements: %u", decode(header.numberOfArrayElements));
178 qDebug(" numberOfFaces: %u", decode(header.numberOfFaces));
179 qDebug(" numberOfMipmapLevels: %u", decode(header.numberOfMipmapLevels));
180 qDebug(" bytesOfKeyValueData: %u", decode(header.bytesOfKeyValueData));
181#endif
182 const bool isCompressedImage = decode(header.glType) == 0 && decode(header.glFormat) == 0
183 && decode(header.pixelDepth) == 0;
184 const bool isCubeMap = decode(header.numberOfFaces) == 6;
185 const bool is2D = decode(header.pixelDepth) == 0 && decode(header.numberOfArrayElements) == 0;
186
187 return is2D && (isCubeMap || isCompressedImage);
188}
189
190QMap<QByteArray, QByteArray> QKtxHandler::decodeKeyValues(QByteArrayView view) const
191{
193 quint32 offset = 0;
194 while (offset < view.size() + sizeof(quint32)) {
195 const quint32 keyAndValueByteSize =
196 decode(qFromUnaligned<quint32>(view.constData() + offset));
197 offset += sizeof(quint32);
198
199 if (offset + keyAndValueByteSize > quint64(view.size()))
200 break; // oob read
201
202 // 'key' is a UTF-8 string ending with a null terminator, 'value' is the rest.
203 // To separate the key and value we convert the complete data to utf-8 and find the first
204 // null terminator from the left, here we split the data into two.
205 const auto str = QString::fromUtf8(view.constData() + offset, keyAndValueByteSize);
206 const int idx = str.indexOf('\0'_L1);
207 if (idx == -1)
208 continue;
209
210 const QByteArray key = str.left(idx).toUtf8();
211 const size_t keySize = key.size() + 1; // Actual data size
212 const QByteArray value = QByteArray::fromRawData(view.constData() + offset + keySize,
213 keyAndValueByteSize - keySize);
214
215 offset = withPadding(offset + keyAndValueByteSize, 4);
216 output.insert(key, value);
217 }
218
219 return output;
220}
221
222quint32 QKtxHandler::decode(quint32 val) const
223{
224 return inverseEndian ? qbswap<quint32>(val) : val;
225}
226
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:394
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
static bool canRead(const QByteArray &suffix, const QByteArray &block)
~QKtxHandler() override
QTextureFileData read() override
Definition qmap.h:186
\inmodule QtCore
const char * valueToKey(int value) const
Returns the string that is used as the name of the given enumeration value, or \nullptr if value is n...
\inmodule QtCore
Definition qsize.h:25
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
Definition qstring.cpp:5161
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
QByteArray toUtf8() const &
Definition qstring.h:563
void setKeyValueMetadata(const QMap< QByteArray, QByteArray > &keyValues)
void setDataLength(int length, int level=0, int face=0)
void setSize(const QSize &size)
void setGLInternalFormat(quint32 format)
void setGLFormat(quint32 format)
void setDataOffset(int offset, int level=0, int face=0)
void setData(const QByteArray &data)
void setLogName(const QByteArray &name)
void setGLBaseInternalFormat(quint32 format)
void setNumLevels(int num)
void setNumFaces(int num)
QIODevice * device() const
QByteArray logName() const
QString str
[2]
Combined button and popup list for selecting options.
int qstrncmp(const char *str1, const char *str2, size_t len)
static QString header(const QString &name)
static bool isCubeMap(const DDSHeader &dds)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static const quint32 platformEndianIdentifier
constexpr quint32 withPadding(quint32 value, quint32 rounding)
#define KTX_IDENTIFIER_LENGTH
static const char ktxIdentifier[KTX_IDENTIFIER_LENGTH]
static const quint32 qktxh_headerSize
static const quint32 inversePlatformEndianIdentifier
#define qDebug
[1]
Definition qlogging.h:160
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLenum GLuint GLint level
GLuint64 key
GLenum GLsizei dataSize
GLenum face
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum GLuint GLintptr offset
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei imageSize
GLuint GLfloat * val
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:45
unsigned long long quint64
Definition qtypes.h:56
unsigned char quint8
Definition qtypes.h:41
#define decode(x)
QT_BEGIN_NAMESPACE typedef uchar * output
QQuickView * view
[0]
quint32 endianness
quint32 bytesOfKeyValueData
quint32 glTypeSize
quint32 numberOfArrayElements
quint32 pixelHeight
quint32 numberOfMipmapLevels
quint32 pixelDepth
quint32 glType
quint8 identifier[KTX_IDENTIFIER_LENGTH]
quint32 glFormat
quint32 numberOfFaces
quint32 glBaseInternalFormat
quint32 glInternalFormat
quint32 pixelWidth