Chromium Code Reviews| Index: src/utils/SkKTXFile.cpp |
| diff --git a/src/utils/SkKTXFile.cpp b/src/utils/SkKTXFile.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ceba49d73fa0fb306cc08e6022025c42e4c81e0c |
| --- /dev/null |
| +++ b/src/utils/SkKTXFile.cpp |
| @@ -0,0 +1,244 @@ |
| + |
| +/* |
| + * 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 "SkKTXFile.h" |
| +#include "SkStream.h" |
| + |
|
robertphillips
2014/06/03 13:27:41
Hmmm - we may need to make this official (in the g
krajcevski
2014/06/03 14:58:35
Done.
|
| +#include "../gpu/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 |
| +}; |
| + |
|
robertphillips
2014/06/03 13:27:41
SkEndianSwap32 ?
krajcevski
2014/06/03 14:46:27
Done.
|
| +static void swap_uint32(uint32_t& v) { |
| + uint8_t *ptr = reinterpret_cast<uint8_t *>(&v); |
| + |
| + uint8_t t; |
| + t = ptr[0]; |
| + ptr[0] = ptr[3]; |
| + ptr[3] = t; |
| + |
| + t = ptr[1]; |
| + ptr[1] = ptr[2]; |
| + ptr[2] = t; |
| +} |
| + |
|
robertphillips
2014/06/03 13:27:41
I'm not entirely sure this method is working as in
krajcevski
2014/06/03 14:46:27
None of the tools that I found produce .ktx files
|
| +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 (bytesRead < this->fDataSz) { |
| + ++bytesRead; |
| + if (*value == '\0') { |
| + break; |
| + } |
| + ++value; |
| + } |
| + |
| + // Error of some sort.. |
|
robertphillips
2014/06/03 13:27:41
Why is "bytesRead > 0" an error?
krajcevski
2014/06/03 14:46:27
It's not.
Done.
|
| + if (bytesRead > 0 || bytesRead == this->fDataSz) { |
| + return false; |
| + } |
| + |
| + fKey.set(key, bytesRead); |
| + if (this->fDataSz > bytesRead) { |
| + fValue.set(value, this->fDataSz - bytesRead); |
| + } else { |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
|
robertphillips
2014/06/03 13:27:41
left align '&' ?
We only pass in const values by r
krajcevski
2014/06/03 14:46:27
Done.
|
| +uint32_t SkKTXFile::readInt(const uint8_t*& buf, size_t &bytesLeft) const { |
| + uint32_t result; |
| + |
| + if(bytesLeft < 4) { |
| + SkASSERT(false); |
| + return 0; |
| + } |
| + |
| + memcpy(&result, buf, 4); |
| + buf += 4; |
| + |
| + if (fSwapBytes) { |
| + swap_uint32(result); |
| + } |
| + |
| + bytesLeft -= 4; |
| + |
| + return result; |
| +} |
| + |
| +bool SkKTXFile::isETC1() const { |
|
robertphillips
2014/06/03 13:27:41
this->valid() ?
yoda-fy the '==' check ?
krajcevski
2014/06/03 14:46:27
Done.
|
| + return valid() && fHeader.fGLInternalFormat == GR_GL_COMPRESSED_RGB8_ETC1; |
| +} |
| + |
| +bool SkKTXFile::isRGBA8() const { |
|
robertphillips
2014/06/03 13:27:41
ditto
krajcevski
2014/06/03 14:46:27
Done.
|
| + return valid() && fHeader.fGLInternalFormat == GR_GL_RGBA8; |
| +} |
| + |
| +bool SkKTXFile::isRGB8() const { |
|
robertphillips
2014/06/03 13:27:41
ditto
krajcevski
2014/06/03 14:46:27
Done.
|
| + return valid() && fHeader.fGLInternalFormat == GR_GL_RGB8; |
| +} |
| + |
| +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 magic = this->readInt(buf, bytesLeft); |
|
robertphillips
2014/06/03 13:27:41
kKTXEndianessCode ?
krajcevski
2014/06/03 14:46:27
Done.
|
| + fSwapBytes = 0x04030201 != magic; |
| + |
| + // 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. In the future, we may support |
| + // additional data types (such as GL_UNSIGNED_SHORT_5_6_5) |
|
robertphillips
2014/06/03 13:27:41
Zero means what in this case?
krajcevski
2014/06/03 14:46:27
Done. Fixed the comment.
|
| + if (fHeader.fGLType != 0 && fHeader.fGLType != GR_GL_UNSIGNED_BYTE) { |
| + return false; |
| + } |
| + |
|
robertphillips
2014/06/03 13:27:41
This comment doesn't seem quite right.
krajcevski
2014/06/03 14:46:27
According to the KTX spec, if the GLType is 0 (com
|
| + // 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; |
| + } |
| + } |
| + |
| + // Next read the key value pairs |
| + if (bytesLeft < fHeader.fBytesOfKeyValueData) { |
|
robertphillips
2014/06/03 13:27:41
// file has been truncated somehow ?
krajcevski
2014/06/03 14:46:27
Done.
|
| + return false; |
| + } |
| + |
| + 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.append(1, &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); |
| + |
| + int height = SkMax32(fHeader.fPixelHeight, 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); |
| + |
| + if (bytesLeft < imgSize) { |
|
robertphillips
2014/06/03 13:27:41
// truncated file ?
krajcevski
2014/06/03 14:46:27
Done.
|
| + return false; |
| + } |
| + |
| + // !FIXME! If support is ever added for cube maps then the padding |
| + // needs to be taken into account here. |
|
robertphillips
2014/06/03 13:27:41
add '{}'s and nesting ?
krajcevski
2014/06/03 14:46:27
Done.
|
| + for (int arrayElement = 0; arrayElement < arrayElements; ++arrayElement) |
| + for (int face = 0; face < faces; ++face) |
| + for (int z = 0; z < depth; ++z) |
| + for (int y = 0; y < height; ++y) { |
|
robertphillips
2014/06/03 13:27:41
extra space between 'pd' and '(' ?
It looks like w
krajcevski
2014/06/03 14:46:27
Yes, there is a comment about it above the SkKTXFi
|
| + 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); |
| +} |