DO NOT MERGE Tool for compressing/decompressing ETC1 textures.

The ETC1 texture format is commonly supported by
OpenGL ES 2.0-capable GPUs.

For historical reasons ETC1 texture files have the
default extension .PKM

This tool relies on the libETC1 library to
compress and decompress the image data.
diff --git a/tools/etc1tool/Android.mk b/tools/etc1tool/Android.mk
new file mode 100644
index 0000000..5536158
--- /dev/null
+++ b/tools/etc1tool/Android.mk
@@ -0,0 +1,40 @@
+# Copyright 2009 Google Inc. All Rights Reserved.
+#
+# Android.mk for etc1tool 
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := etc1tool.cpp
+
+LOCAL_C_INCLUDES += external/libpng
+LOCAL_C_INCLUDES += external/zlib
+LOCAL_C_INCLUDES += build/libs/host/include
+LOCAL_C_INCLUDES += frameworks/base/opengl/include
+
+#LOCAL_WHOLE_STATIC_LIBRARIES := 
+LOCAL_STATIC_LIBRARIES := \
+	libhost \
+	libutils \
+	libcutils \
+	libexpat \
+	libpng \
+	libETC1
+
+LOCAL_LDLIBS := -lz
+
+ifeq ($(HOST_OS),linux)
+LOCAL_LDLIBS += -lrt
+endif
+
+ifeq ($(HOST_OS),windows)
+ifeq ($(strip $(USE_CYGWIN),),)
+LOCAL_LDLIBS += -lws2_32
+endif
+endif
+
+LOCAL_MODULE := etc1tool
+
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/etc1tool/etc1tool.cpp b/tools/etc1tool/etc1tool.cpp
new file mode 100644
index 0000000..34bcbec
--- /dev/null
+++ b/tools/etc1tool/etc1tool.cpp
@@ -0,0 +1,582 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <png.h>
+#include <ETC1/etc1.h>
+
+
+int writePNGFile(const char* pOutput, png_uint_32 width, png_uint_32 height,
+        const png_bytep pImageData, png_uint_32 imageStride);
+
+const char* gpExeName;
+
+static
+void usage(char* message, ...) {
+    if (message) {
+        va_list ap;
+        va_start(ap, message);
+        vfprintf(stderr, message, ap);
+        va_end(ap);
+        fprintf(stderr, "\n\n");
+        fprintf(stderr, "usage:\n");
+    }
+    fprintf(
+            stderr,
+            "%s infile [--help | --encode | --encodeNoHeader | --decode] [--showDifference difffile] [-o outfile]\n",
+            gpExeName);
+    fprintf(stderr, "\tDefault is --encode\n");
+    fprintf(stderr, "\t\t--help           print this usage information.\n");
+    fprintf(stderr,
+            "\t\t--encode         create an ETC1 file from a PNG file.\n");
+    fprintf(
+            stderr,
+            "\t\t--encodeNoHeader create a raw ETC1 data file (without a header) from a PNG file.\n");
+    fprintf(stderr,
+            "\t\t--decode         create a PNG file from an ETC1 file.\n");
+    fprintf(stderr,
+            "\t\t--showDifference difffile    Write difference between original and encoded\n");
+    fprintf(stderr,
+            "\t\t                             image to difffile. (Only valid when encoding).\n");
+    fprintf(stderr,
+            "\tIf outfile is not specified, an outfile path is constructed from infile,\n");
+    fprintf(stderr, "\twith the apropriate suffix (.pkm or .png).\n");
+    exit(1);
+}
+
+// Returns non-zero if an error occured
+
+static
+int changeExtension(char* pPath, size_t pathCapacity, const char* pExtension) {
+    size_t pathLen = strlen(pPath);
+    size_t extensionLen = strlen(pExtension);
+    if (pathLen + extensionLen + 1 > pathCapacity) {
+        return -1;
+    }
+
+    // Check for '.' and '..'
+    if ((pathLen == 1 && pPath[0] == '.') || (pathLen == 2 && pPath[0] == '.'
+            && pPath[1] == '.') || (pathLen >= 2 && pPath[pathLen - 2] == '/'
+            && pPath[pathLen - 1] == '.') || (pathLen >= 3
+            && pPath[pathLen - 3] == '/' && pPath[pathLen - 2] == '.'
+            && pPath[pathLen - 1] == '.')) {
+        return -2;
+    }
+
+    int index;
+    for (index = pathLen - 1; index > 0; index--) {
+        char c = pPath[index];
+        if (c == '/') {
+            // No extension found. Append our extension.
+            strcpy(pPath + pathLen, pExtension);
+            return 0;
+        } else if (c == '.') {
+            strcpy(pPath + index, pExtension);
+            return 0;
+        }
+    }
+
+    // No extension or directory found. Append our extension
+    strcpy(pPath + pathLen, pExtension);
+    return 0;
+}
+
+void PNGAPI user_error_fn(png_structp png_ptr, png_const_charp message) {
+    fprintf(stderr, "PNG error: %s\n", message);
+}
+
+void PNGAPI user_warning_fn(png_structp png_ptr, png_const_charp message) {
+    fprintf(stderr, "PNG warning: %s\n", message);
+}
+
+// Return non-zero on error
+int fwrite_big_endian_uint16(png_uint_32 data, FILE* pOut) {
+    if (fputc(0xff & (data >> 8), pOut) == EOF) {
+        return -1;
+    }
+    if (fputc(0xff & data, pOut) == EOF) {
+        return -1;
+    }
+    return 0;
+}
+
+// Return non-zero on error
+int fread_big_endian_uint16(png_uint_32* data, FILE* pIn) {
+    int a, b;
+    if ((a = fgetc(pIn)) == EOF) {
+        return -1;
+    }
+    if ((b = fgetc(pIn)) == EOF) {
+        return -1;
+    }
+    *data = ((0xff & a) << 8) | (0xff & b);
+    return 0;
+}
+
+// Read a PNG file into a contiguous buffer.
+// Returns non-zero if an error occurred.
+// caller has to delete[] *ppImageData when done with the image.
+
+int read_PNG_File(const char* pInput, etc1_byte** ppImageData,
+        etc1_uint32* pWidth, etc1_uint32* pHeight) {
+    FILE* pIn = NULL;
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+    png_infop end_info = NULL;
+    png_bytep* row_pointers = NULL; // Does not need to be deallocated.
+    png_uint_32 width = 0;
+    png_uint_32 height = 0;
+    int result = -1;
+    etc1_byte* pSourceImage = 0;
+
+    if ((pIn = fopen(pInput, "rb")) == NULL) {
+        fprintf(stderr, "Could not open input file %s for reading: %d\n",
+                pInput, errno);
+        goto exit;
+    }
+
+    static const size_t PNG_HEADER_SIZE = 8;
+    png_byte pngHeader[PNG_HEADER_SIZE];
+    if (fread(pngHeader, 1, PNG_HEADER_SIZE, pIn) != PNG_HEADER_SIZE) {
+        fprintf(stderr, "Could not read PNG header from %s: %d\n", pInput,
+                errno);
+        goto exit;
+    }
+
+    if (png_sig_cmp(pngHeader, 0, PNG_HEADER_SIZE)) {
+        fprintf(stderr, "%s is not a PNG file.\n", pInput);
+        goto exit;
+    }
+
+    if (!(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+            (png_voidp) NULL, user_error_fn, user_warning_fn))) {
+        fprintf(stderr, "Could not initialize png read struct.\n");
+        goto exit;
+    }
+
+    if (!(info_ptr = png_create_info_struct(png_ptr))) {
+        fprintf(stderr, "Could not create info struct.\n");
+        goto exit;
+    }
+    if (!(end_info = png_create_info_struct(png_ptr))) {
+        fprintf(stderr, "Could not create end_info struct.\n");
+        goto exit;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        goto exit;
+    }
+
+    png_init_io(png_ptr, pIn);
+    png_set_sig_bytes(png_ptr, PNG_HEADER_SIZE);
+    png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY
+            | PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_STRIP_ALPHA
+            | PNG_TRANSFORM_PACKING, NULL);
+
+    row_pointers = png_get_rows(png_ptr, info_ptr);
+    {
+        int bit_depth, color_type;
+        png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
+                &color_type, NULL, NULL, NULL);
+    }
+
+    png_uint_32 stride = 3 * width;
+
+    pSourceImage = new etc1_byte[stride * height];
+    if (! pSourceImage) {
+        fprintf(stderr, "Out of memory.\n");
+        goto exit;
+    }
+
+    for (etc1_uint32 y = 0; y < height; y++) {
+        memcpy(pSourceImage + y * stride, row_pointers[y], stride);
+    }
+
+    *pWidth = width;
+    *pHeight = height;
+    *ppImageData = pSourceImage;
+
+    result = 0;
+    exit:
+    if (result) {
+        delete[] pSourceImage;
+    }
+    if (png_ptr) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+    }
+    if (pIn) {
+        fclose(pIn);
+    }
+
+    return result;
+}
+
+// Read a PNG file into a contiguous buffer.
+// Returns non-zero if an error occurred.
+// caller has to delete[] *ppImageData when done with the image.
+int readPKMFile(const char* pInput, etc1_byte** ppImageData,
+        etc1_uint32* pWidth, etc1_uint32* pHeight) {
+    int result = -1;
+    FILE* pIn = NULL;
+    etc1_byte header[ETC_PKM_HEADER_SIZE];
+    png_bytep pEncodedData = NULL;
+    png_bytep pImageData = NULL;
+
+    png_uint_32 width = 0;
+    png_uint_32 height = 0;
+    png_uint_32 stride = 0;
+    png_uint_32 encodedSize = 0;
+
+    if ((pIn = fopen(pInput, "rb")) == NULL) {
+        fprintf(stderr, "Could not open input file %s for reading: %d\n",
+                pInput, errno);
+        goto exit;
+    }
+
+    if (fread(header, sizeof(header), 1, pIn) != 1) {
+        fprintf(stderr, "Could not read header from input file %s: %d\n",
+                pInput, errno);
+        goto exit;
+    }
+
+    if (! etc1_pkm_is_valid(header)) {
+        fprintf(stderr, "Bad header PKM header for input file %s\n", pInput);
+        goto exit;
+    }
+
+    width = etc1_pkm_get_width(header);
+    height = etc1_pkm_get_height(header);
+    encodedSize = etc1_get_encoded_data_size(width, height);
+
+    pEncodedData = new png_byte[encodedSize];
+    if (!pEncodedData) {
+        fprintf(stderr, "Out of memory.\n");
+        goto exit;
+    }
+
+    if (fread(pEncodedData, encodedSize, 1, pIn) != 1) {
+        fprintf(stderr, "Could not read encoded data from input file %s: %d\n",
+                pInput, errno);
+        goto exit;
+    }
+
+    fclose(pIn);
+    pIn = NULL;
+
+    stride = width * 3;
+    pImageData = new png_byte[stride * height];
+    if (!pImageData) {
+        fprintf(stderr, "Out of memory.\n");
+        goto exit;
+    }
+
+    etc1_decode_image(pEncodedData, pImageData, width, height, 3, stride);
+
+    // Success
+    result = 0;
+    *ppImageData = pImageData;
+    pImageData = 0;
+    *pWidth = width;
+    *pHeight = height;
+
+    exit:
+    delete[] pEncodedData;
+    delete[] pImageData;
+    if (pIn) {
+        fclose(pIn);
+    }
+
+    return result;
+}
+
+
+// Encode the file.
+// Returns non-zero if an error occurred.
+
+int encode(const char* pInput, const char* pOutput, bool bEmitHeader, const char* pDiffFile) {
+    FILE* pOut = NULL;
+    etc1_uint32 width = 0;
+    etc1_uint32 height = 0;
+    etc1_uint32 encodedSize = 0;
+    int result = -1;
+    etc1_byte* pSourceImage = 0;
+    etc1_byte* pEncodedData = 0;
+    etc1_byte* pDiffImage = 0; // Used for differencing
+
+    if (read_PNG_File(pInput, &pSourceImage, &width, &height)) {
+        goto exit;
+    }
+
+    encodedSize = etc1_get_encoded_data_size(width, height);
+    pEncodedData = new etc1_byte[encodedSize];
+    if (!pEncodedData) {
+        fprintf(stderr, "Out of memory.\n");
+        goto exit;
+    }
+
+    etc1_encode_image(pSourceImage,
+            width, height, 3, width * 3, pEncodedData);
+
+    if ((pOut = fopen(pOutput, "wb")) == NULL) {
+        fprintf(stderr, "Could not open output file %s: %d\n", pOutput, errno);
+        goto exit;
+    }
+
+    if (bEmitHeader) {
+        etc1_byte header[ETC_PKM_HEADER_SIZE];
+        etc1_pkm_format_header(header, width, height);
+        if (fwrite(header, sizeof(header), 1, pOut) != 1) {
+            fprintf(stderr,
+                    "Could not write header output file %s: %d\n",
+                    pOutput, errno);
+            goto exit;
+        }
+    }
+
+    if (fwrite(pEncodedData, encodedSize, 1, pOut) != 1) {
+        fprintf(stderr,
+                "Could not write encoded data to output file %s: %d\n",
+                pOutput, errno);
+        goto exit;
+    }
+
+    fclose(pOut);
+    pOut = NULL;
+
+    if (pDiffFile) {
+        etc1_uint32 outWidth;
+        etc1_uint32 outHeight;
+        if (readPKMFile(pOutput, &pDiffImage, &outWidth, &outHeight)) {
+            goto exit;
+        }
+        if (outWidth != width || outHeight != height) {
+            fprintf(stderr, "Output file has incorrect bounds: %u, %u != %u, %u\n",
+                    outWidth, outHeight, width, height);
+            goto exit;
+        }
+        const etc1_byte* pSrc = pSourceImage;
+        etc1_byte* pDest = pDiffImage;
+        etc1_uint32 size = width * height * 3;
+        for (etc1_uint32 i = 0; i < size; i++) {
+            int diff = *pSrc++ - *pDest;
+            diff *= diff;
+            diff <<= 3;
+            if (diff < 0) {
+                diff = 0;
+            } else if (diff > 255) {
+                diff = 255;
+            }
+            *pDest++ = (png_byte) diff;
+        }
+        writePNGFile(pDiffFile, outWidth, outHeight, pDiffImage, 3 * outWidth);
+    }
+
+    // Success
+    result = 0;
+
+    exit:
+    delete[] pSourceImage;
+    delete[] pEncodedData;
+    delete[] pDiffImage;
+
+    if (pOut) {
+        fclose(pOut);
+    }
+    return result;
+}
+
+int writePNGFile(const char* pOutput, png_uint_32 width, png_uint_32 height,
+        const png_bytep pImageData, png_uint_32 imageStride) {
+    int result = -1;
+    FILE* pOut = NULL;
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+
+    if (!(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+            (png_voidp) NULL, user_error_fn, user_warning_fn)) || !(info_ptr
+            = png_create_info_struct(png_ptr))) {
+        fprintf(stderr, "Could not initialize PNG library for writing.\n");
+        goto exit;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        goto exit;
+    }
+
+    if ((pOut = fopen(pOutput, "wb")) == NULL) {
+        fprintf(stderr, "Could not open output file %s: %d\n", pOutput, errno);
+        goto exit;
+    }
+
+    png_init_io(png_ptr, pOut);
+
+    png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
+            PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+            PNG_FILTER_TYPE_DEFAULT);
+
+    png_write_info(png_ptr, info_ptr);
+
+    for (png_uint_32 y = 0; y < height; y++) {
+        png_write_row(png_ptr, pImageData + y * imageStride);
+    }
+    png_write_end(png_ptr, info_ptr);
+
+    result = 0;
+
+    exit: if (png_ptr) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+    }
+
+    if (pOut) {
+        fclose(pOut);
+    }
+    return result;
+}
+
+int decode(const char* pInput, const char* pOutput) {
+    int result = -1;
+    png_bytep pImageData = NULL;
+    etc1_uint32 width = 0;
+    etc1_uint32 height = 0;
+
+    if (readPKMFile(pInput, &pImageData, &width, &height)) {
+        goto exit;
+    }
+
+    if (writePNGFile(pOutput, width, height, pImageData, width * 3)) {
+        goto exit;
+    }
+
+    // Success
+    result = 0;
+
+    exit: delete[] pImageData;
+
+    return result;
+}
+
+void multipleEncodeDecodeCheck(bool* pbEncodeDecodeSeen) {
+    if (*pbEncodeDecodeSeen) {
+        usage("At most one occurrence of --encode --encodeNoHeader or --decode is allowed.\n");
+    }
+    *pbEncodeDecodeSeen = true;
+}
+
+int main(int argc, char** argv) {
+    gpExeName = argv[0];
+    const char* pInput = NULL;
+    const char* pOutput = NULL;
+    const char* pDiffFile = NULL;
+    char* pOutputFileBuff = NULL;
+
+    bool bEncodeDecodeSeen = false;
+    bool bEncode = false;
+    bool bEncodeHeader = false;
+    bool bDecode = false;
+    bool bShowDifference = false;
+
+    for (int i = 1; i < argc; i++) {
+        const char* pArg = argv[i];
+        if (pArg[0] == '-') {
+            char c = pArg[1];
+            switch (c) {
+            case 'o':
+                if (pOutput != NULL) {
+                    usage("Only one -o flag allowed.");
+                }
+                if (i + 1 >= argc) {
+                    usage("Expected outfile after -o");
+                }
+                pOutput = argv[++i];
+                break;
+            case '-':
+                if (strcmp(pArg, "--encode") == 0) {
+                    multipleEncodeDecodeCheck(&bEncodeDecodeSeen);
+                    bEncode = true;
+                    bEncodeHeader = true;
+                } else if (strcmp(pArg, "--encodeNoHeader") == 0) {
+                    multipleEncodeDecodeCheck(&bEncodeDecodeSeen);
+                    bEncode = true;
+                    bEncodeHeader = false;
+                } else if (strcmp(pArg, "--decode") == 0) {
+                    multipleEncodeDecodeCheck(&bEncodeDecodeSeen);
+                    bDecode = true;
+                } else if (strcmp(pArg, "--showDifference") == 0) {
+                    if (bShowDifference) {
+                        usage("Only one --showDifference option allowed.\n");
+                    }
+                    bShowDifference = true;
+                    if (i + 1 >= argc) {
+                        usage("Expected difffile after --showDifference");
+                    }
+                    pDiffFile = argv[++i];
+                } else if (strcmp(pArg, "--help") == 0) {
+                    usage( NULL);
+                } else {
+                    usage("Unknown flag %s", pArg);
+                }
+
+                break;
+            default:
+                usage("Unknown flag %s", pArg);
+                break;
+            }
+        } else {
+            if (pInput != NULL) {
+                usage(
+                        "Only one input file allowed. Already have %s, now see %s",
+                        pInput, pArg);
+            }
+            pInput = pArg;
+        }
+    }
+
+    if (!bEncodeDecodeSeen) {
+        bEncode = true;
+        bEncodeHeader = true;
+    }
+    if ((! bEncode) && bShowDifference) {
+        usage("--showDifference is only valid when encoding.");
+    }
+
+    if (!pInput) {
+        usage("Expected an input file.");
+    }
+
+    if (!pOutput) {
+        const char* kDefaultExtension = bEncode ? ".pkm" : ".png";
+        size_t buffSize = strlen(pInput) + strlen(kDefaultExtension) + 1;
+        pOutputFileBuff = new char[buffSize];
+        strcpy(pOutputFileBuff, pInput);
+        if (changeExtension(pOutputFileBuff, buffSize, kDefaultExtension)) {
+            usage("Could not change extension of input file name: %s\n", pInput);
+        }
+        pOutput = pOutputFileBuff;
+    }
+
+    if (bEncode) {
+        encode(pInput, pOutput, bEncodeHeader, pDiffFile);
+    } else {
+        decode(pInput, pOutput);
+    }
+
+    delete[] pOutputFileBuff;
+
+    return 0;
+}