| Index: third_party/ktx/ktx.cpp
|
| diff --git a/third_party/ktx/ktx.cpp b/third_party/ktx/ktx.cpp
|
| index 15c44fcd350fc0f5568be38277b365e8beaaa349..5eaadef3bddfeee9584ae7d4e8efcf971af32f99 100644
|
| --- a/third_party/ktx/ktx.cpp
|
| +++ b/third_party/ktx/ktx.cpp
|
| @@ -7,11 +7,14 @@
|
| */
|
|
|
| #include "ktx.h"
|
| +#include "SkBitmap.h"
|
| #include "SkStream.h"
|
| #include "SkEndian.h"
|
|
|
| #include "gl/GrGLDefines.h"
|
|
|
| +#include "etc1.h"
|
| +
|
| #define KTX_FILE_IDENTIFIER_SIZE 12
|
| static const uint8_t KTX_FILE_IDENTIFIER[KTX_FILE_IDENTIFIER_SIZE] = {
|
| 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
|
| @@ -39,9 +42,11 @@ bool SkKTXFile::KeyValue::readKeyAndValue(const uint8_t* data) {
|
| ++value;
|
|
|
| size_t bytesLeft = this->fDataSz - bytesRead;
|
| - this->fKey.set(key, bytesRead);
|
| +
|
| + // We ignore the null terminator when setting the string value.
|
| + this->fKey.set(key, bytesRead - 1);
|
| if (bytesLeft > 0) {
|
| - this->fValue.set(value, bytesLeft);
|
| + this->fValue.set(value, bytesLeft - 1);
|
| } else {
|
| return false;
|
| }
|
| @@ -49,6 +54,42 @@ bool SkKTXFile::KeyValue::readKeyAndValue(const uint8_t* data) {
|
| return true;
|
| }
|
|
|
| +bool SkKTXFile::KeyValue::writeKeyAndValueForKTX(SkWStream* strm) {
|
| + size_t bytesWritten = 0;
|
| + if (!strm->write(&(this->fDataSz), 4)) {
|
| + return false;
|
| + }
|
| +
|
| + bytesWritten += 4;
|
| +
|
| + // Here we know that C-strings must end with a null terminating
|
| + // character, so when we get a c_str(), it will have as many
|
| + // bytes of data as size() returns plus a zero, so we just
|
| + // write size() + 1 bytes into the stream.
|
| +
|
| + size_t keySize = this->fKey.size() + 1;
|
| + if (!strm->write(this->fKey.c_str(), keySize)) {
|
| + return false;
|
| + }
|
| +
|
| + bytesWritten += keySize;
|
| +
|
| + size_t valueSize = this->fValue.size() + 1;
|
| + if (!strm->write(this->fValue.c_str(), valueSize)) {
|
| + return false;
|
| + }
|
| +
|
| + bytesWritten += valueSize;
|
| +
|
| + size_t bytesWrittenPadFour = (bytesWritten + 3) & ~3;
|
| + uint8_t nullBuf[4] = { 0, 0, 0, 0 };
|
| +
|
| + size_t padding = bytesWrittenPadFour - bytesWritten;
|
| + SkASSERT(padding < 4);
|
| +
|
| + return strm->write(nullBuf, padding);
|
| +}
|
| +
|
| uint32_t SkKTXFile::readInt(const uint8_t** buf, size_t* bytesLeft) const {
|
| SkASSERT(NULL != buf && NULL != bytesLeft);
|
|
|
| @@ -71,6 +112,17 @@ uint32_t SkKTXFile::readInt(const uint8_t** buf, size_t* bytesLeft) const {
|
| return result;
|
| }
|
|
|
| +SkString SkKTXFile::getValueForKey(const SkString& key) const {
|
| + const KeyValue *begin = this->fKeyValuePairs.begin();
|
| + const KeyValue *end = this->fKeyValuePairs.end();
|
| + for (const KeyValue *kv = begin; kv != end; ++kv) {
|
| + if (kv->key() == key) {
|
| + return kv->value();
|
| + }
|
| + }
|
| + return SkString();
|
| +}
|
| +
|
| bool SkKTXFile::isETC1() const {
|
| return this->valid() && GR_GL_COMPRESSED_RGB8_ETC1 == fHeader.fGLInternalFormat;
|
| }
|
| @@ -240,3 +292,208 @@ bool SkKTXFile::is_ktx(SkStreamRewindable* stream) {
|
| }
|
| return is_ktx(buf);
|
| }
|
| +
|
| +SkKTXFile::KeyValue SkKTXFile::CreateKeyValue(const char *cstrKey, const char *cstrValue) {
|
| + SkString key(cstrKey);
|
| + SkString value(cstrValue);
|
| +
|
| + // Size of buffer is length of string plus the null terminators...
|
| + size_t size = key.size() + 1 + value.size() + 1;
|
| +
|
| + SkAutoSMalloc<256> buf(size);
|
| + uint8_t* kvBuf = reinterpret_cast<uint8_t*>(buf.get());
|
| + memcpy(kvBuf, key.c_str(), key.size() + 1);
|
| + memcpy(kvBuf + key.size() + 1, value.c_str(), value.size() + 1);
|
| +
|
| + KeyValue kv(size);
|
| + SkAssertResult(kv.readKeyAndValue(kvBuf));
|
| + return kv;
|
| +}
|
| +
|
| +bool SkKTXFile::WriteETC1ToKTX(SkWStream* stream, const uint8_t *etc1Data,
|
| + uint32_t width, uint32_t height) {
|
| + // First thing's first, write out the magic identifier and endianness...
|
| + if (!stream->write(KTX_FILE_IDENTIFIER, KTX_FILE_IDENTIFIER_SIZE)) {
|
| + return false;
|
| + }
|
| +
|
| + if (!stream->write(&kKTX_ENDIANNESS_CODE, 4)) {
|
| + return false;
|
| + }
|
| +
|
| + Header hdr;
|
| + hdr.fGLType = 0;
|
| + hdr.fGLTypeSize = 1;
|
| + hdr.fGLFormat = 0;
|
| + hdr.fGLInternalFormat = GR_GL_COMPRESSED_RGB8_ETC1;
|
| + hdr.fGLBaseInternalFormat = GR_GL_RGB;
|
| + hdr.fPixelWidth = width;
|
| + hdr.fPixelHeight = height;
|
| + hdr.fNumberOfArrayElements = 0;
|
| + hdr.fNumberOfFaces = 1;
|
| + hdr.fNumberOfMipmapLevels = 1;
|
| +
|
| + // !FIXME! The spec suggests that we put KTXOrientation as a
|
| + // key value pair in the header, but that means that we'd have to
|
| + // pipe through the bitmap's orientation to properly do that.
|
| + hdr.fBytesOfKeyValueData = 0;
|
| +
|
| + // Write the header
|
| + if (!stream->write(&hdr, sizeof(hdr))) {
|
| + return false;
|
| + }
|
| +
|
| + // Write the size of the image data
|
| + etc1_uint32 dataSize = etc1_get_encoded_data_size(width, height);
|
| + if (!stream->write(&dataSize, 4)) {
|
| + return false;
|
| + }
|
| +
|
| + // Write the actual image data
|
| + if (!stream->write(etc1Data, dataSize)) {
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool SkKTXFile::WriteBitmapToKTX(SkWStream* stream, const SkBitmap& bitmap) {
|
| + const SkBitmap::Config config = bitmap.config();
|
| + SkAutoLockPixels alp(bitmap);
|
| +
|
| + const int width = bitmap.width();
|
| + const int height = bitmap.width();
|
| + const uint8_t* src = reinterpret_cast<uint8_t*>(bitmap.getPixels());
|
| + if (NULL == bitmap.getPixels()) {
|
| + return false;
|
| + }
|
| +
|
| + // First thing's first, write out the magic identifier and endianness...
|
| + if (!stream->write(KTX_FILE_IDENTIFIER, KTX_FILE_IDENTIFIER_SIZE) ||
|
| + !stream->write(&kKTX_ENDIANNESS_CODE, 4)) {
|
| + return false;
|
| + }
|
| +
|
| + // Collect our key/value pairs...
|
| + SkTArray<KeyValue> kvPairs;
|
| +
|
| + // Next, write the header based on the bitmap's config.
|
| + Header hdr;
|
| + switch (config) {
|
| + case SkBitmap::kIndex8_Config:
|
| + // There is a compressed format for this, but we don't support it yet.
|
| + SkDebugf("Writing indexed bitmap to KTX unsupported.\n");
|
| + // VVV fall through VVV
|
| + default:
|
| + case SkBitmap::kNo_Config:
|
| + // Bitmap hasn't been configured.
|
| + return false;
|
| +
|
| + case SkBitmap::kA8_Config:
|
| + hdr.fGLType = GR_GL_UNSIGNED_BYTE;
|
| + hdr.fGLTypeSize = 1;
|
| + hdr.fGLFormat = GR_GL_RED;
|
| + hdr.fGLInternalFormat = GR_GL_R8;
|
| + hdr.fGLBaseInternalFormat = GR_GL_RED;
|
| + break;
|
| +
|
| + case SkBitmap::kRGB_565_Config:
|
| + hdr.fGLType = GR_GL_UNSIGNED_SHORT_5_6_5;
|
| + hdr.fGLTypeSize = 2;
|
| + hdr.fGLFormat = GR_GL_RGB;
|
| + hdr.fGLInternalFormat = GR_GL_RGB;
|
| + hdr.fGLBaseInternalFormat = GR_GL_RGB;
|
| + break;
|
| +
|
| + case SkBitmap::kARGB_4444_Config:
|
| + hdr.fGLType = GR_GL_UNSIGNED_SHORT_4_4_4_4;
|
| + hdr.fGLTypeSize = 2;
|
| + hdr.fGLFormat = GR_GL_RGBA;
|
| + hdr.fGLInternalFormat = GR_GL_RGBA4;
|
| + hdr.fGLBaseInternalFormat = GR_GL_RGBA;
|
| + kvPairs.push_back(CreateKeyValue("KTXPremultipliedAlpha", "True"));
|
| + break;
|
| +
|
| + case SkBitmap::kARGB_8888_Config:
|
| + hdr.fGLType = GR_GL_UNSIGNED_BYTE;
|
| + hdr.fGLTypeSize = 1;
|
| + hdr.fGLFormat = GR_GL_RGBA;
|
| + hdr.fGLInternalFormat = GR_GL_RGBA8;
|
| + hdr.fGLBaseInternalFormat = GR_GL_RGBA;
|
| + kvPairs.push_back(CreateKeyValue("KTXPremultipliedAlpha", "True"));
|
| + break;
|
| + }
|
| +
|
| + // Everything else in the header is shared.
|
| + hdr.fPixelWidth = width;
|
| + hdr.fPixelHeight = height;
|
| + hdr.fNumberOfArrayElements = 0;
|
| + hdr.fNumberOfFaces = 1;
|
| + hdr.fNumberOfMipmapLevels = 1;
|
| +
|
| + // Calculate the key value data size
|
| + hdr.fBytesOfKeyValueData = 0;
|
| + for (KeyValue *kv = kvPairs.begin(); kv != kvPairs.end(); ++kv) {
|
| + // Key value size is the size of the key value data,
|
| + // four bytes for saying how big the key value size is
|
| + // and then additional bytes for padding to four byte boundary
|
| + size_t kvsize = kv->size();
|
| + kvsize += 4;
|
| + kvsize = (kvsize + 3) & ~3;
|
| + hdr.fBytesOfKeyValueData += kvsize;
|
| + }
|
| +
|
| + // Write the header
|
| + if (!stream->write(&hdr, sizeof(hdr))) {
|
| + return false;
|
| + }
|
| +
|
| + // Write out each key value pair
|
| + for (KeyValue *kv = kvPairs.begin(); kv != kvPairs.end(); ++kv) {
|
| + if (!kv->writeKeyAndValueForKTX(stream)) {
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + // Calculate the size of the data
|
| + int bpp = bitmap.bytesPerPixel();
|
| + uint32_t dataSz = bpp * width * height;
|
| +
|
| + if (0 >= bpp) {
|
| + return false;
|
| + }
|
| +
|
| + // Write it into the buffer
|
| + if (!stream->write(&dataSz, 4)) {
|
| + return false;
|
| + }
|
| +
|
| + // Write the pixel data...
|
| + const uint8_t* rowPtr = src;
|
| + if (SkBitmap::kARGB_8888_Config == config) {
|
| + for (int j = 0; j < height; ++j) {
|
| + const uint32_t* pixelsPtr = reinterpret_cast<const uint32_t*>(rowPtr);
|
| + for (int i = 0; i < width; ++i) {
|
| + uint32_t pixel = pixelsPtr[i];
|
| + uint8_t dstPixel[4];
|
| + dstPixel[0] = pixel >> SK_R32_SHIFT;
|
| + dstPixel[1] = pixel >> SK_G32_SHIFT;
|
| + dstPixel[2] = pixel >> SK_B32_SHIFT;
|
| + dstPixel[3] = pixel >> SK_A32_SHIFT;
|
| + if (!stream->write(dstPixel, 4)) {
|
| + return false;
|
| + }
|
| + }
|
| + rowPtr += bitmap.rowBytes();
|
| + }
|
| + } else {
|
| + for (int i = 0; i < height; ++i) {
|
| + if (!stream->write(rowPtr, bpp*width)) {
|
| + return false;
|
| + }
|
| + rowPtr += bitmap.rowBytes();
|
| + }
|
| + }
|
| +
|
| + return true;
|
| +}
|
|
|