Revert "Remove all KTX support"
authorLeon Scroggins <scroggo@google.com>
Fri, 16 Dec 2016 13:51:59 +0000 (13:51 +0000)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Fri, 16 Dec 2016 13:52:09 +0000 (13:52 +0000)
This reverts commit ada12ab0557a1f540e775288e2ce44f64fcd8c7e.

Reason for revert: Google3 needs it:
https://test.corp.google.com/ui#id=OCL:142184832:BASE:142184975:1481839118985:32fde8ef

Original change's description:
> Remove all KTX support
>
> It is untested and unused.
>
> Change-Id: I010ff4ad942738f362d42a99af4edbbb1cb0cd71
> Reviewed-on: https://skia-review.googlesource.com/6142
> Commit-Queue: Leon Scroggins <scroggo@google.com>
> Reviewed-by: Mike Klein <mtklein@chromium.org>
> Reviewed-by: Robert Phillips <robertphillips@google.com>
>

TBR=mtklein@chromium.org,mtklein@google.com,robertphillips@google.com,scroggo@google.com
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

Change-Id: I1ea2f9487eb2212efbfcc514122792b70c9e8737
Reviewed-on: https://skia-review.googlesource.com/6181
Commit-Queue: Leon Scroggins <scroggo@google.com>
Reviewed-by: Leon Scroggins <scroggo@google.com>
BUILD.gn
gyp/gpu.gyp
gyp/ktx.gyp [new file with mode: 0644]
public.bzl
src/gpu/SkGr.cpp
third_party/ktx/ktx.cpp [new file with mode: 0644]
third_party/ktx/ktx.h [new file with mode: 0644]
tools/flags/SkCommonFlags.cpp

index 67fe9f5..a048092 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -110,6 +110,7 @@ config("skia_private") {
     "src/utils/win",
     "third_party/etc1",
     "third_party/gif",
+    "third_party/ktx",
   ]
 
   defines = [
@@ -589,6 +590,7 @@ component("skia") {
     "src/utils/mac/SkStream_mac.cpp",
     "third_party/etc1/etc1.cpp",
     "third_party/gif/SkGifImageReader.cpp",
+    "third_party/ktx/ktx.cpp",
   ]
 
   libs = []
index 634a458..8b50fda 100644 (file)
@@ -80,6 +80,7 @@
         'core.gyp:*',
         'utils.gyp:utils',
         'etc1.gyp:libetc1',
+        'ktx.gyp:libSkKTX',
         'sksl.gyp:sksl',
       ],
       'include_dirs': [
diff --git a/gyp/ktx.gyp b/gyp/ktx.gyp
new file mode 100644 (file)
index 0000000..e01ef1f
--- /dev/null
@@ -0,0 +1,34 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+  'variables': {
+    'skia_warnings_as_errors': 0,
+  },
+  'targets': [
+  {
+    'target_name': 'libSkKTX',
+    'type': 'static_library',
+    'include_dirs' : [
+      '../third_party/ktx',
+      '../include/gpu',
+      '../include/private',
+      '../src/core',
+      '../src/gpu',
+      '../src/utils',
+    ],
+    'sources': [
+      '../third_party/ktx/ktx.cpp',
+    ],
+    'dependencies': [
+      'core.gyp:*',
+      'etc1.gyp:libetc1',
+    ],
+    'direct_dependent_settings': {
+      'include_dirs': [
+        '../third_party/ktx',
+      ],
+    },
+  }],
+}
index 33542ea..5c2e09f 100644 (file)
@@ -86,6 +86,8 @@ BASE_SRCS_ALL = struct(
         "third_party/etc1/*.h",
         "third_party/gif/*.cpp",
         "third_party/gif/*.h",
+        "third_party/ktx/*.cpp",
+        "third_party/ktx/*.h",
     ],
     # Note: PRIVATE_HDRS_INCLUDE_LIST is excluded from BASE_SRCS_ALL here
     # because they are required to appear in srcs for some rules but hdrs for
@@ -383,6 +385,7 @@ INCLUDES = [
     "src/utils",
     "third_party/etc1",
     "third_party/gif",
+    "third_party/ktx",
 ]
 
 ################################################################################
index d1cecde..4147855 100644 (file)
@@ -40,6 +40,7 @@
 #include "effects/GrYUVEffect.h"
 
 #ifndef SK_IGNORE_ETC1_SUPPORT
+#  include "ktx.h"
 #  include "etc1.h"
 #endif
 
@@ -87,6 +88,22 @@ GrPixelConfig GrIsCompressedTextureDataSupported(GrContext* ctx, SkData* data,
 
         *outStartOfDataToUpload = bytes + ETC_PKM_HEADER_SIZE;
         return kETC1_GrPixelConfig;
+    } else if (SkKTXFile::is_ktx(bytes, data->size())) {
+        SkKTXFile ktx(data);
+
+        // Is it actually an ETC1 texture?
+        if (!ktx.isCompressedFormat(SkTextureCompressor::kETC1_Format)) {
+            return kUnknown_GrPixelConfig;
+        }
+
+        // Does the data match the dimensions of the bitmap? If not,
+        // then we don't know how to scale the image to match it...
+        if (ktx.width() != expectedW || ktx.height() != expectedH) {
+            return kUnknown_GrPixelConfig;
+        }
+
+        *outStartOfDataToUpload = ktx.pixelData();
+        return kETC1_GrPixelConfig;
     }
 #endif
     return kUnknown_GrPixelConfig;
diff --git a/third_party/ktx/ktx.cpp b/third_party/ktx/ktx.cpp
new file mode 100644 (file)
index 0000000..ba3ba1f
--- /dev/null
@@ -0,0 +1,565 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "ktx.h"
+#include "SkBitmap.h"
+#include "SkStream.h"
+#include "SkEndian.h"
+
+#include "gl/GrGLDefines.h"
+#include "GrConfig.h"
+
+#include "etc1.h"
+
+static inline uint32_t compressed_fmt_to_gl_define(SkTextureCompressor::Format fmt) {
+    static const uint32_t kGLDefineMap[SkTextureCompressor::kFormatCnt] = {
+        GR_GL_COMPRESSED_LUMINANCE_LATC1,      // kLATC_Format
+        GR_GL_COMPRESSED_R11_EAC,              // kR11_EAC_Format
+        GR_GL_COMPRESSED_ETC1_RGB8,            // kETC1_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_4x4,        // kASTC_4x4_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_5x4,        // kASTC_5x4_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_5x5,        // kASTC_5x5_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_6x5,        // kASTC_6x5_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_6x6,        // kASTC_6x6_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_8x5,        // kASTC_8x5_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_8x6,        // kASTC_8x6_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_8x8,        // kASTC_8x8_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_10x5,       // kASTC_10x5_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_10x6,       // kASTC_10x6_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_10x8,       // kASTC_10x8_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_10x10,      // kASTC_10x10_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_12x10,      // kASTC_12x10_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_12x12,      // kASTC_12x12_Format
+    };
+
+    GR_STATIC_ASSERT(0 == SkTextureCompressor::kLATC_Format);
+    GR_STATIC_ASSERT(1 == SkTextureCompressor::kR11_EAC_Format);
+    GR_STATIC_ASSERT(2 == SkTextureCompressor::kETC1_Format);
+    GR_STATIC_ASSERT(3 == SkTextureCompressor::kASTC_4x4_Format);
+    GR_STATIC_ASSERT(4 == SkTextureCompressor::kASTC_5x4_Format);
+    GR_STATIC_ASSERT(5 == SkTextureCompressor::kASTC_5x5_Format);
+    GR_STATIC_ASSERT(6 == SkTextureCompressor::kASTC_6x5_Format);
+    GR_STATIC_ASSERT(7 == SkTextureCompressor::kASTC_6x6_Format);
+    GR_STATIC_ASSERT(8 == SkTextureCompressor::kASTC_8x5_Format);
+    GR_STATIC_ASSERT(9 == SkTextureCompressor::kASTC_8x6_Format);
+    GR_STATIC_ASSERT(10 == SkTextureCompressor::kASTC_8x8_Format);
+    GR_STATIC_ASSERT(11 == SkTextureCompressor::kASTC_10x5_Format);
+    GR_STATIC_ASSERT(12 == SkTextureCompressor::kASTC_10x6_Format);
+    GR_STATIC_ASSERT(13 == SkTextureCompressor::kASTC_10x8_Format);
+    GR_STATIC_ASSERT(14 == SkTextureCompressor::kASTC_10x10_Format);
+    GR_STATIC_ASSERT(15 == SkTextureCompressor::kASTC_12x10_Format);
+    GR_STATIC_ASSERT(16 == SkTextureCompressor::kASTC_12x12_Format);
+    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kGLDefineMap) == SkTextureCompressor::kFormatCnt);
+
+    return kGLDefineMap[fmt];
+}
+
+#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
+};
+
+static const uint32_t kKTX_ENDIANNESS_CODE = 0x04030201;
+
+bool SkKTXFile::KeyValue::readKeyAndValue(const uint8_t* data) {
+    const char *key = reinterpret_cast<const char *>(data);
+    const char *value = key;
+
+    size_t bytesRead = 0;
+    while (*value != '\0' && bytesRead < this->fDataSz) {
+        ++bytesRead;
+        ++value;
+    }
+
+    // Error of some sort..
+    if (bytesRead >= this->fDataSz) {
+        return false;
+    }
+
+    // Read the zero terminator
+    ++bytesRead;
+    ++value;
+
+    size_t bytesLeft = this->fDataSz - 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 - 1);
+    } else {
+        return false;
+    }
+
+    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(buf && bytesLeft);
+
+    uint32_t result;
+
+    if (*bytesLeft < 4) {
+        SkASSERT(false);
+        return 0;
+    }
+
+    memcpy(&result, *buf, 4);
+    *buf += 4;
+
+    if (fSwapBytes) {
+        SkEndianSwap32(result);
+    }
+
+    *bytesLeft -= 4;
+
+    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::isCompressedFormat(SkTextureCompressor::Format fmt) const {
+    if (!this->valid()) {
+        return false;
+    }
+
+    // This has many aliases
+    bool isFmt = false;
+    if (fmt == SkTextureCompressor::kLATC_Format) {
+        isFmt = GR_GL_COMPRESSED_RED_RGTC1 == fHeader.fGLInternalFormat ||
+                GR_GL_COMPRESSED_3DC_X == fHeader.fGLInternalFormat;
+    }
+
+    return isFmt || compressed_fmt_to_gl_define(fmt) == fHeader.fGLInternalFormat;
+}
+
+bool SkKTXFile::isRGBA8() const {
+    return this->valid() && GR_GL_RGBA8 == fHeader.fGLInternalFormat;
+}
+
+bool SkKTXFile::isRGB8() const {
+    return this->valid() && GR_GL_RGB8 == fHeader.fGLInternalFormat;
+}
+
+bool SkKTXFile::readKTXFile(const uint8_t* data, size_t dataLen) {
+    const uint8_t *buf = data;
+    size_t bytesLeft = dataLen;
+
+    // Make sure original KTX header is there... this should have been checked
+    // already by a call to is_ktx()
+    SkASSERT(bytesLeft > KTX_FILE_IDENTIFIER_SIZE);
+    SkASSERT(0 == memcmp(KTX_FILE_IDENTIFIER, buf, KTX_FILE_IDENTIFIER_SIZE));
+    buf += KTX_FILE_IDENTIFIER_SIZE;
+    bytesLeft -= KTX_FILE_IDENTIFIER_SIZE;
+
+    // Read header, but first make sure that we have the proper space: we need
+    // two 32-bit ints: 1 for endianness, and another for the mandatory image
+    // size after the header.
+    if (bytesLeft < 8 + sizeof(Header)) {
+        return false;
+    }
+
+    uint32_t endianness = this->readInt(&buf, &bytesLeft);
+    fSwapBytes = kKTX_ENDIANNESS_CODE != endianness;
+
+    // Read header values
+    fHeader.fGLType                = this->readInt(&buf, &bytesLeft);
+    fHeader.fGLTypeSize            = this->readInt(&buf, &bytesLeft);
+    fHeader.fGLFormat              = this->readInt(&buf, &bytesLeft);
+    fHeader.fGLInternalFormat      = this->readInt(&buf, &bytesLeft);
+    fHeader.fGLBaseInternalFormat  = this->readInt(&buf, &bytesLeft);
+    fHeader.fPixelWidth            = this->readInt(&buf, &bytesLeft);
+    fHeader.fPixelHeight           = this->readInt(&buf, &bytesLeft);
+    fHeader.fPixelDepth            = this->readInt(&buf, &bytesLeft);
+    fHeader.fNumberOfArrayElements = this->readInt(&buf, &bytesLeft);
+    fHeader.fNumberOfFaces         = this->readInt(&buf, &bytesLeft);
+    fHeader.fNumberOfMipmapLevels  = this->readInt(&buf, &bytesLeft);
+    fHeader.fBytesOfKeyValueData   = this->readInt(&buf, &bytesLeft);
+
+    // Check for things that we understand...
+    {
+        // First, we only support compressed formats and single byte
+        // representations at the moment. If the internal format is
+        // compressed, the the GLType field in the header must be zero.
+        // In the future, we may support additional data types (such
+        // as GL_UNSIGNED_SHORT_5_6_5)
+        if (fHeader.fGLType != 0 && fHeader.fGLType != GR_GL_UNSIGNED_BYTE) {
+            return false;
+        }
+
+        // This means that for well-formatted KTX files, the glTypeSize
+        // field must be one...
+        if (fHeader.fGLTypeSize != 1) {
+            return false;
+        }
+
+        // We don't support 3D textures.
+        if (fHeader.fPixelDepth > 1) {
+            return false;
+        }
+
+        // We don't support texture arrays
+        if (fHeader.fNumberOfArrayElements > 1) {
+            return false;
+        }
+
+        // We don't support cube maps
+        if (fHeader.fNumberOfFaces > 1) {
+            return false;
+        }
+
+        // We don't support width and/or height <= 0
+        if (fHeader.fPixelWidth <= 0 || fHeader.fPixelHeight <= 0) {
+            return false;
+        }
+    }
+
+    // Make sure that we have enough bytes left for the key/value
+    // data according to what was said in the header.
+    if (bytesLeft < fHeader.fBytesOfKeyValueData) {
+        return false;
+    }
+
+    // Next read the key value pairs
+    size_t keyValueBytesRead = 0;
+    while (keyValueBytesRead < fHeader.fBytesOfKeyValueData) {
+        uint32_t keyValueBytes = this->readInt(&buf, &bytesLeft);
+        keyValueBytesRead += 4;
+
+        if (keyValueBytes > bytesLeft) {
+            return false;
+        }
+
+        KeyValue kv(keyValueBytes);
+        if (!kv.readKeyAndValue(buf)) {
+            return false;
+        }
+
+        fKeyValuePairs.push_back(kv);
+
+        uint32_t keyValueBytesPadded = (keyValueBytes + 3) & ~3;
+        buf += keyValueBytesPadded;
+        keyValueBytesRead += keyValueBytesPadded;
+        bytesLeft -= keyValueBytesPadded;
+    }
+
+    // Read the pixel data...
+    int mipmaps = SkMax32(fHeader.fNumberOfMipmapLevels, 1);
+    SkASSERT(mipmaps == 1);
+
+    int arrayElements = SkMax32(fHeader.fNumberOfArrayElements, 1);
+    SkASSERT(arrayElements == 1);
+
+    int faces = SkMax32(fHeader.fNumberOfFaces, 1);
+    SkASSERT(faces == 1);
+
+    int depth = SkMax32(fHeader.fPixelDepth, 1);
+    SkASSERT(depth == 1);
+
+    for (int mipmap = 0; mipmap < mipmaps; ++mipmap) {
+        // Make sure that we have at least 4 more bytes for the first image size
+        if (bytesLeft < 4) {
+            return false;
+        }
+
+        uint32_t imgSize = this->readInt(&buf, &bytesLeft);
+
+        // Truncated file.
+        if (bytesLeft < imgSize) {
+            return false;
+        }
+
+        // !FIXME! If support is ever added for cube maps then the padding
+        // needs to be taken into account here.
+        for (int arrayElement = 0; arrayElement < arrayElements; ++arrayElement) {
+            for (int face = 0; face < faces; ++face) {
+                for (int z = 0; z < depth; ++z) {
+                    PixelData pd(buf, imgSize);
+                    fPixelData.append(1, &pd);
+                }
+            }
+        }
+
+        uint32_t imgSizePadded = (imgSize + 3) & ~3;
+        buf += imgSizePadded;
+        bytesLeft -= imgSizePadded;
+    }
+
+    return bytesLeft == 0;
+}
+
+bool SkKTXFile::is_ktx(const uint8_t data[], size_t size) {
+    return size >= KTX_FILE_IDENTIFIER_SIZE &&
+           0 == memcmp(KTX_FILE_IDENTIFIER, data, KTX_FILE_IDENTIFIER_SIZE);
+}
+
+bool SkKTXFile::is_ktx(SkStreamRewindable* stream) {
+    // Read the KTX header and make sure it's valid.
+    unsigned char buf[KTX_FILE_IDENTIFIER_SIZE];
+    bool largeEnough =
+        stream->read((void*)buf, KTX_FILE_IDENTIFIER_SIZE) == KTX_FILE_IDENTIFIER_SIZE;
+    stream->rewind();
+    if (!largeEnough) {
+        return false;
+    }
+    return is_ktx(buf, KTX_FILE_IDENTIFIER_SIZE);
+}
+
+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_ETC1_RGB8;
+    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 pixmap'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::WritePixmapToKTX(SkWStream* stream, const SkPixmap& pixmap) {
+    const SkColorType ct = pixmap.colorType();
+
+    const int width = pixmap.width();
+    const int height = pixmap.height();
+    const uint8_t* src = reinterpret_cast<const uint8_t*>(pixmap.addr());
+    if (!src) {
+        return false;
+    }
+    const size_t rowBytes = pixmap.rowBytes();
+    const int bytesPerPixel = pixmap.info().bytesPerPixel();
+
+    // 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 pixmap's config.
+    Header hdr;
+    switch (ct) {
+        case kIndex_8_SkColorType:
+            // There is a compressed format for this, but we don't support it yet.
+            SkDebugf("Writing indexed pixmap to KTX unsupported.\n");
+            // VVV fall through VVV
+        default:
+        case kUnknown_SkColorType:
+            // Pixmap hasn't been configured.
+            return false;
+
+        case kAlpha_8_SkColorType:
+            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 kRGB_565_SkColorType:
+            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 kARGB_4444_SkColorType:
+            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 kN32_SkColorType:
+            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 = SkToU32(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
+    uint32_t dataSz = bytesPerPixel * width * height;
+
+    if (0 >= bytesPerPixel) {
+        return false;
+    }
+
+    // Write it into the buffer
+    if (!stream->write(&dataSz, 4)) {
+        return false;
+    }
+
+    // Write the pixel data...
+    const uint8_t* rowPtr = src;
+    if (kN32_SkColorType == ct) {
+        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 += rowBytes;
+        }
+    } else {
+        for (int i = 0; i < height; ++i) {
+            if (!stream->write(rowPtr, bytesPerPixel * width)) {
+                return false;
+            }
+            rowPtr += rowBytes;
+        }
+    }
+
+    return true;
+}
+
+bool SkKTXFile::WriteBitmapToKTX(SkWStream* stream, const SkBitmap& bitmap) {
+    SkAutoLockPixels autoLockPixels(bitmap);
+    SkPixmap pixmap;
+    return bitmap.peekPixels(&pixmap) && SkKTXFile::WritePixmapToKTX(stream, pixmap);
+}
diff --git a/third_party/ktx/ktx.h b/third_party/ktx/ktx.h
new file mode 100644 (file)
index 0000000..3aaea15
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkKTXFile_DEFINED
+#define SkKTXFile_DEFINED
+
+#include "SkData.h"
+#include "SkTextureCompressor.h"
+#include "SkTypes.h"
+#include "SkTDArray.h"
+#include "SkString.h"
+#include "SkRefCnt.h"
+
+class SkBitmap;
+class SkPixmap;
+class SkStreamRewindable;
+class SkWStream;
+
+// KTX Image File
+// ---
+// KTX is a general texture data storage file format ratified by the Khronos Group. As an
+// overview, a KTX file contains all of the appropriate values needed to fully specify a
+// texture in an OpenGL application, including the use of compressed data.
+//
+// A full format specification can be found here:
+// http://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
+
+class SkKTXFile {
+public:
+    // The ownership of the data remains with the caller. This class is intended
+    // to be used as a logical wrapper around the data in order to properly
+    // access the pixels.
+    SkKTXFile(SkData* data) : fData(data), fSwapBytes(false) {
+        data->ref();
+        fValid = this->readKTXFile(fData->bytes(), fData->size());
+    }
+
+    bool valid() const { return fValid; }
+
+    int width() const { return static_cast<int>(fHeader.fPixelWidth); }
+    int height() const { return static_cast<int>(fHeader.fPixelHeight); }
+
+    const uint8_t *pixelData(int mipmap = 0) const {
+        SkASSERT(!this->valid() || mipmap < fPixelData.count());
+        return this->valid() ? fPixelData[mipmap].data() : NULL;
+    }
+
+    // If the decoded KTX file has the following key, then it will
+    // return the associated value. If not found, the empty string
+    // is returned.
+    SkString getValueForKey(const SkString& key) const;
+
+    int numMipmaps() const { return static_cast<int>(fHeader.fNumberOfMipmapLevels); }
+
+    bool isCompressedFormat(SkTextureCompressor::Format fmt) const;
+    bool isRGBA8() const;
+    bool isRGB8() const;
+
+    static bool is_ktx(const uint8_t data[], size_t size);
+    static bool is_ktx(SkStreamRewindable* stream);
+
+    static bool WriteETC1ToKTX(SkWStream* stream, const uint8_t *etc1Data,
+                               uint32_t width, uint32_t height);
+    static bool WritePixmapToKTX(SkWStream* stream, const SkPixmap& pixmap);
+    static bool WriteBitmapToKTX(SkWStream* stream, const SkBitmap& bitmap);
+
+private:
+
+    // The blob holding the file data.
+    sk_sp<SkData> fData;
+
+    // This header captures all of the data that describes the format
+    // of the image data in a KTX file.
+    struct Header {
+        uint32_t fGLType;
+        uint32_t fGLTypeSize;
+        uint32_t fGLFormat;
+        uint32_t fGLInternalFormat;
+        uint32_t fGLBaseInternalFormat;
+        uint32_t fPixelWidth;
+        uint32_t fPixelHeight;
+        uint32_t fPixelDepth;
+        uint32_t fNumberOfArrayElements;
+        uint32_t fNumberOfFaces;
+        uint32_t fNumberOfMipmapLevels;
+        uint32_t fBytesOfKeyValueData;
+
+        Header() { memset(this, 0, sizeof(*this)); }
+    } fHeader;
+
+    // A Key Value pair stored in the KTX file. There may be
+    // arbitrarily many of these.
+    class KeyValue {
+    public:
+        KeyValue(size_t size) : fDataSz(size) { }
+        bool readKeyAndValue(const uint8_t *data);
+        size_t size() const { return fDataSz; }
+        const SkString& key() const { return fKey; }
+        const SkString& value() const { return fValue; }
+        bool writeKeyAndValueForKTX(SkWStream* strm);
+    private:
+        const size_t fDataSz;
+        SkString     fKey;
+        SkString     fValue;
+    };
+
+    static KeyValue CreateKeyValue(const char *key, const char *value);
+
+    // The pixel data for a single mipmap level in an image. Based on how
+    // the rest of the data is stored, this may be compressed, a cubemap, etc.
+    // The header will describe the format of this data.
+    class PixelData {
+    public:
+        PixelData(const uint8_t *ptr, size_t sz) : fDataSz(sz), fDataPtr(ptr) { }
+        const uint8_t *data() const { return fDataPtr; }
+        size_t dataSize() const { return fDataSz; }
+    private:
+        const size_t fDataSz;
+        const uint8_t *fDataPtr;
+    };
+
+    // This function is only called once from the constructor. It loads the data
+    // and populates the appropriate fields of this class
+    // (fKeyValuePairs, fPixelData, fSwapBytes)
+    bool readKTXFile(const uint8_t *data, size_t dataLen);
+
+    SkTArray<KeyValue> fKeyValuePairs;
+    SkTDArray<PixelData> fPixelData;
+    bool fValid;
+
+    // If the endianness of the platform is different than the file,
+    // then we need to do proper byte swapping.
+    bool fSwapBytes;
+
+    // Read an integer from a buffer, advance the buffer, and swap
+    // bytes if fSwapBytes is set
+    uint32_t readInt(const uint8_t** buf, size_t* bytesLeft) const;
+};
+
+#endif  // SkKTXFile_DEFINED
index 26b718f..52a5388 100644 (file)
@@ -72,8 +72,8 @@ bool CollectImages(SkCommandLineFlags::StringArray images, SkTArray<SkString>* o
     SkASSERT(output);
 
     static const char* const exts[] = {
-        "bmp", "gif", "jpg", "jpeg", "png", "webp", "astc", "wbmp", "ico",
-        "BMP", "GIF", "JPG", "JPEG", "PNG", "WEBP", "ASTC", "WBMP", "ICO",
+        "bmp", "gif", "jpg", "jpeg", "png", "webp", "ktx", "astc", "wbmp", "ico",
+        "BMP", "GIF", "JPG", "JPEG", "PNG", "WEBP", "KTX", "ASTC", "WBMP", "ICO",
 #ifdef SK_CODEC_DECODES_RAW
         "arw", "cr2", "dng", "nef", "nrw", "orf", "raf", "rw2", "pef", "srw",
         "ARW", "CR2", "DNG", "NEF", "NRW", "ORF", "RAF", "RW2", "PEF", "SRW",