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 |