Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 | 1 |
| 2 /* | 2 /* |
| 3 * Copyright 2014 Google Inc. | 3 * Copyright 2014 Google Inc. |
| 4 * | 4 * |
| 5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
| 6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 #include "ktx.h" | 9 #include "ktx.h" |
| 10 #include "SkBitmap.h" | |
| 10 #include "SkStream.h" | 11 #include "SkStream.h" |
| 11 #include "SkEndian.h" | 12 #include "SkEndian.h" |
| 12 | 13 |
| 13 #include "gl/GrGLDefines.h" | 14 #include "gl/GrGLDefines.h" |
| 14 | 15 |
| 16 #include "etc1.h" | |
| 17 | |
| 15 #define KTX_FILE_IDENTIFIER_SIZE 12 | 18 #define KTX_FILE_IDENTIFIER_SIZE 12 |
| 16 static const uint8_t KTX_FILE_IDENTIFIER[KTX_FILE_IDENTIFIER_SIZE] = { | 19 static const uint8_t KTX_FILE_IDENTIFIER[KTX_FILE_IDENTIFIER_SIZE] = { |
| 17 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A | 20 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A |
| 18 }; | 21 }; |
| 19 | 22 |
| 20 static const uint32_t kKTX_ENDIANNESS_CODE = 0x04030201; | 23 static const uint32_t kKTX_ENDIANNESS_CODE = 0x04030201; |
| 21 | 24 |
| 22 bool SkKTXFile::KeyValue::readKeyAndValue(const uint8_t* data) { | 25 bool SkKTXFile::KeyValue::readKeyAndValue(const uint8_t* data) { |
| 23 const char *key = reinterpret_cast<const char *>(data); | 26 const char *key = reinterpret_cast<const char *>(data); |
| 24 const char *value = key; | 27 const char *value = key; |
| 25 | 28 |
| 26 size_t bytesRead = 0; | 29 size_t bytesRead = 0; |
| 27 while (*value != '\0' && bytesRead < this->fDataSz) { | 30 while (*value != '\0' && bytesRead < this->fDataSz) { |
| 28 ++bytesRead; | 31 ++bytesRead; |
| 29 ++value; | 32 ++value; |
| 30 } | 33 } |
| 31 | 34 |
| 32 // Error of some sort.. | 35 // Error of some sort.. |
| 33 if (bytesRead >= this->fDataSz) { | 36 if (bytesRead >= this->fDataSz) { |
| 34 return false; | 37 return false; |
| 35 } | 38 } |
| 36 | 39 |
| 37 // Read the zero terminator | 40 // Read the zero terminator |
| 38 ++bytesRead; | 41 ++bytesRead; |
| 39 ++value; | 42 ++value; |
| 40 | 43 |
| 41 size_t bytesLeft = this->fDataSz - bytesRead; | 44 size_t bytesLeft = this->fDataSz - bytesRead; |
| 42 this->fKey.set(key, bytesRead); | 45 |
| 46 // We ignore the null terminator when setting the string value. | |
| 47 this->fKey.set(key, bytesRead - 1); | |
| 43 if (bytesLeft > 0) { | 48 if (bytesLeft > 0) { |
| 44 this->fValue.set(value, bytesLeft); | 49 this->fValue.set(value, bytesLeft - 1); |
| 45 } else { | 50 } else { |
| 46 return false; | 51 return false; |
| 47 } | 52 } |
| 48 | 53 |
| 49 return true; | 54 return true; |
| 50 } | 55 } |
| 51 | 56 |
| 57 bool 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; | |
|
robertphillips
2014/06/05 19:12:21
I think we still can't use the "{ 0 }" notation.
krajcevski
2014/06/05 19:29:44
Done.
| |
| 85 uint8_t nullBuf[4] = { 0 }; | |
| 86 | |
| 87 size_t padding = bytesWrittenPadFour - bytesWritten; | |
| 88 SkASSERT(padding < 4); | |
| 89 | |
| 90 return strm->write(nullBuf, padding); | |
| 91 } | |
| 92 | |
| 52 uint32_t SkKTXFile::readInt(const uint8_t** buf, size_t* bytesLeft) const { | 93 uint32_t SkKTXFile::readInt(const uint8_t** buf, size_t* bytesLeft) const { |
| 53 SkASSERT(NULL != buf && NULL != bytesLeft); | 94 SkASSERT(NULL != buf && NULL != bytesLeft); |
| 54 | 95 |
| 55 uint32_t result; | 96 uint32_t result; |
| 56 | 97 |
| 57 if (*bytesLeft < 4) { | 98 if (*bytesLeft < 4) { |
| 58 SkASSERT(false); | 99 SkASSERT(false); |
| 59 return 0; | 100 return 0; |
| 60 } | 101 } |
| 61 | 102 |
| 62 memcpy(&result, *buf, 4); | 103 memcpy(&result, *buf, 4); |
| 63 *buf += 4; | 104 *buf += 4; |
| 64 | 105 |
| 65 if (fSwapBytes) { | 106 if (fSwapBytes) { |
| 66 SkEndianSwap32(result); | 107 SkEndianSwap32(result); |
| 67 } | 108 } |
| 68 | 109 |
| 69 *bytesLeft -= 4; | 110 *bytesLeft -= 4; |
| 70 | 111 |
| 71 return result; | 112 return result; |
| 72 } | 113 } |
| 73 | 114 |
|
robertphillips
2014/06/05 19:12:20
const SkString& key ?
krajcevski
2014/06/05 19:29:44
Done.
| |
| 115 SkString SkKTXFile::getValueForKey(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 | |
| 74 bool SkKTXFile::isETC1() const { | 126 bool SkKTXFile::isETC1() const { |
| 75 return this->valid() && GR_GL_COMPRESSED_RGB8_ETC1 == fHeader.fGLInternalFor mat; | 127 return this->valid() && GR_GL_COMPRESSED_RGB8_ETC1 == fHeader.fGLInternalFor mat; |
| 76 } | 128 } |
| 77 | 129 |
| 78 bool SkKTXFile::isRGBA8() const { | 130 bool SkKTXFile::isRGBA8() const { |
| 79 return this->valid() && GR_GL_RGBA8 == fHeader.fGLInternalFormat; | 131 return this->valid() && GR_GL_RGBA8 == fHeader.fGLInternalFormat; |
| 80 } | 132 } |
| 81 | 133 |
| 82 bool SkKTXFile::isRGB8() const { | 134 bool SkKTXFile::isRGB8() const { |
| 83 return this->valid() && GR_GL_RGB8 == fHeader.fGLInternalFormat; | 135 return this->valid() && GR_GL_RGB8 == fHeader.fGLInternalFormat; |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 233 // Read the KTX header and make sure it's valid. | 285 // Read the KTX header and make sure it's valid. |
| 234 unsigned char buf[KTX_FILE_IDENTIFIER_SIZE]; | 286 unsigned char buf[KTX_FILE_IDENTIFIER_SIZE]; |
| 235 bool largeEnough = | 287 bool largeEnough = |
| 236 stream->read((void*)buf, KTX_FILE_IDENTIFIER_SIZE) == KTX_FILE_IDENTIFIE R_SIZE; | 288 stream->read((void*)buf, KTX_FILE_IDENTIFIER_SIZE) == KTX_FILE_IDENTIFIE R_SIZE; |
| 237 stream->rewind(); | 289 stream->rewind(); |
| 238 if (!largeEnough) { | 290 if (!largeEnough) { |
| 239 return false; | 291 return false; |
| 240 } | 292 } |
| 241 return is_ktx(buf); | 293 return is_ktx(buf); |
| 242 } | 294 } |
| 295 | |
|
robertphillips
2014/06/05 19:12:20
_key -> cstrKey; _value -> cstrValue ?
krajcevski
2014/06/05 19:29:44
Done.
| |
| 296 SkKTXFile::KeyValue SkKTXFile::CreateKeyValue(const char *_key, const char *_val ue) { | |
| 297 SkString key(_key); | |
| 298 SkString value(_value); | |
| 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 | |
| 313 bool 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 | |
| 360 bool SkKTXFile::WriteBitmapToKTX(SkWStream* stream, const SkBitmap& bitmap) { | |
| 361 const SkBitmap::Config config = bitmap.config(); | |
| 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 (config) { | |
| 383 case SkBitmap::kIndex8_Config: | |
| 384 // There is a compressed format for this, but we don't support it ye t. | |
| 385 SkDebugf("Writing indexed bitmap to KTX unsupported.\n"); | |
| 386 // VVV fall through VVV | |
| 387 default: | |
| 388 case SkBitmap::kNo_Config: | |
| 389 // Bitmap hasn't been configured. | |
| 390 return false; | |
| 391 | |
| 392 case SkBitmap::kA8_Config: | |
| 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 SkBitmap::kRGB_565_Config: | |
| 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 SkBitmap::kARGB_4444_Config: | |
| 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 SkBitmap::kARGB_8888_Config: | |
| 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 (SkBitmap::kARGB_8888_Config == config) { | |
| 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 } | |
| OLD | NEW |