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 |
| 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 |
| 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 |
| 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.. |
| 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 |
| 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 { |
| 81 return valid() && fHeader.fGLInternalFormat == GR_GL_COMPRESSED_RGB8_ETC1; |
| 82 } |
| 83 |
| 84 bool SkKTXFile::isRGBA8() const { |
| 85 return valid() && fHeader.fGLInternalFormat == GR_GL_RGBA8; |
| 86 } |
| 87 |
| 88 bool SkKTXFile::isRGB8() const { |
| 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); |
| 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) |
| 132 if (fHeader.fGLType != 0 && fHeader.fGLType != GR_GL_UNSIGNED_BYTE) { |
| 133 return false; |
| 134 } |
| 135 |
| 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) { |
| 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) { |
| 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. |
| 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) { |
| 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 |