diff options
author | Eirik Aavitsland <eirik.aavitsland@qt.io> | 2017-12-06 16:24:53 +0100 |
---|---|---|
committer | Tony Sarajärvi <tony.sarajarvi@qt.io> | 2018-02-01 06:39:27 +0000 |
commit | f6a5820cc1f268436eb8074531002790bc214df7 (patch) | |
tree | 2ab687aab6eee46961e55bd1d84557454fa25bb7 /src/quick/scenegraph | |
parent | f5abe488f1e7c9c31badd0622b7374648bea943a (diff) |
Add basic handler for ktx format texture files
This adds basic support for reading compressed textures stored in .ktx
files, the Khronos texture file format.
Change-Id: Iafb86cff9607eedb26a73989f9b005455ddd316f
Reviewed-by: Kai Koehne <kai.koehne@qt.io>
Diffstat (limited to 'src/quick/scenegraph')
-rw-r--r-- | src/quick/scenegraph/compressedtexture/qsgktxhandler.cpp | 186 | ||||
-rw-r--r-- | src/quick/scenegraph/compressedtexture/qsgktxhandler_p.h | 78 | ||||
-rw-r--r-- | src/quick/scenegraph/scenegraph.pri | 6 | ||||
-rw-r--r-- | src/quick/scenegraph/util/qsgtexturereader.cpp | 3 |
4 files changed, 271 insertions, 2 deletions
diff --git a/src/quick/scenegraph/compressedtexture/qsgktxhandler.cpp b/src/quick/scenegraph/compressedtexture/qsgktxhandler.cpp new file mode 100644 index 0000000000..e3e4ca6824 --- /dev/null +++ b/src/quick/scenegraph/compressedtexture/qsgktxhandler.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgktxhandler_p.h" +#include "qsgcompressedtexture_p.h" +#include <QOpenGLTexture> +#include <QtEndian> + +//#define KTX_DEBUG + +QT_BEGIN_NAMESPACE + +#define KTX_IDENTIFIER_LENGTH 12 +static const char ktxIdentifier[KTX_IDENTIFIER_LENGTH] = { '\xAB', 'K', 'T', 'X', ' ', '1', '1', '\xBB', '\r', '\n', '\x1A', '\n' }; +static const quint32 platformEndianIdentifier = 0x04030201; +static const quint32 inversePlatformEndianIdentifier = 0x01020304; + +struct KTXHeader { + quint8 identifier[KTX_IDENTIFIER_LENGTH]; //Must match ktxIdentifier + quint32 endianness; //Either platformEndianIdentifier or inversePlatformEndianIdentifier, other values not allowed. + quint32 glType; + quint32 glTypeSize; + quint32 glFormat; + quint32 glInternalFormat; + quint32 glBaseInternalFormat; + quint32 pixelWidth; + quint32 pixelHeight; + quint32 pixelDepth; + quint32 numberOfArrayElements; + quint32 numberOfFaces; + quint32 numberOfMipmapLevels; + quint32 bytesOfKeyValueData; +}; + +static const int headerSize = sizeof(KTXHeader); + +// Currently unused, declared for future reference +struct KTXKeyValuePairItem { + quint32 keyAndValueByteSize; + /* + quint8 keyAndValue[keyAndValueByteSize]; + quint8 valuePadding[3 - ((keyAndValueByteSize + 3) % 4)]; + */ +}; + +struct KTXMipmapLevel { + quint32 imageSize; + /* + for each array_element in numberOfArrayElements* + for each face in numberOfFaces + for each z_slice in pixelDepth* + for each row or row_of_blocks in pixelHeight* + for each pixel or block_of_pixels in pixelWidth + Byte data[format-specific-number-of-bytes]** + end + end + end + Byte cubePadding[0-3] + end + end + quint8 mipPadding[3 - ((imageSize + 3) % 4)] + */ +}; + +bool QSGKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block) +{ + Q_UNUSED(suffix) + + return (qstrncmp(block.constData(), ktxIdentifier, KTX_IDENTIFIER_LENGTH) == 0); +} + +QQuickTextureFactory *QSGKtxHandler::read() +{ + if (!device()) + return nullptr; + + QByteArray buf = device()->readAll(); + if (buf.size() < headerSize || !canRead(QByteArray(), buf)) { + qCDebug(QSG_LOG_TEXTUREIO, "Invalid KTX file %s", logName().constData()); + return nullptr; + } + + const KTXHeader *header = reinterpret_cast<const KTXHeader *>(buf.constData()); + if (!checkHeader(*header)) { + qCDebug(QSG_LOG_TEXTUREIO, "Unsupported KTX file format in %s", logName().constData()); + return nullptr; + } + + QSGCompressedTexture::DataPtr texData(QSGCompressedTexture::DataPtr::create()); + + texData->size = QSize(decode(header->pixelWidth), decode(header->pixelHeight)); + texData->format = decode(header->glInternalFormat); + texData->hasAlpha = !QSGCompressedTexture::formatIsOpaque(texData->format); + + // For now, ignore any additional mipmap levels + int preambleSize = headerSize + decode(header->bytesOfKeyValueData); + if (buf.size() >= preambleSize + int(sizeof(KTXMipmapLevel))) { + texData->data = buf; + texData->dataOffset = preambleSize + sizeof(quint32); // for the imageSize + const KTXMipmapLevel *level = reinterpret_cast<const KTXMipmapLevel *>(buf.constData() + preambleSize); + texData->dataLength = decode(level->imageSize); + } + + if (!texData->isValid()) { + qCDebug(QSG_LOG_TEXTUREIO, "Invalid values in header of KTX file %s", logName().constData()); + return nullptr; + } + + texData->logName = logName(); +#ifdef KTX_DEBUG + qDebug() << "KTX file handler read" << texData.data(); +#endif + + return new QSGCompressedTextureFactory(texData); +} + +bool QSGKtxHandler::checkHeader(const KTXHeader &header) +{ + if (header.endianness != platformEndianIdentifier && header.endianness != inversePlatformEndianIdentifier) + return false; + inverseEndian = (header.endianness == inversePlatformEndianIdentifier); +#ifdef KTX_DEBUG + QMetaEnum tfme = QMetaEnum::fromType<QOpenGLTexture::TextureFormat>(); + QMetaEnum ptme = QMetaEnum::fromType<QOpenGLTexture::PixelType>(); + 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(" pixelWidth: %u", decode(header.pixelWidth)); + qDebug(" pixelHeight: %u", decode(header.pixelHeight)); + qDebug(" pixelDepth: %u", decode(header.pixelDepth)); + qDebug(" numberOfArrayElements: %u", decode(header.numberOfArrayElements)); + qDebug(" numberOfFaces: %u", decode(header.numberOfFaces)); + 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)); +} + +quint32 QSGKtxHandler::decode(quint32 val) +{ + return inverseEndian ? qbswap<quint32>(val) : val; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/compressedtexture/qsgktxhandler_p.h b/src/quick/scenegraph/compressedtexture/qsgktxhandler_p.h new file mode 100644 index 0000000000..22f4db65b2 --- /dev/null +++ b/src/quick/scenegraph/compressedtexture/qsgktxhandler_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGKTXHANDLER_H +#define QSGKTXHANDLER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qsgtexturefilehandler_p.h" + +QT_BEGIN_NAMESPACE + +struct KTXHeader; + +class QSGKtxHandler : public QSGTextureFileHandler +{ +public: + using QSGTextureFileHandler::QSGTextureFileHandler; + + static bool canRead(const QByteArray &suffix, const QByteArray &block); + + QQuickTextureFactory *read() override; + +private: + bool checkHeader(const KTXHeader &header); + quint32 decode(quint32 val); + + bool inverseEndian = false; +}; + +QT_END_NAMESPACE + +#endif // QSGKTXHANDLER_H diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index bdd694f95e..34396dcf43 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -233,10 +233,12 @@ qtConfig(opengl(es1|es2)?) { $$PWD/compressedtexture/qsgcompressedatlastexture_p.h \ $$PWD/compressedtexture/qsgcompressedtexture_p.h \ $$PWD/compressedtexture/qsgtexturefilehandler_p.h \ - $$PWD/compressedtexture/qsgpkmhandler_p.h + $$PWD/compressedtexture/qsgpkmhandler_p.h \ + $$PWD/compressedtexture/qsgktxhandler_p.h SOURCES += \ $$PWD/compressedtexture/qsgcompressedatlastexture.cpp \ $$PWD/compressedtexture/qsgcompressedtexture.cpp \ - $$PWD/compressedtexture/qsgpkmhandler.cpp + $$PWD/compressedtexture/qsgpkmhandler.cpp \ + $$PWD/compressedtexture/qsgktxhandler.cpp } diff --git a/src/quick/scenegraph/util/qsgtexturereader.cpp b/src/quick/scenegraph/util/qsgtexturereader.cpp index cf4edf29b8..168afb9e56 100644 --- a/src/quick/scenegraph/util/qsgtexturereader.cpp +++ b/src/quick/scenegraph/util/qsgtexturereader.cpp @@ -43,6 +43,7 @@ #if QT_CONFIG(opengl) #include <private/qsgpkmhandler_p.h> +#include <private/qsgktxhandler_p.h> #endif #include <QFileInfo> @@ -80,6 +81,8 @@ bool QSGTextureReader::isTexture() // Currently the handlers are hardcoded; later maybe a list of plugins if (QSGPkmHandler::canRead(suffix, headerBlock)) { m_handler = new QSGPkmHandler(m_device, logName); + } else if (QSGKtxHandler::canRead(suffix, headerBlock)) { + m_handler = new QSGKtxHandler(m_device, logName); } // else if OtherHandler::canRead() ...etc. } |