Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(546)

Side by Side Diff: third_party/ktx/ktx.cpp

Issue 312353003: Initial KTX encoder (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Code review changes Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« src/images/SkImageDecoder_ktx.cpp ('K') | « third_party/ktx/ktx.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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;
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
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
115 SkString 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
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
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
296 SkKTXFile::KeyValue SkKTXFile::CreateKeyValue(const char *cstrKey, const char *c strValue) {
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
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 }
OLDNEW
« src/images/SkImageDecoder_ktx.cpp ('K') | « third_party/ktx/ktx.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698