Add KTX validation routines

This commit is contained in:
Brad Davis 2017-05-13 18:07:35 -07:00
parent 499897e271
commit 077e56a96f
3 changed files with 522 additions and 83 deletions

View file

@ -180,105 +180,107 @@ namespace ktx {
RGB16F = 0x881B,
RGBA16F = 0x881A,
R32F = 0x822E,
RG32F = 0x8230,
RGB32F = 0x8815,
RGBA32F = 0x8814,
R32F = 0x822E,
RG32F = 0x8230,
RGB32F = 0x8815,
RGBA32F = 0x8814,
R11F_G11F_B10F = 0x8C3A,
RGB9_E5 = 0x8C3D,
R11F_G11F_B10F = 0x8C3A,
RGB9_E5 = 0x8C3D,
R8I = 0x8231,
R8UI = 0x8232,
R16I = 0x8233,
R16UI = 0x8234,
R32I = 0x8235,
R32UI = 0x8236,
RG8I = 0x8237,
RG8UI = 0x8238,
RG16I = 0x8239,
RG16UI = 0x823A,
RG32I = 0x823B,
RG32UI = 0x823C,
R8I = 0x8231,
R8UI = 0x8232,
R16I = 0x8233,
R16UI = 0x8234,
R32I = 0x8235,
R32UI = 0x8236,
RG8I = 0x8237,
RG8UI = 0x8238,
RG16I = 0x8239,
RG16UI = 0x823A,
RG32I = 0x823B,
RG32UI = 0x823C,
RGB8I = 0x8D8F,
RGB8UI = 0x8D7D,
RGB16I = 0x8D89,
RGB16UI = 0x8D77,
RGB8I = 0x8D8F,
RGB8UI = 0x8D7D,
RGB16I = 0x8D89,
RGB16UI = 0x8D77,
RGB32I = 0x8D83,
RGB32UI = 0x8D71,
RGBA8I = 0x8D8E,
RGBA8UI = 0x8D7C,
RGBA16I = 0x8D88,
RGBA16UI = 0x8D76,
RGBA32I = 0x8D82,
RGB32I = 0x8D83,
RGB32UI = 0x8D71,
RGBA8I = 0x8D8E,
RGBA8UI = 0x8D7C,
RGBA16I = 0x8D88,
RGBA16UI = 0x8D76,
RGBA32I = 0x8D82,
RGBA32UI = 0x8D70,
RGBA32UI = 0x8D70,
// GL 4.4 Table 8.13
DEPTH_COMPONENT16 = 0x81A5,
DEPTH_COMPONENT24 = 0x81A6,
DEPTH_COMPONENT32 = 0x81A7,
DEPTH_COMPONENT16 = 0x81A5,
DEPTH_COMPONENT24 = 0x81A6,
DEPTH_COMPONENT32 = 0x81A7,
DEPTH_COMPONENT32F = 0x8CAC,
DEPTH24_STENCIL8 = 0x88F0,
DEPTH32F_STENCIL8 = 0x8CAD,
DEPTH_COMPONENT32F = 0x8CAC,
DEPTH24_STENCIL8 = 0x88F0,
DEPTH32F_STENCIL8 = 0x8CAD,
STENCIL_INDEX1 = 0x8D46,
STENCIL_INDEX4 = 0x8D47,
STENCIL_INDEX8 = 0x8D48,
STENCIL_INDEX16 = 0x8D49,
STENCIL_INDEX1 = 0x8D46,
STENCIL_INDEX4 = 0x8D47,
STENCIL_INDEX8 = 0x8D48,
STENCIL_INDEX16 = 0x8D49,
};
enum class GLInternalFormat_Compressed : uint32_t {
// GL 4.4 Table 8.14
COMPRESSED_RED = 0x8225,
COMPRESSED_RG = 0x8226,
COMPRESSED_RGB = 0x84ED,
COMPRESSED_RGBA = 0x84EE,
COMPRESSED_RED = 0x8225,
COMPRESSED_RG = 0x8226,
COMPRESSED_RGB = 0x84ED,
COMPRESSED_RGBA = 0x84EE,
COMPRESSED_SRGB = 0x8C48,
COMPRESSED_SRGB_ALPHA = 0x8C49,
COMPRESSED_SRGB = 0x8C48,
COMPRESSED_SRGB_ALPHA = 0x8C49,
COMPRESSED_SRGB_S3TC_DXT1_EXT = 0x8C4C,
COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D,
COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E,
COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F,
COMPRESSED_ETC1_RGB8_OES = 0x8D64,
COMPRESSED_RED_RGTC1 = 0x8DBB,
COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC,
COMPRESSED_RG_RGTC2 = 0x8DBD,
COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE,
COMPRESSED_SRGB_S3TC_DXT1_EXT = 0x8C4C,
COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D,
COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E,
COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F,
COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C,
COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D,
COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E,
COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F,
COMPRESSED_RED_RGTC1 = 0x8DBB,
COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC,
COMPRESSED_RG_RGTC2 = 0x8DBD,
COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE,
COMPRESSED_RGB8_ETC2 = 0x9274,
COMPRESSED_SRGB8_ETC2 = 0x9275,
COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276,
COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277,
COMPRESSED_RGBA8_ETC2_EAC = 0x9278,
COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279,
COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C,
COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D,
COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E,
COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F,
COMPRESSED_R11_EAC = 0x9270,
COMPRESSED_SIGNED_R11_EAC = 0x9271,
COMPRESSED_RG11_EAC = 0x9272,
COMPRESSED_SIGNED_RG11_EAC = 0x9273,
COMPRESSED_RGB8_ETC2 = 0x9274,
COMPRESSED_SRGB8_ETC2 = 0x9275,
COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276,
COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277,
COMPRESSED_RGBA8_ETC2_EAC = 0x9278,
COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279,
COMPRESSED_R11_EAC = 0x9270,
COMPRESSED_SIGNED_R11_EAC = 0x9271,
COMPRESSED_RG11_EAC = 0x9272,
COMPRESSED_SIGNED_RG11_EAC = 0x9273,
};
enum class GLBaseInternalFormat : uint32_t {
// GL 4.4 Table 8.11
DEPTH_COMPONENT = 0x1902,
DEPTH_STENCIL = 0x84F9,
RED = 0x1903,
RG = 0x8227,
RGB = 0x1907,
RGBA = 0x1908,
STENCIL_INDEX = 0x1901,
DEPTH_COMPONENT = 0x1902,
DEPTH_STENCIL = 0x84F9,
RED = 0x1903,
RG = 0x8227,
RGB = 0x1907,
RGBA = 0x1908,
STENCIL_INDEX = 0x1901,
};
enum CubeMapFace {
@ -316,14 +318,14 @@ namespace ktx {
Byte identifier[IDENTIFIER_LENGTH];
uint32_t endianness { ENDIAN_TEST };
uint32_t glType;
uint32_t glType { static_cast<uint32_t>(GLType::UNSIGNED_BYTE) };
uint32_t glTypeSize { 0 };
uint32_t glFormat;
uint32_t glInternalFormat;
uint32_t glBaseInternalFormat;
uint32_t glFormat { static_cast<uint32_t>(GLFormat::RGBA) };
uint32_t glInternalFormat { static_cast<uint32_t>(GLInternalFormat_Uncompressed::RGBA8) };
uint32_t glBaseInternalFormat { static_cast<uint32_t>(GLBaseInternalFormat::RGBA) };
uint32_t pixelWidth { 1 };
uint32_t pixelHeight { 0 };
uint32_t pixelHeight { 1 };
uint32_t pixelDepth { 0 };
uint32_t numberOfArrayElements { 0 };
uint32_t numberOfFaces { 1 };
@ -386,8 +388,12 @@ namespace ktx {
void setCube(uint32_t width, uint32_t height) { setDimensions(width, height, 0, 0, NUM_CUBEMAPFACES); }
void setCubeArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1), NUM_CUBEMAPFACES); }
bool isValid() const;
// Generate a set of image descriptors based on the assumption that the full mip pyramid is populated
ImageDescriptors generateImageDescriptors() const;
};
static const size_t KTX_HEADER_SIZE = 64;
static_assert(sizeof(Header) == KTX_HEADER_SIZE, "KTX Header size is static and should not change from the spec");
static const size_t KV_SIZE_WIDTH = 4; // Number of bytes for keyAndValueByteSize
@ -426,6 +432,7 @@ namespace ktx {
const uint32_t _imageSize;
const uint32_t _faceSize;
const uint32_t _padding;
ImageHeader(bool cube, size_t imageOffset, uint32_t imageSize, uint32_t padding) :
_numFaces(cube ? NUM_CUBEMAPFACES : 1),
_imageOffset(imageOffset),
@ -486,6 +493,8 @@ namespace ktx {
public:
~KTX();
static bool validate(const StoragePointer& src);
// Define a KTX object manually to write it somewhere (in a file on disk?)
// This path allocate the Storage where to store header, keyvalues and copy mips
// Then COPY all the data

View file

@ -0,0 +1,376 @@
//
// Created by Bradley Austin Davis on 2017/05/13
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "KTX.h"
#include <unordered_set>
#include <QDebug>
using namespace ktx;
static const std::unordered_set<uint32_t> VALID_GL_TYPES {
(uint32_t)GLType::UNSIGNED_BYTE,
(uint32_t)GLType::BYTE,
(uint32_t)GLType::UNSIGNED_SHORT,
(uint32_t)GLType::SHORT,
(uint32_t)GLType::UNSIGNED_INT,
(uint32_t)GLType::INT,
(uint32_t)GLType::HALF_FLOAT,
(uint32_t)GLType::FLOAT,
(uint32_t)GLType::UNSIGNED_BYTE_3_3_2,
(uint32_t)GLType::UNSIGNED_BYTE_2_3_3_REV,
(uint32_t)GLType::UNSIGNED_SHORT_5_6_5,
(uint32_t)GLType::UNSIGNED_SHORT_5_6_5_REV,
(uint32_t)GLType::UNSIGNED_SHORT_4_4_4_4,
(uint32_t)GLType::UNSIGNED_SHORT_4_4_4_4_REV,
(uint32_t)GLType::UNSIGNED_SHORT_5_5_5_1,
(uint32_t)GLType::UNSIGNED_SHORT_1_5_5_5_REV,
(uint32_t)GLType::UNSIGNED_INT_8_8_8_8,
(uint32_t)GLType::UNSIGNED_INT_8_8_8_8_REV,
(uint32_t)GLType::UNSIGNED_INT_10_10_10_2,
(uint32_t)GLType::UNSIGNED_INT_2_10_10_10_REV,
(uint32_t)GLType::UNSIGNED_INT_24_8,
(uint32_t)GLType::UNSIGNED_INT_10F_11F_11F_REV,
(uint32_t)GLType::UNSIGNED_INT_5_9_9_9_REV,
(uint32_t)GLType::FLOAT_32_UNSIGNED_INT_24_8_REV,
};
static const std::unordered_set<uint32_t> VALID_GL_FORMATS {
(uint32_t)GLFormat::STENCIL_INDEX,
(uint32_t)GLFormat::DEPTH_COMPONENT,
(uint32_t)GLFormat::DEPTH_STENCIL,
(uint32_t)GLFormat::RED,
(uint32_t)GLFormat::GREEN,
(uint32_t)GLFormat::BLUE,
(uint32_t)GLFormat::RG,
(uint32_t)GLFormat::RGB,
(uint32_t)GLFormat::RGBA,
(uint32_t)GLFormat::BGR,
(uint32_t)GLFormat::BGRA,
(uint32_t)GLFormat::RG_INTEGER,
(uint32_t)GLFormat::RED_INTEGER,
(uint32_t)GLFormat::GREEN_INTEGER,
(uint32_t)GLFormat::BLUE_INTEGER,
(uint32_t)GLFormat::RGB_INTEGER,
(uint32_t)GLFormat::RGBA_INTEGER,
(uint32_t)GLFormat::BGR_INTEGER,
(uint32_t)GLFormat::BGRA_INTEGER,
};
static const std::unordered_set<uint32_t> VALID_GL_INTERNAL_FORMATS {
(uint32_t)GLInternalFormat_Uncompressed::R8,
(uint32_t)GLInternalFormat_Uncompressed::R8_SNORM,
(uint32_t)GLInternalFormat_Uncompressed::R16,
(uint32_t)GLInternalFormat_Uncompressed::R16_SNORM,
(uint32_t)GLInternalFormat_Uncompressed::RG8,
(uint32_t)GLInternalFormat_Uncompressed::RG8_SNORM,
(uint32_t)GLInternalFormat_Uncompressed::RG16,
(uint32_t)GLInternalFormat_Uncompressed::RG16_SNORM,
(uint32_t)GLInternalFormat_Uncompressed::R3_G3_B2,
(uint32_t)GLInternalFormat_Uncompressed::RGB4,
(uint32_t)GLInternalFormat_Uncompressed::RGB5,
(uint32_t)GLInternalFormat_Uncompressed::RGB565,
(uint32_t)GLInternalFormat_Uncompressed::RGB8,
(uint32_t)GLInternalFormat_Uncompressed::RGB8_SNORM,
(uint32_t)GLInternalFormat_Uncompressed::RGB10,
(uint32_t)GLInternalFormat_Uncompressed::RGB12,
(uint32_t)GLInternalFormat_Uncompressed::RGB16,
(uint32_t)GLInternalFormat_Uncompressed::RGB16_SNORM,
(uint32_t)GLInternalFormat_Uncompressed::RGBA2,
(uint32_t)GLInternalFormat_Uncompressed::RGBA4,
(uint32_t)GLInternalFormat_Uncompressed::RGB5_A1,
(uint32_t)GLInternalFormat_Uncompressed::RGBA8,
(uint32_t)GLInternalFormat_Uncompressed::RGBA8_SNORM,
(uint32_t)GLInternalFormat_Uncompressed::RGB10_A2,
(uint32_t)GLInternalFormat_Uncompressed::RGB10_A2UI,
(uint32_t)GLInternalFormat_Uncompressed::RGBA12,
(uint32_t)GLInternalFormat_Uncompressed::RGBA16,
(uint32_t)GLInternalFormat_Uncompressed::RGBA16_SNORM,
(uint32_t)GLInternalFormat_Uncompressed::SRGB8,
(uint32_t)GLInternalFormat_Uncompressed::SRGB8_ALPHA8,
(uint32_t)GLInternalFormat_Uncompressed::R16F,
(uint32_t)GLInternalFormat_Uncompressed::RG16F,
(uint32_t)GLInternalFormat_Uncompressed::RGB16F,
(uint32_t)GLInternalFormat_Uncompressed::RGBA16F,
(uint32_t)GLInternalFormat_Uncompressed::R32F,
(uint32_t)GLInternalFormat_Uncompressed::RG32F,
(uint32_t)GLInternalFormat_Uncompressed::RGBA32F,
(uint32_t)GLInternalFormat_Uncompressed::R11F_G11F_B10F,
(uint32_t)GLInternalFormat_Uncompressed::RGB9_E5,
(uint32_t)GLInternalFormat_Uncompressed::R8I,
(uint32_t)GLInternalFormat_Uncompressed::R8UI,
(uint32_t)GLInternalFormat_Uncompressed::R16I,
(uint32_t)GLInternalFormat_Uncompressed::R16UI,
(uint32_t)GLInternalFormat_Uncompressed::R32I,
(uint32_t)GLInternalFormat_Uncompressed::R32UI,
(uint32_t)GLInternalFormat_Uncompressed::RG8I,
(uint32_t)GLInternalFormat_Uncompressed::RG8UI,
(uint32_t)GLInternalFormat_Uncompressed::RG16I,
(uint32_t)GLInternalFormat_Uncompressed::RG16UI,
(uint32_t)GLInternalFormat_Uncompressed::RG32I,
(uint32_t)GLInternalFormat_Uncompressed::RG32UI,
(uint32_t)GLInternalFormat_Uncompressed::RGB8I,
(uint32_t)GLInternalFormat_Uncompressed::RGB8UI,
(uint32_t)GLInternalFormat_Uncompressed::RGB16I,
(uint32_t)GLInternalFormat_Uncompressed::RGB16UI,
(uint32_t)GLInternalFormat_Uncompressed::RGB32I,
(uint32_t)GLInternalFormat_Uncompressed::RGB32UI,
(uint32_t)GLInternalFormat_Uncompressed::RGBA8I,
(uint32_t)GLInternalFormat_Uncompressed::RGBA8UI,
(uint32_t)GLInternalFormat_Uncompressed::RGBA16I,
(uint32_t)GLInternalFormat_Uncompressed::RGBA16UI,
(uint32_t)GLInternalFormat_Uncompressed::RGBA32I,
(uint32_t)GLInternalFormat_Uncompressed::RGBA32UI,
(uint32_t)GLInternalFormat_Uncompressed::DEPTH_COMPONENT16,
(uint32_t)GLInternalFormat_Uncompressed::DEPTH_COMPONENT24,
(uint32_t)GLInternalFormat_Uncompressed::DEPTH_COMPONENT32,
(uint32_t)GLInternalFormat_Uncompressed::DEPTH_COMPONENT32F,
(uint32_t)GLInternalFormat_Uncompressed::DEPTH24_STENCIL8,
(uint32_t)GLInternalFormat_Uncompressed::DEPTH32F_STENCIL8,
(uint32_t)GLInternalFormat_Uncompressed::STENCIL_INDEX1,
(uint32_t)GLInternalFormat_Uncompressed::STENCIL_INDEX4,
(uint32_t)GLInternalFormat_Uncompressed::STENCIL_INDEX8,
(uint32_t)GLInternalFormat_Uncompressed::STENCIL_INDEX16,
};
static const std::unordered_set<uint32_t> VALID_GL_INTERNAL_COMPRESSED_FORMATS {
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_RED,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_RG,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGB,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGBA,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_ETC1_RGB8_OES,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SIGNED_RED_RGTC1,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SIGNED_RG_RGTC2,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGBA_BPTC_UNORM,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_BPTC_UNORM,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGB_BPTC_SIGNED_FLOAT,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGB8_ETC2,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB8_ETC2,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_RGBA8_ETC2_EAC,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_R11_EAC,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SIGNED_R11_EAC,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_RG11_EAC,
(uint32_t)GLInternalFormat_Compressed::COMPRESSED_SIGNED_RG11_EAC,
};
static const std::unordered_set<uint32_t> VALID_GL_BASE_INTERNAL_FORMATS {
(uint32_t)GLBaseInternalFormat::DEPTH_COMPONENT,
(uint32_t)GLBaseInternalFormat::DEPTH_STENCIL,
(uint32_t)GLBaseInternalFormat::RED,
(uint32_t)GLBaseInternalFormat::RG,
(uint32_t)GLBaseInternalFormat::RGB,
(uint32_t)GLBaseInternalFormat::RGBA,
(uint32_t)GLBaseInternalFormat::STENCIL_INDEX,
};
bool Header::isValid() const {
if (0 != memcmp(identifier, IDENTIFIER.data(), IDENTIFIER_LENGTH)) {
qDebug() << "Invalid header identifier";
return false;
}
if (endianness != ENDIAN_TEST && endianness != REVERSE_ENDIAN_TEST) {
qDebug("Invalid endian marker 0x%x", endianness);
return false;
}
//
// GL enum validity
//
if (VALID_GL_BASE_INTERNAL_FORMATS.count(glBaseInternalFormat) != 1) {
qDebug("Invalid base internal format 0x%x", glBaseInternalFormat);
return false;
}
if (glFormat == (uint32_t)GLFormat::COMPRESSED_FORMAT && glType == (uint32_t)GLType::COMPRESSED_TYPE) {
if (VALID_GL_INTERNAL_COMPRESSED_FORMATS.count(glInternalFormat) != 1) {
qDebug("Invalid compressed internal format 0x%x", glInternalFormat);
return false;
}
} else {
if (VALID_GL_TYPES.count(glType) != 1) {
qDebug("Invalid type 0x%x", glType);
return false;
}
if (VALID_GL_FORMATS.count(glFormat) != 1) {
qDebug("Invalid format 0x%x", glFormat);
return false;
}
if (VALID_GL_INTERNAL_FORMATS.count(glInternalFormat) != 1) {
qDebug("Invalid internal format 0x%x", glInternalFormat);
return false;
}
}
//
// Dimensions validity
//
// Textures must at least have a width
// If they have a depth, they must have a height
if ((pixelWidth == 0) || (pixelDepth != 0 && pixelHeight == 0)) {
qDebug() << "Invalid dimensions " << pixelWidth << "x" << pixelHeight << "x" << pixelDepth;
return false;
}
if (numberOfFaces != 1 && numberOfFaces != NUM_CUBEMAPFACES) {
qDebug() << "Invalid number of faces " << numberOfFaces;
return false;
}
// FIXME validate numberOfMipmapLevels based on the dimensions?
if ((bytesOfKeyValueData % 4) != 0) {
qDebug() << "Invalid keyvalue data size " << numberOfFaces;
return false;
}
return true;
}
struct AlignedStreamBuffer {
AlignedStreamBuffer(size_t size, const uint8_t* data)
: _size(size), _data(data) { }
AlignedStreamBuffer(const StoragePointer& storage)
: AlignedStreamBuffer(storage->size(), storage->data()) { }
template<typename T>
bool read(T& t) {
// Ensure we don't read more than we have
if (sizeof(T) > _size) {
return false;
}
// Grab the data
memcpy(&t, _data, sizeof(T));
// Advance the pointer
return skip(sizeof(T));
}
bool skip(size_t skipSize) {
if ((skipSize % 4) != 0) {
skipSize += (3 - ((skipSize + 3) % 4));
}
if (skipSize > _size) {
return false;
}
_data += skipSize;
_size -= skipSize;
return true;
}
AlignedStreamBuffer front(size_t size) const {
return AlignedStreamBuffer { std::min(size, _size), _data };
}
bool empty() const {
return _size == 0;
}
private:
size_t _size;
const uint8_t* _data;
};
bool validateKeyValueData(AlignedStreamBuffer kvbuffer) {
while (!kvbuffer.empty()) {
uint32_t keyValueSize;
// Try to fetch the size of the next key value block
if (!kvbuffer.read(keyValueSize)) {
qDebug() << "Unable to read past key value size";
return false;
}
if (!kvbuffer.skip(keyValueSize)) {
qDebug() << "Unable to skip past key value data";
return false;
}
}
return true;
}
bool KTX::validate(const StoragePointer& src) {
if ((src->size() % 4) != 0) {
// All KTX data is 4-byte aligned
qDebug() << "Invalid size, not 4 byte aligned";
return false;
}
Header header;
AlignedStreamBuffer buffer { src };
if (!buffer.read(header)) {
qDebug() << "Unable to read header";
return false;
}
// Basic header validation, are the enums and size valid?
if (!header.isValid()) {
qDebug() << "Invalid header";
return false;
}
// Validate the key value pairs
if (!validateKeyValueData(buffer.front(header.bytesOfKeyValueData))) {
qDebug() << "Invalid key value data";
return false;
}
// now skip the KV data
if (!buffer.skip(header.bytesOfKeyValueData)) {
qDebug() << "Unable to read past key value data";
return false;
}
// Validate the images
for (uint32_t mip = 0; mip < header.numberOfMipmapLevels; ++mip) {
uint32_t imageSize;
if (!buffer.read(imageSize)) {
qDebug() << "Unable to read image size";
return false;
}
if (header.numberOfArrayElements == 0 && header.numberOfFaces == 6) {
for (uint8_t face = 0; face < NUM_CUBEMAPFACES; ++face) {
if (!buffer.skip(imageSize)) {
qDebug() << "Unable to skip past cubemap data";
return false;
}
}
} else {
if (!buffer.skip(imageSize)) {
return false;
}
}
}
// The buffer should be empty afer we've skipped all of the KTX data
if (!buffer.empty()) {
return false;
}
return true;
}

View file

@ -38,14 +38,12 @@
#include <StatTracker.h>
#include <LogHandler.h>
#include <gpu/Texture.h>
#include <gl/Config.h>
#include <model/TextureMap.h>
#include <ktx/KTX.h>
#include <image/Image.h>
QSharedPointer<FileLogger> logger;
gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true);
@ -59,7 +57,9 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt
OutputDebugStringA(logMessage.toLocal8Bit().constData());
OutputDebugStringA("\n");
#endif
logger->addMessage(qPrintable(logMessage + "\n"));
if (logger) {
logger->addMessage(qPrintable(logMessage + "\n"));
}
}
}
@ -149,5 +149,59 @@ int main(int argc, char** argv) {
return 0;
}
#if 0
static const QString TEST_FOLDER { "H:/ktx_cacheold" };
//static const QString TEST_FOLDER { "C:/Users/bdavis/Git/KTX/testimages" };
//static const QString EXTENSIONS { "4bbdf8f786470e4ab3e672d44b8e8df2.ktx" };
static const QString EXTENSIONS { "*.ktx" };
int mainTemp(int, char**) {
qInstallMessageHandler(messageHandler);
auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS });
for (auto fileInfo : fileInfoList) {
qDebug() << fileInfo.filePath();
std::shared_ptr<storage::Storage> storage { new storage::FileStorage { fileInfo.filePath() } };
if (!ktx::KTX::validate(storage)) {
qDebug() << "KTX invalid";
}
auto ktxFile = ktx::KTX::create(storage);
ktx::KTXDescriptor ktxDescriptor = ktxFile->toDescriptor();
assert(ktxFile->_keyValues == ktxDescriptor.keyValues);
qDebug() << "Contains " << ktxDescriptor.keyValues.size() << " key value pairs";
for (const auto& kv : ktxDescriptor.keyValues) {
qDebug() << "\t" << kv._key.c_str();
}
auto offsetToMinMipKV = ktxDescriptor.getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY);
if (offsetToMinMipKV) {
auto data = storage->data() + ktx::KTX_HEADER_SIZE + offsetToMinMipKV;
auto minMipLevelAvailable = *data;
qDebug() << "\tMin mip available " << minMipLevelAvailable;
assert(minMipLevelAvailable < ktxDescriptor.header.numberOfMipmapLevels);
}
auto storageSize = storage->size();
for (const auto& faceImageDesc : ktxDescriptor.images) {
//assert(0 == (faceImageDesc._faceSize % 4));
for (const auto& faceOffset : faceImageDesc._faceOffsets) {
assert(0 == (faceOffset % 4));
auto faceEndOffset = faceOffset + faceImageDesc._faceSize;
assert(faceEndOffset <= storageSize);
}
}
for (const auto& faceImage : ktxFile->_images) {
for (const ktx::Byte* faceBytes : faceImage._faceBytes) {
assert(0 == (reinterpret_cast<size_t>(faceBytes) % 4));
}
}
}
return 0;
}
#endif
#include "main.moc"