diff options
Diffstat (limited to 'third_party/ktx/ktx.cpp')
-rw-r--r-- | third_party/ktx/ktx.cpp | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/third_party/ktx/ktx.cpp b/third_party/ktx/ktx.cpp new file mode 100644 index 0000000000..15c44fcd35 --- /dev/null +++ b/third_party/ktx/ktx.cpp @@ -0,0 +1,242 @@ + +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "ktx.h" +#include "SkStream.h" +#include "SkEndian.h" + +#include "gl/GrGLDefines.h" + +#define KTX_FILE_IDENTIFIER_SIZE 12 +static const uint8_t KTX_FILE_IDENTIFIER[KTX_FILE_IDENTIFIER_SIZE] = { + 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A +}; + +static const uint32_t kKTX_ENDIANNESS_CODE = 0x04030201; + +bool SkKTXFile::KeyValue::readKeyAndValue(const uint8_t* data) { + const char *key = reinterpret_cast<const char *>(data); + const char *value = key; + + size_t bytesRead = 0; + while (*value != '\0' && bytesRead < this->fDataSz) { + ++bytesRead; + ++value; + } + + // Error of some sort.. + if (bytesRead >= this->fDataSz) { + return false; + } + + // Read the zero terminator + ++bytesRead; + ++value; + + size_t bytesLeft = this->fDataSz - bytesRead; + this->fKey.set(key, bytesRead); + if (bytesLeft > 0) { + this->fValue.set(value, bytesLeft); + } else { + return false; + } + + return true; +} + +uint32_t SkKTXFile::readInt(const uint8_t** buf, size_t* bytesLeft) const { + SkASSERT(NULL != buf && NULL != bytesLeft); + + uint32_t result; + + if (*bytesLeft < 4) { + SkASSERT(false); + return 0; + } + + memcpy(&result, *buf, 4); + *buf += 4; + + if (fSwapBytes) { + SkEndianSwap32(result); + } + + *bytesLeft -= 4; + + return result; +} + +bool SkKTXFile::isETC1() const { + return this->valid() && GR_GL_COMPRESSED_RGB8_ETC1 == fHeader.fGLInternalFormat; +} + +bool SkKTXFile::isRGBA8() const { + return this->valid() && GR_GL_RGBA8 == fHeader.fGLInternalFormat; +} + +bool SkKTXFile::isRGB8() const { + return this->valid() && GR_GL_RGB8 == fHeader.fGLInternalFormat; +} + +bool SkKTXFile::readKTXFile(const uint8_t* data, size_t dataLen) { + const uint8_t *buf = data; + size_t bytesLeft = dataLen; + + // Make sure original KTX header is there... this should have been checked + // already by a call to is_ktx() + SkASSERT(bytesLeft > KTX_FILE_IDENTIFIER_SIZE); + SkASSERT(0 == memcmp(KTX_FILE_IDENTIFIER, buf, KTX_FILE_IDENTIFIER_SIZE)); + buf += KTX_FILE_IDENTIFIER_SIZE; + bytesLeft -= KTX_FILE_IDENTIFIER_SIZE; + + // Read header, but first make sure that we have the proper space: we need + // two 32-bit ints: 1 for endianness, and another for the mandatory image + // size after the header. + if (bytesLeft < 8 + sizeof(Header)) { + return false; + } + + uint32_t endianness = this->readInt(&buf, &bytesLeft); + fSwapBytes = kKTX_ENDIANNESS_CODE != endianness; + + // Read header values + fHeader.fGLType = this->readInt(&buf, &bytesLeft); + fHeader.fGLTypeSize = this->readInt(&buf, &bytesLeft); + fHeader.fGLFormat = this->readInt(&buf, &bytesLeft); + fHeader.fGLInternalFormat = this->readInt(&buf, &bytesLeft); + fHeader.fGLBaseInternalFormat = this->readInt(&buf, &bytesLeft); + fHeader.fPixelWidth = this->readInt(&buf, &bytesLeft); + fHeader.fPixelHeight = this->readInt(&buf, &bytesLeft); + fHeader.fPixelDepth = this->readInt(&buf, &bytesLeft); + fHeader.fNumberOfArrayElements = this->readInt(&buf, &bytesLeft); + fHeader.fNumberOfFaces = this->readInt(&buf, &bytesLeft); + fHeader.fNumberOfMipmapLevels = this->readInt(&buf, &bytesLeft); + fHeader.fBytesOfKeyValueData = this->readInt(&buf, &bytesLeft); + + // Check for things that we understand... + { + // First, we only support compressed formats and single byte + // representations at the moment. If the internal format is + // compressed, the the GLType field in the header must be zero. + // In the future, we may support additional data types (such + // as GL_UNSIGNED_SHORT_5_6_5) + if (fHeader.fGLType != 0 && fHeader.fGLType != GR_GL_UNSIGNED_BYTE) { + return false; + } + + // This means that for well-formatted KTX files, the glTypeSize + // field must be one... + if (fHeader.fGLTypeSize != 1) { + return false; + } + + // We don't support 3D textures. + if (fHeader.fPixelDepth > 1) { + return false; + } + + // We don't support texture arrays + if (fHeader.fNumberOfArrayElements > 1) { + return false; + } + + // We don't support cube maps + if (fHeader.fNumberOfFaces > 1) { + return false; + } + } + + // Make sure that we have enough bytes left for the key/value + // data according to what was said in the header. + if (bytesLeft < fHeader.fBytesOfKeyValueData) { + return false; + } + + // Next read the key value pairs + size_t keyValueBytesRead = 0; + while (keyValueBytesRead < fHeader.fBytesOfKeyValueData) { + uint32_t keyValueBytes = this->readInt(&buf, &bytesLeft); + keyValueBytesRead += 4; + + if (keyValueBytes > bytesLeft) { + return false; + } + + KeyValue kv(keyValueBytes); + if (!kv.readKeyAndValue(buf)) { + return false; + } + + fKeyValuePairs.push_back(kv); + + uint32_t keyValueBytesPadded = (keyValueBytes + 3) & ~3; + buf += keyValueBytesPadded; + keyValueBytesRead += keyValueBytesPadded; + bytesLeft -= keyValueBytesPadded; + } + + // Read the pixel data... + int mipmaps = SkMax32(fHeader.fNumberOfMipmapLevels, 1); + SkASSERT(mipmaps == 1); + + int arrayElements = SkMax32(fHeader.fNumberOfArrayElements, 1); + SkASSERT(arrayElements == 1); + + int faces = SkMax32(fHeader.fNumberOfFaces, 1); + SkASSERT(faces == 1); + + int depth = SkMax32(fHeader.fPixelDepth, 1); + SkASSERT(depth == 1); + + for (int mipmap = 0; mipmap < mipmaps; ++mipmap) { + // Make sure that we have at least 4 more bytes for the first image size + if (bytesLeft < 4) { + return false; + } + + uint32_t imgSize = this->readInt(&buf, &bytesLeft); + + // Truncated file. + if (bytesLeft < imgSize) { + return false; + } + + // !FIXME! If support is ever added for cube maps then the padding + // needs to be taken into account here. + for (int arrayElement = 0; arrayElement < arrayElements; ++arrayElement) { + for (int face = 0; face < faces; ++face) { + for (int z = 0; z < depth; ++z) { + PixelData pd(buf, imgSize); + fPixelData.append(1, &pd); + } + } + } + + uint32_t imgSizePadded = (imgSize + 3) & ~3; + buf += imgSizePadded; + bytesLeft -= imgSizePadded; + } + + return bytesLeft == 0; +} + +bool SkKTXFile::is_ktx(const uint8_t *data) { + return 0 == memcmp(KTX_FILE_IDENTIFIER, data, KTX_FILE_IDENTIFIER_SIZE); +} + +bool SkKTXFile::is_ktx(SkStreamRewindable* stream) { + // Read the KTX header and make sure it's valid. + unsigned char buf[KTX_FILE_IDENTIFIER_SIZE]; + bool largeEnough = + stream->read((void*)buf, KTX_FILE_IDENTIFIER_SIZE) == KTX_FILE_IDENTIFIER_SIZE; + stream->rewind(); + if (!largeEnough) { + return false; + } + return is_ktx(buf); +} |