1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qktxhandler_p.h"
41#include "qtexturefiledata_p.h"
42#include <QtEndian>
43#include <QSize>
44
45//#define KTX_DEBUG
46#ifdef KTX_DEBUG
47#include <QDebug>
48#include <QMetaEnum>
49#include <QOpenGLTexture>
50#endif
51
52QT_BEGIN_NAMESPACE
53
54#define KTX_IDENTIFIER_LENGTH 12
55static const char ktxIdentifier[KTX_IDENTIFIER_LENGTH] = { '\xAB', 'K', 'T', 'X', ' ', '1', '1', '\xBB', '\r', '\n', '\x1A', '\n' };
56static const quint32 platformEndianIdentifier = 0x04030201;
57static const quint32 inversePlatformEndianIdentifier = 0x01020304;
58
59struct KTXHeader {
60 quint8 identifier[KTX_IDENTIFIER_LENGTH]; // Must match ktxIdentifier
61 quint32 endianness; // Either platformEndianIdentifier or inversePlatformEndianIdentifier, other values not allowed.
62 quint32 glType;
63 quint32 glTypeSize;
64 quint32 glFormat;
65 quint32 glInternalFormat;
66 quint32 glBaseInternalFormat;
67 quint32 pixelWidth;
68 quint32 pixelHeight;
69 quint32 pixelDepth;
70 quint32 numberOfArrayElements;
71 quint32 numberOfFaces;
72 quint32 numberOfMipmapLevels;
73 quint32 bytesOfKeyValueData;
74};
75
76static const quint32 headerSize = sizeof(KTXHeader);
77
78// Currently unused, declared for future reference
79struct KTXKeyValuePairItem {
80 quint32 keyAndValueByteSize;
81 /*
82 quint8 keyAndValue[keyAndValueByteSize];
83 quint8 valuePadding[3 - ((keyAndValueByteSize + 3) % 4)];
84 */
85};
86
87struct KTXMipmapLevel {
88 quint32 imageSize;
89 /*
90 for each array_element in numberOfArrayElements*
91 for each face in numberOfFaces
92 for each z_slice in pixelDepth*
93 for each row or row_of_blocks in pixelHeight*
94 for each pixel or block_of_pixels in pixelWidth
95 Byte data[format-specific-number-of-bytes]**
96 end
97 end
98 end
99 Byte cubePadding[0-3]
100 end
101 end
102 quint8 mipPadding[3 - ((imageSize + 3) % 4)]
103 */
104};
105
106bool QKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block)
107{
108 Q_UNUSED(suffix)
109
110 return (qstrncmp(str1: block.constData(), str2: ktxIdentifier, KTX_IDENTIFIER_LENGTH) == 0);
111}
112
113QTextureFileData QKtxHandler::read()
114{
115 if (!device())
116 return QTextureFileData();
117
118 QByteArray buf = device()->readAll();
119 const quint32 dataSize = quint32(buf.size());
120 if (dataSize < headerSize || !canRead(suffix: QByteArray(), block: buf)) {
121 qCDebug(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData());
122 return QTextureFileData();
123 }
124
125 const KTXHeader *header = reinterpret_cast<const KTXHeader *>(buf.constData());
126 if (!checkHeader(header: *header)) {
127 qCDebug(lcQtGuiTextureIO, "Unsupported KTX file format in %s", logName().constData());
128 return QTextureFileData();
129 }
130
131 QTextureFileData texData;
132 texData.setData(buf);
133
134 texData.setSize(QSize(decode(val: header->pixelWidth), decode(val: header->pixelHeight)));
135 texData.setGLFormat(decode(val: header->glFormat));
136 texData.setGLInternalFormat(decode(val: header->glInternalFormat));
137 texData.setGLBaseInternalFormat(decode(val: header->glBaseInternalFormat));
138
139 texData.setNumLevels(decode(val: header->numberOfMipmapLevels));
140 quint32 offset = headerSize + decode(val: header->bytesOfKeyValueData);
141 const int maxLevels = qMin(a: texData.numLevels(), b: 32); // Cap iterations in case of corrupt file.
142 for (int i = 0; i < maxLevels; i++) {
143 if (offset + sizeof(KTXMipmapLevel) > dataSize) // Corrupt file; avoid oob read
144 break;
145 const KTXMipmapLevel *level = reinterpret_cast<const KTXMipmapLevel *>(buf.constData() + offset);
146 quint32 levelLen = decode(val: level->imageSize);
147 texData.setDataOffset(offset: offset + sizeof(KTXMipmapLevel::imageSize), level: i);
148 texData.setDataLength(length: levelLen, level: i);
149 offset += sizeof(KTXMipmapLevel::imageSize) + levelLen + (3 - ((levelLen + 3) % 4));
150 }
151
152 if (!texData.isValid()) {
153 qCDebug(lcQtGuiTextureIO, "Invalid values in header of KTX file %s", logName().constData());
154 return QTextureFileData();
155 }
156
157 texData.setLogName(logName());
158
159#ifdef KTX_DEBUG
160 qDebug() << "KTX file handler read" << texData;
161#endif
162
163 return texData;
164}
165
166bool QKtxHandler::checkHeader(const KTXHeader &header)
167{
168 if (header.endianness != platformEndianIdentifier && header.endianness != inversePlatformEndianIdentifier)
169 return false;
170 inverseEndian = (header.endianness == inversePlatformEndianIdentifier);
171#ifdef KTX_DEBUG
172 QMetaEnum tfme = QMetaEnum::fromType<QOpenGLTexture::TextureFormat>();
173 QMetaEnum ptme = QMetaEnum::fromType<QOpenGLTexture::PixelType>();
174 qDebug("Header of %s:", logName().constData());
175 qDebug(" glType: 0x%x (%s)", decode(header.glType), ptme.valueToKey(decode(header.glType)));
176 qDebug(" glTypeSize: %u", decode(header.glTypeSize));
177 qDebug(" glFormat: 0x%x (%s)", decode(header.glFormat), tfme.valueToKey(decode(header.glFormat)));
178 qDebug(" glInternalFormat: 0x%x (%s)", decode(header.glInternalFormat), tfme.valueToKey(decode(header.glInternalFormat)));
179 qDebug(" glBaseInternalFormat: 0x%x (%s)", decode(header.glBaseInternalFormat), tfme.valueToKey(decode(header.glBaseInternalFormat)));
180 qDebug(" pixelWidth: %u", decode(header.pixelWidth));
181 qDebug(" pixelHeight: %u", decode(header.pixelHeight));
182 qDebug(" pixelDepth: %u", decode(header.pixelDepth));
183 qDebug(" numberOfArrayElements: %u", decode(header.numberOfArrayElements));
184 qDebug(" numberOfFaces: %u", decode(header.numberOfFaces));
185 qDebug(" numberOfMipmapLevels: %u", decode(header.numberOfMipmapLevels));
186 qDebug(" bytesOfKeyValueData: %u", decode(header.bytesOfKeyValueData));
187#endif
188 return ((decode(val: header.glType) == 0) &&
189 (decode(val: header.glFormat) == 0) &&
190 (decode(val: header.pixelDepth) == 0) &&
191 (decode(val: header.numberOfFaces) == 1));
192}
193
194quint32 QKtxHandler::decode(quint32 val)
195{
196 return inverseEndian ? qbswap<quint32>(source: val) : val;
197}
198
199QT_END_NAMESPACE
200

source code of qtbase/src/gui/util/qktxhandler.cpp