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 "ktx.h" 10#include "SkBitmap.h" 11#include "SkStream.h" 12#include "SkEndian.h" 13 14#include "gl/GrGLDefines.h" 15 16#include "etc1.h" 17 18#define KTX_FILE_IDENTIFIER_SIZE 12 19static const uint8_t KTX_FILE_IDENTIFIER[KTX_FILE_IDENTIFIER_SIZE] = { 20 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A 21}; 22 23static const uint32_t kKTX_ENDIANNESS_CODE = 0x04030201; 24 25bool SkKTXFile::KeyValue::readKeyAndValue(const uint8_t* data) { 26 const char *key = reinterpret_cast<const char *>(data); 27 const char *value = key; 28 29 size_t bytesRead = 0; 30 while (*value != '\0' && bytesRead < this->fDataSz) { 31 ++bytesRead; 32 ++value; 33 } 34 35 // Error of some sort.. 36 if (bytesRead >= this->fDataSz) { 37 return false; 38 } 39 40 // Read the zero terminator 41 ++bytesRead; 42 ++value; 43 44 size_t bytesLeft = this->fDataSz - bytesRead; 45 46 // We ignore the null terminator when setting the string value. 47 this->fKey.set(key, bytesRead - 1); 48 if (bytesLeft > 0) { 49 this->fValue.set(value, bytesLeft - 1); 50 } else { 51 return false; 52 } 53 54 return true; 55} 56 57bool SkKTXFile::KeyValue::writeKeyAndValueForKTX(SkWStream* strm) { 58 size_t bytesWritten = 0; 59 if (!strm->write(&(this->fDataSz), 4)) { 60 return false; 61 } 62 63 bytesWritten += 4; 64 65 // Here we know that C-strings must end with a null terminating 66 // character, so when we get a c_str(), it will have as many 67 // bytes of data as size() returns plus a zero, so we just 68 // write size() + 1 bytes into the stream. 69 70 size_t keySize = this->fKey.size() + 1; 71 if (!strm->write(this->fKey.c_str(), keySize)) { 72 return false; 73 } 74 75 bytesWritten += keySize; 76 77 size_t valueSize = this->fValue.size() + 1; 78 if (!strm->write(this->fValue.c_str(), valueSize)) { 79 return false; 80 } 81 82 bytesWritten += valueSize; 83 84 size_t bytesWrittenPadFour = (bytesWritten + 3) & ~3; 85 uint8_t nullBuf[4] = { 0, 0, 0, 0 }; 86 87 size_t padding = bytesWrittenPadFour - bytesWritten; 88 SkASSERT(padding < 4); 89 90 return strm->write(nullBuf, padding); 91} 92 93uint32_t SkKTXFile::readInt(const uint8_t** buf, size_t* bytesLeft) const { 94 SkASSERT(NULL != buf && NULL != bytesLeft); 95 96 uint32_t result; 97 98 if (*bytesLeft < 4) { 99 SkASSERT(false); 100 return 0; 101 } 102 103 memcpy(&result, *buf, 4); 104 *buf += 4; 105 106 if (fSwapBytes) { 107 SkEndianSwap32(result); 108 } 109 110 *bytesLeft -= 4; 111 112 return result; 113} 114 115SkString SkKTXFile::getValueForKey(const SkString& key) const { 116 const KeyValue *begin = this->fKeyValuePairs.begin(); 117 const KeyValue *end = this->fKeyValuePairs.end(); 118 for (const KeyValue *kv = begin; kv != end; ++kv) { 119 if (kv->key() == key) { 120 return kv->value(); 121 } 122 } 123 return SkString(); 124} 125 126bool SkKTXFile::isETC1() const { 127 return this->valid() && GR_GL_COMPRESSED_RGB8_ETC1 == fHeader.fGLInternalFormat; 128} 129 130bool SkKTXFile::isRGBA8() const { 131 return this->valid() && GR_GL_RGBA8 == fHeader.fGLInternalFormat; 132} 133 134bool SkKTXFile::isRGB8() const { 135 return this->valid() && GR_GL_RGB8 == fHeader.fGLInternalFormat; 136} 137 138bool SkKTXFile::readKTXFile(const uint8_t* data, size_t dataLen) { 139 const uint8_t *buf = data; 140 size_t bytesLeft = dataLen; 141 142 // Make sure original KTX header is there... this should have been checked 143 // already by a call to is_ktx() 144 SkASSERT(bytesLeft > KTX_FILE_IDENTIFIER_SIZE); 145 SkASSERT(0 == memcmp(KTX_FILE_IDENTIFIER, buf, KTX_FILE_IDENTIFIER_SIZE)); 146 buf += KTX_FILE_IDENTIFIER_SIZE; 147 bytesLeft -= KTX_FILE_IDENTIFIER_SIZE; 148 149 // Read header, but first make sure that we have the proper space: we need 150 // two 32-bit ints: 1 for endianness, and another for the mandatory image 151 // size after the header. 152 if (bytesLeft < 8 + sizeof(Header)) { 153 return false; 154 } 155 156 uint32_t endianness = this->readInt(&buf, &bytesLeft); 157 fSwapBytes = kKTX_ENDIANNESS_CODE != endianness; 158 159 // Read header values 160 fHeader.fGLType = this->readInt(&buf, &bytesLeft); 161 fHeader.fGLTypeSize = this->readInt(&buf, &bytesLeft); 162 fHeader.fGLFormat = this->readInt(&buf, &bytesLeft); 163 fHeader.fGLInternalFormat = this->readInt(&buf, &bytesLeft); 164 fHeader.fGLBaseInternalFormat = this->readInt(&buf, &bytesLeft); 165 fHeader.fPixelWidth = this->readInt(&buf, &bytesLeft); 166 fHeader.fPixelHeight = this->readInt(&buf, &bytesLeft); 167 fHeader.fPixelDepth = this->readInt(&buf, &bytesLeft); 168 fHeader.fNumberOfArrayElements = this->readInt(&buf, &bytesLeft); 169 fHeader.fNumberOfFaces = this->readInt(&buf, &bytesLeft); 170 fHeader.fNumberOfMipmapLevels = this->readInt(&buf, &bytesLeft); 171 fHeader.fBytesOfKeyValueData = this->readInt(&buf, &bytesLeft); 172 173 // Check for things that we understand... 174 { 175 // First, we only support compressed formats and single byte 176 // representations at the moment. If the internal format is 177 // compressed, the the GLType field in the header must be zero. 178 // In the future, we may support additional data types (such 179 // as GL_UNSIGNED_SHORT_5_6_5) 180 if (fHeader.fGLType != 0 && fHeader.fGLType != GR_GL_UNSIGNED_BYTE) { 181 return false; 182 } 183 184 // This means that for well-formatted KTX files, the glTypeSize 185 // field must be one... 186 if (fHeader.fGLTypeSize != 1) { 187 return false; 188 } 189 190 // We don't support 3D textures. 191 if (fHeader.fPixelDepth > 1) { 192 return false; 193 } 194 195 // We don't support texture arrays 196 if (fHeader.fNumberOfArrayElements > 1) { 197 return false; 198 } 199 200 // We don't support cube maps 201 if (fHeader.fNumberOfFaces > 1) { 202 return false; 203 } 204 } 205 206 // Make sure that we have enough bytes left for the key/value 207 // data according to what was said in the header. 208 if (bytesLeft < fHeader.fBytesOfKeyValueData) { 209 return false; 210 } 211 212 // Next read the key value pairs 213 size_t keyValueBytesRead = 0; 214 while (keyValueBytesRead < fHeader.fBytesOfKeyValueData) { 215 uint32_t keyValueBytes = this->readInt(&buf, &bytesLeft); 216 keyValueBytesRead += 4; 217 218 if (keyValueBytes > bytesLeft) { 219 return false; 220 } 221 222 KeyValue kv(keyValueBytes); 223 if (!kv.readKeyAndValue(buf)) { 224 return false; 225 } 226 227 fKeyValuePairs.push_back(kv); 228 229 uint32_t keyValueBytesPadded = (keyValueBytes + 3) & ~3; 230 buf += keyValueBytesPadded; 231 keyValueBytesRead += keyValueBytesPadded; 232 bytesLeft -= keyValueBytesPadded; 233 } 234 235 // Read the pixel data... 236 int mipmaps = SkMax32(fHeader.fNumberOfMipmapLevels, 1); 237 SkASSERT(mipmaps == 1); 238 239 int arrayElements = SkMax32(fHeader.fNumberOfArrayElements, 1); 240 SkASSERT(arrayElements == 1); 241 242 int faces = SkMax32(fHeader.fNumberOfFaces, 1); 243 SkASSERT(faces == 1); 244 245 int depth = SkMax32(fHeader.fPixelDepth, 1); 246 SkASSERT(depth == 1); 247 248 for (int mipmap = 0; mipmap < mipmaps; ++mipmap) { 249 // Make sure that we have at least 4 more bytes for the first image size 250 if (bytesLeft < 4) { 251 return false; 252 } 253 254 uint32_t imgSize = this->readInt(&buf, &bytesLeft); 255 256 // Truncated file. 257 if (bytesLeft < imgSize) { 258 return false; 259 } 260 261 // !FIXME! If support is ever added for cube maps then the padding 262 // needs to be taken into account here. 263 for (int arrayElement = 0; arrayElement < arrayElements; ++arrayElement) { 264 for (int face = 0; face < faces; ++face) { 265 for (int z = 0; z < depth; ++z) { 266 PixelData pd(buf, imgSize); 267 fPixelData.append(1, &pd); 268 } 269 } 270 } 271 272 uint32_t imgSizePadded = (imgSize + 3) & ~3; 273 buf += imgSizePadded; 274 bytesLeft -= imgSizePadded; 275 } 276 277 return bytesLeft == 0; 278} 279 280bool SkKTXFile::is_ktx(const uint8_t *data) { 281 return 0 == memcmp(KTX_FILE_IDENTIFIER, data, KTX_FILE_IDENTIFIER_SIZE); 282} 283 284bool SkKTXFile::is_ktx(SkStreamRewindable* stream) { 285 // Read the KTX header and make sure it's valid. 286 unsigned char buf[KTX_FILE_IDENTIFIER_SIZE]; 287 bool largeEnough = 288 stream->read((void*)buf, KTX_FILE_IDENTIFIER_SIZE) == KTX_FILE_IDENTIFIER_SIZE; 289 stream->rewind(); 290 if (!largeEnough) { 291 return false; 292 } 293 return is_ktx(buf); 294} 295 296SkKTXFile::KeyValue SkKTXFile::CreateKeyValue(const char *cstrKey, const char *cstrValue) { 297 SkString key(cstrKey); 298 SkString value(cstrValue); 299 300 // Size of buffer is length of string plus the null terminators... 301 size_t size = key.size() + 1 + value.size() + 1; 302 303 SkAutoSMalloc<256> buf(size); 304 uint8_t* kvBuf = reinterpret_cast<uint8_t*>(buf.get()); 305 memcpy(kvBuf, key.c_str(), key.size() + 1); 306 memcpy(kvBuf + key.size() + 1, value.c_str(), value.size() + 1); 307 308 KeyValue kv(size); 309 SkAssertResult(kv.readKeyAndValue(kvBuf)); 310 return kv; 311} 312 313bool SkKTXFile::WriteETC1ToKTX(SkWStream* stream, const uint8_t *etc1Data, 314 uint32_t width, uint32_t height) { 315 // First thing's first, write out the magic identifier and endianness... 316 if (!stream->write(KTX_FILE_IDENTIFIER, KTX_FILE_IDENTIFIER_SIZE)) { 317 return false; 318 } 319 320 if (!stream->write(&kKTX_ENDIANNESS_CODE, 4)) { 321 return false; 322 } 323 324 Header hdr; 325 hdr.fGLType = 0; 326 hdr.fGLTypeSize = 1; 327 hdr.fGLFormat = 0; 328 hdr.fGLInternalFormat = GR_GL_COMPRESSED_RGB8_ETC1; 329 hdr.fGLBaseInternalFormat = GR_GL_RGB; 330 hdr.fPixelWidth = width; 331 hdr.fPixelHeight = height; 332 hdr.fNumberOfArrayElements = 0; 333 hdr.fNumberOfFaces = 1; 334 hdr.fNumberOfMipmapLevels = 1; 335 336 // !FIXME! The spec suggests that we put KTXOrientation as a 337 // key value pair in the header, but that means that we'd have to 338 // pipe through the bitmap's orientation to properly do that. 339 hdr.fBytesOfKeyValueData = 0; 340 341 // Write the header 342 if (!stream->write(&hdr, sizeof(hdr))) { 343 return false; 344 } 345 346 // Write the size of the image data 347 etc1_uint32 dataSize = etc1_get_encoded_data_size(width, height); 348 if (!stream->write(&dataSize, 4)) { 349 return false; 350 } 351 352 // Write the actual image data 353 if (!stream->write(etc1Data, dataSize)) { 354 return false; 355 } 356 357 return true; 358} 359 360bool SkKTXFile::WriteBitmapToKTX(SkWStream* stream, const SkBitmap& bitmap) { 361 const SkColorType ct = bitmap.colorType(); 362 SkAutoLockPixels alp(bitmap); 363 364 const int width = bitmap.width(); 365 const int height = bitmap.width(); 366 const uint8_t* src = reinterpret_cast<uint8_t*>(bitmap.getPixels()); 367 if (NULL == bitmap.getPixels()) { 368 return false; 369 } 370 371 // First thing's first, write out the magic identifier and endianness... 372 if (!stream->write(KTX_FILE_IDENTIFIER, KTX_FILE_IDENTIFIER_SIZE) || 373 !stream->write(&kKTX_ENDIANNESS_CODE, 4)) { 374 return false; 375 } 376 377 // Collect our key/value pairs... 378 SkTArray<KeyValue> kvPairs; 379 380 // Next, write the header based on the bitmap's config. 381 Header hdr; 382 switch (ct) { 383 case kIndex_8_SkColorType: 384 // There is a compressed format for this, but we don't support it yet. 385 SkDebugf("Writing indexed bitmap to KTX unsupported.\n"); 386 // VVV fall through VVV 387 default: 388 case kUnknown_SkColorType: 389 // Bitmap hasn't been configured. 390 return false; 391 392 case kAlpha_8_SkColorType: 393 hdr.fGLType = GR_GL_UNSIGNED_BYTE; 394 hdr.fGLTypeSize = 1; 395 hdr.fGLFormat = GR_GL_RED; 396 hdr.fGLInternalFormat = GR_GL_R8; 397 hdr.fGLBaseInternalFormat = GR_GL_RED; 398 break; 399 400 case kRGB_565_SkColorType: 401 hdr.fGLType = GR_GL_UNSIGNED_SHORT_5_6_5; 402 hdr.fGLTypeSize = 2; 403 hdr.fGLFormat = GR_GL_RGB; 404 hdr.fGLInternalFormat = GR_GL_RGB; 405 hdr.fGLBaseInternalFormat = GR_GL_RGB; 406 break; 407 408 case kARGB_4444_SkColorType: 409 hdr.fGLType = GR_GL_UNSIGNED_SHORT_4_4_4_4; 410 hdr.fGLTypeSize = 2; 411 hdr.fGLFormat = GR_GL_RGBA; 412 hdr.fGLInternalFormat = GR_GL_RGBA4; 413 hdr.fGLBaseInternalFormat = GR_GL_RGBA; 414 kvPairs.push_back(CreateKeyValue("KTXPremultipliedAlpha", "True")); 415 break; 416 417 case kN32_SkColorType: 418 hdr.fGLType = GR_GL_UNSIGNED_BYTE; 419 hdr.fGLTypeSize = 1; 420 hdr.fGLFormat = GR_GL_RGBA; 421 hdr.fGLInternalFormat = GR_GL_RGBA8; 422 hdr.fGLBaseInternalFormat = GR_GL_RGBA; 423 kvPairs.push_back(CreateKeyValue("KTXPremultipliedAlpha", "True")); 424 break; 425 } 426 427 // Everything else in the header is shared. 428 hdr.fPixelWidth = width; 429 hdr.fPixelHeight = height; 430 hdr.fNumberOfArrayElements = 0; 431 hdr.fNumberOfFaces = 1; 432 hdr.fNumberOfMipmapLevels = 1; 433 434 // Calculate the key value data size 435 hdr.fBytesOfKeyValueData = 0; 436 for (KeyValue *kv = kvPairs.begin(); kv != kvPairs.end(); ++kv) { 437 // Key value size is the size of the key value data, 438 // four bytes for saying how big the key value size is 439 // and then additional bytes for padding to four byte boundary 440 size_t kvsize = kv->size(); 441 kvsize += 4; 442 kvsize = (kvsize + 3) & ~3; 443 hdr.fBytesOfKeyValueData += kvsize; 444 } 445 446 // Write the header 447 if (!stream->write(&hdr, sizeof(hdr))) { 448 return false; 449 } 450 451 // Write out each key value pair 452 for (KeyValue *kv = kvPairs.begin(); kv != kvPairs.end(); ++kv) { 453 if (!kv->writeKeyAndValueForKTX(stream)) { 454 return false; 455 } 456 } 457 458 // Calculate the size of the data 459 int bpp = bitmap.bytesPerPixel(); 460 uint32_t dataSz = bpp * width * height; 461 462 if (0 >= bpp) { 463 return false; 464 } 465 466 // Write it into the buffer 467 if (!stream->write(&dataSz, 4)) { 468 return false; 469 } 470 471 // Write the pixel data... 472 const uint8_t* rowPtr = src; 473 if (kN32_SkColorType == ct) { 474 for (int j = 0; j < height; ++j) { 475 const uint32_t* pixelsPtr = reinterpret_cast<const uint32_t*>(rowPtr); 476 for (int i = 0; i < width; ++i) { 477 uint32_t pixel = pixelsPtr[i]; 478 uint8_t dstPixel[4]; 479 dstPixel[0] = pixel >> SK_R32_SHIFT; 480 dstPixel[1] = pixel >> SK_G32_SHIFT; 481 dstPixel[2] = pixel >> SK_B32_SHIFT; 482 dstPixel[3] = pixel >> SK_A32_SHIFT; 483 if (!stream->write(dstPixel, 4)) { 484 return false; 485 } 486 } 487 rowPtr += bitmap.rowBytes(); 488 } 489 } else { 490 for (int i = 0; i < height; ++i) { 491 if (!stream->write(rowPtr, bpp*width)) { 492 return false; 493 } 494 rowPtr += bitmap.rowBytes(); 495 } 496 } 497 498 return true; 499} 500