Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 | |
| 2 /* | |
| 3 * Copyright 2014 Google Inc. | |
| 4 * | |
| 5 * Use of this source code is governed by a BSD-style license that can be | |
| 6 * found in the LICENSE file. | |
| 7 */ | |
| 8 | |
| 9 #include "SkKTXFile.h" | |
| 10 #include "SkStream.h" | |
| 11 | |
|
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.
| |
| 12 #include "../gpu/gl/GrGLDefines.h" | |
| 13 | |
| 14 #define KTX_FILE_IDENTIFIER_SIZE 12 | |
| 15 static const uint8_t KTX_FILE_IDENTIFIER[KTX_FILE_IDENTIFIER_SIZE] = { | |
| 16 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A | |
| 17 }; | |
| 18 | |
|
robertphillips
2014/06/03 13:27:41
SkEndianSwap32 ?
krajcevski
2014/06/03 14:46:27
Done.
| |
| 19 static void swap_uint32(uint32_t& v) { | |
| 20 uint8_t *ptr = reinterpret_cast<uint8_t *>(&v); | |
| 21 | |
| 22 uint8_t t; | |
| 23 t = ptr[0]; | |
| 24 ptr[0] = ptr[3]; | |
| 25 ptr[3] = t; | |
| 26 | |
| 27 t = ptr[1]; | |
| 28 ptr[1] = ptr[2]; | |
| 29 ptr[2] = t; | |
| 30 } | |
| 31 | |
|
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
| |
| 32 bool SkKTXFile::KeyValue::readKeyAndValue(const uint8_t* data) { | |
| 33 const char *key = reinterpret_cast<const char *>(data); | |
| 34 const char *value = key; | |
| 35 | |
| 36 size_t bytesRead = 0; | |
| 37 while (bytesRead < this->fDataSz) { | |
| 38 ++bytesRead; | |
| 39 if (*value == '\0') { | |
| 40 break; | |
| 41 } | |
| 42 ++value; | |
| 43 } | |
| 44 | |
| 45 // 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.
| |
| 46 if (bytesRead > 0 || bytesRead == this->fDataSz) { | |
| 47 return false; | |
| 48 } | |
| 49 | |
| 50 fKey.set(key, bytesRead); | |
| 51 if (this->fDataSz > bytesRead) { | |
| 52 fValue.set(value, this->fDataSz - bytesRead); | |
| 53 } else { | |
| 54 return false; | |
| 55 } | |
| 56 | |
| 57 return true; | |
| 58 } | |
| 59 | |
|
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.
| |
| 60 uint32_t SkKTXFile::readInt(const uint8_t*& buf, size_t &bytesLeft) const { | |
| 61 uint32_t result; | |
| 62 | |
| 63 if(bytesLeft < 4) { | |
| 64 SkASSERT(false); | |
| 65 return 0; | |
| 66 } | |
| 67 | |
| 68 memcpy(&result, buf, 4); | |
| 69 buf += 4; | |
| 70 | |
| 71 if (fSwapBytes) { | |
| 72 swap_uint32(result); | |
| 73 } | |
| 74 | |
| 75 bytesLeft -= 4; | |
| 76 | |
| 77 return result; | |
| 78 } | |
| 79 | |
| 80 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.
| |
| 81 return valid() && fHeader.fGLInternalFormat == GR_GL_COMPRESSED_RGB8_ETC1; | |
| 82 } | |
| 83 | |
| 84 bool SkKTXFile::isRGBA8() const { | |
|
robertphillips
2014/06/03 13:27:41
ditto
krajcevski
2014/06/03 14:46:27
Done.
| |
| 85 return valid() && fHeader.fGLInternalFormat == GR_GL_RGBA8; | |
| 86 } | |
| 87 | |
| 88 bool SkKTXFile::isRGB8() const { | |
|
robertphillips
2014/06/03 13:27:41
ditto
krajcevski
2014/06/03 14:46:27
Done.
| |
| 89 return valid() && fHeader.fGLInternalFormat == GR_GL_RGB8; | |
| 90 } | |
| 91 | |
| 92 bool SkKTXFile::readKTXFile(const uint8_t* data, size_t dataLen) { | |
| 93 const uint8_t *buf = data; | |
| 94 size_t bytesLeft = dataLen; | |
| 95 | |
| 96 // Make sure original KTX header is there... this should have been checked | |
| 97 // already by a call to is_ktx() | |
| 98 SkASSERT(bytesLeft > KTX_FILE_IDENTIFIER_SIZE); | |
| 99 SkASSERT(0 == memcmp(KTX_FILE_IDENTIFIER, buf, KTX_FILE_IDENTIFIER_SIZE)); | |
| 100 buf += KTX_FILE_IDENTIFIER_SIZE; | |
| 101 bytesLeft -= KTX_FILE_IDENTIFIER_SIZE; | |
| 102 | |
| 103 // Read header, but first make sure that we have the proper space: we need | |
| 104 // two 32-bit ints: 1 for endianness, and another for the mandatory image | |
| 105 // size after the header. | |
| 106 if (bytesLeft < 8 + sizeof(Header)) { | |
| 107 return false; | |
| 108 } | |
| 109 | |
| 110 uint32_t magic = this->readInt(buf, bytesLeft); | |
|
robertphillips
2014/06/03 13:27:41
kKTXEndianessCode ?
krajcevski
2014/06/03 14:46:27
Done.
| |
| 111 fSwapBytes = 0x04030201 != magic; | |
| 112 | |
| 113 // Read header values | |
| 114 fHeader.fGLType = this->readInt(buf, bytesLeft); | |
| 115 fHeader.fGLTypeSize = this->readInt(buf, bytesLeft); | |
| 116 fHeader.fGLFormat = this->readInt(buf, bytesLeft); | |
| 117 fHeader.fGLInternalFormat = this->readInt(buf, bytesLeft); | |
| 118 fHeader.fGLBaseInternalFormat = this->readInt(buf, bytesLeft); | |
| 119 fHeader.fPixelWidth = this->readInt(buf, bytesLeft); | |
| 120 fHeader.fPixelHeight = this->readInt(buf, bytesLeft); | |
| 121 fHeader.fPixelDepth = this->readInt(buf, bytesLeft); | |
| 122 fHeader.fNumberOfArrayElements = this->readInt(buf, bytesLeft); | |
| 123 fHeader.fNumberOfFaces = this->readInt(buf, bytesLeft); | |
| 124 fHeader.fNumberOfMipmapLevels = this->readInt(buf, bytesLeft); | |
| 125 fHeader.fBytesOfKeyValueData = this->readInt(buf, bytesLeft); | |
| 126 | |
| 127 // Check for things that we understand... | |
| 128 { | |
| 129 // First, we only support compressed formats and single byte | |
| 130 // representations at the moment. In the future, we may support | |
| 131 // 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.
| |
| 132 if (fHeader.fGLType != 0 && fHeader.fGLType != GR_GL_UNSIGNED_BYTE) { | |
| 133 return false; | |
| 134 } | |
| 135 | |
|
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
| |
| 136 // This means that for well-formatted KTX files, the glTypeSize | |
| 137 // field must be one... | |
| 138 if (fHeader.fGLTypeSize != 1) { | |
| 139 return false; | |
| 140 } | |
| 141 | |
| 142 // We don't support 3D textures. | |
| 143 if (fHeader.fPixelDepth > 1) { | |
| 144 return false; | |
| 145 } | |
| 146 | |
| 147 // We don't support texture arrays | |
| 148 if (fHeader.fNumberOfArrayElements > 1) { | |
| 149 return false; | |
| 150 } | |
| 151 | |
| 152 // We don't support cube maps | |
| 153 if (fHeader.fNumberOfFaces > 1) { | |
| 154 return false; | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 // Next read the key value pairs | |
| 159 if (bytesLeft < fHeader.fBytesOfKeyValueData) { | |
|
robertphillips
2014/06/03 13:27:41
// file has been truncated somehow ?
krajcevski
2014/06/03 14:46:27
Done.
| |
| 160 return false; | |
| 161 } | |
| 162 | |
| 163 size_t keyValueBytesRead = 0; | |
| 164 while (keyValueBytesRead < fHeader.fBytesOfKeyValueData) { | |
| 165 uint32_t keyValueBytes = this->readInt(buf, bytesLeft); | |
| 166 keyValueBytesRead = 4; | |
| 167 | |
| 168 if (keyValueBytes > bytesLeft) { | |
| 169 return false; | |
| 170 } | |
| 171 | |
| 172 KeyValue kv(keyValueBytes); | |
| 173 if (!kv.readKeyAndValue(buf)) { | |
| 174 return false; | |
| 175 } | |
| 176 | |
| 177 fKeyValuePairs.append(1, &kv); | |
| 178 | |
| 179 uint32_t keyValueBytesPadded = (keyValueBytes + 3) & ~3; | |
| 180 buf += keyValueBytesPadded; | |
| 181 keyValueBytesRead += keyValueBytesPadded; | |
| 182 bytesLeft -= keyValueBytesPadded; | |
| 183 } | |
| 184 | |
| 185 // Read the pixel data... | |
| 186 int mipmaps = SkMax32(fHeader.fNumberOfMipmapLevels, 1); | |
| 187 SkASSERT(mipmaps == 1); | |
| 188 | |
| 189 int arrayElements = SkMax32(fHeader.fNumberOfArrayElements, 1); | |
| 190 SkASSERT(arrayElements == 1); | |
| 191 | |
| 192 int faces = SkMax32(fHeader.fNumberOfFaces, 1); | |
| 193 SkASSERT(faces == 1); | |
| 194 | |
| 195 int depth = SkMax32(fHeader.fPixelDepth, 1); | |
| 196 SkASSERT(depth == 1); | |
| 197 | |
| 198 int height = SkMax32(fHeader.fPixelHeight, 1); | |
| 199 | |
| 200 for (int mipmap = 0; mipmap < mipmaps; ++mipmap) { | |
| 201 // Make sure that we have at least 4 more bytes for the first image size | |
| 202 if (bytesLeft < 4) { | |
| 203 return false; | |
| 204 } | |
| 205 | |
| 206 uint32_t imgSize = this->readInt(buf, bytesLeft); | |
| 207 | |
| 208 if (bytesLeft < imgSize) { | |
|
robertphillips
2014/06/03 13:27:41
// truncated file ?
krajcevski
2014/06/03 14:46:27
Done.
| |
| 209 return false; | |
| 210 } | |
| 211 | |
| 212 // !FIXME! If support is ever added for cube maps then the padding | |
| 213 // 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.
| |
| 214 for (int arrayElement = 0; arrayElement < arrayElements; ++arrayElement) | |
| 215 for (int face = 0; face < faces; ++face) | |
| 216 for (int z = 0; z < depth; ++z) | |
| 217 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
| |
| 218 PixelData pd (buf, imgSize); | |
| 219 fPixelData.append(1, &pd); | |
| 220 } | |
| 221 | |
| 222 uint32_t imgSizePadded = (imgSize + 3) & ~3; | |
| 223 buf += imgSizePadded; | |
| 224 bytesLeft -= imgSizePadded; | |
| 225 } | |
| 226 | |
| 227 return bytesLeft == 0; | |
| 228 } | |
| 229 | |
| 230 bool SkKTXFile::is_ktx(const uint8_t *data) { | |
| 231 return 0 == memcmp(KTX_FILE_IDENTIFIER, data, KTX_FILE_IDENTIFIER_SIZE); | |
| 232 } | |
| 233 | |
| 234 bool SkKTXFile::is_ktx(SkStreamRewindable* stream) { | |
| 235 // Read the KTX header and make sure it's valid. | |
| 236 unsigned char buf[KTX_FILE_IDENTIFIER_SIZE]; | |
| 237 bool largeEnough = | |
| 238 stream->read((void*)buf, KTX_FILE_IDENTIFIER_SIZE) == KTX_FILE_IDENTIFIE R_SIZE; | |
| 239 stream->rewind(); | |
| 240 if (!largeEnough) { | |
| 241 return false; | |
| 242 } | |
| 243 return is_ktx(buf); | |
| 244 } | |
| OLD | NEW |